first commit
This commit is contained in:
@@ -0,0 +1,181 @@
|
||||
<?php
|
||||
/**
|
||||
* WP Async Request
|
||||
*
|
||||
* @package WP-Background-Processing
|
||||
*/
|
||||
|
||||
/**
|
||||
* Abstract Imagify_WP_Async_Request class.
|
||||
*
|
||||
* @abstract
|
||||
*/
|
||||
abstract class Imagify_WP_Async_Request {
|
||||
|
||||
/**
|
||||
* Prefix
|
||||
*
|
||||
* (default value: 'wp')
|
||||
*
|
||||
* @var string
|
||||
* @access protected
|
||||
*/
|
||||
protected $prefix = 'wp';
|
||||
|
||||
/**
|
||||
* Action
|
||||
*
|
||||
* (default value: 'async_request')
|
||||
*
|
||||
* @var string
|
||||
* @access protected
|
||||
*/
|
||||
protected $action = 'async_request';
|
||||
|
||||
/**
|
||||
* Identifier
|
||||
*
|
||||
* @var mixed
|
||||
* @access protected
|
||||
*/
|
||||
protected $identifier;
|
||||
|
||||
/**
|
||||
* Data
|
||||
*
|
||||
* (default value: array())
|
||||
*
|
||||
* @var array
|
||||
* @access protected
|
||||
*/
|
||||
protected $data = array();
|
||||
|
||||
/**
|
||||
* Initiate new async request
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->identifier = $this->prefix . '_' . $this->action;
|
||||
|
||||
add_action( 'wp_ajax_' . $this->identifier, array( $this, 'maybe_handle' ) );
|
||||
add_action( 'wp_ajax_nopriv_' . $this->identifier, array( $this, 'maybe_handle' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set data used during the request
|
||||
*
|
||||
* @param array $data Data.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function data( $data ) {
|
||||
$this->data = $data;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch the async request
|
||||
*
|
||||
* @return array|WP_Error
|
||||
*/
|
||||
public function dispatch() {
|
||||
$url = add_query_arg( $this->get_query_args(), $this->get_query_url() );
|
||||
$args = $this->get_post_args();
|
||||
|
||||
return wp_remote_post( esc_url_raw( $url ), $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get query args
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_query_args() {
|
||||
if ( property_exists( $this, 'query_args' ) ) {
|
||||
return $this->query_args;
|
||||
}
|
||||
|
||||
$args = array(
|
||||
'action' => $this->identifier,
|
||||
'nonce' => wp_create_nonce( $this->identifier ),
|
||||
);
|
||||
|
||||
/**
|
||||
* Filters the post arguments used during an async request.
|
||||
*
|
||||
* @param array $url
|
||||
*/
|
||||
return apply_filters( $this->identifier . '_query_args', $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get query URL
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_query_url() {
|
||||
if ( property_exists( $this, 'query_url' ) ) {
|
||||
return $this->query_url;
|
||||
}
|
||||
|
||||
$url = admin_url( 'admin-ajax.php' );
|
||||
|
||||
/**
|
||||
* Filters the post arguments used during an async request.
|
||||
*
|
||||
* @param string $url
|
||||
*/
|
||||
return apply_filters( $this->identifier . '_query_url', $url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get post args
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_post_args() {
|
||||
if ( property_exists( $this, 'post_args' ) ) {
|
||||
return $this->post_args;
|
||||
}
|
||||
|
||||
$args = array(
|
||||
'timeout' => 0.01,
|
||||
'blocking' => false,
|
||||
'body' => $this->data,
|
||||
'cookies' => $_COOKIE,
|
||||
'sslverify' => apply_filters( 'https_local_ssl_verify', false ),
|
||||
);
|
||||
|
||||
/**
|
||||
* Filters the post arguments used during an async request.
|
||||
*
|
||||
* @param array $args
|
||||
*/
|
||||
return apply_filters( $this->identifier . '_post_args', $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe handle
|
||||
*
|
||||
* Check for correct nonce and pass to handler.
|
||||
*/
|
||||
public function maybe_handle() {
|
||||
// Don't lock up other requests while processing
|
||||
session_write_close();
|
||||
|
||||
check_ajax_referer( $this->identifier, 'nonce' );
|
||||
|
||||
$this->handle();
|
||||
|
||||
wp_die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle
|
||||
*
|
||||
* Override this method to perform any actions required
|
||||
* during the async request.
|
||||
*/
|
||||
abstract protected function handle();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,505 @@
|
||||
<?php
|
||||
/**
|
||||
* WP Background Process
|
||||
*
|
||||
* @package WP-Background-Processing
|
||||
*/
|
||||
|
||||
/**
|
||||
* Abstract Imagify_WP_Background_Process class.
|
||||
*
|
||||
* @abstract
|
||||
* @extends Imagify_WP_Async_Request
|
||||
*/
|
||||
abstract class Imagify_WP_Background_Process extends Imagify_WP_Async_Request {
|
||||
|
||||
/**
|
||||
* Action
|
||||
*
|
||||
* (default value: 'background_process')
|
||||
*
|
||||
* @var string
|
||||
* @access protected
|
||||
*/
|
||||
protected $action = 'background_process';
|
||||
|
||||
/**
|
||||
* Start time of current process.
|
||||
*
|
||||
* (default value: 0)
|
||||
*
|
||||
* @var int
|
||||
* @access protected
|
||||
*/
|
||||
protected $start_time = 0;
|
||||
|
||||
/**
|
||||
* Cron_hook_identifier
|
||||
*
|
||||
* @var mixed
|
||||
* @access protected
|
||||
*/
|
||||
protected $cron_hook_identifier;
|
||||
|
||||
/**
|
||||
* Cron_interval_identifier
|
||||
*
|
||||
* @var mixed
|
||||
* @access protected
|
||||
*/
|
||||
protected $cron_interval_identifier;
|
||||
|
||||
/**
|
||||
* Initiate new background process
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
|
||||
$this->cron_hook_identifier = $this->identifier . '_cron';
|
||||
$this->cron_interval_identifier = $this->identifier . '_cron_interval';
|
||||
|
||||
add_action( $this->cron_hook_identifier, array( $this, 'handle_cron_healthcheck' ) );
|
||||
add_filter( 'cron_schedules', array( $this, 'schedule_cron_healthcheck' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function dispatch() {
|
||||
// Schedule the cron healthcheck.
|
||||
$this->schedule_event();
|
||||
|
||||
// Perform remote post.
|
||||
return parent::dispatch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Push to queue
|
||||
*
|
||||
* @param mixed $data Data.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function push_to_queue( $data ) {
|
||||
$this->data[] = $data;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save queue
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function save() {
|
||||
$key = $this->generate_key();
|
||||
|
||||
if ( ! empty( $this->data ) ) {
|
||||
update_site_option( $key, $this->data );
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update queue
|
||||
*
|
||||
* @param string $key Key.
|
||||
* @param array $data Data.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function update( $key, $data ) {
|
||||
if ( ! empty( $data ) ) {
|
||||
update_site_option( $key, $data );
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete queue
|
||||
*
|
||||
* @param string $key Key.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function delete( $key ) {
|
||||
delete_site_option( $key );
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate key
|
||||
*
|
||||
* Generates a unique key based on microtime. Queue items are
|
||||
* given a unique key so that they can be merged upon save.
|
||||
*
|
||||
* @param int $length Length.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function generate_key( $length = 64 ) {
|
||||
$unique = md5( microtime() . rand() );
|
||||
$prepend = $this->identifier . '_batch_';
|
||||
|
||||
return substr( $prepend . $unique, 0, $length );
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe process queue
|
||||
*
|
||||
* Checks whether data exists within the queue and that
|
||||
* the process is not already running.
|
||||
*/
|
||||
public function maybe_handle() {
|
||||
// Don't lock up other requests while processing
|
||||
session_write_close();
|
||||
|
||||
if ( $this->is_process_running() ) {
|
||||
// Background process already running.
|
||||
wp_die();
|
||||
}
|
||||
|
||||
if ( $this->is_queue_empty() ) {
|
||||
// No data to process.
|
||||
wp_die();
|
||||
}
|
||||
|
||||
check_ajax_referer( $this->identifier, 'nonce' );
|
||||
|
||||
$this->handle();
|
||||
|
||||
wp_die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is queue empty
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function is_queue_empty() {
|
||||
global $wpdb;
|
||||
|
||||
$table = $wpdb->options;
|
||||
$column = 'option_name';
|
||||
|
||||
if ( is_multisite() ) {
|
||||
$table = $wpdb->sitemeta;
|
||||
$column = 'meta_key';
|
||||
}
|
||||
|
||||
$key = $wpdb->esc_like( $this->identifier . '_batch_' ) . '%';
|
||||
|
||||
$count = $wpdb->get_var( $wpdb->prepare( "
|
||||
SELECT COUNT(*)
|
||||
FROM {$table}
|
||||
WHERE {$column} LIKE %s
|
||||
", $key ) );
|
||||
|
||||
return ( $count > 0 ) ? false : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is process running
|
||||
*
|
||||
* Check whether the current process is already running
|
||||
* in a background process.
|
||||
*/
|
||||
protected function is_process_running() {
|
||||
if ( get_site_transient( $this->identifier . '_process_lock' ) ) {
|
||||
// Process already running.
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock process
|
||||
*
|
||||
* Lock the process so that multiple instances can't run simultaneously.
|
||||
* Override if applicable, but the duration should be greater than that
|
||||
* defined in the time_exceeded() method.
|
||||
*/
|
||||
protected function lock_process() {
|
||||
$this->start_time = time(); // Set start time of current process.
|
||||
|
||||
$lock_duration = ( property_exists( $this, 'queue_lock_time' ) ) ? $this->queue_lock_time : 60; // 1 minute
|
||||
$lock_duration = apply_filters( $this->identifier . '_queue_lock_time', $lock_duration );
|
||||
|
||||
set_site_transient( $this->identifier . '_process_lock', microtime(), $lock_duration );
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlock process
|
||||
*
|
||||
* Unlock the process so that other instances can spawn.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
protected function unlock_process() {
|
||||
delete_site_transient( $this->identifier . '_process_lock' );
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get batch
|
||||
*
|
||||
* @return stdClass Return the first batch from the queue
|
||||
*/
|
||||
protected function get_batch() {
|
||||
global $wpdb;
|
||||
|
||||
$table = $wpdb->options;
|
||||
$column = 'option_name';
|
||||
$key_column = 'option_id';
|
||||
$value_column = 'option_value';
|
||||
|
||||
if ( is_multisite() ) {
|
||||
$table = $wpdb->sitemeta;
|
||||
$column = 'meta_key';
|
||||
$key_column = 'meta_id';
|
||||
$value_column = 'meta_value';
|
||||
}
|
||||
|
||||
$key = $wpdb->esc_like( $this->identifier . '_batch_' ) . '%';
|
||||
|
||||
$query = $wpdb->get_row( $wpdb->prepare( "
|
||||
SELECT *
|
||||
FROM {$table}
|
||||
WHERE {$column} LIKE %s
|
||||
ORDER BY {$key_column} ASC
|
||||
LIMIT 1
|
||||
", $key ) );
|
||||
|
||||
$batch = new stdClass();
|
||||
$batch->key = $query->$column;
|
||||
$batch->data = maybe_unserialize( $query->$value_column );
|
||||
|
||||
return $batch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle
|
||||
*
|
||||
* Pass each queue item to the task handler, while remaining
|
||||
* within server memory and time limit constraints.
|
||||
*/
|
||||
protected function handle() {
|
||||
$this->lock_process();
|
||||
|
||||
do {
|
||||
$batch = $this->get_batch();
|
||||
|
||||
foreach ( $batch->data as $key => $value ) {
|
||||
$task = $this->task( $value );
|
||||
|
||||
if ( false !== $task ) {
|
||||
$batch->data[ $key ] = $task;
|
||||
} else {
|
||||
unset( $batch->data[ $key ] );
|
||||
}
|
||||
|
||||
if ( $this->time_exceeded() || $this->memory_exceeded() ) {
|
||||
// Batch limits reached.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Update or delete current batch.
|
||||
if ( ! empty( $batch->data ) ) {
|
||||
$this->update( $batch->key, $batch->data );
|
||||
} else {
|
||||
$this->delete( $batch->key );
|
||||
}
|
||||
} while ( ! $this->time_exceeded() && ! $this->memory_exceeded() && ! $this->is_queue_empty() );
|
||||
|
||||
$this->unlock_process();
|
||||
|
||||
// Start next batch or complete process.
|
||||
if ( ! $this->is_queue_empty() ) {
|
||||
$this->dispatch();
|
||||
} else {
|
||||
$this->complete();
|
||||
}
|
||||
|
||||
wp_die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Memory exceeded
|
||||
*
|
||||
* Ensures the batch process never exceeds 90%
|
||||
* of the maximum WordPress memory.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function memory_exceeded() {
|
||||
$memory_limit = $this->get_memory_limit() * 0.9; // 90% of max memory
|
||||
$current_memory = memory_get_usage( true );
|
||||
$return = false;
|
||||
|
||||
if ( $current_memory >= $memory_limit ) {
|
||||
$return = true;
|
||||
}
|
||||
|
||||
return apply_filters( $this->identifier . '_memory_exceeded', $return );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get memory limit
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function get_memory_limit() {
|
||||
if ( function_exists( 'ini_get' ) ) {
|
||||
$memory_limit = ini_get( 'memory_limit' );
|
||||
} else {
|
||||
// Sensible default.
|
||||
$memory_limit = '128M';
|
||||
}
|
||||
|
||||
if ( ! $memory_limit || - 1 === intval( $memory_limit ) ) {
|
||||
// Unlimited, set to 32GB.
|
||||
$memory_limit = '32000M';
|
||||
}
|
||||
|
||||
return wp_convert_hr_to_bytes( $memory_limit );
|
||||
}
|
||||
|
||||
/**
|
||||
* Time exceeded.
|
||||
*
|
||||
* Ensures the batch never exceeds a sensible time limit.
|
||||
* A timeout limit of 30s is common on shared hosting.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function time_exceeded() {
|
||||
$finish = $this->start_time + apply_filters( $this->identifier . '_default_time_limit', 20 ); // 20 seconds
|
||||
$return = false;
|
||||
|
||||
if ( time() >= $finish ) {
|
||||
$return = true;
|
||||
}
|
||||
|
||||
return apply_filters( $this->identifier . '_time_exceeded', $return );
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete.
|
||||
*
|
||||
* Override if applicable, but ensure that the below actions are
|
||||
* performed, or, call parent::complete().
|
||||
*/
|
||||
protected function complete() {
|
||||
// Unschedule the cron healthcheck.
|
||||
$this->clear_scheduled_event();
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule cron healthcheck
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @param mixed $schedules Schedules.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function schedule_cron_healthcheck( $schedules ) {
|
||||
$interval = apply_filters( $this->identifier . '_cron_interval', 5 );
|
||||
|
||||
if ( property_exists( $this, 'cron_interval' ) ) {
|
||||
$interval = apply_filters( $this->identifier . '_cron_interval', $this->cron_interval );
|
||||
}
|
||||
|
||||
// Adds every 5 minutes to the existing schedules.
|
||||
$schedules[ $this->identifier . '_cron_interval' ] = array(
|
||||
'interval' => MINUTE_IN_SECONDS * $interval,
|
||||
'display' => sprintf( __( 'Every %d Minutes' ), $interval ),
|
||||
);
|
||||
|
||||
return $schedules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle cron healthcheck
|
||||
*
|
||||
* Restart the background process if not already running
|
||||
* and data exists in the queue.
|
||||
*/
|
||||
public function handle_cron_healthcheck() {
|
||||
if ( $this->is_process_running() ) {
|
||||
// Background process already running.
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( $this->is_queue_empty() ) {
|
||||
// No data to process.
|
||||
$this->clear_scheduled_event();
|
||||
exit;
|
||||
}
|
||||
|
||||
$this->handle();
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule event
|
||||
*/
|
||||
protected function schedule_event() {
|
||||
if ( ! wp_next_scheduled( $this->cron_hook_identifier ) ) {
|
||||
wp_schedule_event( time(), $this->cron_interval_identifier, $this->cron_hook_identifier );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear scheduled event
|
||||
*/
|
||||
protected function clear_scheduled_event() {
|
||||
$timestamp = wp_next_scheduled( $this->cron_hook_identifier );
|
||||
|
||||
if ( $timestamp ) {
|
||||
wp_unschedule_event( $timestamp, $this->cron_hook_identifier );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel Process
|
||||
*
|
||||
* Stop processing queue items, clear cronjob and delete batch.
|
||||
*
|
||||
*/
|
||||
public function cancel_process() {
|
||||
if ( ! $this->is_queue_empty() ) {
|
||||
$batch = $this->get_batch();
|
||||
|
||||
$this->delete( $batch->key );
|
||||
|
||||
wp_clear_scheduled_hook( $this->cron_hook_identifier );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Task
|
||||
*
|
||||
* Override this method to perform any actions required on each
|
||||
* queue item. Return the modified item for further processing
|
||||
* in the next pass through. Or, return false to remove the
|
||||
* item from the queue.
|
||||
*
|
||||
* @param mixed $item Queue item to iterate over.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract protected function task( $item );
|
||||
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
use Imagify\Traits\InstanceGetterTrait;
|
||||
|
||||
/**
|
||||
* Class handling background processes.
|
||||
*
|
||||
* @since 1.8.1
|
||||
*/
|
||||
abstract class Imagify_Abstract_Background_Process extends Imagify_WP_Background_Process {
|
||||
use InstanceGetterTrait;
|
||||
|
||||
/**
|
||||
* Prefix used to build the global process identifier.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.8.1
|
||||
*/
|
||||
protected $prefix = 'imagify';
|
||||
|
||||
/**
|
||||
* Set to true to automatically displatch at the end of the page.
|
||||
*
|
||||
* @var bool
|
||||
* @since 1.9
|
||||
* @see $this->save()
|
||||
* @see $this->maybe_save_and_dispatch()
|
||||
*/
|
||||
protected $auto_dispatch = false;
|
||||
|
||||
/**
|
||||
* Init: launch a hook that will clear the scheduled events and empty the queue when the plugin is disabled.
|
||||
* This is only a precaution in case something went wrong.
|
||||
*
|
||||
* @since 1.8.1
|
||||
*/
|
||||
public function init() {
|
||||
$this->query_url = admin_url( 'admin-ajax.php' );
|
||||
|
||||
/**
|
||||
* Filter the URL to use for background processes.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param string $query_url An URL.
|
||||
* @param object $this This class instance.
|
||||
*/
|
||||
$this->query_url = apply_filters( 'imagify_background_process_url', $this->query_url, $this );
|
||||
|
||||
if ( ! $this->query_url || ! is_string( $this->query_url ) || ! preg_match( '@^https?://@', $this->query_url ) ) {
|
||||
$this->query_url = admin_url( 'admin-ajax.php' );
|
||||
}
|
||||
|
||||
// Deactivation hook.
|
||||
if ( did_action( static::get_deactivation_hook_name() ) ) {
|
||||
$this->cancel_process();
|
||||
} else {
|
||||
add_action( static::get_deactivation_hook_name(), [ $this, 'cancel_process' ] );
|
||||
}
|
||||
|
||||
// Automatically save and dispatch at the end of the page if the queue is not empty.
|
||||
add_action( 'shutdown', [ $this, 'maybe_save_and_dispatch' ], 666 ); // Evil magic number.
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** OVERRIDES =============================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Cancel Process.
|
||||
* Stop processing queue items, clear cronjob and delete batch.
|
||||
* This is a copy of the parent's method, in case an older version of WP_Background_Process is loaded instead of this one (an old version without this method).
|
||||
*
|
||||
* @since 1.8.1
|
||||
*/
|
||||
public function cancel_process() {
|
||||
if ( method_exists( $this, 'cancel_process' ) ) {
|
||||
parent::cancel_process();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! $this->is_queue_empty() ) {
|
||||
$batch = $this->get_batch();
|
||||
|
||||
$this->delete( $batch->key );
|
||||
|
||||
wp_clear_scheduled_hook( $this->get_event_name() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the queen. No, I meant the queue.
|
||||
* Also empty the queue to avoid to create several batches with the same items.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function save() {
|
||||
if ( empty( $this->data ) ) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
parent::save();
|
||||
|
||||
$this->auto_dispatch = true;
|
||||
$this->data = [];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** TOOLS =================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Save and dispatch if the queue is not empty.
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
public function maybe_save_and_dispatch() {
|
||||
$this->save();
|
||||
|
||||
if ( $this->auto_dispatch ) {
|
||||
$this->dispatch();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cron name.
|
||||
*
|
||||
* @since 1.8.1
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_event_name() {
|
||||
return $this->cron_hook_identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the deactivation hook name.
|
||||
*
|
||||
* @since 1.8.1
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_deactivation_hook_name() {
|
||||
static $deactivation_hook;
|
||||
|
||||
if ( ! isset( $deactivation_hook ) ) {
|
||||
$deactivation_hook = 'deactivate_' . plugin_basename( IMAGIFY_FILE );
|
||||
}
|
||||
|
||||
return $deactivation_hook;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,299 @@
|
||||
<?php
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Basis class that handles events.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
abstract class Imagify_Abstract_Cron {
|
||||
|
||||
/**
|
||||
* Class version.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
*/
|
||||
const VERSION = '1.0';
|
||||
|
||||
/**
|
||||
* Cron name.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected $event_name = '';
|
||||
|
||||
/**
|
||||
* Cron recurrence.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected $event_recurrence = '';
|
||||
|
||||
/**
|
||||
* Cron time.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected $event_time = '';
|
||||
|
||||
/**
|
||||
* The single instance of the class.
|
||||
*
|
||||
* @var object
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected static $_instance;
|
||||
|
||||
/**
|
||||
* Get the main Instance.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return object Main instance.
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! isset( self::$_instance ) ) {
|
||||
self::$_instance = new self();
|
||||
}
|
||||
|
||||
return self::$_instance;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** INIT ==================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Launch hooks.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
public function init() {
|
||||
add_action( 'init', array( $this, 'schedule_event' ) );
|
||||
add_action( $this->get_event_name(), array( $this, 'do_event' ) );
|
||||
add_filter( 'cron_schedules', array( $this, 'maybe_add_recurrence' ) );
|
||||
|
||||
if ( did_action( static::get_deactivation_hook_name() ) ) {
|
||||
$this->unschedule_event();
|
||||
} else {
|
||||
add_action( static::get_deactivation_hook_name(), array( $this, 'unschedule_event' ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** HOOKS =================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Initiate the event.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
public function schedule_event() {
|
||||
if ( ! wp_next_scheduled( $this->get_event_name() ) ) {
|
||||
wp_schedule_event( $this->get_event_timestamp(), $this->get_event_recurrence(), $this->get_event_name() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The event action.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
abstract public function do_event();
|
||||
|
||||
/**
|
||||
* Unschedule the event at plugin or submodule deactivation.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
public function unschedule_event() {
|
||||
wp_clear_scheduled_hook( $this->get_event_name() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the event recurrence schedule.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @see wp_get_schedules()
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $schedules An array of non-default cron schedules. Default empty.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function maybe_add_recurrence( $schedules ) {
|
||||
$default_schedules = array(
|
||||
'hourly' => 1,
|
||||
'twicedaily' => 1,
|
||||
'daily' => 1,
|
||||
);
|
||||
|
||||
$event_recurrence = $this->get_event_recurrence();
|
||||
|
||||
if ( ! empty( $schedules[ $event_recurrence ] ) || ! empty( $default_schedules[ $event_recurrence ] ) ) {
|
||||
return $schedules;
|
||||
}
|
||||
|
||||
$recurrences = array(
|
||||
'weekly' => array(
|
||||
'interval' => WEEK_IN_SECONDS,
|
||||
'display' => __( 'Once Weekly', 'imagify' ),
|
||||
),
|
||||
);
|
||||
|
||||
if ( method_exists( $this, 'get_event_recurrence_attributes' ) ) {
|
||||
$recurrences[ $event_recurrence ] = $this->get_event_recurrence_attributes();
|
||||
}
|
||||
|
||||
if ( ! empty( $recurrences[ $event_recurrence ] ) ) {
|
||||
$schedules[ $event_recurrence ] = $recurrences[ $event_recurrence ];
|
||||
}
|
||||
|
||||
return $schedules;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** TOOLS =================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the cron name.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_event_name() {
|
||||
return $this->event_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cron recurrence.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_event_recurrence() {
|
||||
/**
|
||||
* Filter the recurrence of the event.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $event_recurrence The recurrence.
|
||||
* @param string $event_name Name of the event this recurrence is used for.
|
||||
*/
|
||||
return apply_filters( 'imagify_event_recurrence', $this->event_recurrence, $this->get_event_name() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the time to schedule the event.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_event_time() {
|
||||
/**
|
||||
* Filter the time at which the event is triggered (WordPress time).
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $event_time A 24H formated time: `hour:minute`.
|
||||
* @param string $event_name Name of the event this time is used for.
|
||||
*/
|
||||
return apply_filters( 'imagify_event_time', $this->event_time, $this->get_event_name() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the timestamp to schedule the event.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return int Timestamp.
|
||||
*/
|
||||
public function get_event_timestamp() {
|
||||
return self::get_next_timestamp( $this->get_event_time() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the timestamp of the next (event) date for the given hour.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @source secupress_get_next_cron_timestamp()
|
||||
*
|
||||
* @param string $event_time Time when the event callback should be triggered (WordPress time), formated like `hh:mn` (hour:minute).
|
||||
*
|
||||
* @return int Timestamp.
|
||||
*/
|
||||
public static function get_next_timestamp( $event_time = '00:00' ) {
|
||||
$current_time_int = (int) gmdate( 'Gis' );
|
||||
$event_time_int = (int) str_replace( ':', '', $event_time . '00' );
|
||||
$event_time = explode( ':', $event_time );
|
||||
$event_hour = (int) $event_time[0];
|
||||
$event_minute = (int) $event_time[1];
|
||||
$offset = get_option( 'gmt_offset' ) * HOUR_IN_SECONDS;
|
||||
|
||||
if ( $event_time_int <= $current_time_int ) {
|
||||
// The event time is passed, we need to schedule the event tomorrow.
|
||||
return mktime( $event_hour, $event_minute, 0, (int) gmdate( 'n' ), (int) gmdate( 'j' ) + 1 ) - $offset;
|
||||
}
|
||||
|
||||
// We haven't passed the event time yet, schedule the event today.
|
||||
return mktime( $event_hour, $event_minute, 0 ) - $offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the deactivation hook name.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_deactivation_hook_name() {
|
||||
static $deactivation_hook;
|
||||
|
||||
if ( ! isset( $deactivation_hook ) ) {
|
||||
$deactivation_hook = 'deactivate_' . plugin_basename( IMAGIFY_FILE );
|
||||
}
|
||||
|
||||
return $deactivation_hook;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,883 @@
|
||||
<?php
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Imagify DB base class.
|
||||
*
|
||||
* @since 1.5
|
||||
* @source https://gist.github.com/pippinsplugins/e220a7f0f0f2fbe64608
|
||||
*/
|
||||
abstract class Imagify_Abstract_DB extends Imagify_Abstract_DB_Deprecated implements \Imagify\DB\DBInterface {
|
||||
|
||||
/**
|
||||
* Class version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const VERSION = '1.3';
|
||||
|
||||
/**
|
||||
* Suffix used in the name of the options that store the table versions.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
*/
|
||||
const TABLE_VERSION_OPTION_SUFFIX = '_db_version';
|
||||
|
||||
/**
|
||||
* The single instance of the class.
|
||||
*
|
||||
* @var object
|
||||
* @since 1.5
|
||||
* @access protected
|
||||
*/
|
||||
protected static $_instance;
|
||||
|
||||
/**
|
||||
* The suffix used in the name of the database table (so, without the wpdb prefix).
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected $table;
|
||||
|
||||
/**
|
||||
* The version of our database table.
|
||||
*
|
||||
* @var int
|
||||
* @since 1.5
|
||||
* @since 1.7 Not public anymore, now an integer.
|
||||
* @access protected
|
||||
*/
|
||||
protected $table_version;
|
||||
|
||||
/**
|
||||
* Tell if the table is the same for each site of a Multisite.
|
||||
*
|
||||
* @var bool
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected $table_is_global;
|
||||
|
||||
/**
|
||||
* The name of the primary column.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.5
|
||||
* @since 1.7 Not public anymore.
|
||||
* @access protected
|
||||
*/
|
||||
protected $primary_key;
|
||||
|
||||
/**
|
||||
* The name of our database table.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.5
|
||||
* @since 1.7 Not public anymore.
|
||||
* @access protected
|
||||
*/
|
||||
protected $table_name = '';
|
||||
|
||||
/**
|
||||
* Tell if the table has been created.
|
||||
*
|
||||
* @var bool
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected $table_created = false;
|
||||
|
||||
/**
|
||||
* Stores the list of columns that must be (un)serialized.
|
||||
*
|
||||
* @var array
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected $to_serialize;
|
||||
|
||||
/**
|
||||
* Get things started.
|
||||
*
|
||||
* @since 1.5
|
||||
* @access protected
|
||||
*/
|
||||
protected function __construct() {
|
||||
global $wpdb;
|
||||
|
||||
$prefix = $this->table_is_global ? $wpdb->base_prefix : $wpdb->prefix;
|
||||
|
||||
$this->table_name = $prefix . $this->table;
|
||||
|
||||
if ( ! $this->table_is_up_to_date() ) {
|
||||
/**
|
||||
* The option doesn't exist or is not up-to-date: we must upgrade the table before declaring it ready.
|
||||
* See self::maybe_upgrade_table() for the upgrade.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
$this->set_table_ready();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the main Instance.
|
||||
*
|
||||
* @since 1.6.5
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return object Main instance.
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! isset( self::$_instance ) ) {
|
||||
self::$_instance = new self();
|
||||
}
|
||||
|
||||
return self::$_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init:
|
||||
* - Launch hooks.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
public function init() {
|
||||
add_action( 'admin_init', array( $this, 'maybe_upgrade_table' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if we can work with the tables.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function can_operate() {
|
||||
return $this->table_created;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** TABLE SPECIFICS ========================================================================= */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the column placeholders.
|
||||
*
|
||||
* @since 1.5
|
||||
* @access public
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract public function get_columns();
|
||||
|
||||
/**
|
||||
* Default column values.
|
||||
*
|
||||
* @since 1.5
|
||||
* @access public
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract public function get_column_defaults();
|
||||
|
||||
/**
|
||||
* Get the query to create the table fields.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function get_table_schema();
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** QUERIES ================================================================================= */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Tell if the table is empty or not.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool True if the table contains at least one row.
|
||||
*/
|
||||
public function has_items() {
|
||||
global $wpdb;
|
||||
|
||||
$column = esc_sql( $this->primary_key );
|
||||
|
||||
return (bool) $wpdb->get_var( "SELECT $column FROM $this->table_name LIMIT 1;" ); // WPCS: unprepared SQL ok.
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a row by the primary key.
|
||||
*
|
||||
* @since 1.5
|
||||
* @access public
|
||||
*
|
||||
* @param string $row_id A primary key.
|
||||
* @return array
|
||||
*/
|
||||
public function get( $row_id ) {
|
||||
if ( $row_id <= 0 ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return $this->get_by( $this->primary_key, $row_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a row by a specific column / value.
|
||||
*
|
||||
* @since 1.5
|
||||
* @access public
|
||||
*
|
||||
* @param string $column_where A column name.
|
||||
* @param mixed $column_value A value.
|
||||
* @return array
|
||||
*/
|
||||
public function get_by( $column_where, $column_value ) {
|
||||
global $wpdb;
|
||||
|
||||
$placeholder = $this->get_placeholder( $column_where );
|
||||
$column_where = esc_sql( $column_where );
|
||||
|
||||
$result = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $this->table_name WHERE $column_where = $placeholder LIMIT 1;", $column_value ), ARRAY_A ); // WPCS: unprepared SQL ok, PreparedSQLPlaceholders replacement count ok.
|
||||
|
||||
return (array) $this->cast_row( $result );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a row by the specified column / values.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $column_where A column name.
|
||||
* @param array $column_values An array of values.
|
||||
* @return array
|
||||
*/
|
||||
public function get_in( $column_where, $column_values ) {
|
||||
global $wpdb;
|
||||
|
||||
$column_where = esc_sql( $column_where );
|
||||
$column_values = Imagify_DB::prepare_values_list( $column_values );
|
||||
|
||||
$result = $wpdb->get_row( "SELECT * FROM $this->table_name WHERE $column_where IN ( $column_values ) LIMIT 1;", ARRAY_A ); // WPCS: unprepared SQL ok.
|
||||
|
||||
return (array) $this->cast_row( $result );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a var by the primary key.
|
||||
* Previously named get_column().
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $column_select A column name.
|
||||
* @param string $row_id A primary key.
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_var( $column_select, $row_id ) {
|
||||
if ( $row_id <= 0 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->get_var_by( $column_select, $this->primary_key, $row_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a var by the specified column / value.
|
||||
* Previously named get_column_by().
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $column_select A column name.
|
||||
* @param string $column_where A column name.
|
||||
* @param string $column_value A value.
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_var_by( $column_select, $column_where, $column_value ) {
|
||||
global $wpdb;
|
||||
|
||||
$placeholder = $this->get_placeholder( $column_where );
|
||||
$column = esc_sql( $column_select );
|
||||
$column_where = esc_sql( $column_where );
|
||||
|
||||
$result = $wpdb->get_var( $wpdb->prepare( "SELECT $column FROM $this->table_name WHERE $column_where = $placeholder LIMIT 1;", $column_value ) ); // WPCS: unprepared SQL ok, PreparedSQLPlaceholders replacement count ok.
|
||||
|
||||
return $this->cast( $result, $column_select );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a var by the specified column / values.
|
||||
* Previously named get_column_in().
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $column_select A column name.
|
||||
* @param string $column_where A column name.
|
||||
* @param array $column_values An array of values.
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_var_in( $column_select, $column_where, $column_values ) {
|
||||
global $wpdb;
|
||||
|
||||
$column = esc_sql( $column_select );
|
||||
$column_where = esc_sql( $column_where );
|
||||
$column_values = Imagify_DB::prepare_values_list( $column_values );
|
||||
|
||||
$result = $wpdb->get_var( "SELECT $column FROM $this->table_name WHERE $column_where IN ( $column_values ) LIMIT 1;" ); // WPCS: unprepared SQL ok.
|
||||
|
||||
return $this->cast( $result, $column_select );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve values by the specified column / values.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $column_select A column name.
|
||||
* @param string $column_where A column name.
|
||||
* @param array $column_values An array of values.
|
||||
* @return array
|
||||
*/
|
||||
public function get_column_in( $column_select, $column_where, $column_values ) {
|
||||
global $wpdb;
|
||||
|
||||
$column = esc_sql( $column_select );
|
||||
$column_where = esc_sql( $column_where );
|
||||
$column_values = Imagify_DB::prepare_values_list( $column_values );
|
||||
|
||||
$result = $wpdb->get_col( "SELECT $column FROM $this->table_name WHERE $column_where IN ( $column_values );" ); // WPCS: unprepared SQL ok.
|
||||
|
||||
return $this->cast_col( $result, $column_select );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve values by the specified column / values.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $column_select A column name.
|
||||
* @param string $column_where A column name.
|
||||
* @param array $column_values An array of values.
|
||||
* @return array
|
||||
*/
|
||||
public function get_column_not_in( $column_select, $column_where, $column_values ) {
|
||||
global $wpdb;
|
||||
|
||||
$column = esc_sql( $column_select );
|
||||
$column_where = esc_sql( $column_where );
|
||||
$column_values = Imagify_DB::prepare_values_list( $column_values );
|
||||
|
||||
$result = $wpdb->get_col( "SELECT $column FROM $this->table_name WHERE $column_where NOT IN ( $column_values );" ); // WPCS: unprepared SQL ok.
|
||||
|
||||
return $this->cast_col( $result, $column_select );
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new row.
|
||||
*
|
||||
* @since 1.5
|
||||
* @access public
|
||||
*
|
||||
* @param string $data New data.
|
||||
* @return int The ID.
|
||||
*/
|
||||
public function insert( $data ) {
|
||||
global $wpdb;
|
||||
|
||||
// Initialise column format array.
|
||||
$column_formats = $this->get_columns();
|
||||
|
||||
// Set default values.
|
||||
$data = wp_parse_args( $data, $this->get_column_defaults() );
|
||||
|
||||
// Force fields to lower case.
|
||||
$data = array_change_key_case( $data );
|
||||
|
||||
// White list columns.
|
||||
$data = array_intersect_key( $data, $column_formats );
|
||||
|
||||
// Maybe serialize some values.
|
||||
$data = $this->serialize_columns( $data );
|
||||
|
||||
// Reorder $column_formats to match the order of columns given in $data.
|
||||
$column_formats = array_merge( $data, $column_formats );
|
||||
|
||||
$wpdb->insert( $this->table_name, $data, $column_formats );
|
||||
|
||||
return (int) $wpdb->insert_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a row.
|
||||
*
|
||||
* @since 1.5
|
||||
* @access public
|
||||
*
|
||||
* @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 = array(), $where = '' ) {
|
||||
global $wpdb;
|
||||
|
||||
if ( $row_id <= 0 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! $this->get( $row_id ) ) {
|
||||
$this->insert( $data );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( empty( $where ) ) {
|
||||
$where = $this->primary_key;
|
||||
}
|
||||
|
||||
// Initialise column format array.
|
||||
$column_formats = $this->get_columns();
|
||||
|
||||
// Force fields to lower case.
|
||||
$data = array_change_key_case( $data );
|
||||
|
||||
// White list columns.
|
||||
$data = array_intersect_key( $data, $column_formats );
|
||||
|
||||
// Maybe serialize some values.
|
||||
$data = $this->serialize_columns( $data );
|
||||
|
||||
// Reorder $column_formats to match the order of columns given in $data.
|
||||
$column_formats = array_merge( $data, $column_formats );
|
||||
|
||||
return (bool) $wpdb->update( $this->table_name, $data, array( $where => $row_id ), $column_formats, $this->get_placeholder( $where ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a row identified by the primary key.
|
||||
*
|
||||
* @since 1.5
|
||||
* @access public
|
||||
*
|
||||
* @param string $row_id A primary key.
|
||||
* @return bool
|
||||
*/
|
||||
public function delete( $row_id = 0 ) {
|
||||
global $wpdb;
|
||||
|
||||
if ( $row_id <= 0 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$placeholder = $this->get_placeholder( $this->primary_key );
|
||||
|
||||
return (bool) $wpdb->query( $wpdb->prepare( "DELETE FROM $this->table_name WHERE $this->primary_key = $placeholder", $row_id ) ); // WPCS: unprepared SQL ok, PreparedSQLPlaceholders replacement count ok.
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** TABLE CREATION ========================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Maybe create/upgrade the table in the database.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
public function maybe_upgrade_table() {
|
||||
global $wpdb;
|
||||
|
||||
if ( $this->table_is_up_to_date() ) {
|
||||
// The table has the right version.
|
||||
$this->set_table_ready();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the table.
|
||||
$this->create_table();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create/Upgrade the table in the database.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
public function create_table() {
|
||||
if ( ! Imagify_DB::create_table( $this->get_table_name(), $this->get_table_schema() ) ) {
|
||||
// Failure.
|
||||
$this->set_table_not_ready();
|
||||
$this->delete_db_version();
|
||||
return;
|
||||
}
|
||||
|
||||
// Table successfully created/upgraded.
|
||||
$this->set_table_ready();
|
||||
$this->update_db_version();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set various properties to tell the table is ready to be used.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected function set_table_ready() {
|
||||
global $wpdb;
|
||||
|
||||
$this->table_created = true;
|
||||
$wpdb->{$this->table} = $this->table_name;
|
||||
|
||||
if ( $this->table_is_global ) {
|
||||
$wpdb->global_tables[] = $this->table;
|
||||
} else {
|
||||
$wpdb->tables[] = $this->table;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unset various properties to tell the table is NOT ready to be used.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected function set_table_not_ready() {
|
||||
global $wpdb;
|
||||
|
||||
$this->table_created = false;
|
||||
unset( $wpdb->{$this->table} );
|
||||
|
||||
if ( $this->table_is_global ) {
|
||||
$wpdb->global_tables = array_diff( $wpdb->global_tables, array( $this->table ) );
|
||||
} else {
|
||||
$wpdb->tables = array_diff( $wpdb->tables, array( $this->table ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** TABLE VERSION =========================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the table version.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_table_version() {
|
||||
return $this->table_version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the table is up-to-date (we don't "downgrade" the tables).
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function table_is_up_to_date() {
|
||||
return $this->get_db_version() >= $this->get_table_version();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the table version stored in DB.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return int|bool The version. False if not set yet.
|
||||
*/
|
||||
public function get_db_version() {
|
||||
$option_name = $this->table . self::TABLE_VERSION_OPTION_SUFFIX;
|
||||
|
||||
if ( $this->table_is_global && is_multisite() ) {
|
||||
return get_site_option( $option_name );
|
||||
}
|
||||
|
||||
return get_option( $option_name );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the table version stored in DB.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected function update_db_version() {
|
||||
$option_name = $this->table . self::TABLE_VERSION_OPTION_SUFFIX;
|
||||
|
||||
if ( $this->table_is_global && is_multisite() ) {
|
||||
update_site_option( $option_name, $this->get_table_version() );
|
||||
} else {
|
||||
update_option( $option_name, $this->get_table_version() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the table version stored in DB.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected function delete_db_version() {
|
||||
$option_name = $this->table . self::TABLE_VERSION_OPTION_SUFFIX;
|
||||
|
||||
if ( $this->table_is_global && is_multisite() ) {
|
||||
delete_site_option( $option_name );
|
||||
} else {
|
||||
delete_option( $option_name );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** TOOLS =================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the table name.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_table_name() {
|
||||
return $this->table_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the table is the same for each site of a Multisite.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_table_global() {
|
||||
return $this->table_is_global;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the primary column name.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_primary_key() {
|
||||
return $this->primary_key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the formats related to the given columns.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $columns An array of column names (as keys).
|
||||
* @return array
|
||||
*/
|
||||
public function get_column_formats( $columns ) {
|
||||
if ( ! is_array( $columns ) ) {
|
||||
$columns = array_flip( (array) $columns );
|
||||
}
|
||||
|
||||
// White list columns.
|
||||
return array_intersect_key( $this->get_columns(), $columns );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the placeholder corresponding to the given key.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $key The key.
|
||||
* @return string
|
||||
*/
|
||||
public function get_placeholder( $key ) {
|
||||
$columns = $this->get_columns();
|
||||
return isset( $columns[ $key ] ) ? $columns[ $key ] : '%s';
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the column value must be (un)serialized.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $key The key.
|
||||
* @return bool
|
||||
*/
|
||||
public function is_column_serialized( $key ) {
|
||||
$columns = $this->get_column_defaults();
|
||||
return isset( $columns[ $key ] ) && is_array( $columns[ $key ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast a value.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param mixed $value The value to cast.
|
||||
* @param string $key The corresponding key.
|
||||
* @return mixed
|
||||
*/
|
||||
public function cast( $value, $key ) {
|
||||
if ( null === $value || is_bool( $value ) ) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$placeholder = $this->get_placeholder( $key );
|
||||
|
||||
if ( '%d' === $placeholder ) {
|
||||
return (int) $value;
|
||||
}
|
||||
|
||||
if ( '%f' === $placeholder ) {
|
||||
return (float) $value;
|
||||
}
|
||||
|
||||
if ( $value && $this->is_column_serialized( $key ) ) {
|
||||
return maybe_unserialize( $value );
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast a column.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $values The values to cast.
|
||||
* @param string $column The corresponding column name.
|
||||
* @return array
|
||||
*/
|
||||
public function cast_col( $values, $column ) {
|
||||
if ( ! $values ) {
|
||||
return $values;
|
||||
}
|
||||
|
||||
foreach ( $values as $i => $value ) {
|
||||
$values[ $i ] = $this->cast( $value, $column );
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast a row.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array|object $row_fields A row from the DB.
|
||||
* @return array|object
|
||||
*/
|
||||
public function cast_row( $row_fields ) {
|
||||
if ( ! $row_fields ) {
|
||||
return $row_fields;
|
||||
}
|
||||
|
||||
if ( is_array( $row_fields ) ) {
|
||||
foreach ( $row_fields as $field => $value ) {
|
||||
$row_fields[ $field ] = $this->cast( $value, $field );
|
||||
}
|
||||
} elseif ( is_object( $row_fields ) ) {
|
||||
foreach ( $row_fields as $field => $value ) {
|
||||
$row_fields->$field = $this->cast( $value, $field );
|
||||
}
|
||||
}
|
||||
|
||||
return $row_fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize columns that need to be.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $data An array of values.
|
||||
* @return array
|
||||
*/
|
||||
public function serialize_columns( $data ) {
|
||||
if ( ! isset( $this->to_serialize ) ) {
|
||||
$this->to_serialize = array_filter( $this->get_column_defaults(), 'is_array' );
|
||||
}
|
||||
|
||||
if ( ! $this->to_serialize ) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$serialized_data = array_intersect_key( $data, $this->to_serialize );
|
||||
|
||||
if ( ! $serialized_data ) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$serialized_data = array_map( function( $array ) {
|
||||
// Try not to store empty serialized arrays.
|
||||
return [] === $array ? null : maybe_serialize( $array );
|
||||
}, $serialized_data );
|
||||
|
||||
return array_merge( $data, $serialized_data );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,623 @@
|
||||
<?php
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Abstract class to handle a part of the plugin options.
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
abstract class Imagify_Abstract_Options {
|
||||
|
||||
/**
|
||||
* Class version.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
*/
|
||||
const VERSION = '1.0';
|
||||
|
||||
/**
|
||||
* Suffix used in the name of the option.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected $identifier;
|
||||
|
||||
/**
|
||||
* The default values for the Imagify main options.
|
||||
* These are the "zero state" values.
|
||||
* Don't use null as value.
|
||||
*
|
||||
* @var array
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected $default_values;
|
||||
|
||||
/**
|
||||
* The Imagify main option values used when they are set the first time or reset.
|
||||
* Values identical to default values are not listed.
|
||||
*
|
||||
* @var array
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected $reset_values = array();
|
||||
|
||||
/**
|
||||
* Tell if the option should be autoloaded by WP.
|
||||
* Possible values are 'yes' and 'no'.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected $autoload = 'yes';
|
||||
|
||||
/**
|
||||
* Tell if the option should be a network option.
|
||||
*
|
||||
* @var bool
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected $network_option = false;
|
||||
|
||||
/**
|
||||
* Identifier used in the hook names.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
* @access private
|
||||
*/
|
||||
private $hook_identifier;
|
||||
|
||||
/**
|
||||
* The single instance of the class.
|
||||
*
|
||||
* @var object
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected static $_instance;
|
||||
|
||||
/**
|
||||
* The constructor.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access protected
|
||||
*/
|
||||
protected function __construct() {
|
||||
$this->hook_identifier = rtrim( strtolower( str_replace( 'Imagify_', '', get_class( $this ) ) ), 's' );
|
||||
|
||||
if ( ! is_string( $this->autoload ) ) {
|
||||
$this->autoload = $this->autoload ? 'yes' : 'no';
|
||||
}
|
||||
|
||||
$this->default_values = array_merge( array(
|
||||
'version' => '',
|
||||
), $this->default_values );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the main Instance.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @return object Main instance.
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! isset( self::$_instance ) ) {
|
||||
self::$_instance = new self();
|
||||
}
|
||||
|
||||
return self::$_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch the hooks.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*/
|
||||
public function init() {
|
||||
add_filter( 'sanitize_option_' . $this->get_option_name(), array( $this, 'sanitize_and_validate_on_update' ), 50 );
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** GET/SET/DELETE OPTION(S) ================================================================ */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get an Imagify option.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @param string $key The option name.
|
||||
* @return mixed The option value.
|
||||
*/
|
||||
public function get( $key ) {
|
||||
$default_values = $this->get_default_values();
|
||||
|
||||
if ( ! isset( $default_values[ $key ] ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$default = $default_values[ $key ];
|
||||
|
||||
/**
|
||||
* Pre-filter any Imagify option before read.
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @param mixed $value Value to return instead of the option value. Default null to skip it.
|
||||
* @param mixed $default The default value.
|
||||
*/
|
||||
$value = apply_filters( 'pre_get_imagify_' . $this->get_hook_identifier() . '_' . $key, null, $default );
|
||||
|
||||
if ( isset( $value ) ) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
// Get all values.
|
||||
$values = $this->get_all();
|
||||
|
||||
// Sanitize and validate the value.
|
||||
$value = $this->sanitize_and_validate( $key, $values[ $key ], $default );
|
||||
|
||||
/**
|
||||
* Filter any Imagify option after read.
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @param mixed $value Value of the option.
|
||||
* @param mixed $default The default value. Default false.
|
||||
*/
|
||||
return apply_filters( 'get_imagify_' . $this->get_hook_identifier() . '_' . $key, $value, $default );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all options (no cast, no sanitization, no validation).
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @return array The options.
|
||||
*/
|
||||
public function get_all() {
|
||||
$values = $this->get_raw();
|
||||
|
||||
if ( ! $values ) {
|
||||
return $this->get_reset_values();
|
||||
}
|
||||
|
||||
return imagify_merge_intersect( $values, $this->get_default_values() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set one or multiple options.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @param array $values An array of option name / option value pairs.
|
||||
*/
|
||||
public function set( $values ) {
|
||||
$args = func_get_args();
|
||||
|
||||
if ( isset( $args[1] ) && is_string( $args[0] ) ) {
|
||||
$values = array( $args[0] => $args[1] );
|
||||
}
|
||||
|
||||
if ( ! is_array( $values ) ) {
|
||||
// PABKAC.
|
||||
return;
|
||||
}
|
||||
|
||||
$values = array_merge( $this->get_all(), $values );
|
||||
$values = array_intersect_key( $values, $this->get_default_values() );
|
||||
|
||||
$this->set_raw( $values );
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete one or multiple options.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @param array|string $keys An array of option names or a single option name.
|
||||
*/
|
||||
public function delete( $keys ) {
|
||||
$values = $this->get_raw();
|
||||
|
||||
if ( ! $values ) {
|
||||
if ( false !== $values ) {
|
||||
$this->delete_raw();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$keys = array_flip( (array) $keys );
|
||||
$values = array_diff_key( $values, $keys );
|
||||
|
||||
$this->set_raw( $values );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the option with the given name exists or not.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @param string $key The option name.
|
||||
* @return bool
|
||||
*/
|
||||
public function has( $key ) {
|
||||
return null !== $this->get( $key );
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** GET / UPDATE / DELETE RAW VALUES ======================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the name of the option that stores the settings.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_option_name() {
|
||||
return IMAGIFY_SLUG . '_' . $this->identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the identifier used in the hook names.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_hook_identifier() {
|
||||
return $this->hook_identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the option is autoloaded.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_autoloaded() {
|
||||
return 'yes' === $this->autoload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the option is a network option.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_network_option() {
|
||||
return (bool) $this->network_option;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the raw value of all Imagify options.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @return array|bool The options. False if not set yet. An empty array if invalid.
|
||||
*/
|
||||
public function get_raw() {
|
||||
$values = $this->is_network_option() ? get_site_option( $this->get_option_name() ) : get_option( $this->get_option_name() );
|
||||
|
||||
if ( false !== $values && ! is_array( $values ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the Imagify options.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @param array $values An array of option name / option value pairs.
|
||||
*/
|
||||
public function set_raw( $values ) {
|
||||
if ( ! $values ) {
|
||||
// The option is empty: delete it.
|
||||
$this->delete_raw();
|
||||
|
||||
} elseif ( $this->is_network_option() ) {
|
||||
// Network option.
|
||||
update_site_option( $this->get_option_name(), $values );
|
||||
|
||||
} elseif ( false === get_option( $this->get_option_name() ) ) {
|
||||
// Compat' with WP < 4.2 + autoload: the option doesn't exist in the database.
|
||||
add_option( $this->get_option_name(), $values, '', $this->autoload );
|
||||
} else {
|
||||
// Update the current value.
|
||||
update_option( $this->get_option_name(), $values, $this->autoload );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all Imagify options.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*/
|
||||
public function delete_raw() {
|
||||
$this->is_network_option() ? delete_site_option( $this->get_option_name() ) : delete_option( $this->get_option_name() );
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** DEFAULT + RESET VALUES ================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get default option values.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_default_values() {
|
||||
$default_values = $this->default_values;
|
||||
|
||||
if ( ! empty( $default_values['cached'] ) ) {
|
||||
unset( $default_values['cached'] );
|
||||
return $default_values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow to add more default option values.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $new_values New default option values.
|
||||
* @param array $default_values Plugin default option values.
|
||||
*/
|
||||
$new_values = apply_filters( 'imagify_default_' . $this->get_hook_identifier() . '_values', array(), $default_values );
|
||||
$new_values = is_array( $new_values ) ? $new_values : array();
|
||||
|
||||
if ( $new_values ) {
|
||||
// Don't allow new values to overwrite the plugin values.
|
||||
$new_values = array_diff_key( $new_values, $default_values );
|
||||
}
|
||||
|
||||
if ( $new_values ) {
|
||||
$default_values = array_merge( $default_values, $new_values );
|
||||
$this->default_values = $default_values;
|
||||
}
|
||||
|
||||
$this->default_values['cached'] = 1;
|
||||
|
||||
return $default_values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the values used when the option is empty.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_reset_values() {
|
||||
$reset_values = $this->reset_values;
|
||||
|
||||
if ( ! empty( $reset_values['cached'] ) ) {
|
||||
unset( $reset_values['cached'] );
|
||||
return $reset_values;
|
||||
}
|
||||
|
||||
$default_values = $this->get_default_values();
|
||||
$reset_values = array_merge( $default_values, $reset_values );
|
||||
|
||||
/**
|
||||
* Allow to filter the "reset" option values.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $reset_values Plugin reset option values.
|
||||
*/
|
||||
$new_values = apply_filters( 'imagify_reset_' . $this->get_hook_identifier() . '_values', $reset_values );
|
||||
|
||||
if ( $new_values && is_array( $new_values ) ) {
|
||||
$reset_values = array_merge( $reset_values, $new_values );
|
||||
}
|
||||
|
||||
$this->reset_values = $reset_values;
|
||||
$this->reset_values['cached'] = 1;
|
||||
|
||||
return $reset_values;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** SANITIZATION, VALIDATION ================================================================ */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Sanitize and validate an option value.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @param string $key The option key.
|
||||
* @param mixed $value The value.
|
||||
* @param mixed $default The default value.
|
||||
* @return mixed
|
||||
*/
|
||||
public function sanitize_and_validate( $key, $value, $default = null ) {
|
||||
if ( ! isset( $default ) ) {
|
||||
$default_values = $this->get_default_values();
|
||||
$default = $default_values[ $key ];
|
||||
}
|
||||
|
||||
// Cast the value.
|
||||
$value = self::cast( $value, $default );
|
||||
|
||||
if ( $value === $default ) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
// Version.
|
||||
if ( 'version' === $key ) {
|
||||
return sanitize_text_field( $value );
|
||||
}
|
||||
|
||||
return $this->sanitize_and_validate_value( $key, $value, $default );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize and validate an option value. Basic casts have been made.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @param string $key The option key.
|
||||
* @param mixed $value The value.
|
||||
* @param mixed $default The default value.
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function sanitize_and_validate_value( $key, $value, $default );
|
||||
|
||||
/**
|
||||
* Sanitize and validate Imagify's options before storing them.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @param string $values The option value.
|
||||
* @return array
|
||||
*/
|
||||
public function sanitize_and_validate_on_update( $values ) {
|
||||
$values = is_array( $values ) ? $values : array();
|
||||
$default_values = $this->get_default_values();
|
||||
|
||||
if ( $values ) {
|
||||
foreach ( $default_values as $key => $default ) {
|
||||
if ( isset( $values[ $key ] ) ) {
|
||||
$values[ $key ] = $this->sanitize_and_validate( $key, $values[ $key ], $default );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$values = array_intersect_key( $values, $default_values );
|
||||
|
||||
// Version.
|
||||
if ( empty( $values['version'] ) ) {
|
||||
$values['version'] = IMAGIFY_VERSION;
|
||||
}
|
||||
|
||||
return $this->validate_values_on_update( $values );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate Imagify's options before storing them. Basic sanitization and validation have been made, row by row.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @param string $values The option value.
|
||||
* @return array
|
||||
*/
|
||||
public function validate_values_on_update( $values ) {
|
||||
return $values;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** TOOLS =================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Cast a value, depending on its default value type.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @param mixed $value The value to cast.
|
||||
* @param mixed $default The default value.
|
||||
* @return mixed
|
||||
*/
|
||||
public static function cast( $value, $default ) {
|
||||
if ( is_array( $default ) ) {
|
||||
return is_array( $value ) ? $value : array();
|
||||
}
|
||||
|
||||
if ( is_int( $default ) ) {
|
||||
return (int) $value;
|
||||
}
|
||||
|
||||
if ( is_bool( $default ) ) {
|
||||
return (bool) $value;
|
||||
}
|
||||
|
||||
if ( is_float( $default ) ) {
|
||||
return round( (float) $value, 3 );
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast a float like 3.000 into an integer.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @param float $float The float.
|
||||
* @return float|int
|
||||
*/
|
||||
public static function maybe_cast_float_as_int( $float ) {
|
||||
return ( $float / (int) $float ) === (float) 1 ? (int) $float : $float;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,760 @@
|
||||
<?php
|
||||
use Imagify\Notices\Notices;
|
||||
|
||||
/**
|
||||
* Class that handles stylesheets and JavaScripts.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*/
|
||||
class Imagify_Assets extends Imagify_Assets_Deprecated {
|
||||
|
||||
/**
|
||||
* Class version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const VERSION = '1.0.4';
|
||||
|
||||
/**
|
||||
* Prefix used for stylesheet handles.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const CSS_PREFIX = 'imagify-';
|
||||
|
||||
/**
|
||||
* Prefix used for script handles.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const JS_PREFIX = 'imagify-';
|
||||
|
||||
/**
|
||||
* An array containing our registered styles.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $styles = [];
|
||||
|
||||
/**
|
||||
* An array containing our registered scripts.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $scripts = [];
|
||||
|
||||
/**
|
||||
* Current handle.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $current_handle;
|
||||
|
||||
/**
|
||||
* Current handle type.
|
||||
*
|
||||
* @var string 'css' or 'js'.
|
||||
*/
|
||||
protected $current_handle_type;
|
||||
|
||||
/**
|
||||
* Array of scripts that should be localized when they are enqueued.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $deferred_localizations = [];
|
||||
|
||||
/**
|
||||
* A "random" script version to use when debug is on.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected static $version;
|
||||
|
||||
/**
|
||||
* The single instance of the class.
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected static $_instance;
|
||||
|
||||
/**
|
||||
* The constructor.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function __construct() {
|
||||
if ( ! isset( self::$version ) ) {
|
||||
self::$version = time();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** PUBLIC METHODS ========================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the main Instance.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*
|
||||
* @return object Main instance.
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! isset( self::$_instance ) ) {
|
||||
self::$_instance = new self();
|
||||
}
|
||||
|
||||
return self::$_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch the hooks.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*/
|
||||
public function init() {
|
||||
if ( ! is_admin() ) {
|
||||
add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_styles_and_scripts_frontend' ] );
|
||||
return;
|
||||
}
|
||||
|
||||
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_styles_and_scripts' ], IMAGIFY_INT_MAX );
|
||||
add_action( 'wp_enqueue_media', [ $this, 'enqueue_media_modal' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue stylesheets and scripts for the frontend.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*/
|
||||
public function enqueue_styles_and_scripts_frontend() {
|
||||
if ( ! $this->is_admin_bar_item_showing() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->register_style( 'admin-bar' );
|
||||
$this->register_script( 'admin-bar', 'admin-bar', [ 'jquery' ] );
|
||||
|
||||
$this->enqueue_assets( 'admin-bar' )->localize( 'imagifyAdminBar' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register stylesheets and scripts for the administration area.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*/
|
||||
public function register_styles_and_scripts() {
|
||||
static $done = false;
|
||||
|
||||
if ( $done ) {
|
||||
return;
|
||||
}
|
||||
$done = true;
|
||||
|
||||
/**
|
||||
* 3rd Party Styles.
|
||||
*/
|
||||
$this->register_style( 'sweetalert-core', 'sweetalert2', [], '4.6.6' );
|
||||
|
||||
/**
|
||||
* Imagify Styles.
|
||||
*/
|
||||
$this->register_style( 'sweetalert', 'sweetalert-custom', [ 'sweetalert-core' ] );
|
||||
|
||||
$this->register_style( 'admin-bar' );
|
||||
|
||||
$this->register_style( 'admin' );
|
||||
|
||||
$this->register_style( 'notices', 'notices', [ 'admin' ] ); // Needs SweetAlert on some cases.
|
||||
|
||||
$this->register_style( 'twentytwenty', 'twentytwenty', [ 'admin' ] );
|
||||
|
||||
$this->register_style( 'pricing-modal', 'pricing-modal', [ 'admin' ] );
|
||||
|
||||
$this->register_style( 'bulk', 'bulk', [ 'sweetalert', 'admin' ] );
|
||||
|
||||
$this->register_style( 'options', 'options', [ 'sweetalert', 'admin' ] );
|
||||
|
||||
$this->register_style( 'files-list', 'files-list', [ 'admin' ] );
|
||||
|
||||
/**
|
||||
* 3rd Party Scripts.
|
||||
*/
|
||||
$this->register_script( 'promise-polyfill', 'es6-promise.auto', [], '4.1.1' );
|
||||
|
||||
$this->register_script( 'sweetalert', 'sweetalert2', [ 'promise-polyfill' ], '4.6.6' )->localize( 'imagifySwal' );
|
||||
|
||||
$this->register_script( 'chart', 'chart', [], '2.7.1.0' );
|
||||
|
||||
$this->register_script( 'event-move', 'jquery.event.move', [ 'jquery' ], '2.0.1' );
|
||||
|
||||
/**
|
||||
* Imagify Scripts.
|
||||
*/
|
||||
$this->register_script( 'admin-bar', 'admin-bar', [ 'jquery' ] )->defer_localization( 'imagifyAdminBar' );
|
||||
|
||||
$this->register_script( 'admin', 'admin', [ 'jquery' ] );
|
||||
|
||||
$this->register_script( 'notices', 'notices', [ 'jquery', 'admin' ] )->defer_localization( 'imagifyNotices' ); // Needs SweetAlert on some cases.
|
||||
|
||||
$this->register_script( 'twentytwenty', 'jquery.twentytwenty', [ 'jquery', 'event-move', 'chart', 'admin' ] )->defer_localization( 'imagifyTTT' );
|
||||
|
||||
$this->register_script( 'beat', 'beat', [ 'jquery' ] )->localize( 'imagifybeatSettings' );
|
||||
|
||||
$this->register_script( 'media-modal', 'media-modal', [ 'jquery', 'beat', 'underscore', 'chart', 'admin' ] )->localize( 'imagifyModal' );
|
||||
|
||||
$this->register_script( 'pricing-modal', 'pricing-modal', [ 'jquery', 'admin' ] )->defer_localization( 'imagifyPricingModal' );
|
||||
|
||||
$this->register_script( 'library', 'library', [ 'jquery', 'media-modal' ] )->defer_localization( 'imagifyLibrary' );
|
||||
|
||||
$this->register_script( 'async', 'imagify-gulp' );
|
||||
|
||||
$this->register_script( 'bulk', 'bulk', [ 'jquery', 'beat', 'underscore', 'chart', 'sweetalert', 'async', 'admin' ] )->defer_localization( 'imagifyBulk' );
|
||||
|
||||
$this->register_script( 'options', 'options', [ 'jquery', 'beat', 'sweetalert', 'underscore', 'admin' ] )->defer_localization( 'imagifyOptions' );
|
||||
|
||||
$this->register_script( 'files-list', 'files-list', [ 'jquery', 'beat', 'underscore', 'chart', 'admin' ] )->defer_localization( 'imagifyFiles' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue stylesheets and scripts for the administration area.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*/
|
||||
public function enqueue_styles_and_scripts() {
|
||||
static $done = false;
|
||||
|
||||
if ( $done ) {
|
||||
return;
|
||||
}
|
||||
$done = true;
|
||||
|
||||
/*
|
||||
* Register stylesheets and scripts.
|
||||
*/
|
||||
$this->register_styles_and_scripts();
|
||||
|
||||
/**
|
||||
* Admin bar.
|
||||
*/
|
||||
if ( $this->is_admin_bar_item_showing() ) {
|
||||
$this->enqueue_assets( 'admin-bar' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Notices.
|
||||
*/
|
||||
$notices = Notices::get_instance();
|
||||
|
||||
if ( $notices->has_notices() ) {
|
||||
if ( $notices->display_welcome_steps() || $notices->display_wrong_api_key() ) {
|
||||
// This is where we display things about the API key.
|
||||
$this->enqueue_assets( 'sweetalert' );
|
||||
}
|
||||
|
||||
$this->enqueue_assets( 'notices' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Loaded in the library and attachment edition.
|
||||
*/
|
||||
if ( imagify_is_screen( 'library' ) || imagify_is_screen( 'attachment' ) ) {
|
||||
$this->enqueue_assets( 'twentytwenty' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Loaded in the library.
|
||||
*/
|
||||
if ( imagify_is_screen( 'library' ) ) {
|
||||
$this->enqueue_style( 'admin' )->enqueue_script( 'library' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Loaded in the bulk optimization page.
|
||||
*/
|
||||
if ( imagify_is_screen( 'bulk' ) ) {
|
||||
$this->enqueue_assets( [ 'pricing-modal', 'bulk' ] );
|
||||
}
|
||||
|
||||
/*
|
||||
* Loaded in the settings page.
|
||||
*/
|
||||
if ( imagify_is_screen( 'imagify-settings' ) ) {
|
||||
$this->enqueue_assets( [ 'sweetalert', 'notices', 'twentytwenty', 'pricing-modal', 'options' ] );
|
||||
}
|
||||
|
||||
/*
|
||||
* Loaded in the files list page.
|
||||
*/
|
||||
if ( imagify_is_screen( 'files-list' ) ) {
|
||||
$this->enqueue_assets( [ 'files-list', 'twentytwenty' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered after Imagify CSS and JS have been enqueued.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*/
|
||||
do_action( 'imagify_assets_enqueued' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue stylesheets and scripts for the media modal.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*/
|
||||
public function enqueue_media_modal() {
|
||||
static $done = false;
|
||||
|
||||
if ( $done ) {
|
||||
return;
|
||||
}
|
||||
$done = true;
|
||||
|
||||
/*
|
||||
* Register stylesheets and scripts.
|
||||
*/
|
||||
$this->register_styles_and_scripts();
|
||||
|
||||
$this->enqueue_style( 'admin' )->enqueue_script( 'media-modal' );
|
||||
|
||||
// When the optimization buttons are displayed in the media modal, they are fetched through ajax, so they can’t print the "processing" button template in the footer.
|
||||
Imagify_Views::get_instance()->print_js_template_in_footer( 'button/processing' );
|
||||
|
||||
/**
|
||||
* Triggered after Imagify CSS and JS have been enqueued for the media modal.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*/
|
||||
do_action( 'imagify_media_modal_assets_enqueued' );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** PUBLIC TOOLS ============================================================================ */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Register a style.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*
|
||||
* @param string $handle Name of the stylesheet. Should be unique.
|
||||
* @param string|null $file_name The file name, without the extension. If null, $handle is used.
|
||||
* @param array $dependencies An array of registered stylesheet handles this stylesheet depends on.
|
||||
* @param string|null $version String specifying stylesheet version number. If set to null, the plugin version is used. If SCRIPT_DEBUG is true, a random string is used.
|
||||
* @return object This class instance.
|
||||
*/
|
||||
public function register_style( $handle, $file_name = null, $dependencies = [], $version = null ) {
|
||||
// If we register it, it's one of our styles.
|
||||
$this->styles[ $handle ] = 1;
|
||||
$this->current_handle = $handle;
|
||||
$this->current_handle_type = 'css';
|
||||
|
||||
$file_name = $file_name ? $file_name : $handle;
|
||||
$version = $version ? $version : IMAGIFY_VERSION;
|
||||
$version = $this->is_debug() ? self::$version : $version;
|
||||
$extension = $this->is_debug() ? '.css' : '.min.css';
|
||||
$handle = self::CSS_PREFIX . $handle;
|
||||
$dependencies = $this->prefix_dependencies( $dependencies, 'css' );
|
||||
|
||||
wp_register_style(
|
||||
$handle,
|
||||
IMAGIFY_URL . 'assets/css/' . $file_name . $extension,
|
||||
$dependencies,
|
||||
$version
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue a style.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*
|
||||
* @param string|array $handles Name of the stylesheet. Should be unique. Can be an array to enqueue several stylesheets.
|
||||
* @return object This class instance.
|
||||
*/
|
||||
public function enqueue_style( $handles ) {
|
||||
$handles = (array) $handles;
|
||||
|
||||
foreach ( $handles as $handle ) {
|
||||
$this->current_handle = $handle;
|
||||
$this->current_handle_type = 'css';
|
||||
|
||||
if ( ! empty( $this->styles[ $handle ] ) ) {
|
||||
// If we registered it, it's one of our styles.
|
||||
$handle = self::CSS_PREFIX . $handle;
|
||||
}
|
||||
|
||||
wp_enqueue_style( $handle );
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dequeue a style.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*
|
||||
* @param string|array $handles Name of the stylesheet. Should be unique. Can be an array to dequeue several stylesheets.
|
||||
* @return object This class instance.
|
||||
*/
|
||||
public function dequeue_style( $handles ) {
|
||||
$handles = (array) $handles;
|
||||
|
||||
foreach ( $handles as $handle ) {
|
||||
$this->current_handle = $handle;
|
||||
$this->current_handle_type = 'css';
|
||||
|
||||
if ( ! empty( $this->styles[ $handle ] ) ) {
|
||||
// If we registered it, it's one of our styles.
|
||||
$handle = self::CSS_PREFIX . $handle;
|
||||
}
|
||||
|
||||
wp_dequeue_style( $handle );
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a script.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*
|
||||
* @param string $handle Name of the script. Should be unique.
|
||||
* @param string|null $file_name The file name, without the extension. If null, $handle is used.
|
||||
* @param array $dependencies An array of registered script handles this script depends on.
|
||||
* @param string|null $version String specifying script version number. If set to null, the plugin version is used. If SCRIPT_DEBUG is true, a random string is used.
|
||||
* @return object This class instance.
|
||||
*/
|
||||
public function register_script( $handle, $file_name = null, $dependencies = [], $version = null ) {
|
||||
// If we register it, it's one of our scripts.
|
||||
$this->scripts[ $handle ] = 1;
|
||||
// Set the current handler and handler type.
|
||||
$this->current_handle = $handle;
|
||||
$this->current_handle_type = 'js';
|
||||
|
||||
$file_name = $file_name ? $file_name : $handle;
|
||||
$version = $version ? $version : IMAGIFY_VERSION;
|
||||
$version = $this->is_debug() ? self::$version : $version;
|
||||
$extension = $this->is_debug() ? '.js' : '.min.js';
|
||||
$handle = self::JS_PREFIX . $handle;
|
||||
$dependencies = $this->prefix_dependencies( $dependencies );
|
||||
|
||||
wp_register_script(
|
||||
$handle,
|
||||
IMAGIFY_URL . 'assets/js/' . $file_name . $extension,
|
||||
$dependencies,
|
||||
$version,
|
||||
true
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue a script.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*
|
||||
* @param string|array $handles Name of the script. Should be unique. Can be an array to enqueue several scripts.
|
||||
* @return object This class instance.
|
||||
*/
|
||||
public function enqueue_script( $handles ) {
|
||||
$handles = (array) $handles;
|
||||
|
||||
foreach ( $handles as $handle ) {
|
||||
// Enqueue the corresponding style.
|
||||
if ( ! empty( $this->styles[ $handle ] ) ) {
|
||||
$this->enqueue_style( $handle );
|
||||
}
|
||||
|
||||
$this->current_handle = $handle;
|
||||
$this->current_handle_type = 'js';
|
||||
|
||||
if ( ! empty( $this->scripts[ $handle ] ) ) {
|
||||
// If we registered it, it's one of our scripts.
|
||||
$handle = self::JS_PREFIX . $handle;
|
||||
}
|
||||
|
||||
wp_enqueue_script( $handle );
|
||||
|
||||
// Deferred localization.
|
||||
if ( ! empty( $this->deferred_localizations[ $this->current_handle ] ) ) {
|
||||
array_map( [ $this, 'localize' ], $this->deferred_localizations[ $this->current_handle ] );
|
||||
unset( $this->deferred_localizations[ $this->current_handle ] );
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dequeue a script.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*
|
||||
* @param string|array $handles Name of the script. Should be unique. Can be an array to dequeue several scripts.
|
||||
* @return object This class instance.
|
||||
*/
|
||||
public function dequeue_script( $handles ) {
|
||||
$handles = (array) $handles;
|
||||
|
||||
foreach ( $handles as $handle ) {
|
||||
// Enqueue the corresponding style.
|
||||
if ( ! empty( $this->styles[ $handle ] ) ) {
|
||||
$this->dequeue_style( $handle );
|
||||
}
|
||||
|
||||
$this->current_handle = $handle;
|
||||
$this->current_handle_type = 'js';
|
||||
|
||||
if ( ! empty( $this->scripts[ $handle ] ) ) {
|
||||
// If we registered it, it's one of our scripts.
|
||||
$handle = self::JS_PREFIX . $handle;
|
||||
}
|
||||
|
||||
wp_dequeue_script( $handle );
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Localize a script.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*
|
||||
* @param string $handle Name of the script. Should be unique.
|
||||
* @param string $object_name Name for the JavaScript object. Passed directly, so it should be qualified JS variable. Example: '/[a-zA-Z0-9_]+/'.
|
||||
* @param string|array|null $l10n The data itself. The data can be either a single or multi-dimensional array. If null, $handle is used.
|
||||
* @return object This class instance.
|
||||
*/
|
||||
public function localize_script( $handle, $object_name, $l10n = null ) {
|
||||
$this->current_handle = $handle;
|
||||
$this->current_handle_type = 'js';
|
||||
|
||||
if ( ! isset( $l10n ) ) {
|
||||
$l10n = $handle;
|
||||
}
|
||||
|
||||
if ( is_string( $l10n ) ) {
|
||||
$l10n = $this->get_localization_data( $l10n );
|
||||
}
|
||||
|
||||
if ( ! $l10n ) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ( ! empty( $this->scripts[ $handle ] ) ) {
|
||||
// If we registered it, it's one of our scripts.
|
||||
$handle = self::JS_PREFIX . $handle;
|
||||
}
|
||||
|
||||
wp_localize_script( $handle, $object_name, $l10n );
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue a style and a script that have the same handle.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*
|
||||
* @param string|array $handles Name of the script. Should be unique. Can be an array to enqueue several scripts.
|
||||
* @return object This class instance.
|
||||
*/
|
||||
public function enqueue_assets( $handles ) {
|
||||
$handles = (array) $handles;
|
||||
|
||||
foreach ( $handles as $handle ) {
|
||||
$this->enqueue_script( $handle );
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dequeue a style and a script that have the same handle.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*
|
||||
* @param string|array $handles Name of the script. Should be unique. Can be an array to dequeue several scripts.
|
||||
* @return object This class instance.
|
||||
*/
|
||||
public function dequeue_assets( $handles ) {
|
||||
$handles = (array) $handles;
|
||||
|
||||
foreach ( $handles as $handle ) {
|
||||
$this->dequeue_style( $handle );
|
||||
$this->dequeue_script( $handle );
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue the current script or style.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*
|
||||
* @return object This class instance.
|
||||
*/
|
||||
public function enqueue() {
|
||||
if ( 'js' === $this->current_handle_type ) {
|
||||
$this->enqueue_script( $this->current_handle );
|
||||
} elseif ( 'css' === $this->current_handle_type ) {
|
||||
$this->enqueue_style( $this->current_handle );
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Localize the current script.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*
|
||||
* @param string $object_name Name for the JavaScript object. Passed directly, so it should be qualified JS variable. Example: '/[a-zA-Z0-9_]+/'.
|
||||
* @param string|array|null $l10n The data itself. The data can be either a single or multi-dimensional array. If null, $handle is used.
|
||||
* @return object This class instance.
|
||||
*/
|
||||
public function localize( $object_name, $l10n = null ) {
|
||||
return $this->localize_script( $this->current_handle, $object_name, $l10n );
|
||||
}
|
||||
|
||||
/**
|
||||
* Localize the current script when it is enqueued with `$this->enqueue()` or `$this->enqueue_script()`. This should be used right after `$this->register_script()`.
|
||||
* Be careful, it won't work if the script is enqueued because it's a dependency.
|
||||
* This is handy to not forget to localize the script later. It also prevents to localize the script right away, and maybe execute all localizations while the script is not enqueued (so we localize for nothing).
|
||||
*
|
||||
* @since 1.6.10
|
||||
*
|
||||
* @param string $object_name Name for the JavaScript object. Passed directly, so it should be qualified JS variable. Example: '/[a-zA-Z0-9_]+/'.
|
||||
* @return object This class instance.
|
||||
*/
|
||||
public function defer_localization( $object_name ) {
|
||||
if ( ! isset( $this->deferred_localizations[ $this->current_handle ] ) ) {
|
||||
$this->deferred_localizations[ $this->current_handle ] = [];
|
||||
}
|
||||
|
||||
$this->deferred_localizations[ $this->current_handle ][ $object_name ] = $object_name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a deferred localization.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*
|
||||
* @param string $handle Name of the script. Should be unique.
|
||||
* @param string $object_name Name for the JavaScript object. Passed directly, so it should be qualified JS variable. Example: '/[a-zA-Z0-9_]+/'.
|
||||
* @return object This class instance.
|
||||
*/
|
||||
public function remove_deferred_localization( $handle, $object_name = null ) {
|
||||
if ( empty( $this->deferred_localizations[ $handle ] ) ) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ( $object_name ) {
|
||||
unset( $this->deferred_localizations[ $handle ][ $object_name ] );
|
||||
} else {
|
||||
unset( $this->deferred_localizations[ $handle ] );
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all translations we can use with wp_localize_script().
|
||||
*
|
||||
* @since 1.6.10
|
||||
*
|
||||
* @param string $context The translation context.
|
||||
* @param array $more_data More data to merge.
|
||||
* @return array $translations The translations.
|
||||
*/
|
||||
public function get_localization_data( $context, $more_data = [] ) {
|
||||
$data = get_imagify_localize_script_translations( $context );
|
||||
|
||||
if ( $more_data ) {
|
||||
return array_merge( $data, $more_data );
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** INTERNAL TOOLS ========================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Prefix the dependencies if they are ours.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*
|
||||
* @param array $dependencies An array of registered script handles this script depends on.
|
||||
* @param string $type Type of dependency: css or js.
|
||||
* @return array
|
||||
*/
|
||||
protected function prefix_dependencies( $dependencies, $type = 'js' ) {
|
||||
if ( ! $dependencies ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if ( 'js' === $type ) {
|
||||
$prefix = self::JS_PREFIX;
|
||||
$scripts = $this->scripts;
|
||||
} else {
|
||||
$prefix = self::CSS_PREFIX;
|
||||
$scripts = $this->styles;
|
||||
}
|
||||
|
||||
$depts = [];
|
||||
|
||||
foreach ( $dependencies as $dept ) {
|
||||
if ( ! empty( $scripts[ $dept ] ) ) {
|
||||
$depts[] = $prefix . $dept;
|
||||
} else {
|
||||
$depts[] = $dept;
|
||||
}
|
||||
}
|
||||
|
||||
return $depts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if debug is on.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function is_debug() {
|
||||
return defined( 'IMAGIFY_DEBUG' ) && IMAGIFY_DEBUG || defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the admin bar item is displaying.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function is_admin_bar_item_showing() {
|
||||
if ( defined( 'IMAGIFY_HIDDEN_ACCOUNT' ) && IMAGIFY_HIDDEN_ACCOUNT ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return get_imagify_option( 'api_key' ) && is_admin_bar_showing() && imagify_get_context( 'wp' )->current_user_can( 'manage' ) && get_imagify_option( 'admin_bar_menu' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,652 @@
|
||||
<?php
|
||||
|
||||
use Imagify\Traits\InstanceGetterTrait;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Class that handles the auto-optimization process.
|
||||
* This occurs when a new image is uploaded, and when an optimized image is worked with (resized, etc).
|
||||
* The process will work only if wp_generate_attachment_metadata() and wp_update_attachment_metadata() are used.
|
||||
*
|
||||
* @since 1.8.4
|
||||
*/
|
||||
class Imagify_Auto_Optimization extends Imagify_Auto_Optimization_Deprecated {
|
||||
use InstanceGetterTrait;
|
||||
|
||||
/**
|
||||
* An array containing all the "steps" an attachment is going through.
|
||||
* This is used to decide the behavior of the automatic optimization.
|
||||
*
|
||||
* @var array {
|
||||
* An array of arrays with attachment ID as keys.
|
||||
* Each array can contain the following:
|
||||
*
|
||||
* @type $upload int Set to 1 if the attachment is a new upload.
|
||||
* @type $generate int Set to 1 when going though wp_generate_attachment_metadata().
|
||||
* @type $update int Set to 1 when going though wp_update_attachment_metadata().
|
||||
* }
|
||||
* @since 1.8.4
|
||||
* @since 1.9.10 Private.
|
||||
* @since 1.9.10 Items are arrays instead of 1s.
|
||||
*/
|
||||
private $attachments = [];
|
||||
|
||||
/**
|
||||
* Tell if we’re using WP 5.3+.
|
||||
*
|
||||
* @var bool
|
||||
* @since 1.9.10
|
||||
*/
|
||||
private $is_wp_53;
|
||||
|
||||
/**
|
||||
* The ID of the attachment that failed to be uploaded.
|
||||
*
|
||||
* @var int
|
||||
* @since 1.9.8
|
||||
*/
|
||||
protected $upload_failure_id = 0;
|
||||
|
||||
/**
|
||||
* Used to prevent an auto-optimization locally.
|
||||
*
|
||||
* @var array
|
||||
* @since 1.8.4
|
||||
*/
|
||||
private static $prevented = [];
|
||||
|
||||
/**
|
||||
* Used to prevent an auto-optimization internally.
|
||||
*
|
||||
* @var array
|
||||
* @since 1.9.8
|
||||
*/
|
||||
private static $prevented_internally = [];
|
||||
|
||||
/**
|
||||
* Init.
|
||||
*
|
||||
* @since 1.8.4
|
||||
*/
|
||||
public function init() {
|
||||
global $wp_version;
|
||||
|
||||
$priority = IMAGIFY_INT_MAX - 30;
|
||||
$this->is_wp_53 = version_compare( $wp_version, '5.3-alpha1' ) >= 0;
|
||||
|
||||
// Automatic optimization tunel.
|
||||
add_action( 'add_attachment', [ $this, 'store_upload_ids' ], $priority );
|
||||
add_filter( 'wp_generate_attachment_metadata', [ $this, 'maybe_store_generate_step' ], $priority, 2 );
|
||||
add_filter( 'wp_update_attachment_metadata', [ $this, 'store_ids_to_optimize' ], $priority, 2 );
|
||||
|
||||
if ( $this->is_wp_53 ) {
|
||||
// WP 5.3+.
|
||||
add_action( 'imagify_after_auto_optimization_init', [ $this, 'do_auto_optimization' ], $priority, 2 );
|
||||
// Upload failure recovering.
|
||||
add_action( 'wp_ajax_media-create-image-subsizes', [ $this, 'prevent_auto_optimization_when_recovering_from_upload_failure' ], -5 ); // Before WP’s hook (priority 1).
|
||||
} else {
|
||||
add_action( 'updated_post_meta', [ $this, 'do_auto_optimization_after_meta_update' ], $priority, 4 );
|
||||
add_action( 'added_post_meta', [ $this, 'do_auto_optimization_after_meta_update' ], $priority, 4 );
|
||||
}
|
||||
|
||||
add_action( 'deleted_post_meta', [ $this, 'unset_optimization' ], $priority, 3 );
|
||||
|
||||
// Prevent to re-optimize when updating the image width and height (when resizing the full image).
|
||||
add_action( 'imagify_before_update_wp_media_data_dimensions', [ __CLASS__, 'prevent_optimization' ], 5 );
|
||||
add_action( 'imagify_after_update_wp_media_data_dimensions', [ __CLASS__, 'allow_optimization' ], 5 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the hooks.
|
||||
*
|
||||
* @since 1.8.4
|
||||
*/
|
||||
public function remove_hooks() {
|
||||
$priority = IMAGIFY_INT_MAX - 30;
|
||||
|
||||
// Automatic optimization tunel.
|
||||
remove_action( 'add_attachment', [ $this, 'store_upload_ids' ], $priority );
|
||||
remove_filter( 'wp_generate_attachment_metadata', [ $this, 'maybe_store_generate_step' ], $priority );
|
||||
remove_filter( 'wp_update_attachment_metadata', [ $this, 'store_ids_to_optimize' ], $priority );
|
||||
|
||||
if ( $this->is_wp_53 ) {
|
||||
// WP 5.3+.
|
||||
remove_action( 'imagify_after_auto_optimization_init', [ $this, 'do_auto_optimization' ], $priority );
|
||||
// Upload failure recovering.
|
||||
remove_action( 'wp_ajax_media-create-image-subsizes', [ $this, 'prevent_auto_optimization_when_recovering_from_upload_failure' ], -5 );
|
||||
} else {
|
||||
remove_action( 'updated_post_meta', [ $this, 'do_auto_optimization_after_meta_update' ], $priority );
|
||||
remove_action( 'added_post_meta', [ $this, 'do_auto_optimization_after_meta_update' ], $priority );
|
||||
}
|
||||
|
||||
remove_action( 'deleted_post_meta', [ $this, 'unset_optimization' ], $priority );
|
||||
|
||||
// Prevent to re-optimize when updating the image width and height (when resizing the full image).
|
||||
remove_action( 'imagify_before_update_wp_media_data_dimensions', [ __CLASS__, 'prevent_optimization' ], 5 );
|
||||
remove_action( 'imagify_after_update_wp_media_data_dimensions', [ __CLASS__, 'allow_optimization' ], 5 );
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** HOOKS =================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Store the "upload step" when an attachment has just been uploaded.
|
||||
*
|
||||
* @since 1.8.4
|
||||
* @see $this->store_ids_to_optimize()
|
||||
*
|
||||
* @param int $attachment_id Current attachment ID.
|
||||
*/
|
||||
public function store_upload_ids( $attachment_id ) {
|
||||
if ( ! self::is_optimization_prevented( $attachment_id ) && imagify_is_attachment_mime_type_supported( $attachment_id ) ) {
|
||||
$this->set_step( $attachment_id, 'upload' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the "generate step" when wp_generate_attachment_metadata() is used.
|
||||
*
|
||||
* @since 1.9.10
|
||||
*
|
||||
* @param array $metadata An array of attachment meta data.
|
||||
* @param int $attachment_id Current attachment ID.
|
||||
* @return array
|
||||
*/
|
||||
public function maybe_store_generate_step( $metadata, $attachment_id ) {
|
||||
if ( self::is_optimization_prevented( $attachment_id ) ) {
|
||||
return $metadata;
|
||||
}
|
||||
|
||||
if ( empty( $metadata ) || ! imagify_is_attachment_mime_type_supported( $attachment_id ) ) {
|
||||
$this->unset_steps( $attachment_id );
|
||||
return $metadata;
|
||||
}
|
||||
|
||||
$this->set_step( $attachment_id, 'generate' );
|
||||
|
||||
return $metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* After the attachment meta data has been generated (partially, since WP 5.3), init the auto-optimization.
|
||||
* Two cases are possible to trigger the optimization:
|
||||
* - It's a new upload and auto-optimization is enabled.
|
||||
* - It's not a new upload (it is regenerated) and the attachment is already optimized.
|
||||
*
|
||||
* @since 1.8.4
|
||||
*
|
||||
* @param array $metadata An array of attachment meta data.
|
||||
* @param int $attachment_id Current attachment ID.
|
||||
* @return array
|
||||
*/
|
||||
public function store_ids_to_optimize( $metadata, $attachment_id ) {
|
||||
static $auto_optimize;
|
||||
|
||||
if ( self::is_optimization_prevented( $attachment_id ) ) {
|
||||
return $metadata;
|
||||
}
|
||||
|
||||
if ( empty( $metadata ) || ! imagify_is_attachment_mime_type_supported( $attachment_id ) ) {
|
||||
$this->unset_steps( $attachment_id );
|
||||
return $metadata;
|
||||
}
|
||||
|
||||
if ( ! $this->has_step( $attachment_id, 'generate' ) ) {
|
||||
return $metadata;
|
||||
}
|
||||
|
||||
$is_new_upload = $this->has_step( $attachment_id, 'upload' );
|
||||
|
||||
if ( $is_new_upload ) {
|
||||
// It's a new upload.
|
||||
if ( ! isset( $auto_optimize ) ) {
|
||||
$auto_optimize = get_imagify_option( 'auto_optimize' );
|
||||
}
|
||||
|
||||
if ( ! $auto_optimize ) {
|
||||
/**
|
||||
* Fires when a new attachment is uploaded but auto-optimization is disabled.
|
||||
*
|
||||
* @since 1.8.4
|
||||
*
|
||||
* @param int $attachment_id Attachment ID.
|
||||
* @param array $metadata An array of attachment meta data.
|
||||
*/
|
||||
do_action( 'imagify_new_attachment_auto_optimization_disabled', $attachment_id, $metadata );
|
||||
|
||||
return $metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow to prevent automatic optimization for a specific attachment.
|
||||
*
|
||||
* @since 1.6.12
|
||||
*
|
||||
* @param bool $optimize True to optimize, false otherwise.
|
||||
* @param int $attachment_id Attachment ID.
|
||||
* @param array $metadata An array of attachment meta data.
|
||||
*/
|
||||
$optimize = apply_filters( 'imagify_auto_optimize_attachment', true, $attachment_id, $metadata );
|
||||
|
||||
if ( ! $optimize ) {
|
||||
return $metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* It's a new upload and auto-optimization is enabled.
|
||||
*/
|
||||
}
|
||||
|
||||
if ( ! $is_new_upload ) {
|
||||
// An existing attachment being regenerated (or something).
|
||||
$process = imagify_get_optimization_process( $attachment_id, 'wp' );
|
||||
|
||||
if ( ! $process->is_valid() ) {
|
||||
// Uh?
|
||||
return $metadata;
|
||||
}
|
||||
|
||||
if ( ! $process->get_data()->get_optimization_status() ) {
|
||||
/**
|
||||
* Fires when an attachment is updated but not optimized yet.
|
||||
*
|
||||
* @since 1.8.4
|
||||
*
|
||||
* @param int $attachment_id Attachment ID.
|
||||
* @param array $metadata An array of attachment meta data.
|
||||
*/
|
||||
do_action( 'imagify_not_optimized_attachment_updated', $attachment_id, $metadata );
|
||||
|
||||
return $metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow to prevent automatic reoptimization for a specific attachment.
|
||||
*
|
||||
* @since 1.8.4
|
||||
*
|
||||
* @param bool $optimize True to optimize, false otherwise.
|
||||
* @param int $attachment_id Attachment ID.
|
||||
* @param array $metadata An array of attachment meta data.
|
||||
*/
|
||||
$optimize = apply_filters( 'imagify_auto_optimize_optimized_attachment', true, $attachment_id, $metadata );
|
||||
|
||||
if ( ! $optimize ) {
|
||||
return $metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* The attachment already exists and was already optimized.
|
||||
*/
|
||||
}
|
||||
|
||||
// Ready for the next step.
|
||||
$this->set_step( $attachment_id, 'update' );
|
||||
|
||||
/**
|
||||
* Triggered after a media auto-optimization init.
|
||||
*
|
||||
* @since 1.9.8
|
||||
*
|
||||
* @param int $attachment_id The media ID.
|
||||
* @param bool $is_new_upload True if it's a new upload. False otherwize.
|
||||
*/
|
||||
do_action( 'imagify_after_auto_optimization_init', $attachment_id, $is_new_upload );
|
||||
|
||||
return $metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch auto optimization immediately after the post meta '_wp_attachment_metadata' is added or updated.
|
||||
*
|
||||
* @since 1.9
|
||||
* @since 1.9 Previously named do_auto_optimization().
|
||||
*
|
||||
* @param int $meta_id ID of the metadata entry.
|
||||
* @param int $attachment_id Current attachment ID.
|
||||
* @param string $meta_key Meta key.
|
||||
* @param mixed $metadata Meta value.
|
||||
*/
|
||||
public function do_auto_optimization_after_meta_update( $meta_id, $attachment_id, $meta_key, $metadata ) {
|
||||
if ( '_wp_attachment_metadata' !== $meta_key ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( self::is_optimization_prevented( $attachment_id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! $this->has_step( $attachment_id, 'update' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->do_auto_optimization( $attachment_id, $this->has_step( $attachment_id, 'upload' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch auto optimization immediately after the post meta '_wp_attachment_metadata' is added or updated.
|
||||
*
|
||||
* @since 1.8.4
|
||||
* @since 1.9.8 Changed signature.
|
||||
*
|
||||
* @param int $attachment_id The media ID.
|
||||
* @param bool $is_new_upload True if it's a new upload. False otherwize.
|
||||
*/
|
||||
public function do_auto_optimization( $attachment_id, $is_new_upload ) {
|
||||
$this->unset_steps( $attachment_id );
|
||||
|
||||
$process = imagify_get_optimization_process( $attachment_id, 'wp' );
|
||||
|
||||
/**
|
||||
* Fires before an attachment auto-optimization is triggered.
|
||||
*
|
||||
* @since 1.8.4
|
||||
*
|
||||
* @param int $attachment_id The attachment ID.
|
||||
* @param bool $is_new_upload True if it's a new upload. False otherwize.
|
||||
*/
|
||||
do_action_deprecated( 'imagify_before_auto_optimization_launch', [ $attachment_id, $is_new_upload ], '1.9', 'imagify_before_auto_optimization' );
|
||||
|
||||
/**
|
||||
* Triggered before a media is auto-optimized.
|
||||
*
|
||||
* @since 1.8.4
|
||||
*
|
||||
* @param int $attachment_id The media ID.
|
||||
* @param bool $is_new_upload True if it's a new upload. False otherwize.
|
||||
*/
|
||||
do_action( 'imagify_before_auto_optimization', $attachment_id, $is_new_upload );
|
||||
|
||||
if ( $is_new_upload ) {
|
||||
/**
|
||||
* It's a new upload.
|
||||
*/
|
||||
// Optimize.
|
||||
$process->optimize( null, [ 'is_new_upload' => 1 ] );
|
||||
} else {
|
||||
/**
|
||||
* The media has already been optimized (or at least it has been tried).
|
||||
*/
|
||||
$process_data = $process->get_data();
|
||||
|
||||
// Get the optimization level before deleting the optimization data.
|
||||
$optimization_level = $process_data->get_optimization_level();
|
||||
|
||||
// Some specifics for the image editor.
|
||||
if ( isset( $_POST['action'], $_POST['do'], $_POST['postid'] ) && 'image-editor' === $_POST['action'] && (int) $_POST['postid'] === $attachment_id ) { // WPCS: CSRF ok.
|
||||
check_ajax_referer( 'image_editor-' . $attachment_id );
|
||||
|
||||
if ( ! current_user_can( 'edit_post', $attachment_id ) ) {
|
||||
imagify_die();
|
||||
}
|
||||
|
||||
// Restore the backup file.
|
||||
$result = $process->restore();
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
// Restoration failed, there is no good way to handle this case.
|
||||
$process_data->delete_optimization_data();
|
||||
}
|
||||
} else {
|
||||
// Remove old optimization data.
|
||||
$process_data->delete_optimization_data();
|
||||
}
|
||||
|
||||
// Optimize.
|
||||
$process->optimize( $optimization_level );
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered after a media auto-optimization is launched.
|
||||
*
|
||||
* @since 1.8.4
|
||||
*
|
||||
* @param int $attachment_id The media ID.
|
||||
* @param bool $is_new_upload True if it's a new upload. False otherwize.
|
||||
*/
|
||||
do_action( 'imagify_after_auto_optimization', $attachment_id, $is_new_upload );
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the attachment ID from the $attachments property if the post meta '_wp_attachment_metadata' is deleted.
|
||||
*
|
||||
* @since 1.8.4
|
||||
*
|
||||
* @param int $meta_ids An array of deleted metadata entry IDs.
|
||||
* @param int $attachment_id Current attachment ID.
|
||||
* @param string $meta_key Meta key.
|
||||
*/
|
||||
public function unset_optimization( $meta_ids, $attachment_id, $meta_key ) {
|
||||
if ( '_wp_attachment_metadata' !== $meta_key ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->unset_steps( $attachment_id );
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** HOOKS FOR WP 5.3+’S UPLOAD FAILURE RECOVERING =========================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* With WP 5.3+, prevent auto-optimization when WP tries to create thumbnails after an upload error, because it triggers wp_update_attachment_metadata() for each thumbnail size.
|
||||
*
|
||||
* @since 1.9.8
|
||||
* @see wp_ajax_media_create_image_subsizes()
|
||||
* @see wp_update_image_subsizes()
|
||||
*/
|
||||
public function prevent_auto_optimization_when_recovering_from_upload_failure() {
|
||||
if ( ! check_ajax_referer( 'media-form', false, false ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! current_user_can( 'upload_files' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! imagify_get_context( 'wp' )->current_user_can( 'auto-optimize' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$attachment_id = ! empty( $_POST['attachment_id'] ) ? (int) $_POST['attachment_id'] : 0;
|
||||
|
||||
if ( empty( $attachment_id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! imagify_is_attachment_mime_type_supported( $attachment_id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->upload_failure_id = $attachment_id;
|
||||
|
||||
// Auto-optimization will be done on shutdown.
|
||||
ob_start( [ $this, 'maybe_do_auto_optimization_after_recovering_from_upload_failure' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe launch auto-optimization after recovering from an upload failure, when all thumbnails are created.
|
||||
*
|
||||
* @since 1.9.8
|
||||
* @see wp_ajax_media_create_image_subsizes()
|
||||
*
|
||||
* @param string $content Buffer’s content.
|
||||
* @return string Buffer’s content.
|
||||
*/
|
||||
public function maybe_do_auto_optimization_after_recovering_from_upload_failure( $content ) {
|
||||
if ( empty( $content ) ) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
if ( empty( $this->upload_failure_id ) ) {
|
||||
// Uh?
|
||||
return $content;
|
||||
}
|
||||
|
||||
if ( ! get_post( $this->upload_failure_id ) ) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
$json = @json_decode( $content );
|
||||
|
||||
if ( empty( $json->success ) ) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
$attachment_id = $this->upload_failure_id;
|
||||
$metadata = wp_get_attachment_metadata( $attachment_id );
|
||||
|
||||
// Launch the process.
|
||||
$this->upload_failure_id = 0;
|
||||
$this->set_step( $attachment_id, 'generate' );
|
||||
$this->store_ids_to_optimize( $metadata, $attachment_id );
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** TOOLS =================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Set a "step" for an attachment.
|
||||
*
|
||||
* @since 1.9.10
|
||||
* @see $this->attachments
|
||||
*
|
||||
* @param int $attachment_id Current attachment ID.
|
||||
* @param string $step The step to add.
|
||||
*/
|
||||
public function set_step( $attachment_id, $step ) {
|
||||
if ( empty( $this->attachments[ $attachment_id ] ) ) {
|
||||
$this->attachments[ $attachment_id ] = [];
|
||||
}
|
||||
|
||||
$this->attachments[ $attachment_id ][ $step ] = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unset a "step" for an attachment.
|
||||
*
|
||||
* @since 1.9.10
|
||||
* @see $this->attachments
|
||||
*
|
||||
* @param int $attachment_id Current attachment ID.
|
||||
* @param string $step The step to add.
|
||||
*/
|
||||
public function unset_step( $attachment_id, $step ) {
|
||||
unset( $this->attachments[ $attachment_id ][ $step ] );
|
||||
|
||||
if ( empty( $this->attachments[ $attachment_id ] ) ) {
|
||||
$this->unset_steps( $attachment_id );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unset all "steps" for an attachment.
|
||||
*
|
||||
* @since 1.9.10
|
||||
* @see $this->attachments
|
||||
*
|
||||
* @param int $attachment_id Current attachment ID.
|
||||
*/
|
||||
public function unset_steps( $attachment_id ) {
|
||||
unset( $this->attachments[ $attachment_id ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if a "step" for an attachment exists.
|
||||
*
|
||||
* @since 1.9.10
|
||||
* @see $this->attachments
|
||||
*
|
||||
* @param int $attachment_id Current attachment ID.
|
||||
* @param string $step The step to add.
|
||||
* @return bool
|
||||
*/
|
||||
public function has_step( $attachment_id, $step ) {
|
||||
return ! empty( $this->attachments[ $attachment_id ][ $step ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent an auto-optimization locally.
|
||||
* How to use it:
|
||||
* Imagify_Auto_Optimization::prevent_optimization( $attachment_id );
|
||||
* wp_update_attachment_metadata( $attachment_id );
|
||||
* Imagify_Auto_Optimization::allow_optimization( $attachment_id );
|
||||
*
|
||||
* @since 1.8.4
|
||||
* @since 1.9.8 Prevents/Allows can stack.
|
||||
*
|
||||
* @param int $attachment_id Current attachment ID.
|
||||
*/
|
||||
public static function prevent_optimization( $attachment_id ) {
|
||||
if ( ! isset( self::$prevented[ $attachment_id ] ) ) {
|
||||
self::$prevented[ $attachment_id ] = 1;
|
||||
} else {
|
||||
++self::$prevented[ $attachment_id ];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow an auto-optimization locally.
|
||||
* How to use it:
|
||||
* Imagify_Auto_Optimization::prevent_optimization( $attachment_id );
|
||||
* wp_update_attachment_metadata( $attachment_id );
|
||||
* Imagify_Auto_Optimization::allow_optimization( $attachment_id );
|
||||
*
|
||||
* @since 1.8.4
|
||||
* @since 1.9.8 Prevents/Allows can stack.
|
||||
*
|
||||
* @param int $attachment_id Current attachment ID.
|
||||
*/
|
||||
public static function allow_optimization( $attachment_id ) {
|
||||
if ( ! isset( self::$prevented[ $attachment_id ] ) ) {
|
||||
return;
|
||||
}
|
||||
--self::$prevented[ $attachment_id ];
|
||||
|
||||
if ( self::$prevented[ $attachment_id ] <= 0 ) {
|
||||
unset( self::$prevented[ $attachment_id ] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if an auto-optimization is prevented locally.
|
||||
*
|
||||
* @since 1.8.4
|
||||
*
|
||||
* @param int $attachment_id Current attachment ID.
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_optimization_prevented( $attachment_id ) {
|
||||
return ! empty( self::$prevented[ $attachment_id ] ) || ! empty( self::$prevented_internally[ $attachment_id ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent an auto-optimization internally.
|
||||
*
|
||||
* @since 1.9.8
|
||||
*
|
||||
* @param int $attachment_id Current attachment ID.
|
||||
*/
|
||||
protected static function prevent_optimization_internally( $attachment_id ) {
|
||||
self::$prevented_internally[ $attachment_id ] = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow an auto-optimization internally.
|
||||
*
|
||||
* @since 1.9.8
|
||||
*
|
||||
* @param int $attachment_id Current attachment ID.
|
||||
*/
|
||||
protected static function allow_optimization_internally( $attachment_id ) {
|
||||
unset( self::$prevented_internally[ $attachment_id ] );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Class that handles the cron that calculate and cache the library size.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class Imagify_Cron_Library_Size extends Imagify_Abstract_Cron {
|
||||
|
||||
/**
|
||||
* Class version.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
*/
|
||||
const VERSION = '1.0';
|
||||
|
||||
/**
|
||||
* Cron name.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected $event_name = 'imagify_update_library_size_calculations_event';
|
||||
|
||||
/**
|
||||
* Cron recurrence.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected $event_recurrence = 'weekly';
|
||||
|
||||
/**
|
||||
* Cron time.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected $event_time = '04:00';
|
||||
|
||||
/**
|
||||
* The single instance of the class.
|
||||
*
|
||||
* @var object
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected static $_instance;
|
||||
|
||||
/**
|
||||
* Get the main Instance.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return object Main instance.
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! isset( self::$_instance ) ) {
|
||||
self::$_instance = new self();
|
||||
}
|
||||
|
||||
return self::$_instance;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** HOOKS =================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* The event action.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
public function do_event() {
|
||||
imagify_do_async_job( array(
|
||||
'action' => 'imagify_update_estimate_sizes',
|
||||
'_ajax_nonce' => wp_create_nonce( 'update_estimate_sizes' ),
|
||||
) );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Class that handles the plugin rating cron.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class Imagify_Cron_Rating extends Imagify_Abstract_Cron {
|
||||
|
||||
/**
|
||||
* Class version.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
*/
|
||||
const VERSION = '1.0';
|
||||
|
||||
/**
|
||||
* Cron name.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected $event_name = 'imagify_rating_event';
|
||||
|
||||
/**
|
||||
* Cron recurrence.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected $event_recurrence = 'daily';
|
||||
|
||||
/**
|
||||
* Cron time.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected $event_time = '15:00';
|
||||
|
||||
/**
|
||||
* The single instance of the class.
|
||||
*
|
||||
* @var object
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected static $_instance;
|
||||
|
||||
/**
|
||||
* Get the main Instance.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return object Main instance.
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! isset( self::$_instance ) ) {
|
||||
self::$_instance = new self();
|
||||
}
|
||||
|
||||
return self::$_instance;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** HOOKS =================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Initiate the event.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
public function schedule_event() {
|
||||
if ( ! wp_next_scheduled( $this->get_event_name() ) && ! get_site_transient( 'do_imagify_rating_cron' ) ) {
|
||||
wp_schedule_event( $this->get_event_timestamp(), $this->get_event_recurrence(), $this->get_event_name() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The event action.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
public function do_event() {
|
||||
// Stop the process if the plugin isn't installed for 3 days.
|
||||
if ( get_site_transient( 'imagify_seen_rating_notice' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$user = get_imagify_user();
|
||||
|
||||
if ( ! is_wp_error( $user ) && isset( $user->image_count ) && (int) $user->image_count > 100 ) {
|
||||
set_site_transient( 'imagify_user_images_count', $user->image_count );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
use Imagify\Traits\InstanceGetterTrait;
|
||||
|
||||
/**
|
||||
* Class that scans the custom folders to keep files in sync in the database.
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
class Imagify_Cron_Sync_Files extends Imagify_Abstract_Cron {
|
||||
use InstanceGetterTrait;
|
||||
|
||||
/**
|
||||
* Class version.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
*/
|
||||
const VERSION = '1.0';
|
||||
|
||||
/**
|
||||
* Cron name.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
*/
|
||||
protected $event_name = 'imagify_sync_files';
|
||||
|
||||
/**
|
||||
* Cron recurrence.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
*/
|
||||
protected $event_recurrence = 'daily';
|
||||
|
||||
/**
|
||||
* Cron time.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
*/
|
||||
protected $event_time = '02:00';
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** HOOKS =================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* The event action.
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
public function do_event() {
|
||||
$folders_db = Imagify_Folders_DB::get_instance();
|
||||
$files_db = Imagify_Files_DB::get_instance();
|
||||
|
||||
if ( ! $folders_db->can_operate() || ! $files_db->can_operate() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! Imagify_Requirements::is_api_key_valid() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( Imagify_Requirements::is_over_quota() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->set_no_time_limit();
|
||||
|
||||
/**
|
||||
* Get the folders from DB.
|
||||
*/
|
||||
$folders = Imagify_Custom_Folders::get_folders();
|
||||
|
||||
if ( ! $folders ) {
|
||||
return;
|
||||
}
|
||||
|
||||
Imagify_Custom_Folders::synchronize_files_from_folders( $folders );
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
103
wp/wp-content/plugins/imagify/inc/classes/class-imagify-data.php
Normal file
103
wp/wp-content/plugins/imagify/inc/classes/class-imagify-data.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Class that handles the plugin data.
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
class Imagify_Data extends Imagify_Abstract_Options {
|
||||
|
||||
/**
|
||||
* Class version.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
*/
|
||||
const VERSION = '1.0';
|
||||
|
||||
/**
|
||||
* Suffix used in the name of the option.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected $identifier = 'data';
|
||||
|
||||
/**
|
||||
* The default values for the Imagify main options.
|
||||
* These are the "zero state" values.
|
||||
* Don't use null as value.
|
||||
*
|
||||
* @var array
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected $default_values = array(
|
||||
'total_size_images_library' => 0.0,
|
||||
'average_size_images_per_month' => 0.0,
|
||||
'previous_quota_percent' => 0.0,
|
||||
);
|
||||
|
||||
/**
|
||||
* The single instance of the class.
|
||||
*
|
||||
* @var object
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected static $_instance;
|
||||
|
||||
/**
|
||||
* Get the main Instance.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @return object Main instance.
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! isset( self::$_instance ) ) {
|
||||
self::$_instance = new self();
|
||||
}
|
||||
|
||||
return self::$_instance;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** SANITIZATION, VALIDATION ================================================================ */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Sanitize and validate an option value. Basic casts have been made.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @param string $key The option key.
|
||||
* @param mixed $value The value.
|
||||
* @param mixed $default The default value.
|
||||
* @return mixed
|
||||
*/
|
||||
public function sanitize_and_validate_value( $key, $value, $default ) {
|
||||
switch ( $key ) {
|
||||
case 'total_size_images_library':
|
||||
case 'average_size_images_per_month':
|
||||
if ( $value <= 0 ) {
|
||||
// Invalid.
|
||||
return 0.0;
|
||||
}
|
||||
return $value;
|
||||
|
||||
case 'previous_quota_percent':
|
||||
$value = round( $value, 1 );
|
||||
return min( max( 0, $value ), 100 );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
564
wp/wp-content/plugins/imagify/inc/classes/class-imagify-db.php
Normal file
564
wp/wp-content/plugins/imagify/inc/classes/class-imagify-db.php
Normal file
@@ -0,0 +1,564 @@
|
||||
<?php
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Imagify DB class. It reunites tools to work with the DB.
|
||||
*
|
||||
* @since 1.6.13
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class Imagify_DB {
|
||||
|
||||
/**
|
||||
* Class version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const VERSION = '1.0.1';
|
||||
|
||||
/**
|
||||
* Some hosts limit the number of JOINs in SQL queries, but we need them.
|
||||
*
|
||||
* @since 1.6.13
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
public static function unlimit_joins() {
|
||||
global $wpdb;
|
||||
static $done = false;
|
||||
|
||||
if ( $done ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$done = true;
|
||||
$query = 'SET SQL_BIG_SELECTS=1';
|
||||
|
||||
/**
|
||||
* Filter the SQL query allowing to remove the limit on JOINs.
|
||||
*
|
||||
* @since 1.6.13
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string|bool $query The query. False to prevent any query.
|
||||
*/
|
||||
$query = apply_filters( 'imagify_db_unlimit_joins_query', $query );
|
||||
|
||||
if ( $query && is_string( $query ) ) {
|
||||
$wpdb->query( $query ); // WPCS: unprepared SQL ok.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change an array of values into a comma separated list, ready to be used in a `IN ()` clause.
|
||||
*
|
||||
* @since 1.6.13
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $values An array of values.
|
||||
* @return string A comma separated list of values.
|
||||
*/
|
||||
public static function prepare_values_list( $values ) {
|
||||
$values = esc_sql( (array) $values );
|
||||
$values = array_map( array( __CLASS__, 'quote_string' ), $values );
|
||||
return implode( ',', $values );
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap a value in quotes, unless it's an integer.
|
||||
*
|
||||
* @since 1.6.13
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param int|string $value A value.
|
||||
* @return int|string
|
||||
*/
|
||||
public static function quote_string( $value ) {
|
||||
return is_numeric( $value ) ? $value : "'" . addcslashes( $value, "'" ) . "'";
|
||||
}
|
||||
|
||||
/**
|
||||
* First half of escaping for LIKE special characters % and _ before preparing for MySQL.
|
||||
* Use this only before wpdb::prepare() or esc_sql(). Reversing the order is very bad for security.
|
||||
*
|
||||
* Example Prepared Statement:
|
||||
* $wild = '%';
|
||||
* $find = 'only 43% of planets';
|
||||
* $like = $wild . $wpdb->esc_like( $find ) . $wild;
|
||||
* $sql = $wpdb->prepare( "SELECT * FROM $wpdb->posts WHERE post_content LIKE %s", $like );
|
||||
*
|
||||
* Example Escape Chain:
|
||||
* $sql = esc_sql( $wpdb->esc_like( $input ) );
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $text The raw text to be escaped. The input typed by the user should have no extra or deleted slashes.
|
||||
* @return string Text in the form of a LIKE phrase. The output is not SQL safe. Call $wpdb::prepare() or real_escape next.
|
||||
*/
|
||||
public static function esc_like( $text ) {
|
||||
global $wpdb;
|
||||
|
||||
if ( method_exists( $wpdb, 'esc_like' ) ) {
|
||||
// Introduced in WP 4.0.0.
|
||||
return $wpdb->esc_like( $text );
|
||||
}
|
||||
|
||||
return addcslashes( $text, '_%\\' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Imagify mime types, ready to be used in a `IN ()` clause.
|
||||
*
|
||||
* @since 1.6.13
|
||||
* @since 1.9 Added $type parameter.
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $type One of 'image', 'not-image'. Any other value will return all mime types.
|
||||
* @return string A comma separated list of mime types.
|
||||
*/
|
||||
public static function get_mime_types( $type = null ) {
|
||||
static $mime_types = [];
|
||||
|
||||
if ( empty( $type ) ) {
|
||||
$type = 'all';
|
||||
}
|
||||
|
||||
if ( ! isset( $mime_types[ $type ] ) ) {
|
||||
$mime_types[ $type ] = self::prepare_values_list( imagify_get_mime_types( $type ) );
|
||||
}
|
||||
|
||||
return $mime_types[ $type ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get post statuses related to attachments, ready to be used in a `IN ()` clause.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string A comma separated list of post statuses.
|
||||
*/
|
||||
public static function get_post_statuses() {
|
||||
static $statuses;
|
||||
|
||||
if ( ! isset( $statuses ) ) {
|
||||
$statuses = self::prepare_values_list( imagify_get_post_statuses() );
|
||||
}
|
||||
|
||||
return $statuses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the SQL JOIN clause to use to get only attachments that have the required WP metadata.
|
||||
* It returns an empty string if the database has no attachments without the required metadada.
|
||||
* It also triggers Imagify_DB::unlimit_joins().
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $id_field An ID field to match the metadata ID against in the JOIN clause.
|
||||
* Default is the posts table `ID` field, using the `p` alias: `p.ID`.
|
||||
* In case of "false" value or PEBKAC, fallback to the same field without alias.
|
||||
* @param bool $matching Set to false to get a query to fetch metas NOT matching the file extensions.
|
||||
* @param bool $test Test if the site has attachments without required metadata before returning the query. False to bypass the test and get the query anyway.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_required_wp_metadata_join_clause( $id_field = 'p.ID', $matching = true, $test = true ) {
|
||||
global $wpdb;
|
||||
|
||||
if ( $test && ! imagify_has_attachments_without_required_metadata() ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
self::unlimit_joins();
|
||||
$clause = '';
|
||||
|
||||
if ( ! $id_field || ! is_string( $id_field ) ) {
|
||||
$id_field = "$wpdb->posts.ID";
|
||||
}
|
||||
|
||||
$join = $matching ? 'INNER' : 'LEFT';
|
||||
|
||||
foreach ( self::get_required_wp_metadata_aliases() as $meta_name => $alias ) {
|
||||
$clause .= "
|
||||
$join JOIN $wpdb->postmeta AS $alias
|
||||
ON ( $id_field = $alias.post_id AND $alias.meta_key = '$meta_name' )";
|
||||
}
|
||||
|
||||
return $clause;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the SQL part to be used in a WHERE clause, to get only attachments that have (in)valid '_wp_attached_file' and '_wp_attachment_metadata' metadatas.
|
||||
* It returns an empty string if the database has no attachments without the required metadada.
|
||||
*
|
||||
* @since 1.7
|
||||
* @since 1.7.1.2 Use a single $arg parameter instead of 3. New $prepared parameter.
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $args {
|
||||
* Optional. An array of arguments.
|
||||
*
|
||||
* string $aliases The aliases to use for the meta values.
|
||||
* bool $matching Set to false to get a query to fetch invalid metas.
|
||||
* bool $test Test if the site has attachments without required metadata before returning the query. False to bypass the test and get the query anyway.
|
||||
* bool $prepared Set to true if the query will be prepared with using $wpdb->prepare().
|
||||
* }.
|
||||
* @return string A query.
|
||||
*/
|
||||
public static function get_required_wp_metadata_where_clause( $args = array() ) {
|
||||
static $query = array();
|
||||
|
||||
$args = imagify_merge_intersect( $args, array(
|
||||
'aliases' => array(),
|
||||
'matching' => true,
|
||||
'test' => true,
|
||||
'prepared' => false,
|
||||
) );
|
||||
|
||||
list( $aliases, $matching, $test, $prepared ) = array_values( $args );
|
||||
|
||||
if ( $test && ! imagify_has_attachments_without_required_metadata() ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ( $aliases && is_string( $aliases ) ) {
|
||||
$aliases = array(
|
||||
'_wp_attached_file' => $aliases,
|
||||
);
|
||||
} elseif ( ! is_array( $aliases ) ) {
|
||||
$aliases = array();
|
||||
}
|
||||
|
||||
$aliases = imagify_merge_intersect( $aliases, self::get_required_wp_metadata_aliases() );
|
||||
$key = implode( '|', $aliases ) . '|' . (int) $matching;
|
||||
|
||||
if ( isset( $query[ $key ] ) ) {
|
||||
return $prepared ? str_replace( '%', '%%', $query[ $key ] ) : $query[ $key ];
|
||||
}
|
||||
|
||||
unset( $args['prepared'] );
|
||||
$alias_1 = $aliases['_wp_attached_file'];
|
||||
$alias_2 = $aliases['_wp_attachment_metadata'];
|
||||
$extensions = self::get_extensions_where_clause( $args );
|
||||
|
||||
if ( $matching ) {
|
||||
$query[ $key ] = "AND $alias_1.meta_value NOT LIKE '%://%' AND $alias_1.meta_value NOT LIKE '_:\\\\\%' $extensions";
|
||||
} else {
|
||||
$query[ $key ] = "AND ( $alias_2.meta_value IS NULL OR $alias_1.meta_value IS NULL OR $alias_1.meta_value LIKE '%://%' OR $alias_1.meta_value LIKE '_:\\\\\%' $extensions )";
|
||||
}
|
||||
|
||||
return $prepared ? str_replace( '%', '%%', $query[ $key ] ) : $query[ $key ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the SQL part to be used in a WHERE clause, to get only attachments that have a valid file extensions.
|
||||
* It returns an empty string if the database has no attachments without the required metadada.
|
||||
*
|
||||
* @since 1.7
|
||||
* @since 1.7.1.2 Use a single $arg parameter instead of 3. New $prepared parameter.
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $args {
|
||||
* Optional. An array of arguments.
|
||||
*
|
||||
* string $alias The alias to use for the meta value.
|
||||
* bool $matching Set to false to get a query to fetch metas NOT matching the file extensions.
|
||||
* bool $test Test if the site has attachments without required metadata before returning the query. False to bypass the test and get the query anyway.
|
||||
* bool $prepared Set to true if the query will be prepared with using $wpdb->prepare().
|
||||
* }.
|
||||
* @return string A query.
|
||||
*/
|
||||
public static function get_extensions_where_clause( $args = false ) {
|
||||
static $extensions;
|
||||
static $query = array();
|
||||
|
||||
$args = imagify_merge_intersect( $args, array(
|
||||
'alias' => array(),
|
||||
'matching' => true,
|
||||
'test' => true,
|
||||
'prepared' => false,
|
||||
) );
|
||||
|
||||
list( $alias, $matching, $test, $prepared ) = array_values( $args );
|
||||
|
||||
if ( $test && ! imagify_has_attachments_without_required_metadata() ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ( ! isset( $extensions ) ) {
|
||||
$extensions = array_keys( imagify_get_mime_types() );
|
||||
$extensions = implode( '|', $extensions );
|
||||
$extensions = explode( '|', $extensions );
|
||||
}
|
||||
|
||||
if ( ! $alias ) {
|
||||
$alias = self::get_required_wp_metadata_aliases();
|
||||
$alias = $alias['_wp_attached_file'];
|
||||
}
|
||||
|
||||
$key = $alias . '|' . (int) $matching;
|
||||
|
||||
if ( isset( $query[ $key ] ) ) {
|
||||
return $prepared ? str_replace( '%', '%%', $query[ $key ] ) : $query[ $key ];
|
||||
}
|
||||
|
||||
if ( $matching ) {
|
||||
$query[ $key ] = "AND ( LOWER( $alias.meta_value ) LIKE '%." . implode( "' OR LOWER( $alias.meta_value ) LIKE '%.", $extensions ) . "' )";
|
||||
} else {
|
||||
$query[ $key ] = "OR ( LOWER( $alias.meta_value ) NOT LIKE '%." . implode( "' AND LOWER( $alias.meta_value ) NOT LIKE '%.", $extensions ) . "' )";
|
||||
}
|
||||
|
||||
return $prepared ? str_replace( '%', '%%', $query[ $key ] ) : $query[ $key ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the aliases used for the metas in self::get_required_wp_metadata_join_clause(), self::get_required_wp_metadata_where_clause(), and self::get_extensions_where_clause().
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array An array with the meta name as key and its alias as value.
|
||||
*/
|
||||
public static function get_required_wp_metadata_aliases() {
|
||||
return array(
|
||||
'_wp_attached_file' => 'imrwpmt1',
|
||||
'_wp_attachment_metadata' => 'imrwpmt2',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine two arrays with some specific keys.
|
||||
* We use this function to combine the result of 2 SQL queries.
|
||||
*
|
||||
* @since 1.6.13
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $keys An array of keys.
|
||||
* @param array $values An array of arrays like array( 'id' => id, 'value' => value ).
|
||||
* @param int $keep_keys_order Set to true to return an array ordered like $keys instead of $values.
|
||||
* @return array The combined arrays.
|
||||
*/
|
||||
public static function combine_query_results( $keys, $values, $keep_keys_order = false ) {
|
||||
if ( ! $keys || ! $values ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$result = array();
|
||||
$keys = array_flip( $keys );
|
||||
|
||||
foreach ( $values as $v ) {
|
||||
if ( isset( $keys[ $v['id'] ] ) ) {
|
||||
$result[ $v['id'] ] = $v['value'];
|
||||
}
|
||||
}
|
||||
|
||||
if ( $keep_keys_order ) {
|
||||
$keys = array_intersect_key( $keys, $result );
|
||||
return array_replace( $keys, $result );
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper to retrieve all values from one or several post metas, given a list of post IDs.
|
||||
* The $wpdb cache is flushed to save memory.
|
||||
*
|
||||
* @since 1.6.13
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $metas An array of meta names like:
|
||||
* array(
|
||||
* 'key1' => 'meta_name_1',
|
||||
* 'key2' => 'meta_name_2',
|
||||
* 'key3' => 'meta_name_3',
|
||||
* )
|
||||
* If a key contains 'data', the results will be unserialized.
|
||||
* @param array $ids An array of post IDs.
|
||||
* @return array An array of arrays of results like:
|
||||
* array(
|
||||
* 'key1' => array( post_id_1 => 'result_1', post_id_2 => 'result_2', post_id_3 => 'result_3' ),
|
||||
* 'key2' => array( post_id_1 => 'result_4', post_id_3 => 'result_5' ),
|
||||
* 'key3' => array( post_id_1 => 'result_6', post_id_2 => 'result_7' ),
|
||||
* )
|
||||
*/
|
||||
public static function get_metas( $metas, $ids ) {
|
||||
global $wpdb;
|
||||
|
||||
if ( ! $ids ) {
|
||||
return array_fill_keys( array_keys( $metas ), array() );
|
||||
}
|
||||
|
||||
$sql_ids = implode( ',', $ids );
|
||||
|
||||
foreach ( $metas as $result_name => $meta_name ) {
|
||||
$metas[ $result_name ] = $wpdb->get_results( // WPCS: unprepared SQL ok.
|
||||
"SELECT pm.post_id as id, pm.meta_value as value
|
||||
FROM $wpdb->postmeta as pm
|
||||
WHERE pm.meta_key = '$meta_name'
|
||||
AND pm.post_id IN ( $sql_ids )
|
||||
ORDER BY pm.post_id DESC",
|
||||
ARRAY_A
|
||||
);
|
||||
|
||||
$wpdb->flush();
|
||||
$metas[ $result_name ] = self::combine_query_results( $ids, $metas[ $result_name ], true );
|
||||
|
||||
if ( strpos( $result_name, 'data' ) !== false ) {
|
||||
$metas[ $result_name ] = array_map( 'maybe_unserialize', $metas[ $result_name ] );
|
||||
}
|
||||
}
|
||||
|
||||
return $metas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create/Upgrade the table in the database.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $table_name The (prefixed) table name.
|
||||
* @param string $schema_query Query representing the table schema.
|
||||
* @return bool True on success. False otherwise.
|
||||
*/
|
||||
public static function create_table( $table_name, $schema_query ) {
|
||||
global $wpdb;
|
||||
|
||||
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
|
||||
|
||||
$wpdb->hide_errors();
|
||||
|
||||
$schema_query = trim( $schema_query );
|
||||
$charset_collate = $wpdb->get_charset_collate();
|
||||
|
||||
dbDelta( "CREATE TABLE $table_name ($schema_query) $charset_collate;" );
|
||||
|
||||
return empty( $wpdb->last_error ) && self::table_exists( $table_name );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the given table exists.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $table_name Full name of the table (with DB prefix).
|
||||
* @return bool
|
||||
*/
|
||||
public static function table_exists( $table_name ) {
|
||||
global $wpdb;
|
||||
|
||||
$escaped_table = self::esc_like( $table_name );
|
||||
$result = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $escaped_table ) );
|
||||
|
||||
return $result === $table_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache transients used for optimization process locks.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $context The context.
|
||||
* @param array $media_ids The media IDs.
|
||||
*/
|
||||
public static function cache_process_locks( $context, $media_ids ) {
|
||||
global $wpdb;
|
||||
|
||||
if ( ! $context || ! $media_ids || wp_using_ext_object_cache() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Sanitize the IDs.
|
||||
$media_ids = array_filter( $media_ids );
|
||||
$media_ids = array_unique( $media_ids );
|
||||
|
||||
if ( ! $media_ids ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$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();
|
||||
|
||||
// Do 1 DB query per context (and cache results) before doing 1 get_transient() (2 DB queries) per media ID.
|
||||
$prefix = $is_network_wide ? '_site_transient_' : '_transient_';
|
||||
|
||||
if ( $is_network_wide && is_multisite() ) {
|
||||
$network_id = function_exists( 'get_current_network_id' ) ? get_current_network_id() : (int) $wpdb->siteid;
|
||||
$cache_prefix = "$network_id:";
|
||||
$notoptions_key = "$network_id:notoptions";
|
||||
$cache_group = 'site-options';
|
||||
$results = $wpdb->get_results(
|
||||
$wpdb->prepare(
|
||||
"SELECT meta_key as name, meta_value as value FROM $wpdb->sitemeta WHERE ( meta_key LIKE %s OR meta_key LIKE %s ) AND site_id = %d",
|
||||
$prefix . $transient_name,
|
||||
$prefix . 'timeout_' . $transient_name,
|
||||
$network_id
|
||||
),
|
||||
OBJECT_K
|
||||
); // WPCS: unprepared SQL ok.
|
||||
} else {
|
||||
$cache_prefix = '';
|
||||
$notoptions_key = 'notoptions';
|
||||
$cache_group = 'options';
|
||||
$results = $wpdb->get_results(
|
||||
$wpdb->prepare(
|
||||
"SELECT option_name as name, option_value as value FROM $wpdb->options WHERE ( option_name LIKE %s OR option_name LIKE %s )",
|
||||
$prefix . $transient_name,
|
||||
$prefix . 'timeout_' . $transient_name
|
||||
),
|
||||
OBJECT_K
|
||||
); // WPCS: unprepared SQL ok.
|
||||
}
|
||||
|
||||
$not_exist = [];
|
||||
|
||||
foreach ( [ '', 'timeout_' ] as $maybe_timeout ) {
|
||||
foreach ( $media_ids as $id ) {
|
||||
$option_name = $prefix . $maybe_timeout . str_replace( '%', $id, $transient_name );
|
||||
|
||||
if ( isset( $results[ $option_name ] ) ) {
|
||||
// Cache the value.
|
||||
$value = $results[ $option_name ]->value;
|
||||
$value = maybe_unserialize( $value );
|
||||
wp_cache_set( "$cache_prefix$option_name", $value, $cache_group );
|
||||
} else {
|
||||
// No value.
|
||||
$not_exist[ $option_name ] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $not_exist ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Cache the options that don't exist in the DB.
|
||||
$notoptions = wp_cache_get( $notoptions_key, $cache_group );
|
||||
$notoptions = is_array( $notoptions ) ? $notoptions : [];
|
||||
$notoptions = array_merge( $notoptions, $not_exist );
|
||||
|
||||
wp_cache_set( $notoptions_key, $notoptions, $cache_group );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* DB class that handles files in "custom folders".
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class Imagify_Files_DB extends Imagify_Abstract_DB {
|
||||
|
||||
/**
|
||||
* Class version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const VERSION = '1.1';
|
||||
|
||||
/**
|
||||
* The single instance of the class.
|
||||
*
|
||||
* @var object
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected static $_instance;
|
||||
|
||||
/**
|
||||
* The suffix used in the name of the database table (so, without the wpdb prefix).
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected $table = 'imagify_files';
|
||||
|
||||
/**
|
||||
* The version of our database table.
|
||||
*
|
||||
* @var int
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected $table_version = 102;
|
||||
|
||||
/**
|
||||
* Tell if the table is the same for each site of a Multisite.
|
||||
*
|
||||
* @var bool
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected $table_is_global = true;
|
||||
|
||||
/**
|
||||
* The name of the primary column.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected $primary_key = 'file_id';
|
||||
|
||||
/**
|
||||
* Get the main Instance.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return object Main instance.
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! isset( self::$_instance ) ) {
|
||||
self::$_instance = new self();
|
||||
}
|
||||
|
||||
return self::$_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the column placeholders.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_columns() {
|
||||
return array(
|
||||
'file_id' => '%d',
|
||||
'folder_id' => '%d',
|
||||
'file_date' => '%s',
|
||||
'path' => '%s',
|
||||
'hash' => '%s',
|
||||
'mime_type' => '%s',
|
||||
'modified' => '%d',
|
||||
'width' => '%d',
|
||||
'height' => '%d',
|
||||
'original_size' => '%d',
|
||||
'optimized_size' => '%d',
|
||||
'percent' => '%d',
|
||||
'optimization_level' => '%d',
|
||||
'status' => '%s',
|
||||
'error' => '%s',
|
||||
'data' => '%s',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default column values.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_column_defaults() {
|
||||
return array(
|
||||
'file_id' => 0,
|
||||
'folder_id' => 0,
|
||||
'file_date' => '0000-00-00 00:00:00',
|
||||
'path' => '',
|
||||
'hash' => '',
|
||||
'mime_type' => '',
|
||||
'modified' => 0,
|
||||
'width' => 0,
|
||||
'height' => 0,
|
||||
'original_size' => 0,
|
||||
'optimized_size' => null,
|
||||
'percent' => null,
|
||||
'optimization_level' => null,
|
||||
'status' => null,
|
||||
'error' => null,
|
||||
'data' => [],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query to create the table fields.
|
||||
*
|
||||
* For with and height: `smallint(2) unsigned` means 65,535px max.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_table_schema() {
|
||||
return "
|
||||
file_id bigint(20) unsigned NOT NULL auto_increment,
|
||||
folder_id bigint(20) unsigned NOT NULL default 0,
|
||||
file_date datetime NOT NULL default '0000-00-00 00:00:00',
|
||||
path varchar(191) NOT NULL default '',
|
||||
hash varchar(32) NOT NULL default '',
|
||||
mime_type varchar(100) NOT NULL default '',
|
||||
modified tinyint(1) unsigned NOT NULL default 0,
|
||||
width smallint(2) unsigned NOT NULL default 0,
|
||||
height smallint(2) unsigned NOT NULL default 0,
|
||||
original_size int(4) unsigned NOT NULL default 0,
|
||||
optimized_size int(4) unsigned default NULL,
|
||||
percent smallint(2) unsigned default NULL,
|
||||
optimization_level tinyint(1) unsigned default NULL,
|
||||
status varchar(20) default NULL,
|
||||
error varchar(255) default NULL,
|
||||
data longtext default NULL,
|
||||
PRIMARY KEY (file_id),
|
||||
UNIQUE KEY path (path),
|
||||
KEY folder_id (folder_id),
|
||||
KEY optimization_level (optimization_level),
|
||||
KEY status (status),
|
||||
KEY modified (modified)";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Class allowing to filter DirectoryIterator, to return only files that Imagify can optimize and folders.
|
||||
* It also allows to remove forbidden folders.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class Imagify_Files_Iterator extends FilterIterator {
|
||||
|
||||
/**
|
||||
* Class version.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
const VERSION = '1.0.2';
|
||||
|
||||
/**
|
||||
* Tell if the iterator will return both folders and images, or only images.
|
||||
*
|
||||
* @var bool
|
||||
* @since 1.7
|
||||
*/
|
||||
protected $include_folders;
|
||||
|
||||
/**
|
||||
* Filesystem object.
|
||||
*
|
||||
* @var object Imagify_Filesystem
|
||||
* @since 1.7.1
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $filesystem;
|
||||
|
||||
/**
|
||||
* Check whether the current element of the iterator is acceptable.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param object $iterator The iterator that is being filtered.
|
||||
* @param bool $include_folders True to return both folders and images. False to return only images.
|
||||
*/
|
||||
public function __construct( $iterator, $include_folders = true ) {
|
||||
parent::__construct( $iterator );
|
||||
$this->include_folders = (bool) $include_folders;
|
||||
$this->filesystem = Imagify_Filesystem::get_instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the current element of the iterator is acceptable.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool Returns whether the current element of the iterator is acceptable through this filter.
|
||||
*/
|
||||
public function accept() {
|
||||
static $extensions, $has_extension_method;
|
||||
|
||||
$file_path = $this->current()->getPathname();
|
||||
|
||||
// Prevent triggering an open_basedir restriction error.
|
||||
$file_name = $this->filesystem->file_name( $file_path );
|
||||
|
||||
if ( '.' === $file_name || '..' === $file_name ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Forbidden file/folder paths and names.
|
||||
$is_dir = $this->isDir();
|
||||
|
||||
if ( $is_dir ) {
|
||||
$file_path = trailingslashit( $file_path );
|
||||
}
|
||||
|
||||
if ( Imagify_Files_Scan::is_path_forbidden( $file_path ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// OK for folders.
|
||||
if ( $this->include_folders && $is_dir ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Only files.
|
||||
if ( ! $this->current()->isFile() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only files with the required extension.
|
||||
if ( ! isset( $extensions ) ) {
|
||||
$extensions = array_keys( imagify_get_mime_types() );
|
||||
$extensions = implode( '|', $extensions );
|
||||
}
|
||||
|
||||
if ( ! isset( $has_extension_method ) ) {
|
||||
// This method was introduced in php 5.3.6.
|
||||
$has_extension_method = method_exists( $this->current(), 'getExtension' );
|
||||
}
|
||||
|
||||
if ( $has_extension_method ) {
|
||||
$file_extension = strtolower( $this->current()->getExtension() );
|
||||
} else {
|
||||
$file_extension = strtolower( $this->filesystem->path_info( $file_path, 'extension' ) );
|
||||
}
|
||||
|
||||
return preg_match( '@^' . $extensions . '$@', $file_extension );
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Class allowing to filter RecursiveDirectoryIterator, to return only files that Imagify can optimize.
|
||||
* It also allows to remove forbidden folders.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class Imagify_Files_Recursive_Iterator extends RecursiveFilterIterator {
|
||||
|
||||
/**
|
||||
* Class version.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
const VERSION = '1.0.2';
|
||||
|
||||
/**
|
||||
* Filesystem object.
|
||||
*
|
||||
* @var object Imagify_Filesystem
|
||||
* @since 1.7.1
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $filesystem;
|
||||
|
||||
/**
|
||||
* Check whether the current element of the iterator is acceptable.
|
||||
*
|
||||
* @since 1.7.1
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param object $iterator The iterator that is being filtered.
|
||||
*/
|
||||
public function __construct( $iterator ) {
|
||||
parent::__construct( $iterator );
|
||||
$this->filesystem = Imagify_Filesystem::get_instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the current element of the iterator is acceptable.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool Returns whether the current element of the iterator is acceptable through this filter.
|
||||
*/
|
||||
public function accept() {
|
||||
static $extensions, $has_extension_method;
|
||||
|
||||
$file_path = $this->current()->getPathname();
|
||||
|
||||
// Prevent triggering an open_basedir restriction error.
|
||||
$file_name = $this->filesystem->file_name( $file_path );
|
||||
|
||||
if ( '.' === $file_name || '..' === $file_name ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( $this->current()->isDir() ) {
|
||||
$file_path = trailingslashit( $file_path );
|
||||
}
|
||||
|
||||
if ( Imagify_Files_Scan::is_path_forbidden( $file_path ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// OK for folders.
|
||||
if ( $this->hasChildren() ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Only files.
|
||||
if ( ! $this->current()->isFile() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only files with the required extension.
|
||||
if ( ! isset( $extensions ) ) {
|
||||
$extensions = array_keys( imagify_get_mime_types() );
|
||||
$extensions = implode( '|', $extensions );
|
||||
}
|
||||
|
||||
if ( ! isset( $has_extension_method ) ) {
|
||||
// This method was introduced in php 5.3.6.
|
||||
$has_extension_method = method_exists( $this->current(), 'getExtension' );
|
||||
}
|
||||
|
||||
if ( $has_extension_method ) {
|
||||
$file_extension = strtolower( $this->current()->getExtension() );
|
||||
} else {
|
||||
$file_extension = strtolower( $this->filesystem->path_info( $file_path, 'extension' ) );
|
||||
}
|
||||
|
||||
return preg_match( '@^' . $extensions . '$@', $file_extension );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,717 @@
|
||||
<?php
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Class handling everything that is related to "custom folders optimization".
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class Imagify_Files_Scan {
|
||||
|
||||
/**
|
||||
* Class version.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
const VERSION = '1.1.1';
|
||||
|
||||
/**
|
||||
* Get files (optimizable by Imagify) recursively from a specific folder.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $folder An absolute path to a folder.
|
||||
* @return array|object An array of absolute paths. A WP_Error object on error.
|
||||
*/
|
||||
public static function get_files_from_folder( $folder ) {
|
||||
$filesystem = imagify_get_filesystem();
|
||||
|
||||
// Formate and validate the folder path.
|
||||
if ( ! is_string( $folder ) ) {
|
||||
return new WP_Error( 'invalid_folder', __( 'Invalid folder.', 'imagify' ) );
|
||||
}
|
||||
|
||||
$folder = realpath( $folder );
|
||||
|
||||
if ( ! $folder ) {
|
||||
return new WP_Error( 'folder_not_exists', __( 'This folder does not exist.', 'imagify' ) );
|
||||
}
|
||||
|
||||
if ( ! $filesystem->is_dir( $folder ) ) {
|
||||
return new WP_Error( 'not_a_folder', __( 'This file is not a folder.', 'imagify' ) );
|
||||
}
|
||||
|
||||
if ( self::is_path_forbidden( trailingslashit( $folder ) ) ) {
|
||||
return new WP_Error( 'folder_forbidden', __( 'This folder is not allowed.', 'imagify' ) );
|
||||
}
|
||||
|
||||
// Finally we made all our validations.
|
||||
if ( $filesystem->is_site_root( $folder ) ) {
|
||||
// For the site's root, we don't look in sub-folders.
|
||||
$dir = new DirectoryIterator( $folder );
|
||||
$dir = new Imagify_Files_Iterator( $dir, false );
|
||||
$images = array();
|
||||
|
||||
foreach ( new IteratorIterator( $dir ) as $file ) {
|
||||
$images[] = $file->getPathname();
|
||||
}
|
||||
|
||||
return $images;
|
||||
}
|
||||
|
||||
/**
|
||||
* 4096 stands for FilesystemIterator::SKIP_DOTS, which was introduced in php 5.3.0.
|
||||
* 8192 stands for FilesystemIterator::UNIX_PATHS, which was introduced in php 5.3.0.
|
||||
*/
|
||||
$dir = new RecursiveDirectoryIterator( $folder, 4096 | 8192 );
|
||||
$dir = new Imagify_Files_Recursive_Iterator( $dir );
|
||||
$images = new RecursiveIteratorIterator( $dir );
|
||||
$images = array_keys( iterator_to_array( $images ) );
|
||||
|
||||
return $images;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** FORBIDDEN FOLDERS AND FILES ============================================================= */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Tell if a path is autorized.
|
||||
* When testing a folder, the path MUST have a trailing slash.
|
||||
*
|
||||
* @since 1.7.1
|
||||
* @since 1.8 The path must have a trailing slash if for a folder.
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $file_path A file or folder absolute path.
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_path_autorized( $file_path ) {
|
||||
return ! self::is_path_forbidden( $file_path );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if a path is forbidden.
|
||||
* When testing a folder, the path MUST have a trailing slash.
|
||||
*
|
||||
* @since 1.7
|
||||
* @since 1.8 The path must have a trailing slash if for a folder.
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $file_path A file or folder absolute path.
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_path_forbidden( $file_path ) {
|
||||
static $folders;
|
||||
|
||||
$filesystem = imagify_get_filesystem();
|
||||
|
||||
if ( self::is_filename_forbidden( $filesystem->file_name( $file_path ) ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( $filesystem->is_symlinked( $file_path ) ) {
|
||||
// Files outside the site's folder are forbidden.
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( ! isset( $folders ) ) {
|
||||
$folders = self::get_forbidden_folders();
|
||||
$folders = array_map( 'strtolower', $folders );
|
||||
$folders = array_flip( $folders );
|
||||
}
|
||||
|
||||
$file_path = self::normalize_path_for_comparison( $file_path );
|
||||
|
||||
if ( isset( $folders[ $file_path ] ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$delim = Imagify_Filesystem::PATTERN_DELIMITER;
|
||||
|
||||
foreach ( self::get_forbidden_folder_patterns() as $pattern ) {
|
||||
if ( preg_match( $delim . '^' . $pattern . $delim, $file_path ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $folders as $folder => $i ) {
|
||||
if ( strpos( $file_path, $folder ) === 0 ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of folders where Imagify won't look for files to optimize.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array A list of absolute paths.
|
||||
*/
|
||||
public static function get_forbidden_folders() {
|
||||
static $folders;
|
||||
|
||||
if ( isset( $folders ) ) {
|
||||
return $folders;
|
||||
}
|
||||
|
||||
$filesystem = imagify_get_filesystem();
|
||||
$site_root = $filesystem->get_site_root();
|
||||
$abspath = $filesystem->get_abspath();
|
||||
$folders = array(
|
||||
// Server.
|
||||
$site_root . 'cgi-bin', // `cgi-bin`
|
||||
// WordPress.
|
||||
$abspath . 'wp-admin', // `wp-admin`
|
||||
$abspath . WPINC, // `wp-includes`
|
||||
WP_CONTENT_DIR . '/mu-plugins', // MU plugins.
|
||||
WP_CONTENT_DIR . '/upgrade', // Upgrade.
|
||||
// Plugins.
|
||||
WP_CONTENT_DIR . '/bps-backup', // BulletProof Security.
|
||||
self::get_ewww_tools_path(), // EWWW: /wp-content/ewww.
|
||||
WP_CONTENT_DIR . '/ngg', // NextGen Gallery.
|
||||
WP_CONTENT_DIR . '/ngg_styles', // NextGen Gallery.
|
||||
WP_CONTENT_DIR . '/w3tc-config', // W3 Total Cache.
|
||||
WP_CONTENT_DIR . '/wfcache', // WP Fastest Cache.
|
||||
WP_CONTENT_DIR . '/wp-rocket-config', // WP Rocket.
|
||||
Imagify_Custom_Folders::get_backup_dir_path(), // Imagify "Custom folders" backup: /imagify-backup.
|
||||
IMAGIFY_PATH, // Imagify plugin: /wp-content/plugins/imagify.
|
||||
self::get_shortpixel_path(), // ShortPixel: /wp-content/uploads/ShortpixelBackups.
|
||||
);
|
||||
|
||||
if ( ! is_multisite() ) {
|
||||
$uploads_dir = $filesystem->get_upload_basedir( true );
|
||||
$ngg_galleries = self::get_ngg_galleries_path();
|
||||
|
||||
if ( $ngg_galleries ) {
|
||||
$folders[] = $ngg_galleries; // NextGen Gallery: /wp-content/gallery.
|
||||
}
|
||||
|
||||
$folders[] = $uploads_dir . 'formidable'; // Formidable Forms: /wp-content/uploads/formidable.
|
||||
$folders[] = get_imagify_backup_dir_path( true ); // Imagify Media Library backup: /wp-content/uploads/backup.
|
||||
$folders[] = self::get_wc_logs_path(); // WooCommerce Logs: /wp-content/uploads/wc-logs.
|
||||
$folders[] = $uploads_dir . 'woocommerce_uploads'; // WooCommerce uploads: /wp-content/uploads/woocommerce_uploads.
|
||||
}
|
||||
|
||||
$folders = array_map( array( $filesystem, 'normalize_dir_path' ), $folders );
|
||||
|
||||
/**
|
||||
* Add folders to the list of forbidden ones.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $added_folders List of absolute paths.
|
||||
* @param array $folders List of folders already forbidden.
|
||||
*/
|
||||
$added_folders = apply_filters( 'imagify_add_forbidden_folders', array(), $folders );
|
||||
$added_folders = array_filter( (array) $added_folders );
|
||||
$added_folders = array_filter( $added_folders, 'is_string' );
|
||||
|
||||
if ( ! $added_folders ) {
|
||||
return $folders;
|
||||
}
|
||||
|
||||
$added_folders = array_map( array( $filesystem, 'normalize_dir_path' ), $added_folders );
|
||||
|
||||
$folders = array_merge( $folders, $added_folders );
|
||||
$folders = array_flip( array_flip( $folders ) );
|
||||
|
||||
return $folders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of folder patterns where Imagify won't look for files to optimize. This is meant for paths that are dynamic.
|
||||
* `^` will be prepended to each pattern (aka, the pattern must match an absolute path).
|
||||
* Pattern delimiter is `Imagify_Filesystem::PATTERN_DELIMITER`.
|
||||
* Paths tested against these patterns are lower-cased.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array A list of regex patterns.
|
||||
*/
|
||||
public static function get_forbidden_folder_patterns() {
|
||||
static $folders;
|
||||
|
||||
if ( isset( $folders ) ) {
|
||||
return $folders;
|
||||
}
|
||||
|
||||
$folders = array();
|
||||
|
||||
// Media Library: /wp\-content/uploads/(sites/\d+/)?\d{4}/\d{2}/.
|
||||
$folders[] = self::get_media_library_pattern();
|
||||
|
||||
if ( is_multisite() ) {
|
||||
/**
|
||||
* On multisite we can't exclude Imagify's library backup folders, or any other folder located in the uploads folders (created by other plugins): there are too many ways it can fail.
|
||||
* Only exception we're aware of so far is NextGen Gallery, because it provides a clear pattern to use.
|
||||
*/
|
||||
$ngg_galleries = self::get_ngg_galleries_multisite_pattern();
|
||||
|
||||
if ( $ngg_galleries ) {
|
||||
// NextGen Gallery: /wp\-content/uploads/sites/\d+/nggallery/.
|
||||
$folders[] = $ngg_galleries;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add folder patterns to the list of forbidden ones.
|
||||
* Don't forget to use `Imagify_Files_Scan::normalize_path_for_regex( $path )`!
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $added_folders List of patterns.
|
||||
* @param array $folders List of patterns already forbidden.
|
||||
*/
|
||||
$added_folders = apply_filters( 'imagify_add_forbidden_folder_patterns', array(), $folders );
|
||||
$added_folders = array_filter( (array) $added_folders );
|
||||
$added_folders = array_filter( $added_folders, 'is_string' );
|
||||
|
||||
if ( ! $added_folders ) {
|
||||
return $folders;
|
||||
}
|
||||
|
||||
$folders = array_merge( $folders, $added_folders );
|
||||
$folders = array_flip( array_flip( $folders ) );
|
||||
|
||||
return $folders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if a file/folder name is forbidden.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $file_name A file or folder name.
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_filename_forbidden( $file_name ) {
|
||||
static $file_names;
|
||||
|
||||
if ( ! isset( $file_names ) ) {
|
||||
$file_names = array_flip( self::get_forbidden_file_names() );
|
||||
}
|
||||
|
||||
return isset( $file_names[ strtolower( $file_name ) ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of file names that Imagify won't optimize.
|
||||
* It can contain folder names. Names are case-lowered.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array A list of file names
|
||||
*/
|
||||
public static function get_forbidden_file_names() {
|
||||
static $file_names;
|
||||
|
||||
if ( isset( $file_names ) ) {
|
||||
return $file_names;
|
||||
}
|
||||
|
||||
$file_names = array(
|
||||
'.',
|
||||
'..',
|
||||
'.DS_Store',
|
||||
'.git',
|
||||
'.svn',
|
||||
'backup',
|
||||
'backups',
|
||||
'cache',
|
||||
'lang',
|
||||
'langs',
|
||||
'languages',
|
||||
'node_modules',
|
||||
'Thumbs.db',
|
||||
);
|
||||
$file_names = array_map( 'strtolower', $file_names );
|
||||
|
||||
/**
|
||||
* Add file names to the list of forbidden ones.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $added_file_names List of file names.
|
||||
* @param array $file_names List of file names already forbidden.
|
||||
*/
|
||||
$added_file_names = apply_filters( 'imagify_add_forbidden_file_names', array(), $file_names );
|
||||
|
||||
if ( ! $added_file_names || ! is_array( $added_file_names ) ) {
|
||||
return $file_names;
|
||||
}
|
||||
|
||||
$added_file_names = array_filter( $added_file_names, 'is_string' );
|
||||
$added_file_names = array_map( 'strtolower', $added_file_names );
|
||||
|
||||
$file_names = array_merge( $file_names, $added_file_names );
|
||||
$file_names = array_flip( array_flip( $file_names ) );
|
||||
|
||||
return $file_names;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** PLACEHOLDERS ============================================================================ */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Add a placeholder to a path.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $file_path An absolute path.
|
||||
* @return string A "placeholdered" path.
|
||||
*/
|
||||
public static function add_placeholder( $file_path ) {
|
||||
$file_path = wp_normalize_path( $file_path );
|
||||
$locations = self::get_placeholder_paths();
|
||||
|
||||
foreach ( $locations as $placeholder => $location_path ) {
|
||||
if ( strpos( $file_path, $location_path ) === 0 ) {
|
||||
return preg_replace( '@^' . preg_quote( $location_path, '@' ) . '@', $placeholder, $file_path );
|
||||
}
|
||||
}
|
||||
|
||||
// Should not happen.
|
||||
return $file_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change a path with a placeholder into a real path or URL.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $file_path A path with a placeholder.
|
||||
* @param string $type What to return: 'path' or 'url'.
|
||||
* @return string An absolute path or a URL.
|
||||
*/
|
||||
public static function remove_placeholder( $file_path, $type = 'path' ) {
|
||||
if ( 'path' === $type ) {
|
||||
$locations = self::get_placeholder_paths();
|
||||
} else {
|
||||
$locations = self::get_placeholder_urls();
|
||||
}
|
||||
|
||||
foreach ( $locations as $placeholder => $location_path ) {
|
||||
if ( strpos( $file_path, $placeholder ) === 0 ) {
|
||||
return preg_replace( '@^' . preg_quote( $placeholder, '@' ) . '@', $location_path, $file_path );
|
||||
}
|
||||
}
|
||||
|
||||
// Should not happen.
|
||||
return $file_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get array of pairs of placeholder => corresponding path.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_placeholder_paths() {
|
||||
static $replacements;
|
||||
|
||||
if ( isset( $replacements ) ) {
|
||||
return $replacements;
|
||||
}
|
||||
|
||||
$filesystem = imagify_get_filesystem();
|
||||
$replacements = array(
|
||||
'{{PLUGINS}}/' => WP_PLUGIN_DIR,
|
||||
'{{MU_PLUGINS}}/' => WPMU_PLUGIN_DIR,
|
||||
'{{THEMES}}/' => WP_CONTENT_DIR . '/themes',
|
||||
'{{UPLOADS}}/' => $filesystem->get_main_upload_basedir(),
|
||||
'{{CONTENT}}/' => WP_CONTENT_DIR,
|
||||
'{{ROOT}}/' => $filesystem->get_site_root(),
|
||||
);
|
||||
$replacements = array_map( array( $filesystem, 'normalize_dir_path' ), $replacements );
|
||||
|
||||
return $replacements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get array of pairs of placeholder => corresponding URL.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_placeholder_urls() {
|
||||
static $replacements;
|
||||
|
||||
if ( isset( $replacements ) ) {
|
||||
return $replacements;
|
||||
}
|
||||
|
||||
$filesystem = imagify_get_filesystem();
|
||||
$replacements = array(
|
||||
'{{PLUGINS}}/' => plugins_url( '/' ),
|
||||
'{{MU_PLUGINS}}/' => plugins_url( '/', WPMU_PLUGIN_DIR . '/.' ),
|
||||
'{{THEMES}}/' => content_url( 'themes/' ),
|
||||
'{{UPLOADS}}/' => $filesystem->get_main_upload_baseurl(),
|
||||
'{{CONTENT}}/' => content_url( '/' ),
|
||||
'{{ROOT}}/' => $filesystem->get_site_root_url(),
|
||||
);
|
||||
|
||||
return $replacements;
|
||||
}
|
||||
|
||||
/**
|
||||
* A file_exists() for paths with a placeholder.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $file_path The file path.
|
||||
* @return bool
|
||||
*/
|
||||
public static function placeholder_path_exists( $file_path ) {
|
||||
return imagify_get_filesystem()->is_readable( self::remove_placeholder( $file_path ) );
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** PATHS =================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the path to NextGen galleries on monosites.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|bool An absolute path. False if it can't be retrieved.
|
||||
*/
|
||||
public static function get_ngg_galleries_path() {
|
||||
$galleries_path = get_site_option( 'ngg_options' );
|
||||
|
||||
if ( empty( $galleries_path['gallerypath'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$filesystem = imagify_get_filesystem();
|
||||
$galleries_path = $filesystem->normalize_dir_path( $galleries_path['gallerypath'] );
|
||||
$galleries_path = trim( $galleries_path, '/' ); // Something like `wp-content/gallery`.
|
||||
|
||||
$ngg_root = defined( 'NGG_GALLERY_ROOT_TYPE' ) ? NGG_GALLERY_ROOT_TYPE : 'site';
|
||||
|
||||
if ( $galleries_path && 'content' === $ngg_root ) {
|
||||
$ngg_root = $filesystem->normalize_dir_path( WP_CONTENT_DIR );
|
||||
$ngg_root = trim( $ngg_root, '/' ); // Something like `abs-path/to/wp-content`.
|
||||
|
||||
$exploded_root = explode( '/', $ngg_root );
|
||||
$exploded_galleries = explode( '/', $galleries_path );
|
||||
$first_gallery_dirname = reset( $exploded_galleries );
|
||||
$last_root_dirname = end( $exploded_root );
|
||||
|
||||
if ( $last_root_dirname === $first_gallery_dirname ) {
|
||||
array_shift( $exploded_galleries );
|
||||
$galleries_path = implode( '/', $exploded_galleries );
|
||||
}
|
||||
}
|
||||
|
||||
if ( 'content' === $ngg_root ) {
|
||||
$ngg_root = $filesystem->normalize_dir_path( WP_CONTENT_DIR );
|
||||
} else {
|
||||
$ngg_root = $filesystem->get_abspath();
|
||||
}
|
||||
|
||||
if ( strpos( $galleries_path, $ngg_root ) !== 0 ) {
|
||||
$galleries_path = $ngg_root . $galleries_path;
|
||||
}
|
||||
|
||||
return $galleries_path . '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to WooCommerce logs on monosites.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string An absolute path.
|
||||
*/
|
||||
public static function get_wc_logs_path() {
|
||||
if ( defined( 'WC_LOG_DIR' ) ) {
|
||||
return WC_LOG_DIR;
|
||||
}
|
||||
|
||||
return imagify_get_filesystem()->get_upload_basedir( true ) . 'wc-logs/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to EWWW optimization tools.
|
||||
* It is the same for all sites on multisite.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string An absolute path.
|
||||
*/
|
||||
public static function get_ewww_tools_path() {
|
||||
if ( defined( 'EWWW_IMAGE_OPTIMIZER_TOOL_PATH' ) ) {
|
||||
return EWWW_IMAGE_OPTIMIZER_TOOL_PATH;
|
||||
}
|
||||
|
||||
return WP_CONTENT_DIR . '/ewww/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to ShortPixel backup folder.
|
||||
* It is the same for all sites on multisite (and yes, you'll get a surprise if your upload base dir -aka uploads/sites/12/- is not 2 folders deeper than theuploads folder).
|
||||
*
|
||||
* @since 1.8
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string An absolute path.
|
||||
*/
|
||||
public static function get_shortpixel_path() {
|
||||
if ( defined( 'SHORTPIXEL_BACKUP_FOLDER' ) ) {
|
||||
return trailingslashit( SHORTPIXEL_BACKUP_FOLDER );
|
||||
}
|
||||
|
||||
$filesystem = imagify_get_filesystem();
|
||||
$path = $filesystem->get_upload_basedir( true );
|
||||
$path = is_main_site() ? $path : $filesystem->dir_path( $filesystem->dir_path( $path ) );
|
||||
|
||||
return $path . 'ShortpixelBackups/';
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** REGEX PATTERNS ========================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the regex pattern used to match the paths to the media library.
|
||||
* Pattern delimiter is `Imagify_Filesystem::PATTERN_DELIMITER`.
|
||||
* Paths tested against these patterns are lower-cased.
|
||||
*
|
||||
* @since 1.8
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string Something like `/wp\-content/uploads/(sites/\d+/)?\d{4}/\d{2}/`.
|
||||
*/
|
||||
public static function get_media_library_pattern() {
|
||||
$filesystem = imagify_get_filesystem();
|
||||
$uploads_dir = self::normalize_path_for_regex( $filesystem->get_main_upload_basedir() );
|
||||
|
||||
if ( ! is_multisite() ) {
|
||||
if ( get_option( 'uploads_use_yearmonth_folders' ) ) {
|
||||
// In year/month folders.
|
||||
return $uploads_dir . '\d{4}/\d{2}/';
|
||||
}
|
||||
|
||||
// Not in year/month folders.
|
||||
return $uploads_dir . '[^/]+$';
|
||||
}
|
||||
|
||||
$pattern = $filesystem->get_multisite_uploads_subdir_pattern();
|
||||
|
||||
if ( get_option( 'uploads_use_yearmonth_folders' ) ) {
|
||||
// In year/month folders.
|
||||
return $uploads_dir . '(' . $pattern . ')?\d{4}/\d{2}/';
|
||||
}
|
||||
|
||||
// Not in year/month folders.
|
||||
return $uploads_dir . '(' . $pattern . ')?[^/]+$';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the regex pattern used to match the paths to NextGen galleries on multisite.
|
||||
* Pattern delimiter is `Imagify_Filesystem::PATTERN_DELIMITER`.
|
||||
* Paths tested against these patterns are lower-cased.
|
||||
*
|
||||
* @since 1.8
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|bool Something like `/wp-content/uploads/sites/\d+/nggallery/`. False if it can't be retrieved.
|
||||
*/
|
||||
public static function get_ngg_galleries_multisite_pattern() {
|
||||
$galleries_path = self::get_ngg_galleries_path(); // Something like `wp-content/uploads/sites/%BLOG_ID%/nggallery/`.
|
||||
|
||||
if ( ! $galleries_path ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$galleries_path = self::normalize_path_for_regex( $galleries_path );
|
||||
$galleries_path = str_replace( array( '%blog_name%', '%blog_id%' ), array( '.+', '\d+' ), $galleries_path );
|
||||
|
||||
return $galleries_path;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** NORMALIZATION TOOLS ===================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Normalize a file path, aiming for path comparison.
|
||||
* The path is normalized and case-lowered.
|
||||
*
|
||||
* @since 1.7
|
||||
* @since 1.8 No trailing slash anymore, because it can be used for files.
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $file_path The file path.
|
||||
* @return string The normalized file path.
|
||||
*/
|
||||
public static function normalize_path_for_comparison( $file_path ) {
|
||||
return strtolower( wp_normalize_path( $file_path ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize a file path, aiming for use in a regex pattern.
|
||||
* The path is normalized, case-lowered, and escaped.
|
||||
*
|
||||
* @since 1.8
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $file_path The file path.
|
||||
* @return string The normalized file path.
|
||||
*/
|
||||
public static function normalize_path_for_regex( $file_path ) {
|
||||
return preg_quote( imagify_get_filesystem()->normalize_path_for_comparison( $file_path ), Imagify_Filesystem::PATTERN_DELIMITER );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,474 @@
|
||||
<?php
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Class handling stats related to "custom folders optimization".
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class Imagify_Files_Stats {
|
||||
|
||||
/**
|
||||
* Class version.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
const VERSION = '1.0';
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** COUNT FILES ============================================================================= */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Count number of images in custom folders.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return int The number of images.
|
||||
*/
|
||||
public static function count_all_files() {
|
||||
/**
|
||||
* Filter the number of images in custom folders.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param int|bool $pre_count Default is false. Provide an integer.
|
||||
*/
|
||||
$pre_count = apply_filters( 'imagify_count_files', false );
|
||||
|
||||
if ( false !== $pre_count ) {
|
||||
return (int) $pre_count;
|
||||
}
|
||||
|
||||
return self::count_files( 'all' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Count number of images in custom folders with an error.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return int The number of images.
|
||||
*/
|
||||
public static function count_error_files() {
|
||||
/**
|
||||
* Filter the number of images in custom folders with an error.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param int|bool $pre_count Default is false. Provide an integer.
|
||||
*/
|
||||
$pre_count = apply_filters( 'imagify_count_error_files', false );
|
||||
|
||||
if ( false !== $pre_count ) {
|
||||
return (int) $pre_count;
|
||||
}
|
||||
|
||||
return self::count_files( 'error' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Count number of images successfully optimized by Imagify in custom folders.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return int The number of images.
|
||||
*/
|
||||
public static function count_success_files() {
|
||||
/**
|
||||
* Filter the number of images successfully optimized by Imagify in custom folders.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param int|bool $pre_count Default is false. Provide an integer.
|
||||
*/
|
||||
$pre_count = apply_filters( 'imagify_count_success_files', false );
|
||||
|
||||
if ( false !== $pre_count ) {
|
||||
return (int) $pre_count;
|
||||
}
|
||||
|
||||
return self::count_files( 'success' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Count number of optimized images in custom folders.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return int The number of images.
|
||||
*/
|
||||
public static function count_optimized_files() {
|
||||
/**
|
||||
* Filter the number of optimized images in custom folders.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param int|bool $pre_count Default is false. Provide an integer.
|
||||
*/
|
||||
$pre_count = apply_filters( 'imagify_count_optimized_files', false );
|
||||
|
||||
if ( false !== $pre_count ) {
|
||||
return (int) $pre_count;
|
||||
}
|
||||
|
||||
return self::count_files( 'optimized' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Count number of unoptimized images in custom folders.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return int The number of images.
|
||||
*/
|
||||
public static function count_unoptimized_files() {
|
||||
/**
|
||||
* Filter the number of unoptimized images in custom folders.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param int|bool $pre_count Default is false. Provide an integer.
|
||||
*/
|
||||
$pre_count = apply_filters( 'imagify_count_unoptimized_files', false );
|
||||
|
||||
if ( false !== $pre_count ) {
|
||||
return (int) $pre_count;
|
||||
}
|
||||
|
||||
return self::count_files( 'unoptimized' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Count number of images without status in custom folders.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return int The number of images.
|
||||
*/
|
||||
public static function count_no_status_files() {
|
||||
/**
|
||||
* Filter the number of images without status in custom folders.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param int|bool $pre_count Default is false. Provide an integer.
|
||||
*/
|
||||
$pre_count = apply_filters( 'imagify_count_no_status_files', false );
|
||||
|
||||
if ( false !== $pre_count ) {
|
||||
return (int) $pre_count;
|
||||
}
|
||||
|
||||
return self::count_files( 'none' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Count number of images in custom folders.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $status The status of these folders: all, success, already_optimized, optimized, error, none, unoptimized.
|
||||
* "none" if for files without status.
|
||||
* "optimized" regroups "success" and "already_optimized".
|
||||
* "unoptimized" regroups "error" and "none".
|
||||
* @return int The number of images.
|
||||
*/
|
||||
public static function count_files( $status = 'all' ) {
|
||||
global $wpdb;
|
||||
static $count = array();
|
||||
|
||||
$status = self::validate_status( $status );
|
||||
|
||||
if ( isset( $count[ $status ] ) ) {
|
||||
return $count[ $status ];
|
||||
}
|
||||
|
||||
$files_db = Imagify_Files_DB::get_instance();
|
||||
|
||||
if ( ! $files_db->can_operate() ) {
|
||||
$count[ $status ] = 0;
|
||||
return $count[ $status ];
|
||||
}
|
||||
|
||||
switch ( $status ) {
|
||||
case 'all':
|
||||
$status = '';
|
||||
break;
|
||||
|
||||
case 'none':
|
||||
$status = 'status IS NULL';
|
||||
break;
|
||||
|
||||
case 'optimized':
|
||||
$status = "status IN ('success','already_optimized')";
|
||||
break;
|
||||
|
||||
case 'unoptimized':
|
||||
$status = "( status = 'error' OR status IS NULL )";
|
||||
break;
|
||||
|
||||
default:
|
||||
// "success", "already_optimized", "error".
|
||||
$status = "status = '$status'";
|
||||
}
|
||||
|
||||
$table_name = $files_db->get_table_name();
|
||||
$status = $status ? "WHERE $status" : '';
|
||||
|
||||
$count[ $status ] = (int) $wpdb->get_var( // WPCS: unprepared SQL ok.
|
||||
"SELECT COUNT( file_id ) FROM $table_name $status"
|
||||
);
|
||||
|
||||
return $count[ $status ];
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** PERCENTS ================================================================================ */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Count percent of optimized images in custom folders.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return int The percent of optimized images.
|
||||
*/
|
||||
public static function percent_optimized_files() {
|
||||
/**
|
||||
* Filter the percent of optimized images in custom folders.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param int|bool $percent Default is false. Provide an integer.
|
||||
*/
|
||||
$percent = apply_filters( 'imagify_percent_optimized_files', false );
|
||||
|
||||
if ( false !== $percent ) {
|
||||
return (int) $percent;
|
||||
}
|
||||
|
||||
$total_files = self::count_all_files();
|
||||
$total_optimized_files = self::count_optimized_files();
|
||||
|
||||
if ( ! $total_files || ! $total_optimized_files ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return min( round( 100 * $total_optimized_files / $total_files ), 100 );
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** GET FILE SIZES ========================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Sum up all optimized sizes of all successfully optimized files.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return int The sizes sum in bytes.
|
||||
*/
|
||||
public static function get_optimized_size() {
|
||||
/**
|
||||
* Filter the optimized sizes of all successfully optimized files.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param int|bool $pre_size Default is false. Provide an integer.
|
||||
*/
|
||||
$pre_size = apply_filters( 'imagify_get_optimized_files_size', false );
|
||||
|
||||
if ( false !== $pre_size ) {
|
||||
return (int) $pre_size;
|
||||
}
|
||||
|
||||
return self::get_size( 'optimized' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sum up all original sizes of all successfully optimized files.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return int The sizes sum in bytes.
|
||||
*/
|
||||
public static function get_original_size() {
|
||||
/**
|
||||
* Filter the original sizes of all successfully optimized files.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param int|bool $pre_size Default is false. Provide an integer.
|
||||
*/
|
||||
$pre_size = apply_filters( 'imagify_get_original_files_size', false );
|
||||
|
||||
if ( false !== $pre_size ) {
|
||||
return (int) $pre_size;
|
||||
}
|
||||
|
||||
return self::get_size( 'original' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sum up all (optimized|original) sizes of all successfully optimized files.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $type "optimized" or "original".
|
||||
* @return int The sizes sum in bytes.
|
||||
*/
|
||||
public static function get_size( $type = null ) {
|
||||
global $wpdb;
|
||||
static $sizes = array();
|
||||
|
||||
$type = 'optimized' === $type ? 'optimized_size' : 'original_size';
|
||||
|
||||
if ( isset( $sizes[ $type ] ) ) {
|
||||
return $sizes[ $type ];
|
||||
}
|
||||
|
||||
$files_db = Imagify_Files_DB::get_instance();
|
||||
|
||||
if ( ! $files_db->can_operate() ) {
|
||||
$sizes[ $type ] = 0;
|
||||
return $sizes[ $type ];
|
||||
}
|
||||
|
||||
$table_name = $files_db->get_table_name();
|
||||
$sizes[ $type ] = (int) $wpdb->get_var( // WPCS: unprepared SQL ok.
|
||||
"SELECT SUM( $type ) FROM $table_name WHERE status = 'success'"
|
||||
);
|
||||
|
||||
return $sizes[ $type ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sum up all original sizes.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return int The sizes sum in bytes.
|
||||
*/
|
||||
public static function get_overall_original_size() {
|
||||
global $wpdb;
|
||||
static $size;
|
||||
|
||||
if ( isset( $size ) ) {
|
||||
return $size;
|
||||
}
|
||||
|
||||
$files_db = Imagify_Files_DB::get_instance();
|
||||
|
||||
if ( ! $files_db->can_operate() ) {
|
||||
$size = 0;
|
||||
return $size;
|
||||
}
|
||||
|
||||
$table_name = $files_db->get_table_name();
|
||||
$size = round( $wpdb->get_var( "SELECT SUM( original_size ) FROM $table_name" ) ); // WPCS: unprepared SQL ok.
|
||||
|
||||
return $size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the average size of the images uploaded per month.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return int The current average size of images uploaded per month in bytes.
|
||||
*/
|
||||
public static function calculate_average_size_per_month() {
|
||||
global $wpdb;
|
||||
static $average;
|
||||
|
||||
if ( isset( $average ) ) {
|
||||
return $average;
|
||||
}
|
||||
|
||||
$files_db = Imagify_Files_DB::get_instance();
|
||||
|
||||
if ( ! $files_db->can_operate() ) {
|
||||
$average = 0;
|
||||
return $average;
|
||||
}
|
||||
|
||||
$table_name = $files_db->get_table_name();
|
||||
$average = round( $wpdb->get_var( "SELECT AVG( size ) AS average_size_per_month FROM ( SELECT SUM( original_size ) AS size FROM $table_name GROUP BY YEAR( file_date ), MONTH( file_date ) ) AS size_per_month" ) ); // WPCS: unprepared SQL ok.
|
||||
|
||||
return $average;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** TOOLS =================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Validate a status.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $status The status of these folders: all, success, already_optimized, optimized, error, none, unoptimized.
|
||||
* "none" if for files without status.
|
||||
* "optimized" regroups "success" and "already_optimized".
|
||||
* "unoptimized" regroups "error" and "none".
|
||||
* @return string Fallback to 'all' if the status is not valid.
|
||||
*/
|
||||
public static function validate_status( $status = 'all' ) {
|
||||
$statuses = array(
|
||||
'all' => 1,
|
||||
'success' => 1,
|
||||
'already_optimized' => 1,
|
||||
'error' => 1,
|
||||
'none' => 1,
|
||||
'optimized' => 1,
|
||||
'unoptimized' => 1,
|
||||
);
|
||||
|
||||
return isset( $statuses[ $status ] ) ? $status : 'all';
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,238 @@
|
||||
<?php
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* DB class that handles files in "custom folders".
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class Imagify_Folders_DB extends Imagify_Abstract_DB {
|
||||
|
||||
/**
|
||||
* Class version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const VERSION = '1.0.1';
|
||||
|
||||
/**
|
||||
* The single instance of the class.
|
||||
*
|
||||
* @var object
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected static $_instance;
|
||||
|
||||
/**
|
||||
* The suffix used in the name of the database table (so, without the wpdb prefix).
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected $table = 'imagify_folders';
|
||||
|
||||
/**
|
||||
* The version of our database table.
|
||||
*
|
||||
* @var int
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected $table_version = 100;
|
||||
|
||||
/**
|
||||
* Tell if the table is the same for each site of a Multisite.
|
||||
*
|
||||
* @var bool
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected $table_is_global = true;
|
||||
|
||||
/**
|
||||
* The name of the primary column.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected $primary_key = 'folder_id';
|
||||
|
||||
/**
|
||||
* Get the main Instance.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return object Main instance.
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! isset( self::$_instance ) ) {
|
||||
self::$_instance = new self();
|
||||
}
|
||||
|
||||
return self::$_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whitelist of columns.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_columns() {
|
||||
return array(
|
||||
'folder_id' => '%d',
|
||||
'path' => '%s',
|
||||
'active' => '%d',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default column values.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_column_defaults() {
|
||||
return array(
|
||||
'folder_id' => 0,
|
||||
'path' => '',
|
||||
'active' => 0,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query to create the table fields.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_table_schema() {
|
||||
return "
|
||||
folder_id bigint(20) unsigned NOT NULL auto_increment,
|
||||
path varchar(191) NOT NULL default '',
|
||||
active tinyint(1) unsigned NOT NULL default 0,
|
||||
PRIMARY KEY (folder_id),
|
||||
UNIQUE KEY path (path),
|
||||
KEY active (active)";
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if folders are selected in the plugin settings.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has_active_folders() {
|
||||
global $wpdb;
|
||||
|
||||
$column = esc_sql( $this->get_primary_key() );
|
||||
|
||||
return (bool) $wpdb->get_var( "SELECT $column FROM $this->table_name WHERE active = 1 LIMIT 1;" ); // WPCS: unprepared SQL ok.
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve active folders (checked in the settings).
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $column_select A column name.
|
||||
* @return array
|
||||
*/
|
||||
public function get_active_folders_column( $column_select ) {
|
||||
global $wpdb;
|
||||
|
||||
$column = esc_sql( $column_select );
|
||||
|
||||
$result = $wpdb->get_col( "SELECT $column FROM $this->table_name WHERE active = 1;" ); // WPCS: unprepared SQL ok.
|
||||
|
||||
return $this->cast_col( $result, $column_select );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve active folders (checked in the settings) by the specified column / values.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $column_select A column name.
|
||||
* @param string $column_where A column name.
|
||||
* @param array $column_values An array of values.
|
||||
* @return array
|
||||
*/
|
||||
public function get_active_folders_column_in( $column_select, $column_where, $column_values ) {
|
||||
global $wpdb;
|
||||
|
||||
$column = esc_sql( $column_select );
|
||||
$column_where = esc_sql( $column_where );
|
||||
$column_values = Imagify_DB::prepare_values_list( $column_values );
|
||||
|
||||
$result = $wpdb->get_col( "SELECT $column FROM $this->table_name WHERE $column_where IN ( $column_values ) AND active = 1;" ); // WPCS: unprepared SQL ok.
|
||||
|
||||
return $this->cast_col( $result, $column_select );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve active folders (checked in the settings) by the specified column / values.
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $column_select A column name.
|
||||
* @param string $column_where A column name.
|
||||
* @param array $column_values An array of values.
|
||||
* @return array
|
||||
*/
|
||||
public function get_active_folders_column_not_in( $column_select, $column_where, $column_values ) {
|
||||
global $wpdb;
|
||||
|
||||
$column = esc_sql( $column_select );
|
||||
$column_where = esc_sql( $column_where );
|
||||
$column_values = Imagify_DB::prepare_values_list( $column_values );
|
||||
|
||||
$result = $wpdb->get_col( "SELECT $column FROM $this->table_name WHERE $column_where NOT IN ( $column_values ) AND active = 1;" ); // WPCS: unprepared SQL ok.
|
||||
|
||||
return $this->cast_col( $result, $column_select );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve not active folders (not checked in the settings).
|
||||
*
|
||||
* @since 1.7
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $column_select A column name.
|
||||
* @return array
|
||||
*/
|
||||
public function get_inactive_folders_column( $column_select ) {
|
||||
global $wpdb;
|
||||
|
||||
$column = esc_sql( $column_select );
|
||||
|
||||
$result = $wpdb->get_col( "SELECT $column FROM $this->table_name WHERE active != 1;" ); // WPCS: unprepared SQL ok.
|
||||
|
||||
return $this->cast_col( $result, $column_select );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
<?php
|
||||
|
||||
use Imagify\Traits\InstanceGetterTrait;
|
||||
|
||||
/**
|
||||
* Class that handles the plugin options.
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
class Imagify_Options extends Imagify_Abstract_Options {
|
||||
use InstanceGetterTrait;
|
||||
|
||||
/**
|
||||
* Suffix used in the name of the option.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
*/
|
||||
protected $identifier = 'settings';
|
||||
|
||||
/**
|
||||
* The default values for the Imagify main options.
|
||||
* These are the "zero state" values.
|
||||
* Don't use null as value.
|
||||
*
|
||||
* @var array
|
||||
* @since 1.7
|
||||
*/
|
||||
protected $default_values = [
|
||||
'api_key' => '',
|
||||
'optimization_level' => 2,
|
||||
'lossless' => 0,
|
||||
'auto_optimize' => 0,
|
||||
'backup' => 0,
|
||||
'resize_larger' => 0,
|
||||
'resize_larger_w' => 0,
|
||||
'convert_to_webp' => 0,
|
||||
'display_webp' => 0,
|
||||
'display_webp_method' => 'picture',
|
||||
'cdn_url' => '',
|
||||
'disallowed-sizes' => [],
|
||||
'admin_bar_menu' => 0,
|
||||
'partner_links' => 0,
|
||||
];
|
||||
|
||||
/**
|
||||
* The Imagify main option values used when they are set the first time or reset.
|
||||
* Values identical to default values are not listed.
|
||||
*
|
||||
* @var array
|
||||
* @since 1.7
|
||||
*/
|
||||
protected $reset_values = [
|
||||
'optimization_level' => 2,
|
||||
'auto_optimize' => 1,
|
||||
'backup' => 1,
|
||||
'convert_to_webp' => 1,
|
||||
'admin_bar_menu' => 1,
|
||||
'partner_links' => 1,
|
||||
];
|
||||
|
||||
/**
|
||||
* The constructor.
|
||||
* Side note: $this->hook_identifier value is "option".
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
protected function __construct() {
|
||||
if ( defined( 'IMAGIFY_API_KEY' ) && IMAGIFY_API_KEY ) {
|
||||
$this->default_values['api_key'] = (string) IMAGIFY_API_KEY;
|
||||
}
|
||||
|
||||
if ( function_exists( 'wp_get_original_image_path' ) ) {
|
||||
$this->reset_values['resize_larger'] = 1;
|
||||
|
||||
$filter_cb = [ imagify_get_context( 'wp' ), 'get_resizing_threshold' ];
|
||||
$filtered = has_filter( 'big_image_size_threshold', $filter_cb );
|
||||
|
||||
if ( $filtered ) {
|
||||
remove_filter( 'big_image_size_threshold', $filter_cb, IMAGIFY_INT_MAX );
|
||||
}
|
||||
|
||||
/** This filter is documented in wp-admin/includes/image.php */
|
||||
$this->reset_values['resize_larger_w'] = (int) apply_filters( 'big_image_size_threshold', 2560, [ 0, 0 ], '', 0 );
|
||||
$this->reset_values['resize_larger_w'] = $this->sanitize_and_validate_value( 'resize_larger_w', $this->reset_values['resize_larger_w'], $this->default_values['resize_larger_w'] );
|
||||
|
||||
if ( $filtered ) {
|
||||
add_filter( 'big_image_size_threshold', $filter_cb, IMAGIFY_INT_MAX );
|
||||
}
|
||||
}
|
||||
|
||||
$this->network_option = imagify_is_active_for_network();
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** SANITIZATION, VALIDATION ================================================================ */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Sanitize and validate an option value. Basic casts have been made.
|
||||
*
|
||||
* @since 1.7
|
||||
*
|
||||
* @param string $key The option key.
|
||||
* @param mixed $value The value.
|
||||
* @param mixed $default The default value.
|
||||
* @return mixed
|
||||
*/
|
||||
public function sanitize_and_validate_value( $key, $value, $default ) {
|
||||
static $max_sizes;
|
||||
|
||||
switch ( $key ) {
|
||||
case 'api_key':
|
||||
if ( defined( 'IMAGIFY_API_KEY' ) && IMAGIFY_API_KEY ) {
|
||||
return (string) IMAGIFY_API_KEY;
|
||||
}
|
||||
return $value ? sanitize_key( $value ) : '';
|
||||
|
||||
case 'optimization_level':
|
||||
if ( $value < 0 || $value > 2 ) {
|
||||
// For an invalid value, return the "reset" value.
|
||||
$reset_values = $this->get_reset_values();
|
||||
return $reset_values[ $key ];
|
||||
}
|
||||
return $value;
|
||||
|
||||
case 'auto_optimize':
|
||||
case 'backup':
|
||||
case 'lossless':
|
||||
case 'resize_larger':
|
||||
case 'convert_to_webp':
|
||||
case 'display_webp':
|
||||
case 'admin_bar_menu':
|
||||
case 'partner_links':
|
||||
return 1;
|
||||
|
||||
case 'resize_larger_w':
|
||||
if ( $value <= 0 ) {
|
||||
// Invalid.
|
||||
return $default;
|
||||
}
|
||||
if ( ! isset( $max_sizes ) ) {
|
||||
$max_sizes = get_imagify_max_intermediate_image_size();
|
||||
}
|
||||
if ( $value < $max_sizes['width'] ) {
|
||||
// Invalid.
|
||||
return $max_sizes['width'];
|
||||
}
|
||||
return $value;
|
||||
|
||||
case 'disallowed-sizes':
|
||||
if ( ! $value ) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
$value = array_keys( $value );
|
||||
$value = array_map( 'sanitize_text_field', $value );
|
||||
return array_fill_keys( $value, 1 );
|
||||
|
||||
case 'display_webp_method':
|
||||
$values = [
|
||||
'picture' => 1,
|
||||
'rewrite' => 1,
|
||||
];
|
||||
if ( isset( $values[ $value ] ) ) {
|
||||
return $value;
|
||||
}
|
||||
// For an invalid value, return the "reset" value.
|
||||
$reset_values = $this->get_reset_values();
|
||||
return $reset_values[ $key ];
|
||||
|
||||
case 'cdn_url':
|
||||
$cdn_source = \Imagify\Webp\Picture\Display::get_instance()->get_cdn_source( $value );
|
||||
|
||||
if ( 'option' !== $cdn_source['source'] ) {
|
||||
/**
|
||||
* If the URL is defined via constant or filter, unset the option.
|
||||
* This is useful when the CDN is disabled: there is no need to do anything then.
|
||||
*/
|
||||
return '';
|
||||
}
|
||||
|
||||
return $cdn_source['url'];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate Imagify's options before storing them. Basic sanitization and validation have been made, row by row.
|
||||
*
|
||||
* @since 1.7
|
||||
*
|
||||
* @param string $values The option value.
|
||||
* @return array
|
||||
*/
|
||||
public function validate_values_on_update( $values ) {
|
||||
// The max width for the "Resize larger images" option can't be 0.
|
||||
if ( empty( $values['resize_larger_w'] ) ) {
|
||||
unset( $values['resize_larger'], $values['resize_larger_w'] );
|
||||
}
|
||||
|
||||
// Don't display wepb if conversion is disabled.
|
||||
if ( empty( $values['convert_to_webp'] ) ) {
|
||||
unset( $values['convert_to_webp'], $values['display_webp'] );
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,343 @@
|
||||
<?php
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Class to check if the current WordPress and PHP versions meet our requirements.
|
||||
*
|
||||
* @since 1.9
|
||||
* @source Based on class WP_Rocket_Requirements_Check from WP Rocket plugin.
|
||||
* @author Grégory Viguier
|
||||
* @author Remy Perona
|
||||
*/
|
||||
class Imagify_Requirements_Check {
|
||||
/**
|
||||
* Plugin Name.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
* @access private
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
private $plugin_name;
|
||||
|
||||
/**
|
||||
* Plugin filepath.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
* @access private
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
private $plugin_file;
|
||||
|
||||
/**
|
||||
* Plugin version.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
* @access private
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
private $plugin_version;
|
||||
|
||||
/**
|
||||
* Last plugin version handling the current version of WP.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
* @access private
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
private $wp_last_version;
|
||||
|
||||
/**
|
||||
* Last plugin version handling the current version of PHP.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
* @access private
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
private $php_last_version;
|
||||
|
||||
/**
|
||||
* Required WordPress version.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
* @access private
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
private $wp_version;
|
||||
|
||||
/**
|
||||
* Required PHP version.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
* @access private
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
private $php_version;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $args {
|
||||
* Arguments to populate the class properties.
|
||||
*
|
||||
* @type string $plugin_name Plugin name.
|
||||
* @type string $plugin_file Plugin filepath.
|
||||
* @type string $plugin_version Plugin version.
|
||||
* @type string $wp_last_version Last plugin version handling the current version of WP.
|
||||
* @type string $php_last_version Last plugin version handling the current version of PHP.
|
||||
* @type string $wp_version Required WordPress version.
|
||||
* @type string $php_version Required PHP version.
|
||||
* }
|
||||
*/
|
||||
public function __construct( $args ) {
|
||||
foreach ( array( 'plugin_name', 'plugin_file', 'plugin_version', 'wp_last_version', 'php_last_version', 'wp_version', 'php_version' ) as $setting ) {
|
||||
if ( isset( $args[ $setting ] ) ) {
|
||||
$this->$setting = $args[ $setting ];
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $this->wp_last_version ) ) {
|
||||
$this->wp_last_version = '1.6.14.2';
|
||||
}
|
||||
|
||||
if ( empty( $this->php_last_version ) ) {
|
||||
$this->php_last_version = '1.8.4.1';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if all requirements are ok, if not, display a notice and the rollback.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function check() {
|
||||
if ( ! $this->php_passes() || ! $this->wp_passes() ) {
|
||||
add_action( 'admin_notices', array( $this, 'print_notice' ) );
|
||||
add_action( 'admin_post_imagify_rollback', array( $this, 'rollback' ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current PHP version is equal or superior to the required PHP version.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access private
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function php_passes() {
|
||||
return version_compare( PHP_VERSION, $this->php_version ) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current WordPress version is equal or superior to the required PHP version.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access private
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function wp_passes() {
|
||||
global $wp_version;
|
||||
|
||||
return version_compare( $wp_version, $this->wp_version ) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last version of the plugin that can run with the current WP and PHP versions.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access private
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_last_version() {
|
||||
$last_version = '';
|
||||
|
||||
if ( ! $this->php_passes() ) {
|
||||
$last_version = $this->php_last_version;
|
||||
}
|
||||
|
||||
if ( ! $this->wp_passes() ) {
|
||||
$last_version = ! $last_version || version_compare( $last_version, $this->wp_last_version ) > 0 ? $this->wp_last_version : $last_version;
|
||||
}
|
||||
|
||||
return $last_version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the current user can rollback.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access private
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function current_user_can() {
|
||||
$describer = 'manage';
|
||||
$capacity = $this->is_active_for_network() ? 'manage_network_options' : 'manage_options';
|
||||
// This filter is documented in classes/Context/AbstractContext.php.
|
||||
$capacity = (string) apply_filters( 'imagify_capacity', $capacity, $describer, 'wp' );
|
||||
|
||||
$user_can = current_user_can( $capacity );
|
||||
// This filter is documented in classes/Context/AbstractContext.php.
|
||||
$user_can = (bool) apply_filters( 'imagify_current_user_can', $user_can, $capacity, $describer, null, 'wp' );
|
||||
|
||||
return $user_can;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if Imagify is activated on the network.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access private
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* return bool True if Imagify is activated on the network.
|
||||
*/
|
||||
private function is_active_for_network() {
|
||||
if ( ! is_multisite() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||
}
|
||||
|
||||
return is_plugin_active_for_network( plugin_basename( $this->plugin_file ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Warn if PHP version is less than 5.4 and offers to rollback.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
public function print_notice() {
|
||||
if ( ! $this->current_user_can() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
imagify_load_translations();
|
||||
|
||||
$message = array();
|
||||
$required = array();
|
||||
$rollback_url = wp_nonce_url( admin_url( 'admin-post.php?action=imagify_rollback' ), 'imagify_rollback' );
|
||||
|
||||
if ( ! $this->php_passes() ) {
|
||||
/* translators: %1$s = Plugin name, %2$s = PHP version required. */
|
||||
$message[] = sprintf( esc_html__( 'To use this %1$s version, please ask your web host how to upgrade your server to PHP %2$s or higher.', 'imagify' ), $this->plugin_name, $this->php_version );
|
||||
$required[] = 'PHP ' . $this->php_version;
|
||||
}
|
||||
|
||||
if ( ! $this->wp_passes() ) {
|
||||
/* translators: %1$s = Plugin name, %2$s = WordPress version required. */
|
||||
$message[] = sprintf( esc_html__( 'To use this %1$s version, please upgrade WordPress to version %2$s or higher.', 'imagify' ), $this->plugin_name, $this->wp_version );
|
||||
$required[] = 'WordPress ' . $this->wp_version;
|
||||
}
|
||||
|
||||
$message = '<p>' . implode( '<br/>', $message ) . "</p>\n";
|
||||
$required = wp_sprintf_l( '%l', $required );
|
||||
|
||||
/* translators: %1$s = Plugin name, %2$s = Plugin version, $3$s is something like "PHP 5.4" or "PHP 5.4 and WordPress 4.0". */
|
||||
$message = '<p>' . sprintf( esc_html__( 'To function properly, %1$s %2$s requires at least %3$s.', 'imagify' ), '<strong>' . $this->plugin_name . '</strong>', $this->plugin_version, $required ) . "</p>\n" . $message;
|
||||
|
||||
$message .= '<p>' . esc_html__( 'If you are not able to upgrade, you can rollback to the previous version by using the button below.', 'imagify' ) . "</p>\n";
|
||||
/* translators: %s = Previous plugin version. */
|
||||
$message .= '<p class="submit"><a href="' . esc_url( $rollback_url ) . '" class="button">' . sprintf( __( 'Re-install version %s', 'imagify' ), $this->get_last_version() ) . '</a></p>';
|
||||
|
||||
echo '<div class="notice notice-error">' . $message . '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the rollback.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
public function rollback() {
|
||||
check_ajax_referer( 'imagify_rollback' );
|
||||
|
||||
if ( ! $this->current_user_can() ) {
|
||||
wp_die();
|
||||
}
|
||||
|
||||
imagify_load_translations();
|
||||
|
||||
$plugin_transient = get_site_transient( 'update_plugins' );
|
||||
$plugin_basename = plugin_basename( $this->plugin_file );
|
||||
$plugin_folder = dirname( $plugin_basename );
|
||||
$last_version = $this->get_last_version();
|
||||
$package_filename = $plugin_folder . '.' . $last_version . '.zip';
|
||||
|
||||
$plugin_transient->checked[ $plugin_basename ] = $last_version;
|
||||
|
||||
if ( ! empty( $plugin_transient->response[ $plugin_basename ] ) ) {
|
||||
$tmp_obj = $plugin_transient->response[ $plugin_basename ];
|
||||
} elseif ( ! empty( $plugin_transient->no_update[ $plugin_basename ] ) ) {
|
||||
$tmp_obj = $plugin_transient->no_update[ $plugin_basename ];
|
||||
} else {
|
||||
$tmp_obj = (object) array(
|
||||
'id' => 'w.org/plugins/' . $plugin_folder,
|
||||
'slug' => $plugin_folder,
|
||||
'plugin' => $plugin_basename,
|
||||
'new_version' => $last_version,
|
||||
'url' => 'https://wordpress.org/plugins/' . $plugin_folder . '/',
|
||||
'package' => 'https://downloads.wordpress.org/plugin/' . $package_filename,
|
||||
'icons' => array(),
|
||||
'banners' => array(),
|
||||
'banners_rtl' => array(),
|
||||
);
|
||||
}
|
||||
|
||||
$tmp_obj->new_version = $last_version;
|
||||
$tmp_obj->package = preg_replace( '@/[^/]+$@', '/' . $package_filename, $tmp_obj->package );
|
||||
|
||||
$plugin_transient->response[ $plugin_basename ] = $tmp_obj;
|
||||
unset( $plugin_transient->no_update[ $plugin_basename ] );
|
||||
|
||||
set_site_transient( 'update_plugins', $plugin_transient );
|
||||
|
||||
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
|
||||
|
||||
/* translators: %s is the plugin name. */
|
||||
$title = sprintf( __( '%s Update Rollback', 'imagify' ), $this->plugin_name );
|
||||
$nonce = 'upgrade-plugin_' . $plugin_basename;
|
||||
$url = 'update.php?action=upgrade-plugin&plugin=' . rawurlencode( $plugin_basename );
|
||||
$upgrader_skin = new Plugin_Upgrader_Skin( compact( 'title', 'nonce', 'url', 'plugin' ) );
|
||||
$upgrader = new Plugin_Upgrader( $upgrader_skin );
|
||||
|
||||
$upgrader->upgrade( $plugin_basename );
|
||||
|
||||
wp_die(
|
||||
'',
|
||||
/* translators: %s is the plugin name. */
|
||||
sprintf( __( '%s Update Rollback', 'imagify' ), $this->plugin_name ),
|
||||
array( 'response' => 200 )
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,339 @@
|
||||
<?php
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Class used to check that Imagify has everything it needs.
|
||||
*
|
||||
* @since 1.7.1
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class Imagify_Requirements {
|
||||
/**
|
||||
* Cache the test results.
|
||||
*
|
||||
* @var object
|
||||
* @access protected
|
||||
* @since 1.7.1
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected static $supports = array();
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** SERVER ================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Test for cURL.
|
||||
*
|
||||
* @since 1.7.1
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param bool $reset_cache True to get a fresh value.
|
||||
* @return bool
|
||||
*/
|
||||
public static function supports_curl( $reset_cache = false ) {
|
||||
if ( $reset_cache || ! isset( self::$supports['curl'] ) ) {
|
||||
self::$supports['curl'] = function_exists( 'curl_init' ) && function_exists( 'curl_exec' );
|
||||
}
|
||||
|
||||
return self::$supports['curl'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for imageMagick and GD.
|
||||
* Similar to _wp_image_editor_choose(), but allows to test for multiple mime types at once.
|
||||
*
|
||||
* @since 1.7.1
|
||||
* @access public
|
||||
* @see _wp_image_editor_choose()
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param bool $reset_cache True to get a fresh value.
|
||||
* @return bool
|
||||
*/
|
||||
public static function supports_image_editor( $reset_cache = false ) {
|
||||
if ( ! $reset_cache && isset( self::$supports['image_editor'] ) ) {
|
||||
return self::$supports['image_editor'];
|
||||
}
|
||||
|
||||
require_once ABSPATH . WPINC . '/class-wp-image-editor.php';
|
||||
require_once ABSPATH . WPINC . '/class-wp-image-editor-gd.php';
|
||||
require_once ABSPATH . WPINC . '/class-wp-image-editor-imagick.php';
|
||||
|
||||
self::$supports['image_editor'] = false;
|
||||
|
||||
$args = array(
|
||||
'path' => IMAGIFY_PATH . 'assets/images/imagify-logo.png',
|
||||
'mime_types' => imagify_get_mime_types( 'image' ),
|
||||
'methods' => Imagify_Attachment::get_editor_methods(),
|
||||
);
|
||||
|
||||
/** This filter is documented in /wp-includes/media.php. */
|
||||
$implementations = apply_filters( 'wp_image_editors', array( 'WP_Image_Editor_Imagick', 'WP_Image_Editor_GD' ) );
|
||||
|
||||
foreach ( $implementations as $implementation ) {
|
||||
if ( ! call_user_func( array( $implementation, 'test' ), $args ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ( $args['mime_types'] as $mime_type ) {
|
||||
if ( ! call_user_func( array( $implementation, 'supports_mime_type' ), $mime_type ) ) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
if ( array_diff( $args['methods'], get_class_methods( $implementation ) ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
self::$supports['image_editor'] = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return self::$supports['image_editor'];
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** WORDPRESS =============================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Test for the uploads directory.
|
||||
*
|
||||
* @since 1.7.1
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param bool $reset_cache True to get a fresh value.
|
||||
* @return bool
|
||||
*/
|
||||
public static function supports_uploads( $reset_cache = false ) {
|
||||
if ( ! $reset_cache && isset( self::$supports['uploads'] ) ) {
|
||||
return self::$supports['uploads'];
|
||||
}
|
||||
|
||||
self::$supports['uploads'] = Imagify_Filesystem::get_instance()->get_upload_basedir();
|
||||
|
||||
if ( self::$supports['uploads'] ) {
|
||||
self::$supports['uploads'] = Imagify_Filesystem::get_instance()->is_writable( self::$supports['uploads'] );
|
||||
}
|
||||
|
||||
return self::$supports['uploads'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if external requests are blocked for Imagify.
|
||||
*
|
||||
* @since 1.7.1
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param bool $reset_cache True to get a fresh value.
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_imagify_blocked( $reset_cache = false ) {
|
||||
if ( ! $reset_cache && isset( self::$supports['imagify_blocked'] ) ) {
|
||||
return self::$supports['imagify_blocked'];
|
||||
}
|
||||
|
||||
if ( ! defined( 'WP_HTTP_BLOCK_EXTERNAL' ) || ! WP_HTTP_BLOCK_EXTERNAL ) {
|
||||
self::$supports['imagify_blocked'] = false;
|
||||
return self::$supports['imagify_blocked'];
|
||||
}
|
||||
|
||||
if ( ! defined( 'WP_ACCESSIBLE_HOSTS' ) ) {
|
||||
self::$supports['imagify_blocked'] = true;
|
||||
return self::$supports['imagify_blocked'];
|
||||
}
|
||||
|
||||
$accessible_hosts = explode( ',', WP_ACCESSIBLE_HOSTS );
|
||||
$accessible_hosts = array_map( 'trim', $accessible_hosts );
|
||||
$accessible_hosts = array_flip( $accessible_hosts );
|
||||
|
||||
if ( isset( $accessible_hosts['*.imagify.io'] ) ) {
|
||||
self::$supports['imagify_blocked'] = false;
|
||||
return self::$supports['imagify_blocked'];
|
||||
}
|
||||
|
||||
if ( isset( $accessible_hosts['imagify.io'], $accessible_hosts['app.imagify.io'], $accessible_hosts['storage.imagify.io'] ) ) {
|
||||
self::$supports['imagify_blocked'] = false;
|
||||
return self::$supports['imagify_blocked'];
|
||||
}
|
||||
|
||||
self::$supports['imagify_blocked'] = true;
|
||||
return self::$supports['imagify_blocked'];
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** IMAGIFY BACKUP DIRECTORIES ============================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Test for the attachments backup directory.
|
||||
*
|
||||
* @since 1.7.1
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param bool $reset_cache True to get a fresh value.
|
||||
* @return bool
|
||||
*/
|
||||
public static function attachments_backup_dir_is_writable( $reset_cache = false ) {
|
||||
if ( $reset_cache || ! isset( self::$supports['attachment_backups'] ) ) {
|
||||
self::$supports['attachment_backups'] = imagify_backup_dir_is_writable();
|
||||
}
|
||||
|
||||
return self::$supports['attachment_backups'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for the custom folders backup directory.
|
||||
*
|
||||
* @since 1.7.1
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param bool $reset_cache True to get a fresh value.
|
||||
* @return bool
|
||||
*/
|
||||
public static function custom_folders_backup_dir_is_writable( $reset_cache = false ) {
|
||||
if ( $reset_cache || ! isset( self::$supports['custom_folder_backups'] ) ) {
|
||||
self::$supports['custom_folder_backups'] = Imagify_Custom_Folders::backup_dir_is_writable();
|
||||
}
|
||||
|
||||
return self::$supports['custom_folder_backups'];
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** IMAGIFY API ============================================================================= */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Determine if the Imagify API is available by checking the API version.
|
||||
* The result is cached for 3 minutes.
|
||||
*
|
||||
* @since 1.7.1
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param bool $reset_cache True to get a fresh value.
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_api_up( $reset_cache = false ) {
|
||||
if ( ! $reset_cache && isset( self::$supports['api_up'] ) ) {
|
||||
return self::$supports['api_up'];
|
||||
}
|
||||
|
||||
$transient_name = 'imagify_check_api_version';
|
||||
$transient_expiration = 3 * MINUTE_IN_SECONDS;
|
||||
$transient_value = $reset_cache ? false : get_site_transient( $transient_name );
|
||||
|
||||
if ( false !== $transient_value ) {
|
||||
self::$supports['api_up'] = (bool) $transient_value;
|
||||
return self::$supports['api_up'];
|
||||
}
|
||||
|
||||
self::$supports['api_up'] = ! is_wp_error( get_imagify_api_version() );
|
||||
$transient_value = (int) self::$supports['api_up'];
|
||||
|
||||
set_site_transient( $transient_name, $transient_value, $transient_expiration );
|
||||
|
||||
return self::$supports['api_up'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for the Imagify API key validity.
|
||||
* A positive result is cached for 1 year.
|
||||
*
|
||||
* @since 1.7.1
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param bool $reset_cache True to get a fresh value.
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_api_key_valid( $reset_cache = false ) {
|
||||
if ( $reset_cache ) {
|
||||
self::reset_cache( 'api_key_valid' );
|
||||
}
|
||||
|
||||
if ( isset( self::$supports['api_key_valid'] ) ) {
|
||||
return self::$supports['api_key_valid'];
|
||||
}
|
||||
|
||||
if ( ! Imagify_Options::get_instance()->get( 'api_key' ) ) {
|
||||
self::$supports['api_key_valid'] = false;
|
||||
return self::$supports['api_key_valid'];
|
||||
}
|
||||
|
||||
if ( get_site_transient( 'imagify_check_licence_1' ) ) {
|
||||
self::$supports['api_key_valid'] = true;
|
||||
return self::$supports['api_key_valid'];
|
||||
}
|
||||
|
||||
if ( is_wp_error( get_imagify_user() ) ) {
|
||||
self::$supports['api_key_valid'] = false;
|
||||
return self::$supports['api_key_valid'];
|
||||
}
|
||||
|
||||
self::$supports['api_key_valid'] = true;
|
||||
set_site_transient( 'imagify_check_licence_1', 1, YEAR_IN_SECONDS );
|
||||
|
||||
return self::$supports['api_key_valid'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for the Imagify account quota.
|
||||
*
|
||||
* @since 1.7.1
|
||||
* @since 1.9.9 Return false when the API cannot be reached.
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param bool $reset_cache True to get a fresh value.
|
||||
* @return bool True when over quota. False otherwise, even when the API cannot be reached.
|
||||
*/
|
||||
public static function is_over_quota( $reset_cache = false ) {
|
||||
if ( ! $reset_cache && isset( self::$supports['over_quota'] ) ) {
|
||||
return self::$supports['over_quota'];
|
||||
}
|
||||
|
||||
$user = new Imagify_User();
|
||||
|
||||
self::$supports['over_quota'] = $user->get_error() ? false : $user->is_over_quota();
|
||||
|
||||
return self::$supports['over_quota'];
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** CLASS CACHE ============================================================================= */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Reset a test cache.
|
||||
*
|
||||
* @since 1.7.1
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $cache_key Cache key.
|
||||
*/
|
||||
public static function reset_cache( $cache_key ) {
|
||||
unset( self::$supports[ $cache_key ] );
|
||||
|
||||
$transients = array(
|
||||
'api_up' => 'imagify_check_api_version',
|
||||
'api_key_valid' => 'imagify_check_licence_1',
|
||||
);
|
||||
|
||||
if ( isset( $transients[ $cache_key ] ) && get_site_transient( $transients[ $cache_key ] ) ) {
|
||||
delete_site_transient( $transients[ $cache_key ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,923 @@
|
||||
<?php
|
||||
use Imagify\Notices\Notices;
|
||||
|
||||
/**
|
||||
* Class that handles the plugin settings.
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
class Imagify_Settings {
|
||||
|
||||
/**
|
||||
* Class version.
|
||||
*
|
||||
* @since 1.7
|
||||
* @var string
|
||||
*/
|
||||
const VERSION = '1.0.1';
|
||||
|
||||
/**
|
||||
* The settings group.
|
||||
*
|
||||
* @since 1.7
|
||||
* @var string
|
||||
*/
|
||||
protected $settings_group;
|
||||
|
||||
/**
|
||||
* The option name.
|
||||
*
|
||||
* @since 1.7
|
||||
* @var string
|
||||
*/
|
||||
protected $option_name;
|
||||
|
||||
/**
|
||||
* The options instance.
|
||||
*
|
||||
* @since 1.7
|
||||
* @var object
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* The single instance of the class.
|
||||
*
|
||||
* @since 1.7
|
||||
* @var object
|
||||
*/
|
||||
protected static $_instance;
|
||||
|
||||
/**
|
||||
* The constructor.
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
protected function __construct() {
|
||||
$this->options = Imagify_Options::get_instance();
|
||||
$this->option_name = $this->options->get_option_name();
|
||||
$this->settings_group = IMAGIFY_SLUG;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the main Instance.
|
||||
*
|
||||
* @since 1.7
|
||||
* @return object Main instance.
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! isset( self::$_instance ) ) {
|
||||
self::$_instance = new self();
|
||||
}
|
||||
|
||||
return self::$_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch the hooks.
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
public function init() {
|
||||
add_filter( 'sanitize_option_' . $this->option_name, [ $this, 'populate_values_on_save' ], 5 );
|
||||
add_action( 'admin_init', [ $this, 'register' ] );
|
||||
add_filter( 'option_page_capability_' . $this->settings_group, [ $this, 'get_capability' ] );
|
||||
|
||||
if ( imagify_is_active_for_network() ) {
|
||||
add_filter( 'pre_update_site_option_' . $this->option_name, [
|
||||
$this,
|
||||
'maybe_set_redirection',
|
||||
], 10, 2 );
|
||||
add_action( 'update_site_option_' . $this->option_name, [
|
||||
$this,
|
||||
'after_save_network_options',
|
||||
], 10, 3 );
|
||||
add_action( 'admin_post_update', [ $this, 'update_site_option_on_network' ] );
|
||||
} else {
|
||||
add_filter( 'pre_update_option_' . $this->option_name, [ $this, 'maybe_set_redirection' ], 10, 2 );
|
||||
add_action( 'update_option_' . $this->option_name, [ $this, 'after_save_options' ], 10, 2 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** VARIOUS HELPERS ========================================================================= */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the name of the settings group.
|
||||
*
|
||||
* @since 1.7
|
||||
* @return string
|
||||
*/
|
||||
public function get_settings_group() {
|
||||
return $this->settings_group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL to use as form action.
|
||||
*
|
||||
* @since 1.7
|
||||
* @return string
|
||||
*/
|
||||
public function get_form_action() {
|
||||
return imagify_is_active_for_network() ? admin_url( 'admin-post.php' ) : admin_url( 'options.php' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if we're submitting the settings form.
|
||||
*
|
||||
* @since 1.7
|
||||
* @return bool
|
||||
*/
|
||||
public function is_form_submit() {
|
||||
return filter_input( INPUT_POST, 'option_page', FILTER_SANITIZE_STRING ) === $this->settings_group && filter_input( INPUT_POST, 'action', FILTER_SANITIZE_STRING ) === 'update';
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** ON FORM SUBMIT ========================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* On form submit, handle some specific values.
|
||||
* This must be hooked before Imagify_Options::sanitize_and_validate_on_update().
|
||||
*
|
||||
* @since 1.7
|
||||
*
|
||||
* @param array $values The option values.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function populate_values_on_save( $values ) {
|
||||
if ( ! $this->is_form_submit() ) {
|
||||
return $values;
|
||||
}
|
||||
|
||||
$values = is_array( $values ) ? $values : [];
|
||||
|
||||
/**
|
||||
* Disabled thumbnail sizes.
|
||||
*/
|
||||
$values = $this->populate_disallowed_sizes( $values );
|
||||
|
||||
/**
|
||||
* Custom folders.
|
||||
*/
|
||||
$values = $this->populate_custom_folders( $values );
|
||||
|
||||
/**
|
||||
* Filter settings when saved via the settings page.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param array $values The option values.
|
||||
*/
|
||||
$values = apply_filters( 'imagify_settings_on_save', $values );
|
||||
|
||||
return (array) $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* On form submit, handle disallowed thumbnail sizes.
|
||||
*
|
||||
* @since 1.7
|
||||
*
|
||||
* @param array $values The option values.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function populate_disallowed_sizes( $values ) {
|
||||
$values['disallowed-sizes'] = [];
|
||||
|
||||
if ( isset( $values['disallowed-sizes-reversed'] ) && is_array( $values['disallowed-sizes-reversed'] ) ) {
|
||||
$checked = ! empty( $values['disallowed-sizes-checked'] ) && is_array( $values['disallowed-sizes-checked'] ) ? array_flip( $values['disallowed-sizes-checked'] ) : [];
|
||||
|
||||
if ( ! empty( $values['disallowed-sizes-reversed'] ) ) {
|
||||
foreach ( $values['disallowed-sizes-reversed'] as $size_key ) {
|
||||
if ( ! isset( $checked[ $size_key ] ) ) {
|
||||
// The checkbox is not checked: the size is disabled.
|
||||
$values['disallowed-sizes'][ $size_key ] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unset( $values['disallowed-sizes-reversed'], $values['disallowed-sizes-checked'] );
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* On form submit, handle the custom folders.
|
||||
*
|
||||
* @since 1.7
|
||||
*
|
||||
* @param array $values The option values.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function populate_custom_folders( $values ) {
|
||||
if ( ! imagify_can_optimize_custom_folders() ) {
|
||||
// The databases are not ready or the user has not the permission.
|
||||
unset( $values['custom_folders'] );
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
if ( ! isset( $values['custom_folders'] ) ) {
|
||||
// No selected folders: set them all inactive.
|
||||
Imagify_Custom_Folders::deactivate_all_folders();
|
||||
// Remove files that are in inactive folders and are not optimized.
|
||||
Imagify_Custom_Folders::remove_unoptimized_files_from_inactive_folders();
|
||||
// Remove empty inactive folders.
|
||||
Imagify_Custom_Folders::remove_empty_inactive_folders();
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
if ( ! is_array( $values['custom_folders'] ) ) {
|
||||
// Invalid value.
|
||||
unset( $values['custom_folders'] );
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
$selected = array_filter( $values['custom_folders'] );
|
||||
unset( $values['custom_folders'] );
|
||||
|
||||
if ( ! $selected ) {
|
||||
// No selected folders: set them all inactive.
|
||||
Imagify_Custom_Folders::deactivate_all_folders();
|
||||
// Remove files that are in inactive folders and are not optimized.
|
||||
Imagify_Custom_Folders::remove_unoptimized_files_from_inactive_folders();
|
||||
// Remove empty inactive folders.
|
||||
Imagify_Custom_Folders::remove_empty_inactive_folders();
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
// Normalize the paths, remove duplicates, and remove sub-paths.
|
||||
$selected = array_map( 'sanitize_text_field', $selected );
|
||||
$selected = array_map( 'wp_normalize_path', $selected );
|
||||
$selected = array_map( 'trailingslashit', $selected );
|
||||
$selected = array_flip( array_flip( $selected ) );
|
||||
$selected = Imagify_Custom_Folders::remove_sub_paths( $selected );
|
||||
|
||||
// Remove the active status from the folders that are not selected.
|
||||
Imagify_Custom_Folders::deactivate_not_selected_folders( $selected );
|
||||
|
||||
// Add the active status to the folders that are selected (and already in the DB).
|
||||
$selected = Imagify_Custom_Folders::activate_selected_folders( $selected );
|
||||
|
||||
// If we still have paths here, they need to be added to the DB with an active status.
|
||||
Imagify_Custom_Folders::insert_folders( $selected );
|
||||
|
||||
// Remove files that are in inactive folders and are not optimized.
|
||||
Imagify_Custom_Folders::remove_unoptimized_files_from_inactive_folders();
|
||||
|
||||
// Reassign files to active folders.
|
||||
Imagify_Custom_Folders::reassign_inactive_files();
|
||||
|
||||
// Remove empty inactive folders.
|
||||
Imagify_Custom_Folders::remove_empty_inactive_folders();
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** SETTINGS API ============================================================================ */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Add Imagify' settings to the settings API whitelist.
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
public function register() {
|
||||
register_setting( $this->settings_group, $this->option_name );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the user capacity needed to save Imagify's main options from the settings page.
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
public function get_capability() {
|
||||
return imagify_get_context( 'wp' )->get_capacity( 'manage' );
|
||||
}
|
||||
|
||||
/**
|
||||
* If the user clicked the "Save & Go to Bulk Optimizer" button, set a redirection to the bulk optimizer.
|
||||
* We use this hook because it can be triggered even if the option value hasn't changed.
|
||||
*
|
||||
* @since 1.7
|
||||
*
|
||||
* @param mixed $value The new, unserialized option value.
|
||||
* @param mixed $old_value The old option value.
|
||||
*
|
||||
* @return mixed The option value.
|
||||
*/
|
||||
public function maybe_set_redirection( $value, $old_value ) {
|
||||
if ( isset( $_POST['submit-goto-bulk'] ) ) { // WPCS: CSRF ok.
|
||||
$_REQUEST['_wp_http_referer'] = esc_url_raw( get_admin_url( get_current_blog_id(), 'upload.php?page=imagify-bulk-optimization' ) );
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to launch some actions after saving the network options.
|
||||
*
|
||||
* @since 1.7
|
||||
*
|
||||
* @param string $option Name of the network option.
|
||||
* @param mixed $value Current value of the network option.
|
||||
* @param mixed $old_value Old value of the network option.
|
||||
*/
|
||||
public function after_save_network_options( $option, $value, $old_value ) {
|
||||
$this->after_save_options( $old_value, $value );
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to launch some actions after saving the options.
|
||||
*
|
||||
* @since 1.7
|
||||
*
|
||||
* @param mixed $old_value The old option value.
|
||||
* @param mixed $value The new option value.
|
||||
*/
|
||||
public function after_save_options( $old_value, $value ) {
|
||||
$old_key = isset( $old_value['api_key'] ) ? $old_value['api_key'] : '';
|
||||
$new_key = isset( $value['api_key'] ) ? $value['api_key'] : '';
|
||||
|
||||
if ( $old_key === $new_key ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle API key validation cache and notices.
|
||||
if ( Imagify_Requirements::is_api_key_valid( true ) ) {
|
||||
Notices::dismiss_notice( 'wrong-api-key' );
|
||||
} else {
|
||||
Notices::renew_notice( 'wrong-api-key' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* `options.php` does not handle network options. Let's use `admin-post.php` for multisite installations.
|
||||
*
|
||||
* @since 1.9.11 deprecate 'whitelist_options' filter.
|
||||
* @since 1.7
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function update_site_option_on_network() {
|
||||
global $wp_version;
|
||||
|
||||
if ( empty( $_POST['option_page'] ) || $_POST['option_page'] !== $this->settings_group ) { // WPCS: CSRF ok.
|
||||
return;
|
||||
}
|
||||
|
||||
/** This filter is documented in /wp-admin/options.php. */
|
||||
$capability = apply_filters( 'option_page_capability_' . $this->settings_group, 'manage_network_options' );
|
||||
|
||||
if ( ! current_user_can( $capability ) ) {
|
||||
imagify_die();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! imagify_check_nonce( $this->settings_group . '-options' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( version_compare( $wp_version, '5.5', '>=' ) ) {
|
||||
$allowed_options = apply_filters_deprecated(
|
||||
'whitelist_options',
|
||||
[ [] ],
|
||||
'5.5.0',
|
||||
'allowed_options',
|
||||
__( 'Please consider writing more inclusive code.' )
|
||||
);
|
||||
} else {
|
||||
$allowed_options = apply_filters( 'whitelist_options', [] );
|
||||
}
|
||||
|
||||
$allowed_options = apply_filters( 'allowed_options', $allowed_options );
|
||||
|
||||
if ( ! isset( $allowed_options[ $this->settings_group ] ) ) {
|
||||
imagify_die( __( '<strong>ERROR</strong>: options page not found.' ) );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$options = $allowed_options[ $this->settings_group ];
|
||||
|
||||
if ( $options ) {
|
||||
foreach ( $options as $option ) {
|
||||
$option = trim( $option );
|
||||
$value = null;
|
||||
|
||||
if ( isset( $_POST[ $option ] ) ) {
|
||||
$value = wp_unslash( $_POST[ $option ] );
|
||||
if ( ! is_array( $value ) ) {
|
||||
$value = trim( $value );
|
||||
}
|
||||
$value = wp_unslash( $value );
|
||||
}
|
||||
|
||||
update_site_option( $option, $value );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect back to the settings page that was submitted.
|
||||
*/
|
||||
imagify_maybe_redirect( false, [ 'settings-updated' => 'true' ] );
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** FIELDS ================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Display a single checkbox.
|
||||
*
|
||||
* @since 1.7
|
||||
*
|
||||
* @param array $args Arguments:
|
||||
* {option_name} string The option name. E.g. 'disallowed-sizes'. Mandatory.
|
||||
* {label} string The label to use.
|
||||
* {info} string Text to display in an "Info box" after the field. A 'aria-describedby' attribute will automatically be created.
|
||||
* {attributes} array A list of HTML attributes, as 'attribute' => 'value'.
|
||||
* {current_value} int|bool USE ONLY WHEN DEALING WITH DATA THAT IS NOT SAVED IN THE PLUGIN OPTIONS. If not provided, the field will automatically get the value from the options.
|
||||
*/
|
||||
public function field_checkbox( $args ) {
|
||||
$args = array_merge( [
|
||||
'option_name' => '',
|
||||
'label' => '',
|
||||
'info' => '',
|
||||
'attributes' => [],
|
||||
// To not use the plugin settings: use an integer.
|
||||
'current_value' => null,
|
||||
], $args );
|
||||
|
||||
if ( ! $args['option_name'] || ! $args['label'] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( is_numeric( $args['current_value'] ) || is_bool( $args['current_value'] ) ) {
|
||||
// We don't use the plugin settings.
|
||||
$current_value = (int) (bool) $args['current_value'];
|
||||
} else {
|
||||
// This is a normal plugin setting.
|
||||
$current_value = $this->options->get( $args['option_name'] );
|
||||
}
|
||||
|
||||
$option_name_class = sanitize_html_class( $args['option_name'] );
|
||||
$attributes = [
|
||||
'name' => $this->option_name . '[' . $args['option_name'] . ']',
|
||||
'id' => 'imagify_' . $option_name_class,
|
||||
];
|
||||
|
||||
if ( $args['info'] && empty( $attributes['aria-describedby'] ) ) {
|
||||
$attributes['aria-describedby'] = 'describe-' . $option_name_class;
|
||||
}
|
||||
|
||||
$attributes = array_merge( $attributes, $args['attributes'] );
|
||||
$args['attributes'] = self::build_attributes( $attributes );
|
||||
?>
|
||||
<input type="checkbox" value="1" <?php
|
||||
checked( $current_value, 1 );
|
||||
?><?php
|
||||
echo $args['attributes'];
|
||||
?> />
|
||||
<!-- Empty onclick attribute to make clickable labels on iTruc & Mac -->
|
||||
<label for="<?php
|
||||
echo $attributes['id'];
|
||||
?>" onclick=""><?php
|
||||
echo $args['label'];
|
||||
?></label>
|
||||
<?php
|
||||
if ( ! $args['info'] ) {
|
||||
return;
|
||||
}
|
||||
?>
|
||||
<span id="<?php
|
||||
echo $attributes['aria-describedby'];
|
||||
?>" class="imagify-info">
|
||||
<span class="dashicons dashicons-info"></span>
|
||||
<?php
|
||||
echo $args['info'];
|
||||
?>
|
||||
</span>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a checkbox group.
|
||||
*
|
||||
* @since 1.7
|
||||
*
|
||||
* @param array $args Arguments:
|
||||
* {option_name} string The option name. E.g. 'disallowed-sizes'. Mandatory.
|
||||
* {legend} string Label to use for the <legend> tag.
|
||||
* {values} array List of values to display, in the form of 'value' => 'Label'. Mandatory.
|
||||
* {disabled_values} array Values to be disabled. Values are the array keys.
|
||||
* {reverse_check} bool If true, the values that will be stored in the option are the ones that are unchecked. It requires special treatment when saving (detect what values are unchecked).
|
||||
* {attributes} array A list of HTML attributes, as 'attribute' => 'value'.
|
||||
* {current_values} array USE ONLY WHEN DEALING WITH DATA THAT IS NOT SAVED IN THE PLUGIN OPTIONS. If not provided, the field will automatically get the value from the options.
|
||||
*/
|
||||
public function field_checkbox_list( $args ) {
|
||||
$args = array_merge( [
|
||||
'option_name' => '',
|
||||
'legend' => '',
|
||||
'values' => [],
|
||||
'disabled_values' => [],
|
||||
'reverse_check' => false,
|
||||
'attributes' => [],
|
||||
// To not use the plugin settings: use an array.
|
||||
'current_values' => false,
|
||||
], $args );
|
||||
|
||||
if ( ! $args['option_name'] || ! $args['values'] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( is_array( $args['current_values'] ) ) {
|
||||
// We don't use the plugin settings.
|
||||
$current_values = $args['current_values'];
|
||||
} else {
|
||||
// This is a normal plugin setting.
|
||||
$current_values = $this->options->get( $args['option_name'] );
|
||||
}
|
||||
|
||||
$option_name_class = sanitize_html_class( $args['option_name'] );
|
||||
$attributes = array_merge( [
|
||||
'name' => $this->option_name . '[' . $args['option_name'] . ( $args['reverse_check'] ? '-checked' : '' ) . '][]',
|
||||
'id' => 'imagify_' . $option_name_class . '_%s',
|
||||
'class' => 'imagify-row-check',
|
||||
], $args['attributes'] );
|
||||
|
||||
$id_attribute = $attributes['id'];
|
||||
unset( $attributes['id'] );
|
||||
$args['attributes'] = self::build_attributes( $attributes );
|
||||
|
||||
$current_values = array_diff_key( $current_values, $args['disabled_values'] );
|
||||
$nb_of_values = count( $args['values'] );
|
||||
$display_check_all = $nb_of_values > 3;
|
||||
$nb_of_checked = 0;
|
||||
?>
|
||||
<fieldset class="imagify-check-group<?php
|
||||
echo $nb_of_values > 5 ? ' imagify-is-scrollable' : '';
|
||||
?>">
|
||||
<?php
|
||||
if ( $args['legend'] ) {
|
||||
?>
|
||||
<legend class="screen-reader-text"><?php
|
||||
echo $args['legend'];
|
||||
?></legend>
|
||||
<?php
|
||||
}
|
||||
|
||||
foreach ( $args['values'] as $value => $label ) {
|
||||
$input_id = sprintf( $id_attribute, sanitize_html_class( $value ) );
|
||||
$disabled = isset( $args['disabled_values'][ $value ] );
|
||||
|
||||
if ( $args['reverse_check'] ) {
|
||||
$checked = ! $disabled && ! isset( $current_values[ $value ] );
|
||||
} else {
|
||||
$checked = ! $disabled && isset( $current_values[ $value ] );
|
||||
}
|
||||
|
||||
$nb_of_checked = $checked ? $nb_of_checked + 1 : $nb_of_checked;
|
||||
|
||||
if ( $args['reverse_check'] ) {
|
||||
echo '<input type="hidden" name="' . $this->option_name . '[' . $args['option_name'] . '-reversed][]" value="' . esc_attr( $value ) . '" />';
|
||||
}
|
||||
?>
|
||||
<p>
|
||||
<input type="checkbox" value="<?php
|
||||
echo esc_attr( $value );
|
||||
?>" id="<?php
|
||||
echo $input_id;
|
||||
?>"<?php
|
||||
echo $args['attributes'];
|
||||
?> <?php
|
||||
checked( $checked );
|
||||
?> <?php
|
||||
disabled( $disabled );
|
||||
?>/>
|
||||
<label for="<?php
|
||||
echo $input_id;
|
||||
?>" onclick=""><?php
|
||||
echo $label;
|
||||
?></label>
|
||||
</p>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</fieldset>
|
||||
<?php
|
||||
if ( $display_check_all ) {
|
||||
if ( $args['reverse_check'] ) {
|
||||
$all_checked = ! array_intersect_key( $args['values'], $current_values );
|
||||
} else {
|
||||
$all_checked = ! array_diff_key( $args['values'], $current_values );
|
||||
}
|
||||
?>
|
||||
<p class="hide-if-no-js imagify-select-all-buttons">
|
||||
<button type="button" class="imagify-link-like imagify-select-all<?php
|
||||
echo $all_checked ? ' imagify-is-inactive" aria-disabled="true' : '';
|
||||
?>" data-action="select"><?php
|
||||
_e( 'Select All', 'imagify' );
|
||||
?></button>
|
||||
|
||||
<span class="imagify-pipe"></span>
|
||||
|
||||
<button type="button" class="imagify-link-like imagify-select-all<?php
|
||||
echo $nb_of_checked ? '' : ' imagify-is-inactive" aria-disabled="true';
|
||||
?>" data-action="unselect"><?php
|
||||
_e( 'Unselect All', 'imagify' );
|
||||
?></button>
|
||||
</p>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a radio list group.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param array $args {
|
||||
* Arguments.
|
||||
*
|
||||
* @type string $option_name The option name. E.g. 'disallowed-sizes'. Mandatory.
|
||||
* @type string $legend Label to use for the <legend> tag.
|
||||
* @type string $info Text to display in an "Info box" after the field. A 'aria-describedby' attribute will automatically be created.
|
||||
* @type array $values List of values to display, in the form of 'value' => 'Label'. Mandatory.
|
||||
* @type array $attributes A list of HTML attributes, as 'attribute' => 'value'.
|
||||
* @type array $current_value USE ONLY WHEN DEALING WITH DATA THAT IS NOT SAVED IN THE PLUGIN OPTIONS. If not provided, the field will automatically get the value from the options.
|
||||
* }
|
||||
*/
|
||||
public function field_radio_list( $args ) {
|
||||
$args = array_merge( [
|
||||
'option_name' => '',
|
||||
'legend' => '',
|
||||
'info' => '',
|
||||
'values' => [],
|
||||
'attributes' => [],
|
||||
// To not use the plugin settings: use an array.
|
||||
'current_value' => false,
|
||||
], $args );
|
||||
|
||||
if ( ! $args['option_name'] || ! $args['values'] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( is_array( $args['current_value'] ) ) {
|
||||
// We don't use the plugin settings.
|
||||
$current_value = $args['current_value'];
|
||||
} else {
|
||||
// This is a normal plugin setting.
|
||||
$current_value = $this->options->get( $args['option_name'] );
|
||||
}
|
||||
|
||||
$option_name_class = sanitize_html_class( $args['option_name'] );
|
||||
$attributes = array_merge( [
|
||||
'name' => $this->option_name . '[' . $args['option_name'] . ']',
|
||||
'id' => 'imagify_' . $option_name_class . '_%s',
|
||||
'class' => 'imagify-row-radio',
|
||||
], $args['attributes'] );
|
||||
|
||||
$id_attribute = $attributes['id'];
|
||||
unset( $attributes['id'] );
|
||||
$args['attributes'] = self::build_attributes( $attributes );
|
||||
?>
|
||||
<fieldset class="imagify-radio-group">
|
||||
<?php
|
||||
if ( $args['legend'] ) {
|
||||
?>
|
||||
<legend class="screen-reader-text"><?php
|
||||
echo $args['legend'];
|
||||
?></legend>
|
||||
<?php
|
||||
}
|
||||
|
||||
foreach ( $args['values'] as $value => $label ) {
|
||||
$input_id = sprintf( $id_attribute, sanitize_html_class( $value ) );
|
||||
?>
|
||||
<input type="radio" value="<?php
|
||||
echo esc_attr( $value );
|
||||
?>" id="<?php
|
||||
echo $input_id;
|
||||
?>"<?php
|
||||
echo $args['attributes'];
|
||||
?> <?php
|
||||
checked( $current_value, $value );
|
||||
?>/>
|
||||
<label for="<?php
|
||||
echo $input_id;
|
||||
?>" onclick=""><?php
|
||||
echo $label;
|
||||
?></label>
|
||||
<br/>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</fieldset>
|
||||
<?php
|
||||
if ( ! $args['info'] ) {
|
||||
return;
|
||||
}
|
||||
?>
|
||||
<span id="<?php
|
||||
echo $attributes['aria-describedby'];
|
||||
?>" class="imagify-info">
|
||||
<span class="dashicons dashicons-info"></span>
|
||||
<?php
|
||||
echo $args['info'];
|
||||
?>
|
||||
</span>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a text box.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @param array $args Arguments:
|
||||
* {option_name} string The option name. E.g. 'disallowed-sizes'. Mandatory.
|
||||
* {label} string The label to use.
|
||||
* {info} string Text to display in an "Info box" after the field. A 'aria-describedby' attribute will automatically be created.
|
||||
* {attributes} array A list of HTML attributes, as 'attribute' => 'value'.
|
||||
* {current_value} int|bool USE ONLY WHEN DEALING WITH DATA THAT IS NOT SAVED IN THE PLUGIN OPTIONS. If not provided, the field will automatically get the value from the options.
|
||||
*/
|
||||
public function field_text_box( $args ) {
|
||||
$args = array_merge( [
|
||||
'option_name' => '',
|
||||
'label' => '',
|
||||
'info' => '',
|
||||
'attributes' => [],
|
||||
// To not use the plugin settings.
|
||||
'current_value' => null,
|
||||
], $args );
|
||||
|
||||
if ( ! $args['option_name'] || ! $args['label'] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( is_numeric( $args['current_value'] ) || is_string( $args['current_value'] ) ) {
|
||||
// We don't use the plugin settings.
|
||||
$current_value = $args['current_value'];
|
||||
} else {
|
||||
// This is a normal plugin setting.
|
||||
$current_value = $this->options->get( $args['option_name'] );
|
||||
}
|
||||
|
||||
$option_name_class = sanitize_html_class( $args['option_name'] );
|
||||
$attributes = [
|
||||
'name' => $this->option_name . '[' . $args['option_name'] . ']',
|
||||
'id' => 'imagify_' . $option_name_class,
|
||||
];
|
||||
|
||||
if ( $args['info'] && empty( $attributes['aria-describedby'] ) ) {
|
||||
$attributes['aria-describedby'] = 'describe-' . $option_name_class;
|
||||
}
|
||||
|
||||
$attributes = array_merge( $attributes, $args['attributes'] );
|
||||
$args['attributes'] = self::build_attributes( $attributes );
|
||||
?>
|
||||
<!-- Empty onclick attribute to make clickable labels on iTruc & Mac -->
|
||||
<label for="<?php
|
||||
echo $attributes['id'];
|
||||
?>" onclick=""><?php
|
||||
echo $args['label'];
|
||||
?></label>
|
||||
<input type="text" value="<?php
|
||||
echo esc_attr( $current_value );
|
||||
?>"<?php
|
||||
echo $args['attributes'];
|
||||
?> />
|
||||
<?php
|
||||
if ( ! $args['info'] ) {
|
||||
return;
|
||||
}
|
||||
?>
|
||||
<span id="<?php
|
||||
echo $attributes['aria-describedby'];
|
||||
?>" class="imagify-info">
|
||||
<span class="dashicons dashicons-info"></span>
|
||||
<?php
|
||||
echo $args['info'];
|
||||
?>
|
||||
</span>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a simple hidden input.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @param array $args Arguments:
|
||||
* {option_name} string The option name. E.g. 'disallowed-sizes'. Mandatory.
|
||||
* {attributes} array A list of HTML attributes, as 'attribute' => 'value'.
|
||||
* {current_value} int|bool USE ONLY WHEN DEALING WITH DATA THAT IS NOT SAVED IN THE PLUGIN OPTIONS. If not provided, the field will automatically get the value from the options.
|
||||
*/
|
||||
public function field_hidden( $args ) {
|
||||
$args = array_merge( [
|
||||
'option_name' => '',
|
||||
'attributes' => [],
|
||||
// To not use the plugin settings.
|
||||
'current_value' => null,
|
||||
], $args );
|
||||
|
||||
if ( ! $args['option_name'] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( is_numeric( $args['current_value'] ) || is_string( $args['current_value'] ) ) {
|
||||
// We don't use the plugin settings.
|
||||
$current_value = $args['current_value'];
|
||||
} else {
|
||||
// This is a normal plugin setting.
|
||||
$current_value = $this->options->get( $args['option_name'] );
|
||||
}
|
||||
|
||||
$option_name_class = sanitize_html_class( $args['option_name'] );
|
||||
$attributes = [
|
||||
'name' => $this->option_name . '[' . $args['option_name'] . ']',
|
||||
'id' => 'imagify_' . $option_name_class,
|
||||
];
|
||||
|
||||
$attributes = array_merge( $attributes, $args['attributes'] );
|
||||
$args['attributes'] = self::build_attributes( $attributes );
|
||||
?>
|
||||
<input type="hidden" value="<?php
|
||||
echo esc_attr( $current_value );
|
||||
?>"<?php
|
||||
echo $args['attributes'];
|
||||
?> />
|
||||
<?php
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** FIELD VALUES ============================================================================ */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the thumbnail sizes.
|
||||
*
|
||||
* @since 1.7
|
||||
* @return array A list of thumbnail sizes in the form of 'medium' => 'medium - 300 × 300'.
|
||||
*/
|
||||
public static function get_thumbnail_sizes() {
|
||||
static $sizes;
|
||||
|
||||
if ( isset( $sizes ) ) {
|
||||
return $sizes;
|
||||
}
|
||||
|
||||
$sizes = get_imagify_thumbnail_sizes();
|
||||
|
||||
foreach ( $sizes as $size_key => $size_data ) {
|
||||
$sizes[ $size_key ] = sprintf( '%s - %d × %d', esc_html( stripslashes( $size_data['name'] ) ), $size_data['width'], $size_data['height'] );
|
||||
}
|
||||
|
||||
return $sizes;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** TOOLS =================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Create HTML attributes from an array.
|
||||
*
|
||||
* @since 1.7
|
||||
*
|
||||
* @param array $attributes A list of attribute pairs.
|
||||
*
|
||||
* @return string HTML attributes.
|
||||
*/
|
||||
public static function build_attributes( $attributes ) {
|
||||
if ( ! $attributes || ! is_array( $attributes ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$out = '';
|
||||
|
||||
foreach ( $attributes as $attribute => $value ) {
|
||||
$out .= ' ' . $attribute . '="' . esc_attr( $value ) . '"';
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
}
|
||||
262
wp/wp-content/plugins/imagify/inc/classes/class-imagify-user.php
Normal file
262
wp/wp-content/plugins/imagify/inc/classes/class-imagify-user.php
Normal file
@@ -0,0 +1,262 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Imagify User class.
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
class Imagify_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;
|
||||
|
||||
/**
|
||||
* 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->error = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the possible error returned when fetching user data.
|
||||
*
|
||||
* @since 1.9.9
|
||||
*
|
||||
* @return bool|\WP_Error A \WP_Error object if the request to fetch the user data failed. False overwise.
|
||||
*/
|
||||
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 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() )
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,744 @@
|
||||
<?php
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Class that handles templates and menus.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class Imagify_Views {
|
||||
|
||||
/**
|
||||
* Class version.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
*/
|
||||
const VERSION = '1.1';
|
||||
|
||||
/**
|
||||
* Slug used for the settings page URL.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected $slug_settings;
|
||||
|
||||
/**
|
||||
* Slug used for the bulk optimization page URL.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected $slug_bulk;
|
||||
|
||||
/**
|
||||
* Slug used for the "custom folders" page URL.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected $slug_files;
|
||||
|
||||
/**
|
||||
* A list of JS templates to print at the end of the page.
|
||||
*
|
||||
* @var array
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
*/
|
||||
protected $templates_in_footer = [];
|
||||
|
||||
/**
|
||||
* Stores the "custom folders" files list instance.
|
||||
*
|
||||
* @var object Imagify_Files_List_Table
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected $list_table;
|
||||
|
||||
/**
|
||||
* Filesystem object.
|
||||
*
|
||||
* @var object Imagify_Filesystem
|
||||
* @since 1.7.1
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $filesystem;
|
||||
|
||||
/**
|
||||
* The single instance of the class.
|
||||
*
|
||||
* @var object
|
||||
* @since 1.7
|
||||
* @access protected
|
||||
*/
|
||||
protected static $_instance;
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** INSTANCE/INIT =========================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* The constructor.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access protected
|
||||
*/
|
||||
protected function __construct() {
|
||||
$this->slug_settings = IMAGIFY_SLUG;
|
||||
$this->slug_bulk = IMAGIFY_SLUG . '-bulk-optimization';
|
||||
$this->slug_files = IMAGIFY_SLUG . '-files';
|
||||
$this->filesystem = Imagify_Filesystem::get_instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the main Instance.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @return object Main instance.
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! isset( self::$_instance ) ) {
|
||||
self::$_instance = new self();
|
||||
}
|
||||
|
||||
return self::$_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch the hooks.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*/
|
||||
public function init() {
|
||||
// Menu items.
|
||||
add_action( 'admin_menu', [ $this, 'add_site_menus' ] );
|
||||
|
||||
if ( imagify_is_active_for_network() ) {
|
||||
add_action( 'network_admin_menu', [ $this, 'add_network_menus' ] );
|
||||
}
|
||||
|
||||
// Action links in plugins list.
|
||||
$basename = plugin_basename( IMAGIFY_FILE );
|
||||
add_filter( 'plugin_action_links_' . $basename, [ $this, 'plugin_action_links' ] );
|
||||
add_filter( 'network_admin_plugin_action_links_' . $basename, [ $this, 'plugin_action_links' ] );
|
||||
|
||||
// Save the "per page" option value from the files list screen.
|
||||
add_filter( 'set-screen-option', [ 'Imagify_Files_List_Table', 'save_screen_options' ], 10, 3 );
|
||||
|
||||
// JS templates in footer.
|
||||
add_action( 'admin_print_footer_scripts', [ $this, 'print_js_templates' ] );
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** MENU ITEMS ============================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Add sub-menus for all sites.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*/
|
||||
public function add_site_menus() {
|
||||
$wp_context = imagify_get_context( 'wp' );
|
||||
|
||||
// Sub-menu item: bulk optimization.
|
||||
add_media_page( __( 'Bulk Optimization', 'imagify' ), __( 'Bulk Optimization', 'imagify' ), $wp_context->get_capacity( 'bulk-optimize' ), $this->get_bulk_page_slug(), [ $this, 'display_bulk_page' ] );
|
||||
|
||||
if ( imagify_is_active_for_network() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin is not network activated.
|
||||
*/
|
||||
if ( imagify_can_optimize_custom_folders() ) {
|
||||
// Sub-menu item: custom folders list.
|
||||
$cf_context = imagify_get_context( 'custom-folders' );
|
||||
$screen_id = add_media_page( __( 'Other Media optimized by Imagify', 'imagify' ), __( 'Other Media', 'imagify' ), $cf_context->get_capacity( 'optimize' ), $this->get_files_page_slug(), [ $this, 'display_files_list' ] );
|
||||
|
||||
if ( $screen_id ) {
|
||||
// Load the data for this page.
|
||||
add_action( 'load-' . $screen_id, [ $this, 'load_files_list' ] );
|
||||
}
|
||||
}
|
||||
|
||||
// Sub-menu item: settings.
|
||||
add_options_page( 'Imagify', 'Imagify', $wp_context->get_capacity( 'manage' ), $this->get_settings_page_slug(), [ $this, 'display_settings_page' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add menu and sub-menus in the network admin when Imagify is network-activated.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*/
|
||||
public function add_network_menus() {
|
||||
global $submenu;
|
||||
|
||||
$wp_context = imagify_get_context( 'wp' );
|
||||
|
||||
if ( ! imagify_can_optimize_custom_folders() ) {
|
||||
// Main item: settings (edge case).
|
||||
add_menu_page( 'Imagify', 'Imagify', $wp_context->get_capacity( 'manage' ), $this->get_settings_page_slug(), array( $this, 'display_settings_page' ) );
|
||||
return;
|
||||
}
|
||||
|
||||
$cf_context = imagify_get_context( 'custom-folders' );
|
||||
|
||||
// Main item: bulk optimization (custom folders).
|
||||
add_menu_page( __( 'Bulk Optimization', 'imagify' ), 'Imagify', $cf_context->current_user_can( 'bulk-optimize' ), $this->get_bulk_page_slug(), array( $this, 'display_bulk_page' ) );
|
||||
|
||||
// Sub-menu item: custom folders list.
|
||||
$screen_id = add_submenu_page( $this->get_bulk_page_slug(), __( 'Other Media optimized by Imagify', 'imagify' ), __( 'Other Media', 'imagify' ), $cf_context->current_user_can( 'bulk-optimize' ), $this->get_files_page_slug(), array( $this, 'display_files_list' ) );
|
||||
|
||||
// Sub-menu item: settings.
|
||||
add_submenu_page( $this->get_bulk_page_slug(), 'Imagify', __( 'Settings', 'imagify' ), $wp_context->get_capacity( 'manage' ), $this->get_settings_page_slug(), array( $this, 'display_settings_page' ) );
|
||||
|
||||
// Change the sub-menu label.
|
||||
if ( ! empty( $submenu[ $this->get_bulk_page_slug() ] ) ) {
|
||||
$submenu[ $this->get_bulk_page_slug() ][0][0] = __( 'Bulk Optimization', 'imagify' ); // WPCS: override ok.
|
||||
}
|
||||
|
||||
if ( $screen_id ) {
|
||||
// On the "Other Media optimized by Imagify" page, load the data.
|
||||
add_action( 'load-' . $screen_id, array( $this, 'load_files_list' ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** PLUGIN ACTION LINKS ===================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Add links to the plugin row in the plugins list.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @param array $actions An array of action links.
|
||||
* @return array
|
||||
*/
|
||||
public function plugin_action_links( $actions ) {
|
||||
array_unshift( $actions, sprintf( '<a href="%s" target="_blank">%s</a>', esc_url( imagify_get_external_url( 'documentation' ) ), __( 'Documentation', 'imagify' ) ) );
|
||||
array_unshift( $actions, sprintf( '<a href="%s">%s</a>', esc_url( get_imagify_admin_url( 'bulk-optimization' ) ), __( 'Bulk Optimization', 'imagify' ) ) );
|
||||
array_unshift( $actions, sprintf( '<a href="%s">%s</a>', esc_url( get_imagify_admin_url() ), __( 'Settings', 'imagify' ) ) );
|
||||
return $actions;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** MAIN PAGE TEMPLATES ===================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* The main settings page.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*/
|
||||
public function display_settings_page() {
|
||||
$this->print_template( 'page-settings' );
|
||||
}
|
||||
|
||||
/**
|
||||
* The bulk optimization page.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*/
|
||||
public function display_bulk_page() {
|
||||
$types = array();
|
||||
$data = array(
|
||||
// Limits.
|
||||
'unoptimized_attachment_limit' => 0,
|
||||
// What to optimize.
|
||||
'icon' => 'images-alt2',
|
||||
'title' => __( 'Optimize your media files', 'imagify' ),
|
||||
'groups' => array(),
|
||||
);
|
||||
|
||||
if ( imagify_is_screen( 'bulk' ) ) {
|
||||
if ( ! is_network_admin() ) {
|
||||
/**
|
||||
* Library: in each site.
|
||||
*/
|
||||
$types['library|wp'] = 1;
|
||||
}
|
||||
|
||||
if ( imagify_can_optimize_custom_folders() && ( imagify_is_active_for_network() && is_network_admin() || ! imagify_is_active_for_network() ) ) {
|
||||
/**
|
||||
* Custom folders: in network admin only if network activated, in each site otherwise.
|
||||
*/
|
||||
$types['custom-folders|custom-folders'] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the types to display in the bulk optimization page.
|
||||
*
|
||||
* @since 1.7.1
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $types The folder types displayed on the page. If a folder type is "library", the context should be suffixed after a pipe character. They are passed as array keys.
|
||||
*/
|
||||
$types = apply_filters( 'imagify_bulk_page_types', $types );
|
||||
$types = array_filter( (array) $types );
|
||||
|
||||
if ( isset( $types['library|wp'] ) ) {
|
||||
// Limits.
|
||||
$data['unoptimized_attachment_limit'] += imagify_get_unoptimized_attachment_limit();
|
||||
// Group.
|
||||
$data['groups']['library'] = array(
|
||||
/**
|
||||
* The group_id corresponds to the file names like 'part-bulk-optimization-results-row-{$group_id}'.
|
||||
* It is also used in get_imagify_localize_script_translations().
|
||||
*/
|
||||
'group_id' => 'library',
|
||||
'context' => 'wp',
|
||||
'title' => __( 'Media Library', 'imagify' ),
|
||||
/* translators: 1 is the opening of a link, 2 is the closing of this link. */
|
||||
'footer' => sprintf( __( 'You can also re-optimize your media files from your %1$sMedia Library%2$s screen.', 'imagify' ), '<a href="' . esc_url( admin_url( 'upload.php' ) ) . '">', '</a>' ),
|
||||
);
|
||||
}
|
||||
|
||||
if ( isset( $types['custom-folders|custom-folders'] ) ) {
|
||||
if ( ! Imagify_Folders_DB::get_instance()->has_items() ) {
|
||||
// New Feature!
|
||||
$data['no-custom-folders'] = true;
|
||||
} elseif ( Imagify_Folders_DB::get_instance()->has_active_folders() ) {
|
||||
// Group.
|
||||
$data['groups']['custom-folders'] = array(
|
||||
'group_id' => 'custom-folders',
|
||||
'context' => 'custom-folders',
|
||||
'title' => __( 'Custom folders', 'imagify' ),
|
||||
/* translators: 1 is the opening of a link, 2 is the closing of this link. */
|
||||
'footer' => sprintf( __( 'You can re-optimize your media files more finely directly in the %1$smedia management%2$s.', 'imagify' ), '<a href="' . esc_url( get_imagify_admin_url( 'files-list' ) ) . '">', '</a>' ),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Add generic stats.
|
||||
$data = array_merge( $data, imagify_get_bulk_stats( $types, array(
|
||||
'fullset' => true,
|
||||
) ) );
|
||||
|
||||
/**
|
||||
* Filter the data to use on the bulk optimization page.
|
||||
*
|
||||
* @since 1.7
|
||||
* @since 1.7.1 Added the $types parameter.
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $data The data to use.
|
||||
* @param array $types The folder types displayed on the page. They are passed as array keys.
|
||||
*/
|
||||
$data = apply_filters( 'imagify_bulk_page_data', $data, $types );
|
||||
|
||||
$this->print_template( 'page-bulk', $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* The page displaying the "custom folders" files.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*/
|
||||
public function display_files_list() {
|
||||
$this->print_template( 'page-files-list' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate the "custom folders" list table data.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*/
|
||||
public function load_files_list() {
|
||||
// Instantiate the list.
|
||||
$this->list_table = new Imagify_Files_List_Table( array(
|
||||
'screen' => 'imagify-files',
|
||||
) );
|
||||
|
||||
// Query the Items.
|
||||
$this->list_table->prepare_items();
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** GETTERS ================================================================================= */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the settings page slug.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_settings_page_slug() {
|
||||
return $this->slug_settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bulk optimization page slug.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_bulk_page_slug() {
|
||||
return $this->slug_bulk;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the "custom folders" files page slug.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_files_page_slug() {
|
||||
return $this->slug_files;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** PAGE TESTS ============================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Tell if we’re displaying the settings page.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_settings_page() {
|
||||
global $pagenow;
|
||||
|
||||
$page = filter_input( INPUT_GET, 'page', FILTER_SANITIZE_STRING );
|
||||
|
||||
if ( $this->get_settings_page_slug() !== $page ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( imagify_is_active_for_network() ) {
|
||||
return 'admin.php' === $pagenow;
|
||||
}
|
||||
|
||||
return 'options-general.php' === $pagenow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if we’re displaying the bulk optimization page.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_bulk_page() {
|
||||
global $pagenow;
|
||||
|
||||
$page = filter_input( INPUT_GET, 'page', FILTER_SANITIZE_STRING );
|
||||
|
||||
return 'upload.php' === $pagenow && $this->get_bulk_page_slug() === $page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if we’re displaying the custom files list page.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_files_page() {
|
||||
global $pagenow;
|
||||
|
||||
$page = filter_input( INPUT_GET, 'page', FILTER_SANITIZE_STRING );
|
||||
|
||||
return 'upload.php' === $pagenow && $this->get_files_page_slug() === $page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if we’re displaying the WP media library page.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_wp_library_page() {
|
||||
global $pagenow;
|
||||
|
||||
$page = filter_input( INPUT_GET, 'page', FILTER_SANITIZE_STRING );
|
||||
|
||||
return 'upload.php' === $pagenow && ! $page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if we’re displaying a media page.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_media_page() {
|
||||
global $pagenow, $typenow;
|
||||
|
||||
return 'post.php' === $pagenow && 'attachment' === $typenow;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** QUOTA =================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the remaining quota in percent.
|
||||
*
|
||||
* @since 1.8.1
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_quota_percent() {
|
||||
static $quota;
|
||||
|
||||
if ( isset( $quota ) ) {
|
||||
return $quota;
|
||||
}
|
||||
|
||||
$user = new Imagify_User();
|
||||
$quota = $user->get_percent_unconsumed_quota();
|
||||
|
||||
return $quota;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the HTML class used for the quota (to change the color when out of quota for example).
|
||||
*
|
||||
* @since 1.8.1
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_quota_class() {
|
||||
static $class;
|
||||
|
||||
if ( isset( $class ) ) {
|
||||
return $class;
|
||||
}
|
||||
|
||||
$quota = $this->get_quota_percent();
|
||||
$class = 'imagify-bar-';
|
||||
|
||||
if ( $quota <= 20 ) {
|
||||
$class .= 'negative';
|
||||
} elseif ( $quota <= 50 ) {
|
||||
$class .= 'neutral';
|
||||
} else {
|
||||
$class .= 'positive';
|
||||
}
|
||||
|
||||
return $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the HTML tag used for the quota (the weather-like icon).
|
||||
*
|
||||
* @since 1.8.1
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_quota_icon() {
|
||||
static $icon;
|
||||
|
||||
if ( isset( $icon ) ) {
|
||||
return $icon;
|
||||
}
|
||||
|
||||
$quota = $this->get_quota_percent();
|
||||
|
||||
if ( $quota <= 20 ) {
|
||||
$icon = '<img src="' . IMAGIFY_ASSETS_IMG_URL . 'stormy.svg" width="64" height="63" alt="" />';
|
||||
} elseif ( $quota <= 50 ) {
|
||||
$icon = '<img src="' . IMAGIFY_ASSETS_IMG_URL . 'cloudy-sun.svg" width="63" height="64" alt="" />';
|
||||
} else {
|
||||
$icon = '<img src="' . IMAGIFY_ASSETS_IMG_URL . 'sun.svg" width="63" height="64" alt="" />';
|
||||
}
|
||||
|
||||
return $icon;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** GENERIC TEMPLATE TOOLS ================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get a template contents.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @param string $template The template name.
|
||||
* @param mixed $data Some data to pass to the template.
|
||||
* @return string|bool The page contents. False if the template doesn't exist.
|
||||
*/
|
||||
public function get_template( $template, $data = array() ) {
|
||||
$path = str_replace( '_', '-', $template );
|
||||
$path = IMAGIFY_PATH . 'views/' . $template . '.php';
|
||||
|
||||
if ( ! $this->filesystem->exists( $path ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ob_start();
|
||||
include $path;
|
||||
$contents = ob_get_clean();
|
||||
|
||||
return trim( (string) $contents );
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a template.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @param string $template The template name.
|
||||
* @param mixed $data Some data to pass to the template.
|
||||
*/
|
||||
public function print_template( $template, $data = array() ) {
|
||||
echo $this->get_template( $template, $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a template to the list of JS templates to print at the end of the page.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*
|
||||
* @param string $template The template name.
|
||||
*/
|
||||
public function print_js_template_in_footer( $template ) {
|
||||
if ( isset( $this->templates_in_footer[ $template ] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch ( $template ) {
|
||||
case 'button/processing':
|
||||
$data = [ 'label' => '{{ data.label }}' ];
|
||||
break;
|
||||
default:
|
||||
$data = [];
|
||||
}
|
||||
|
||||
$this->templates_in_footer[ $template ] = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the JS templates that have been added to the "queue".
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
* @access public
|
||||
*/
|
||||
public function print_js_templates() {
|
||||
if ( ! $this->templates_in_footer ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $this->templates_in_footer as $template => $data ) {
|
||||
$template_id = str_replace( [ '/', '_' ], '-', $template );
|
||||
|
||||
echo '<script type="text/html" id="tmpl-imagify-' . $template_id . '">';
|
||||
$this->print_template( $template, $data );
|
||||
echo '</script>';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** TOOLS =================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Create HTML attributes from an array.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $attributes A list of attribute pairs.
|
||||
* @return string HTML attributes.
|
||||
*/
|
||||
public function build_attributes( $attributes ) {
|
||||
if ( ! $attributes || ! is_array( $attributes ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$out = '';
|
||||
|
||||
foreach ( $attributes as $attribute => $value ) {
|
||||
if ( '' === $value ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$out .= ' ' . $attribute . '="' . esc_attr( $value ) . '"';
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
}
|
||||
644
wp/wp-content/plugins/imagify/inc/classes/class-imagify.php
Normal file
644
wp/wp-content/plugins/imagify/inc/classes/class-imagify.php
Normal file
@@ -0,0 +1,644 @@
|
||||
<?php
|
||||
use Imagify\Traits\InstanceGetterTrait;
|
||||
|
||||
/**
|
||||
* Imagify.io API for WordPress.
|
||||
*/
|
||||
class Imagify {
|
||||
use InstanceGetterTrait;
|
||||
|
||||
/**
|
||||
* The Imagify API endpoint.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const API_ENDPOINT = IMAGIFY_APP_API_URL;
|
||||
|
||||
/**
|
||||
* The Imagify API key.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $api_key = '';
|
||||
|
||||
/**
|
||||
* Random key used to store the API key in the request args.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $secure_key = '';
|
||||
|
||||
/**
|
||||
* HTTP headers. Each http call must fill it (even if it's with an empty array).
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $headers = [];
|
||||
|
||||
/**
|
||||
* All (default) HTTP headers. They must not be modified once the class is instanciated, or it will affect any following HTTP calls.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $all_headers = [];
|
||||
|
||||
/**
|
||||
* Filesystem object.
|
||||
*
|
||||
* @var object Imagify_Filesystem
|
||||
* @since 1.7.1
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $filesystem;
|
||||
|
||||
/**
|
||||
* Use data fetched from the API.
|
||||
*
|
||||
* @var \stdClass|\WP_Error
|
||||
* @since 1.9.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected static $user;
|
||||
|
||||
/**
|
||||
* The constructor.
|
||||
*/
|
||||
protected function __construct() {
|
||||
if ( ! class_exists( 'Imagify_Filesystem' ) ) {
|
||||
// Dirty patch used when updating from 1.7.
|
||||
include_once IMAGIFY_PATH . 'inc/classes/class-imagify-filesystem.php';
|
||||
}
|
||||
|
||||
$this->api_key = get_imagify_option( 'api_key' );
|
||||
$this->secure_key = $this->generate_secure_key();
|
||||
$this->filesystem = Imagify_Filesystem::get_instance();
|
||||
|
||||
$this->all_headers['Accept'] = 'Accept: application/json';
|
||||
$this->all_headers['Content-Type'] = 'Content-Type: application/json';
|
||||
$this->all_headers['Authorization'] = 'Authorization: token ' . $this->api_key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get your Imagify account infos.
|
||||
*
|
||||
* @since 1.6.5
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function get_user() {
|
||||
global $wp_current_filter;
|
||||
|
||||
if ( isset( static::$user ) ) {
|
||||
return static::$user;
|
||||
}
|
||||
|
||||
if ( in_array( 'upgrader_post_install', (array) $wp_current_filter, true ) ) {
|
||||
// Dirty patch used when updating from 1.7.
|
||||
static::$user = new WP_Error();
|
||||
return static::$user;
|
||||
}
|
||||
|
||||
$this->headers = $this->all_headers;
|
||||
static::$user = $this->http_call( 'users/me/', [ 'timeout' => 10 ] );
|
||||
|
||||
if ( is_wp_error( static::$user ) ) {
|
||||
return static::$user;
|
||||
}
|
||||
|
||||
$maybe_missing = [
|
||||
'account_type' => 'free',
|
||||
'quota' => 0,
|
||||
'extra_quota' => 0,
|
||||
'extra_quota_consumed' => 0,
|
||||
'consumed_current_month_quota' => 0,
|
||||
];
|
||||
|
||||
foreach ( $maybe_missing as $name => $value ) {
|
||||
if ( ! isset( static::$user->$name ) ) {
|
||||
static::$user->$name = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return static::$user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a user on your Imagify account.
|
||||
*
|
||||
* @since 1.6.5
|
||||
*
|
||||
* @param array $data All user data.
|
||||
* @return object
|
||||
*/
|
||||
public function create_user( $data ) {
|
||||
$this->headers = [];
|
||||
$data = array_merge(
|
||||
$data,
|
||||
[
|
||||
'from_plugin' => true,
|
||||
'partner' => imagify_get_partner(),
|
||||
]
|
||||
);
|
||||
|
||||
if ( ! $data['partner'] ) {
|
||||
unset( $data['partner'] );
|
||||
}
|
||||
|
||||
$response = $this->http_call(
|
||||
'users/',
|
||||
[
|
||||
'method' => 'POST',
|
||||
'post_data' => $data,
|
||||
]
|
||||
);
|
||||
|
||||
if ( ! is_wp_error( $response ) ) {
|
||||
imagify_delete_partner();
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing user on your Imagify account.
|
||||
*
|
||||
* @since 1.6.5
|
||||
*
|
||||
* @param string $data All user data.
|
||||
* @return object
|
||||
*/
|
||||
public function update_user( $data ) {
|
||||
$this->headers = $this->all_headers;
|
||||
|
||||
return $this->http_call(
|
||||
'users/me/',
|
||||
[
|
||||
'method' => 'PUT',
|
||||
'post_data' => $data,
|
||||
'timeout' => 10,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check your Imagify API key status.
|
||||
*
|
||||
* @since 1.6.5
|
||||
*
|
||||
* @param string $data The license key.
|
||||
* @return object
|
||||
*/
|
||||
public function get_status( $data ) {
|
||||
static $status = [];
|
||||
|
||||
if ( isset( $status[ $data ] ) ) {
|
||||
return $status[ $data ];
|
||||
}
|
||||
|
||||
$this->headers = [
|
||||
'Authorization' => 'Authorization: token ' . $data,
|
||||
];
|
||||
|
||||
$uri = 'status/';
|
||||
$partner = imagify_get_partner();
|
||||
|
||||
if ( $partner ) {
|
||||
$uri .= '?partner=' . $partner;
|
||||
}
|
||||
|
||||
$status[ $data ] = $this->http_call( $uri, [ 'timeout' => 10 ] );
|
||||
|
||||
return $status[ $data ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Imagify API version.
|
||||
*
|
||||
* @since 1.6.5
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function get_api_version() {
|
||||
static $api_version;
|
||||
|
||||
if ( ! isset( $api_version ) ) {
|
||||
$this->headers = [
|
||||
'Authorization' => $this->all_headers['Authorization'],
|
||||
];
|
||||
|
||||
$api_version = $this->http_call( 'version/', [ 'timeout' => 5 ] );
|
||||
}
|
||||
|
||||
return $api_version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Public Info.
|
||||
*
|
||||
* @since 1.6.5
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function get_public_info() {
|
||||
$this->headers = $this->all_headers;
|
||||
|
||||
return $this->http_call( 'public-info' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimize an image from its binary content.
|
||||
*
|
||||
* @since 1.6.5
|
||||
* @since 1.6.7 $data['image'] can contain the file path (prefered) or the result of `curl_file_create()`.
|
||||
*
|
||||
* @param string $data All options.
|
||||
* @return object
|
||||
*/
|
||||
public function upload_image( $data ) {
|
||||
$this->headers = [
|
||||
'Authorization' => $this->all_headers['Authorization'],
|
||||
];
|
||||
|
||||
return $this->http_call(
|
||||
'upload/',
|
||||
[
|
||||
'method' => 'POST',
|
||||
'post_data' => $data,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimize an image from its URL.
|
||||
*
|
||||
* @since 1.6.5
|
||||
*
|
||||
* @param string $data All options. Details here: --.
|
||||
* @return object
|
||||
*/
|
||||
public function fetch_image( $data ) {
|
||||
$this->headers = $this->all_headers;
|
||||
|
||||
return $this->http_call(
|
||||
'fetch/',
|
||||
[
|
||||
'method' => 'POST',
|
||||
'post_data' => wp_json_encode( $data ),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get prices for plans.
|
||||
*
|
||||
* @since 1.6.5
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function get_plans_prices() {
|
||||
$this->headers = $this->all_headers;
|
||||
|
||||
return $this->http_call( 'pricing/plan/' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all prices (Plans included).
|
||||
*
|
||||
* @since 1.6.5
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function get_all_prices() {
|
||||
$this->headers = $this->all_headers;
|
||||
|
||||
return $this->http_call( 'pricing/all/' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get coupon code data.
|
||||
*
|
||||
* @since 1.6.5
|
||||
*
|
||||
* @param string $coupon A coupon code.
|
||||
* @return object
|
||||
*/
|
||||
public function check_coupon_code( $coupon ) {
|
||||
$this->headers = $this->all_headers;
|
||||
|
||||
return $this->http_call( 'coupons/' . $coupon . '/' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get information about current discount.
|
||||
*
|
||||
* @since 1.6.5
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function check_discount() {
|
||||
$this->headers = $this->all_headers;
|
||||
|
||||
return $this->http_call( 'pricing/discount/' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an HTTP call using curl.
|
||||
*
|
||||
* @since 1.6.5
|
||||
* @since 1.6.7 Use `wp_remote_request()` when possible (when we don't need to send an image).
|
||||
*
|
||||
* @param string $url The URL to call.
|
||||
* @param array $args The request args.
|
||||
* @return object
|
||||
*/
|
||||
private function http_call( $url, $args = [] ) {
|
||||
$args = array_merge(
|
||||
[
|
||||
'method' => 'GET',
|
||||
'post_data' => null,
|
||||
'timeout' => 45,
|
||||
],
|
||||
$args
|
||||
);
|
||||
|
||||
$endpoint = trim( $url, '/' );
|
||||
/**
|
||||
* Filter the timeout value for any request to the API.
|
||||
*
|
||||
* @since 1.6.7
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param int $timeout Timeout value in seconds.
|
||||
* @param string $endpoint The targetted endpoint. It's basically URI without heading nor trailing slash.
|
||||
*/
|
||||
$args['timeout'] = apply_filters( 'imagify_api_http_request_timeout', $args['timeout'], $endpoint );
|
||||
|
||||
// We need to send an image: we must use cURL directly.
|
||||
if ( isset( $args['post_data']['image'] ) ) {
|
||||
return $this->curl_http_call( $url, $args );
|
||||
}
|
||||
|
||||
$args = array_merge(
|
||||
[
|
||||
'headers' => [],
|
||||
'body' => $args['post_data'],
|
||||
'sslverify' => apply_filters( 'https_ssl_verify', false ),
|
||||
],
|
||||
$args
|
||||
);
|
||||
|
||||
unset( $args['post_data'] );
|
||||
|
||||
if ( $this->headers ) {
|
||||
foreach ( $this->headers as $name => $value ) {
|
||||
$value = explode( ':', $value, 2 );
|
||||
$value = end( $value );
|
||||
|
||||
$args['headers'][ $name ] = trim( $value );
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $args['headers']['Authorization'] ) ) {
|
||||
// Make sure our API has not overwritten by some other plugin.
|
||||
$args[ $this->secure_key ] = preg_replace( '/^token /', '', $args['headers']['Authorization'] );
|
||||
|
||||
if ( ! has_filter( 'http_request_args', [ $this, 'force_api_key_header' ] ) ) {
|
||||
add_filter( 'http_request_args', [ $this, 'force_api_key_header' ], IMAGIFY_INT_MAX + 25, 2 );
|
||||
}
|
||||
}
|
||||
|
||||
$response = wp_remote_request( self::API_ENDPOINT . $url, $args );
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$http_code = wp_remote_retrieve_response_code( $response );
|
||||
$response = wp_remote_retrieve_body( $response );
|
||||
|
||||
return $this->handle_response( $response, $http_code );
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an HTTP call using curl.
|
||||
*
|
||||
* @since 1.6.7
|
||||
* @throws Exception When curl_init() fails.
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $url The URL to call.
|
||||
* @param array $args The request arguments.
|
||||
* @return object
|
||||
*/
|
||||
private function curl_http_call( $url, $args = [] ) {
|
||||
// Check if curl is available.
|
||||
if ( ! Imagify_Requirements::supports_curl() ) {
|
||||
return new WP_Error( 'curl', 'cURL isn\'t installed on the server.' );
|
||||
}
|
||||
|
||||
try {
|
||||
$url = self::API_ENDPOINT . $url;
|
||||
$ch = curl_init();
|
||||
|
||||
if ( false === $ch ) {
|
||||
throw new Exception( 'Could not initialize a new cURL handle' );
|
||||
}
|
||||
|
||||
if ( isset( $args['post_data']['image'] ) && is_string( $args['post_data']['image'] ) && $this->filesystem->exists( $args['post_data']['image'] ) ) {
|
||||
$args['post_data']['image'] = curl_file_create( $args['post_data']['image'] );
|
||||
}
|
||||
|
||||
// Handle proxies.
|
||||
$proxy = new WP_HTTP_Proxy();
|
||||
|
||||
if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) {
|
||||
curl_setopt( $ch, CURLOPT_PROXYTYPE, CURLPROXY_HTTP );
|
||||
curl_setopt( $ch, CURLOPT_PROXY, $proxy->host() );
|
||||
curl_setopt( $ch, CURLOPT_PROXYPORT, $proxy->port() );
|
||||
|
||||
if ( $proxy->use_authentication() ) {
|
||||
curl_setopt( $ch, CURLOPT_PROXYAUTH, CURLAUTH_ANY );
|
||||
curl_setopt( $ch, CURLOPT_PROXYUSERPWD, $proxy->authentication() );
|
||||
}
|
||||
}
|
||||
|
||||
if ( 'POST' === $args['method'] ) {
|
||||
curl_setopt( $ch, CURLOPT_POST, true );
|
||||
curl_setopt( $ch, CURLOPT_POSTFIELDS, $args['post_data'] );
|
||||
} elseif ( 'PUT' === $args['method'] ) {
|
||||
curl_setopt( $ch, CURLOPT_CUSTOMREQUEST, 'PUT' );
|
||||
curl_setopt( $ch, CURLOPT_POSTFIELDS, $args['post_data'] );
|
||||
}
|
||||
|
||||
if ( defined( 'CURLOPT_PROTOCOLS' ) ) {
|
||||
curl_setopt( $ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS );
|
||||
}
|
||||
|
||||
$user_agent = apply_filters( 'http_headers_useragent', 'WordPress/' . get_bloginfo( 'version' ) . '; ' . get_bloginfo( 'url' ), $url );
|
||||
|
||||
curl_setopt( $ch, CURLOPT_URL, $url );
|
||||
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
|
||||
curl_setopt( $ch, CURLOPT_HEADER, false );
|
||||
curl_setopt( $ch, CURLOPT_HTTPHEADER, $this->headers );
|
||||
curl_setopt( $ch, CURLOPT_TIMEOUT, $args['timeout'] );
|
||||
curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, $args['timeout'] );
|
||||
curl_setopt( $ch, CURLOPT_USERAGENT, $user_agent );
|
||||
@curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, false );
|
||||
curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, false );
|
||||
curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false );
|
||||
|
||||
/**
|
||||
* Tell which http version to use with cURL during image optimization.
|
||||
*
|
||||
* @since 1.8.4.1
|
||||
* @since 1.9.9 Default value is `false`.
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param $use_version_1_0 bool True to use version 1.0. False for 1.1. Default is false.
|
||||
*/
|
||||
if ( apply_filters( 'imagify_curl_http_version_1_0', false ) ) {
|
||||
curl_setopt( $ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0 );
|
||||
} else {
|
||||
curl_setopt( $ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1 );
|
||||
}
|
||||
|
||||
$response = curl_exec( $ch );
|
||||
$error = curl_error( $ch );
|
||||
$http_code = (int) curl_getinfo( $ch, CURLINFO_HTTP_CODE );
|
||||
|
||||
if ( is_resource( $ch ) ) {
|
||||
curl_close( $ch );
|
||||
} else {
|
||||
unset( $ch );
|
||||
}
|
||||
} catch ( Exception $e ) {
|
||||
$args['headers'] = $this->headers;
|
||||
/**
|
||||
* Fires after a failed curl request.
|
||||
*
|
||||
* @since 1.6.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $url The requested URL.
|
||||
* @param array $args The request arguments.
|
||||
* @param object $e The raised Exception.
|
||||
*/
|
||||
do_action( 'imagify_curl_http_response', $url, $args, $e );
|
||||
|
||||
return new WP_Error( 'curl', 'An error occurred (' . $e->getMessage() . ')' );
|
||||
} // End try().
|
||||
|
||||
$args['headers'] = $this->headers;
|
||||
|
||||
/**
|
||||
* Fires after a successful curl request.
|
||||
*
|
||||
* @since 1.6.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $url The requested URL.
|
||||
* @param array $args The request arguments.
|
||||
* @param string $response The request response.
|
||||
* @param int $http_code The request HTTP code.
|
||||
* @param string $error An error message.
|
||||
*/
|
||||
do_action( 'imagify_curl_http_response', $url, $args, $response, $http_code, $error );
|
||||
|
||||
return $this->handle_response( $response, $http_code, $error );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the request response and maybe trigger an error.
|
||||
*
|
||||
* @since 1.6.7
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $response The request response.
|
||||
* @param int $http_code The request HTTP code.
|
||||
* @param string $error An error message.
|
||||
* @return object
|
||||
*/
|
||||
private function handle_response( $response, $http_code, $error = '' ) {
|
||||
$response = json_decode( $response );
|
||||
|
||||
if ( 401 === $http_code ) {
|
||||
// Reset the API validity cache if the API key is not valid.
|
||||
Imagify_Requirements::reset_cache( 'api_key_valid' );
|
||||
}
|
||||
|
||||
if ( 200 !== $http_code && ! empty( $response->code ) ) {
|
||||
if ( ! empty( $response->detail ) ) {
|
||||
return new WP_Error( 'error ' . $http_code, $response->detail );
|
||||
}
|
||||
if ( ! empty( $response->image ) ) {
|
||||
$error = (array) $response->image;
|
||||
$error = reset( $error );
|
||||
return new WP_Error( 'error ' . $http_code, $error );
|
||||
}
|
||||
}
|
||||
|
||||
if ( 413 === $http_code ) {
|
||||
return new WP_Error( 'error ' . $http_code, 'Your image is too big to be uploaded on our server.' );
|
||||
}
|
||||
|
||||
if ( 200 !== $http_code ) {
|
||||
$error = trim( (string) $error );
|
||||
$error = '' !== $error ? ' - ' . htmlentities( $error ) : '';
|
||||
return new WP_Error( 'error ' . $http_code, "Our server returned an error ({$http_code}{$error})" );
|
||||
}
|
||||
|
||||
if ( ! is_object( $response ) ) {
|
||||
return new WP_Error( 'invalid response', 'Our server returned an invalid response.', $response );
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a random key.
|
||||
* Similar to wp_generate_password() but without filter.
|
||||
*
|
||||
* @since 1.8.4
|
||||
* @see wp_generate_password()
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function generate_secure_key() {
|
||||
$length = wp_rand( 12, 20 );
|
||||
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_ []{}<>~`+=,.;:/?|';
|
||||
$password = '';
|
||||
|
||||
for ( $i = 0; $i < $length; $i++ ) {
|
||||
$password .= substr( $chars, wp_rand( 0, strlen( $chars ) - 1 ), 1 );
|
||||
}
|
||||
|
||||
return $password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the arguments used in an HTTP request, to make sure our API key has not been overwritten by some other plugin.
|
||||
*
|
||||
* @since 1.8.4
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $args An array of HTTP request arguments.
|
||||
* @param string $url The request URL.
|
||||
* @return array
|
||||
*/
|
||||
public function force_api_key_header( $args, $url ) {
|
||||
if ( strpos( $url, self::API_ENDPOINT ) === false ) {
|
||||
return $args;
|
||||
}
|
||||
|
||||
if ( ! empty( $args['headers']['Authorization'] ) || ! empty( $args[ $this->secure_key ] ) ) {
|
||||
if ( ! empty( $args[ $this->secure_key ] ) ) {
|
||||
$args['headers']['Authorization'] = 'token ' . $args[ $this->secure_key ];
|
||||
} else {
|
||||
$args['headers']['Authorization'] = 'token ' . $this->api_key;
|
||||
}
|
||||
}
|
||||
|
||||
return $args;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user