This commit is contained in:
2024-05-20 15:37:46 +03:00
commit 00b7dbd0b7
10404 changed files with 3285853 additions and 0 deletions

View File

@@ -0,0 +1,107 @@
<?php
namespace ShortPixel;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
class Woocommerce
{
// public function $new_sizes = array();
protected static $SIGNAL = false;
public function __construct()
{
add_action('plugins_loaded', array($this, 'hooks'));
}
public function hooks()
{
if (\wpSPIO()->env()->plugin_active('woocommerce'))
{
add_filter('woocommerce_regenerate_images_intermediate_image_sizes', array($this, 'signalWC'));
add_filter('woocommerce_debug_tools', array($this, 'addWarning'));
// If new images are created, drop the optimize data of them . Late as possible, this is a hook often used by plugins to refine.
add_filter('intermediate_image_sizes_advanced', array($this, 'handleCreateImages'), 99, 3);
}
}
// This hook is ran just before create new images / regenerating them. Only then signal to check for optimized thumbs et al.
public function signalWC()
{
self::$SIGNAL = true;
}
/** Hook to run when Wordpress is about to generate new thumbnails. Remove backup and optimize data if that happens */
public function handleCreateImages($new_sizes, $image_meta, $attach_id)
{
// No signal, no run.
if (false === self::$SIGNAL)
{
return $new_sizes;
}
if (count($new_sizes) === 0)
{
self::$SIGNAL = false;
return $new_sizes;
}
$fs = \wpSPIO()->filesystem();
$mediaImage = $fs->getMediaImage($attach_id);
$changes = false;
if (is_object($mediaImage))
{
// Performance; This item is not in database, hence not optimized in any way.
if (! is_null($mediaImage->getMeta('databaseID')))
{
foreach($new_sizes as $new_size => $data)
{
$thumbnailObj = $mediaImage->getThumbNail($new_size);
if (is_object($thumbnailObj) && $thumbnailObj->isOptimized())
{
$thumbnailObj->onDelete();
$changes = true;
}
}
}
else {
}
}
if (true === $changes)
{
$mediaImage->saveMeta();
}
self::$SIGNAL = false;
return $new_sizes;
}
public function addWarning($tools)
{
if (isset($tools['regenerate_thumbnails']) && \wpSPIO()->env()->is_autoprocess)
{
$text = $tools['regenerate_thumbnails']['desc'];
$text .= sprintf(
'<br><br><strong class="red">%1$s</strong> %2$s',
__( 'ShortPixel Image Optimizer Note:', 'shortpixel-image-optimiser' ),
__( 'The ShortPixel Image Optimizer plugin is set to automatically optimize images on upload. When running the thumbnails tools, each image that is not optimized will be added to the queue. It is recommend to disable this option while running these tools', 'shortpixel-image-optimiser')
);
$tools['regenerate_thumbnails']['desc'] = $text;
}
return $tools;
}
} // class
$w = new Woocommerce();

View File

@@ -0,0 +1,176 @@
<?php
namespace ShortPixel;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
class cacheRemover
{
protected $has_supercache = false; // supercache seems to replace quite fine, without our help. @todo Test if this is needed
protected $has_w3tc = false;
protected $has_wpengine = false;
protected $has_fastestcache = false;
protected $has_siteground = false;
protected $has_litespeed = false;
private static $instance;
public function __construct()
{
$this->addHooks();
$this->checkCaches();
}
public static function getInstance()
{
if (is_null(self::$instance))
self::$instance = new cacheRemover();
return self::$instance;
}
public function addHooks()
{
add_action('shortpixel/image/optimised', array($this, 'flushCache'));
}
/** Checks which cache plugins are active on the moment a flush is needed */
public function checkCaches()
{
if ( function_exists( 'w3tc_pgcache_flush' ) )
$this->has_w3tc = true;
if ( function_exists('wp_cache_clean_cache') )
$this->has_supercache = true;
if ( class_exists( 'WpeCommon' ) )
$this->has_wpengine = true;
global $wp_fastest_cache;
if ( method_exists( 'WpFastestCache', 'deleteCache' ) && !empty( $wp_fastest_cache ) )
$this->has_fastestcache = true;
// SG SuperCacher
if (function_exists('sg_cachepress_purge_cache')) {
$this->has_siteground = true;
}
if (defined( 'LSCWP_DIR' ))
{
$this->has_litespeed = true;
}
// @todo WpRocket?
// @todo BlueHost Caching?
}
/* Tries to flush cache there were we have issues
*
* @param Array $args Argument Array to provide data.
*/
public function flushCache($imageItem)
{
if ($imageItem->get('type') == 'custom')
{
$post_id = 0;
}
else {
$post_id = $imageItem->get('id');
}
// important - first check the available cache plugins
$this->checkCaches();
$bool = apply_filters('shortpixel/external/flush_cache', true, $post_id, $imageItem);
if (false === $bool)
{
return false;
}
// general WP
if ($post_id > 0)
clean_post_cache($post_id);
else
wp_cache_flush();
/* Verified working without.
if ($this->has_supercache)
$this->removeSuperCache();
*/
if ($this->has_w3tc)
$this->removeW3tcCache();
if ($this->has_wpengine)
$this->removeWpeCache();
if ($this->has_siteground)
$this->removeSiteGround();
if ($this->has_fastestcache)
$this->removeFastestCache();
if ($this->has_litespeed)
$this->litespeedReset($imageItem);
}
protected function removeSuperCache()
{
global $file_prefix, $supercachedir;
if ( empty( $supercachedir ) && function_exists( 'get_supercache_dir' ) ) {
$supercachedir = get_supercache_dir();
}
wp_cache_clean_cache( $file_prefix );
}
protected function removeW3tcCache()
{
w3tc_pgcache_flush();
}
protected function removeWpeCache()
{
if ( method_exists( 'WpeCommon', 'purge_memcached' ) ) {
\WpeCommon::purge_memcached();
}
if ( method_exists( 'WpeCommon', 'clear_maxcdn_cache' ) ) {
\WpeCommon::clear_maxcdn_cache();
}
if ( method_exists( 'WpeCommon', 'purge_varnish_cache' ) ) {
\WpeCommon::purge_varnish_cache();
}
}
protected function removeFastestCache()
{
global $wp_fastest_cache;
$wp_fastest_cache->deleteCache();
}
protected function removeSiteGround()
{
sg_cachepress_purge_cache();
}
protected function litespeedReset($imageItem)
{
// Suppress the notices on purge.
if (! defined( 'LITESPEED_PURGE_SILENT' ))
{
define('LITESPEED_PURGE_SILENT', true);
}
$urls = $imageItem->getAllUrls();
foreach($urls as $url)
{
do_action('litespeed_purge_url', $url, false, true);
}
// do_action('litespeed_media_reset', $post_id);
}
}
cacheRemover::getInstance();

View File

@@ -0,0 +1,259 @@
<?php
namespace ShortPixel;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
use ShortPixel\Notices\NoticeController as Notices;
// @todo Clean up unused lines in this file. (cloudflare)
class CloudFlareAPI {
private $email; // $_cloudflareEmail
private $authkey; // $_cloudflareAuthKey
private $zone_id; // $_cloudflareZoneID
private $token;
private $setup_done = false;
private $config_ok = false;
private $use_token = false;
private $cf_exists = true;
private $api_url = 'https://api.cloudflare.com/client/v4/zones/';
/*public function __construct($cloudflareEmail, $cloudflareAuthKey, $cloudflareZoneID) {
$this->set_up($cloudflareEmail, $cloudflareAuthKey, $cloudflareZoneID);
$this->set_up_required_hooks();
} */
public function __construct()
{
add_action('shortpixel/image/optimised', array( $this, 'check_cloudflare' ), 10 );
add_action('shortpixel/image/before_restore', array($this, 'check_cloudflare'), 10);
}
public function setup()
{
$this->email = \wpSPIO()->settings()->cloudflareEmail;
$this->authkey = \wpSPIO()->settings()->cloudflareAuthKey;
$this->zone_id = (defined('SHORTPIXEL_CFZONE') ) ? SHORTPIXEL_CFZONE : \wpSPIO()->settings()->cloudflareZoneID;
$this->token = (defined('SHORTPIXEL_CFTOKEN') ) ? SHORTPIXEL_CFTOKEN : \wpSPIO()->settings()->cloudflareToken;
if (! empty($this->token) && ! empty($this->zone_id))
{
$this->use_token = true;
$this->config_ok = true;
}
elseif(! empty($this->email) && ! empty($this->authkey) && ! empty($this->zone_id))
{
$this->config_ok = true;
}
if (empty($this->zone_id))
{
//Log::addWarn('CF - ZoneID setting is obligatory');
}
$this->setup_done = true;
}
public function check_cloudflare($imageObj)
{
if (! $this->setup_done)
$this->setup();
if ($this->config_ok)
{
if (! function_exists('curl_init'))
{
Log::addWarn('Cloudflare Config OK, but no CURL to request');
}
else
$this->start_cloudflare_cache_purge_process($imageObj);
}
}
/**
* @desc Start the process of purging all cache for image URL (includes all the image sizes/thumbnails)f1
*
* @param $image_id - WordPress image media ID
*/
private function start_cloudflare_cache_purge_process( $imageItem ) {
// Fetch CloudFlare API credentials
/*$cloudflare_auth_email = $this->_cloudflareEmail;
$cloudflare_auth_key = $this->_cloudflareAuthKey;
$cloudflare_zone_id = $this->_cloudflareZoneID; */
// Fetch all WordPress install possible thumbnail sizes ( this will not return the full size option )
$fetch_images_sizes = get_intermediate_image_sizes();
$purge_array = array();
$prepare_request_info = array();
// if full image size tag is missing, we need to add it
if ( ! in_array( 'full', $fetch_images_sizes ) ) {
$fetch_images_sizes[] = 'full';
}
/*
if ( $imageItem->getType() == 'media' ) {
// The item is a Media Library item, fetch the URL for each image size
foreach ( $fetch_images_sizes as $size ) {
// 0 - url; 1 - width; 2 - height
$image_attributes = wp_get_attachment_image_src( $image_id, $size );
// Append to the list
array_push( $purge_array, $image_attributes[0] );
}
} else {
// The item is a Custom Media item
$fs = \wpSPIO()->filesystem();
$item = $fs->getImage( $image_id, 'custom' );
$item_url = $fs->pathToUrl( $item );
array_push( $purge_array, $item_url );
}
*/
$fs = \wpSPIO()->filesystem();
$image_paths[] = $imageItem->getURL();
if ($imageItem->getWebp() !== false)
$image_paths[] = $fs->pathToUrl($imageItem->getWebp());
if ($imageItem->getAvif() !== false)
$image_paths[] = $fs->pathToUrl($imageItem->getAvif());
if ($imageItem->get('type') == 'media')
{
if ($imageItem->hasOriginal())
{
$originalFile = $imageItem->getOriginalFile();
$image_paths[] = $originalFile->getURL();
if ($originalFile->getWebp() !== false)
$image_paths[] = $fs->pathToUrl($originalFile->getWebp());
if ($originalFile->getAvif() !== false)
$image_paths[] = $fs->pathToUrl($originalFile->getAvif());
}
if (count($imageItem->get('thumbnails')) > 0)
{
foreach($imageItem->get('thumbnails') as $thumbObj)
{
$image_paths[] = $thumbObj->getURL();
if ($thumbObj->getWebp() !== false)
$image_paths[] = $fs->pathToUrl($thumbObj->getWebp());
if ($thumbObj->getAvif() !== false)
$image_paths[] = $fs->pathToUrl($thumbObj->getAvif());
}
}
}
if ( ! empty( $image_paths ) ) {
//$prepare_request_info['files'] = $image_url_for_purge;
// Encode the data into JSON before send
$dispatch_purge_info = function_exists('wp_json_encode') ? wp_json_encode( $prepare_request_info ) : json_encode( $prepare_request_info );
// Set headers for remote API to authenticate for the request
/* $dispatch_header = array(
'X-Auth-Email: ' . $cloudflare_auth_email,
'X-Auth-Key: ' . $cloudflare_auth_key,
'Content-Type: application/json'
); */
$response = $this->delete_url_cache_request_action($image_paths);
// Start the process of cache purge
/* $request_response = $this->delete_url_cache_request_action( "https://api.cloudflare.com/client/v4/zones/" . $cloudflare_zone_id . "/purge_cache", $dispatch_purge_info, $dispatch_header ); */
} else {
// No use in running the process
}
}
/**
* @desc Send a delete cache request to CloudFlare for specified URL(s)
* Implements -> https://api.cloudflare.com/#zone-purge-files-by-url
* @return array|mixed|object - Request response as decoded JSON
*/
private function delete_url_cache_request_action( $files ) {
$request_url = $this->api_url . $this->zone_id . '/purge_cache';
$postfields = array('files' => $files);
return $this->doRequest($request_url, $postfields);
}
private function addAuth($headers)
{
if ($this->use_token)
{
$headers['authorization'] = 'Authorization: Bearer ' . $this->token;
}
else
{
$headers['x-auth-email'] = 'X-Auth-Email: ' . $this->email;
$headers['x-auth-key'] = 'X-Auth-Key: ' . $this->authkey;
}
return $headers;
}
/**
* @param $url String . Api Url to target with zone_id and acton
* @param $postfields Array . Fields for POST
* @param $headers Valid HTTP headers to add.
*/
private function doRequest($url, $postfields, $headers = array())
{
if(!function_exists('curl_init'))
{ return false; }
$curl_connection = curl_init();
$default_headers =
array('content_type' => 'Content-Type: application/json');
$default_headers = $this->addAuth($default_headers);
$headers = wp_parse_args($headers, $default_headers);
$headers = array_filter(array_values($headers));
$postfields = wp_json_encode($postfields);
curl_setopt( $curl_connection, CURLOPT_URL, $url );
curl_setopt( $curl_connection, CURLOPT_CUSTOMREQUEST, "POST" );
curl_setopt( $curl_connection, CURLOPT_POSTFIELDS, $postfields);
curl_setopt( $curl_connection, CURLOPT_RETURNTRANSFER, true );
curl_setopt( $curl_connection, CURLOPT_HTTPHEADER, $headers );
curl_setopt( $curl_connection, CURLOPT_CONNECTTIMEOUT, 5); // in seconds!
curl_setopt( $curl_connection, CURLOPT_TIMEOUT, 10); // in seconds!
curl_setopt( $curl_connection, CURLOPT_USERAGENT, '"User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.87 Safari/537.36"' );
$request_response = curl_exec( $curl_connection );
$result = json_decode( $request_response, true );
curl_close( $curl_connection );
if ( ! is_array( $result ) ) {
Log::addWarn( 'ShortPixel - CloudFlare: The CloudFlare API is not responding correctly', $result);
} elseif ( isset( $result['success'] ) && isset( $result['errors'] ) && false === (bool) $result['success'] ) {
Log::addWarn( 'ShortPixel - CloudFlare, Error messages: '
. (isset($result['errors']['message']) ? $result['errors']['message'] : json_encode($result['errors'])) );
} else {
Log::addInfo('ShortPixel - CloudFlare successfully requested clear cache for: ', array($postfields));
}
return $result;
}
}
$c = new CloudFlareAPI(); // monitor hook.

View File

@@ -0,0 +1,60 @@
<?php
namespace ShortPixel;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
use ShortPixel\Controller\OtherMediaController as OtherMediaController;
// Gravity Forms integrations.
class gravityForms
{
public function __construct()
{
// @todo All this off, because it can only fatal error.
// add_filter( 'gform_save_field_value', array($this,'shortPixelGravityForms'), 10, 5 );
}
function shortPixelGravityForms( $value, $lead, $field, $form ) {
if($field->type == 'post_image') {
$this->handleGravityFormsImageField($value);
}
return $value;
}
public function handleGravityFormsImageField($value) {
$fs = \wpSPIO()->filesystem();
$otherMediaController = OtherMediaController::getInstance();
$uploadBase = $fs->getWPUploadBase();
$gravFolder = $otherMediaController->getFolderByPath($uploadBase->getPath() . 'gravity_forms');
if (! $gravFolder->exists())
return false;
/* no clue what this legacy is suppposed to be.
if(strpos($value , '|:|')) {
$cleanup = explode('|:|', $value);
$value = $cleanup[0];
}
*/
if (! $gravFolder->get('in_db'))
{
$otherMediaController->addDirectory($gravFolder->getPath());
}
//ShortPixel is monitoring the gravity forms folder, add the image to queue
// $uploadDir = wp_upload_dir();
//$localPath = str_replace($uploadDir['baseurl'], SHORTPIXEL_UPLOADS_BASE, $value);
//return $shortPixelObj->addPathToCustomFolder($localPath, $folder->getId(), 0);
}
} // class
$g = new gravityForms();

View File

@@ -0,0 +1,70 @@
<?php
namespace ShortPixel;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
use ShortPixel\Notices\NoticeController as Notices;
// Image gallery plugins that require a few small extra's
class ImageGalleries
{
public function __construct()
{
add_action('admin_init', array($this, 'addConstants'));
add_filter('shortpixel/init/optimize_on_screens', array($this, 'add_screen_loads'));
}
// This adds constants for mentioned plugins checking for specific suffixes on addUnlistedImages.
// @integration Envira Gallery
// @integration Soliloquy
public function addConstants()
{
//if( !defined('SHORTPIXEL_CUSTOM_THUMB_SUFFIXES')) {
if(\is_plugin_active('envira-gallery/envira-gallery.php') ||
\is_plugin_active('soliloquy-lite/soliloquy-lite.php') ||
\is_plugin_active('soliloquy/soliloquy.php') ||
\is_plugin_active('envira-gallery-lite/envira-gallery-lite.php')
)
{
add_filter('shortpixel/image/unlisted_suffixes', array($this, 'envira_suffixes'));
//define('SHORTPIXEL_CUSTOM_THUMB_SUFFIXES', '_c,_tl,_tr,_br,_bl');
// }
// not in use?
// elseif(defined('SHORTPIXEL_CUSTOM_THUMB_SUFFIX')) {
// define('SHORTPIXEL_CUSTOM_THUMB_SUFFIXES', SHORTPIXEL_CUSTOM_THUMB_SUFFIX);
// }
}
}
public function add_screen_loads($screens)
{
// Envira Gallery Lite
$screens[] = 'edit-envira';
$screens[] = 'envira';
// Solo Cuy
$screens[] = 'edit-soliloquy';
$screens[] = 'soliloquy';
return $screens;
}
public function envira_suffixes($suffixes)
{
$envira_suffixes = array('_c','_tl','_tr','_br','_bl', '-\d+x\d+');
$suffixes = array_merge($suffixes, $envira_suffixes);
return $suffixes;
}
} // class
$c = new ImageGalleries();

View File

@@ -0,0 +1,376 @@
<?php
namespace ShortPixel;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
use ShortPixel\Notices\NoticeController as Notice;
use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
use ShortPixel\Model\File\DirectoryOtherMediaModel as DirectoryOtherMediaModel;
use ShortPixel\Controller\OtherMediaController as OtherMediaController;
use ShortPixel\Controller\AdminNoticesController as AdminNoticesController;
use ShortPixel\NextGenViewController as NextGenViewController;
// @integration NextGen Gallery
class NextGenController
{
protected static $instance;
// protected $view;
private $enableOverride = false; // when activating NG will not report active yet, but try to refresh folders. Do so.
// ngg_created_new_gallery
public function __construct()
{
add_filter('shortpixel/init/optimize_on_screens', array($this, 'add_screen_loads'));
add_action('plugins_loaded', array($this, 'hooks'));
add_action('deactivate_nextgen-gallery/nggallery.php', array($this, 'resetNotification'));
}
public function hooks()
{
$controller = new NextGenViewController();
if ($this->optimizeNextGen()) // if optimization is on, hook.
{
add_action('ngg_update_addgallery_page', array( $this, 'addNextGenGalleriesToCustom'));
add_action('ngg_added_new_image', array($this,'handleImageUpload'));
}
if ($this->has_nextgen())
{
add_action('ngg_delete_image', array($this, 'OnDeleteImage'),10, 2); // this works only on single images!
add_action('shortpixel/othermedia/folder/load', array($this, 'loadFolder'), 10, 2);
// Off because this causes bad UX ( refresh folder but no images added)
//add_action('shortpixel/othermedia/addfiles', array($this, 'checkAddFiles'), 10, 3);
add_filter( 'ngg_manage_images_columns', array( $controller, 'nggColumns' ) );
add_filter( 'ngg_manage_images_number_of_columns', array( $controller, 'nggCountColumns' ) );
add_filter( 'ngg_manage_images_column_7_header', array( $controller, 'nggColumnHeader' ) );
add_filter( 'ngg_manage_images_column_7_content', array( $this, 'loadNextGenItem' ), 10,2 );
add_filter('ngg_manage_gallery_fields', array($this, 'refreshFolderOnLoad'), 10, 2);
}
}
// Use GetInstance, don't use the construct.
public static function getInstance()
{
if (is_null(self::$instance))
self::$instance = new NextGenController();
return self::$instance;
}
public function has_nextgen()
{
if (defined('NGG_PLUGIN'))
return true;
else
return false;
}
public function optimizeNextGen()
{
if (true === $this->enableOverride || \wpSPIO()->settings()->includeNextGen == 1)
{
return true;
}
return false;
}
public function isNextGenScreen()
{
$screens = $this->add_screen_loads(array());
if (! is_admin())
{
return false;
}
if (! function_exists('get_current_screen'))
{
return false;
}
$screen_id = \wpSPIO()->env()->screen_id;
if (in_array($screen_id, $screens))
return true;
else
return false;
}
/** called from settingController when enabling the nextGen settings */
public function enableNextGen($silent)
{
$this->enableOverride = true;
$this->addNextGenGalleriesToCustom($silent);
}
public function add_screen_loads($use_screens)
{
$use_screens[] = 'toplevel_page_nextgen-gallery'; // toplevel
$use_screens[] = 'nextgen-gallery_page_ngg_addgallery'; // add gallery
$use_screens[] = 'nextgen-gallery_page_nggallery-manage-album'; // manage album
$use_screens[] = 'nextgen-gallery_page_nggallery-manage-gallery'; // manage toplevel gallery
$use_screens[] = 'nggallery-manage-images'; // images in gallery overview
return $use_screens;
}
public function loadNextGenItem($unknown, $picture)
{
$viewController = new NextGenViewController();
$viewController->loadItem($picture);
}
public function refreshFolderOnLoad($array, $gallery)
{
$galleries = $this->getGalleries($gallery->gid);
if (isset($galleries[0]))
{
$otherMedia = OtherMediaController::getInstance();
$galleryFolder = $galleries[0];
$folder = $otherMedia->getFolderByPath($galleryFolder->getPath());
$folder->refreshFolder(true);
}
return $array;
}
/** Enables nextGen, add galleries to custom folders
* @param boolean $silent Throw a notice or not. This seems to be based if nextgen was already activated previously or not.
*/
/*
public function nextGenEnabled($silent)
{
$this->addNextGenGalleriesToCustom($silent);
} */
/** Tries to find a nextgen gallery for a shortpixel folder.
* Purpose is to test if this folder is a nextgen gallery
* Problem is that NG stores folders in a short format, not from root while SPIO stores whole path
* Assumption: The last two directory names should lead to an unique gallery and if so, it's nextgen
* @param $id int Folder ID
* @param $directory DirectoryOtherMediaModel Directory Object
*/
public function loadFolder($id, $directory)
{
$path = $directory->getPath();
// No reason to check this?
if ($directory->get('status') == DirectoryOtherMediaModel::DIRECTORY_STATUS_NEXTGEN)
{ return; }
$path_split = array_filter(explode('/', $path));
$searchPath = trailingslashit(implode('/', array_slice($path_split, -2, 2)));
global $wpdb;
$sql = "SELECT gid FROM {$wpdb->prefix}ngg_gallery WHERE path LIKE %s";
$sql = $wpdb->prepare($sql, '%' . $searchPath . '');
$gid = $wpdb->get_var($sql);
if (! is_null($gid) && is_numeric($gid))
{
$res = $directory->set('status', DirectoryOtherMediaModel::DIRECTORY_STATUS_NEXTGEN);
$directory->save();
//echo $gid;
}
}
/** Hook. If there is a refreshFolder action on a nextGen Directory, but the optimize Nextgen setting is off, it should not add those files to the custom Media */
public function checkAddFiles($bool, $files, $dirObj)
{
// Nothing nextgen.
if ($dirObj->get('is_nextgen') === false)
{
return $bool;
}
// If it's nextgen, but the setting is not on, reject those files.
if ($this->optimizeNextGen() === false)
{
return false;
}
return $bool;
}
/* @return DirectoryModel */
public function getGalleries($id = null)
{
global $wpdb;
$fs = \wpSPIO()->filesystem();
$homepath = $fs->getWPFileBase();
$sql = "SELECT path FROM {$wpdb->prefix}ngg_gallery";
if (! is_null($id))
{
$sql .= ' WHERE gid = %d';
$sql = $wpdb->prepare($sql, $id);
}
$result = $wpdb->get_results($sql);
$galleries = array();
foreach($result as $row)
{
$directory = $fs->getDirectory($homepath->getPath() . $row->path);
if ($directory->exists())
$galleries[] = $directory;
}
return $galleries;
}
/** Adds nextGen galleries to custom table
* Note - this function does *Not* check if nextgen is enabled, not if checks custom Tables. Use nextgenEnabled for this.
* Enabled checks are not an external class issue, so must be done before calling.
*/
public function addNextGenGalleriesToCustom($silent = true) {
$fs = \wpSPIO()->filesystem();
$homepath = $fs->getWPFileBase();
//add the NextGen galleries to custom folders
$ngGalleries = $this->getGalleries(); // DirectoryModel return.
$otherMedia = OtherMediaController::getInstance();
foreach($ngGalleries as $gallery)
{
$folder = $otherMedia->getFolderByPath($gallery->getPath());
if ($folder->get('in_db') === true)
{
if ($folder->get('status') !== 1)
{
$folder->set('status', DirectoryOtherMediaModel::DIRECTORY_STATUS_NEXTGEN);
$folder->save();
}
continue;
}
else
{
// Try to silently fail this if directory is not allowed.
if (false === $folder->checkDirectory(true))
{
continue;
}
$directory = $otherMedia->addDirectory($gallery->getPath());
if (! $directory)
{
Log::addWarn('Could not add this directory' . $gallery->getPath() );
}
else
{
$directory->set('status', DirectoryOtherMediaModel::DIRECTORY_STATUS_NEXTGEN);
$directory->save();
}
}
}
if (count($ngGalleries) > 0)
{
// put timestamp to this setting.
$settings = \wpSPIO()->settings();
$settings->hasCustomFolders = time();
}
}
public function handleImageUpload($image)
{
$otherMedia = OtherMediaController::getInstance();
//$fs = \wpSPIO()->filesystem();
if ($this->optimizeNextGen() === true) {
$imageFsPath = $this->getImageAbspath($image);
$otherMedia->addImage($imageFsPath, array('is_nextgen' => true));
}
}
public function resetNotification()
{
Notice::removeNoticeByID('MSG_INTEGRATION_NGGALLERY');
}
public function onDeleteImage($nggId, $size)
{
$image = $this->getNGImageByID($nggId);
$paths = array();
if ($size === false)
{
$imageSizes = $this->getImageSizes($image);
foreach($imageSizes as $size)
{
$paths[] = $this->getImageAbspath($image, $size);
}
}
else {
$paths = array_merge($paths, $this->getImageAbspath($image, $size));
}
foreach($paths as $path)
{
$otherMediaController = OtherMediaController::getInstance();
$mediaItem = $otherMediaController->getCustomImageByPath($path);
$mediaItem->onDelete();
}
}
public function updateImageSize($nggId, $path) {
$image = $this->getNGImageByID($nggId);
$dimensions = getimagesize($this->getImageAbspath($image));
$size_meta = array('width' => $dimensions[0], 'height' => $dimensions[1]);
$image->meta_data = array_merge($image->meta_data, $size_meta);
$image->meta_data['full'] = $size_meta;
$this->saveToNextGen($image);
}
protected function getNGImageByID($nggId)
{
$mapper = \C_Image_Mapper::get_instance();
$image = $mapper->find($nggId);
return $image;
}
/* @param NextGen Image */
protected function saveToNextGen($image)
{
$mapper = \C_Image_Mapper::get_instance();
$mapper->save($image);
}
protected function getImageAbspath($image, $size = 'full') {
$storage = \C_Gallery_Storage::get_instance();
return $storage->get_image_abspath($image, $size);
}
protected function getImageSizes($image)
{
$storage = \C_Gallery_Storage::get_instance();
return $storage->get_image_sizes($image);
}
} // class.
$ng = NextGenController::getInstance();

View File

@@ -0,0 +1,78 @@
<?php
namespace ShortPixel;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
use ShortPixel\Notices\NoticeController as Notice;
use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
use ShortPixel\Helper\UiHelper as UiHelper;
use ShortPixel\Controller\OtherMediaController as OtherMediaController;
/* Class for View integration in the Nextgen gallery */
class NextGenViewController extends \ShortPixel\ViewController
{
protected static $nggColumnIndex = 0;
protected $template = 'view-list-media';
protected function hooks()
{
}
public function nggColumns( $defaults ) {
self::$nggColumnIndex = count($defaults) + 1;
/* add_filter( 'ngg_manage_images_column_' . self::$nggColumnIndex . '_header', array( '\ShortPixel\nextGenViewController', 'nggColumnHeader' ) );
add_filter( 'ngg_manage_images_column_' . self::$nggColumnIndex . '_content', array( '\ShortPixel\nextGenViewController', 'nggColumnContent' ), 10, 2 );
$defaults['wp-shortPixelNgg'] = 'ShortPixel Compression'; */
return $defaults;
}
public function nggCountColumns( $count ) {
return $count + 1;
}
public function nggColumnHeader( $default ) {
wp_enqueue_style('dashicons');
$this->loadView('snippets/part-comparer');
return __('ShortPixel Compression','shortpixel-image-optimiser');
}
public function loadItem( $nextGenObj ) {
$this->view = new \stdClass; // reset every row
$otherMediaController = OtherMediaController::getInstance();
$mediaItem = $otherMediaController->getCustomImageByPath($nextGenObj->imagePath);
$this->view->mediaItem = $mediaItem;
$this->view->id = $mediaItem->get('id');
$this->view->text = UiHelper::getStatusText($mediaItem);
$this->view->list_actions = UiHelper::getListActions($mediaItem);
if ( count($this->view->list_actions) > 0)
$this->view->list_actions = UiHelper::renderBurgerList($this->view->list_actions, $mediaItem);
else
$this->view->list_actions = '';
$this->view->actions = UiHelper::getActions($mediaItem);
//$this->view->actions = $actions;
if (! $this->userIsAllowed)
{
$this->view->actions = array();
$this->view->list_actions = '';
}
$this->loadView($this->template, false);
}
} // class

View File

@@ -0,0 +1,88 @@
<?php
namespace ShortPixel\External\Offload;
use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
// Class to check what offloader to use and load it. To offload.
class Offloader
{
private static $instance;
private static $offload_instance;
private $offloadName;
public static function getInstance()
{
if (is_null(self::$instance))
{
self::$instance = new Offloader();
}
return self::$instance;
}
public function __construct()
{
add_action('plugins_loaded', array($this, 'load'));
add_action('as3cf_init', array($this, 'initS3Offload'));
}
public function load()
{
$bool = $this->checkVirtualLoaders();
if (true === $bool)
{
self::$offload_instance = new VirtualFileSystem($this->offloadName);
}
}
protected function checkVirtualLoaders()
{
if ( class_exists('\Stack\Config') ) // Bitpoke Stack MU
{
$this->offloadName = 'stack';
return true;
}
elseif (defined('STACK_MEDIA_BUCKET'))
{
$this->offloadName = 'stack';
return true;
}
elseif (class_exists('\S3_Uploads\Plugin'))
{
$this->offloadName = 's3-uploads-human';
return true;
}
/* (Doesn't work)
elseif (function_exists('ud_check_stateless_media'))
{
$this->offloadName = 'wp-stateless';
return true;
} */
return false;
}
// If As3cfInit is called check WpOffload runtime. This is later in order than plugins_loaded!
public function initS3Offload($as3cf)
{
if (is_null(self::$offload_instance))
{
$this->offloadName = 'wp-offload';
self::$offload_instance = new wpOffload($as3cf);
}
else {
Log::addError('Instance is not null - other virtual component has loaded! (' . $this->offloadName . ')');
}
}
public function getOffloadName()
{
return $this->offloadName;
}
}
Offloader::getInstance(); // init

View File

@@ -0,0 +1,57 @@
<?php
namespace ShortPixel\External\Offload;
use ShortPixel\Model\File\FileModel as FileModel;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class VirtualFileSystem
{
protected $offloadName;
public function __construct($name)
{
$this->offloadName = $name;
$this->listen();
}
public function listen()
{
// $fs = \wpSPIO()->fileSystem()->startTrustedMode(); // @todo check if this works trusted mode forever.
add_filter('shortpixel/image/urltopath', array($this, 'checkIfOffloaded'), 10,2);
add_filter('shortpixel/file/virtual/translate', array($this, 'getLocalPathByURL'));
add_filter('shortpixel/file/virtual/heavy_features', array($this, 'extraFeatures'), 10);
}
public function checkIfOffloaded($bool, $url)
{
// Slow as it is, check nothing.
if ($offloadName = 's3-uploads-human')
{
return FileModel::$VIRTUAL_STATELESS;
}
if (file_exists($url))
{
return FileModel::$VIRTUAL_STATELESS;
}
return false;
}
public function getLocalPathByURL($path)
{
return $path;
}
// Features like addUNlisted and retina's ( check outside the WP metadata realm ) add a lot of extra time to stateless / remote filesystems. Disable by default to prevent pages from not loading.
public function extraFeatures()
{
return false;
}
} // class

View File

@@ -0,0 +1,690 @@
<?php
namespace ShortPixel\External\Offload;
use ShortPixel\Model\File\FileModel as FileModel;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
use ShortPixel\Notices\NoticeController as Notice;
use ShortPixel\Controller\QuotaController as QuotaController;
use ShortPixel\Controller\ResponseController as ResponseController;
// @integration WP Offload Media Lite
class wpOffload
{
protected $as3cf;
protected $active = false;
protected $offloading = true;
private $itemClassName;
private $useHandlers = false; // Check for newer ItemHandlers or Compat mode.
protected $shouldPrevent = true; // if offload should be prevented. This is turned off when SPIO want to tell S3 to offload. Better than removing filter.
protected $settings;
protected $is_cname = false;
protected $cname;
private static $sources; // cache for url > source_id lookup, to prevent duplicate queries.
private static $offloadPrevented = array();
// if might have to do these checks many times for each thumbnails, keep it fastish.
//protected $retrievedCache = array();
public function __construct($as3cf)
{
// This must be called before WordPress' init.
$this->init($as3cf);
}
public function init($as3cf)
{
if (! class_exists('\DeliciousBrains\WP_Offload_Media\Items\Media_Library_Item'))
{
Notice::addWarning(__('Your S3-Offload plugin version doesn\'t seem to be compatible. Please upgrade the S3-Offload plugin', 'shortpixel-image-optimiser'), true);
return false;
}
$this->itemClassName = '\DeliciousBrains\WP_Offload_Media\Items\Media_Library_Item';
if (method_exists($as3cf, 'get_item_handler'))
{
$this->useHandlers = true; // we have a new version
}
else {
Notice::addWarning(__('Your S3-Offload plugin version doesn\'t seem to be compatible. Please upgrade the S3-Offload plugin', 'shortpixel-image-optimiser'), true);
return false;
}
$this->as3cf = $as3cf;
$this->active = true;
// if setting to upload to bucket is off, don't hook or do anything really.
if (! $this->as3cf->get_setting( 'copy-to-s3' ))
{
$this->offloading = false;
}
/* // Lets see if this can be without
if ('cloudfront' === $this->as3cf->get_setting( 'domain' ))
{
$this->is_cname = true;
$this->cname = $this->as3cf->get_setting( 'cloudfront' );
} */
// $provider = $this->as3cf->get_provider();
add_action('shortpixel/image/optimised', array($this, 'image_upload'), 10);
add_action('shortpixel/image/after_restore', array($this, 'image_restore'), 10, 3); // hit this when restoring.
add_action('shortpixel-thumbnails-before-regenerate', array($this, 'remove_remote'), 10);
add_action('shortpixel/converter/prevent-offload', array($this, 'preventOffload'), 10);
add_action('shortpixel/converter/prevent-offload-off', array($this, 'preventOffloadOff'), 10);
// add_action('shortpixel_restore_after_pathget', array($this, 'remove_remote')); // not optimal -> has to do w/ doRestore and when URL/PATH is available when not on server .
// Seems this better served by _after? If it fails, it's removed from remote w/o filechange.
// add_action('shortpixel/image/convertpng2jpg_before', array($this, 'remove_remote'));
add_filter('as3cf_attachment_file_paths', array($this, 'add_webp_paths'));
// add_filter('as3cf_remove_source_files_from_provider', array($this, 'remove_webp_paths'), 10);
// add_action('shortpixel/image/convertpng2jpg_success', array($this, 'image_converted'), 10);
add_filter('as3cf_remove_source_files_from_provider', array($this, 'remove_webp_paths'));
// add_filter('shortpixel/restore/targetfile', array($this, 'returnOriginalFile'),10,2);
add_filter('as3cf_pre_update_attachment_metadata', array($this, 'preventUpdateMetaData'), 10,4);
add_filter('as3cf_pre_handle_item_upload', array($this, 'preventInitialUploadHandler'), 10,3);
//add_filter('as3cf_get_attached_file', array($this, 'fixScaledUrl'), 10, 4);
add_filter('shortpixel_get_original_image_path', array($this, 'checkScaledUrl'), 10,2);
// add_filter('as3cf_get_attached_file_noop', array($this, 'fixScaledUrl'), 10,4);
//add_filter('shortpixel_get_attached_file', array($this, 'get_raw_attached_file'),10, 2);
// add_filter('shortpixel_get_original_image_path', array($this, 'get_raw_original_path'), 10, 2);
add_filter('shortpixel/image/urltopath', array($this, 'checkIfOffloaded'), 10,2);
add_filter('shortpixel/file/virtual/translate', array($this, 'getLocalPathByURL'));
// for webp picture paths rendered via output
// add_filter('shortpixel_webp_image_base', array($this, 'checkWebpRemotePath'), 10, 2);
add_filter('shortpixel/front/webp_notfound', array($this, 'fixWebpRemotePath'), 10, 4);
// Fix for updating source paths when converting
add_action('shortpixel/image/convertpng2jpg_success', array($this, 'updateOriginalPath'));
}
public function returnOriginalFile($file, $attach_id)
{
$file = get_attached_file($attach_id, true);
return $file;
}
private function getMediaClass()
{
if ($this->useHandlers)
{
$class = $this->as3cf->get_source_type_class('media-library');
}
else
{
$class = $this->itemClassName; //backward compat.
}
return $class;
}
// This is used in the converted. Might be deployed elsewhere for better control.
public function preventOffload($attach_id)
{
self::$offloadPrevented[$attach_id] = true;
}
public function preventOffloadOff($attach_id)
{
unset(self::$offloadPrevented[$attach_id]);
}
// When Offload is not offloaded but is created during the process of generate metadata in WP, wp_create_image_subsizes fires an update metadata after just moving the upload, before making any thumbnails. If this is the case and the file has an -scaled / original image setup, the original_source_path becomes the same as the source_path which creates issue later on when dealing with optimizing it, if the file is deleted on local server. Prevent this, and lean on later update metadata.
public function preventUpdateMetaData($bool, $data, $post_id, $old_provider_object)
{
if (isset(self::$offloadPrevented[$post_id]))
{
return true ; // return true to cancel.
}
return $bool;
}
/**
* @param $id attachment id (WP)
* @param $mediaItem MediaLibraryModel SPIO
* @param $clean - boolean - if restore did all files (clean) or partial (not clean)
*/
public function image_restore($mediaItem, $id, $clean)
{
$settings = \wpSPIO()->settings();
// Only medialibrary offloading supported.
if ('media' !== $mediaItem->get('type') )
{
return false;
}
// If there are excluded sizes, there are not in backups. might not be left on remote, or ( if delete ) on server, so just generate the images and move them.
$mediaItem->wpCreateImageSizes();
$result = $this->remove_remote($id);
$this->image_upload($mediaItem);
}
public function remove_remote($id)
{
$a3cfItem = $this->getItemById($id); // MediaItem is AS3CF Object
if ($a3cfItem === false)
{
Log::addDebug('S3-Offload MediaItem not remote - ' . $id);
return false;
}
$remove = \DeliciousBrains\WP_Offload_Media\Items\Remove_Provider_Handler::get_item_handler_key_name();
$itemHandler = $this->as3cf->get_item_handler($remove);
$result = $itemHandler->handle($a3cfItem, array( 'verify_exists_on_local' => false)); //handle it then.
return $result;
}
/** @return Returns S3Ofload MediaItem, or false when this does not exist */
protected function getItemById($id, $create = false)
{
$class = $this->getMediaClass();
$mediaItem = $class::get_by_source_id($id);
if (true === $create && $mediaItem === false)
{
$mediaItem = $class::create_from_source_id($id);
}
return $mediaItem;
}
/** Cache source requests to improve performance
* @param $url string The URL that is being checked
* @param $source_id int Source ID of the item URL to be cached
* @return int|boolean|null Returns source_if or false ( not offloaded ) if found, returns null if not sourcecached.
*/
private function sourceCache($url, $source_id = null)
{
if ($source_id === null && isset(static::$sources[$url]))
{
$source_id = static::$sources[$url];
return $source_id;
}
elseif ($source_id !== null)
{
if (! isset(static::$sources[$url]))
{
static::$sources[$url] = $source_id;
}
return $source_id;
}
return null;
}
public function checkIfOffloaded($bool, $url)
{
$source_id = $this->sourceCache($url);
$orig_url = $url;
if (is_null($source_id))
{
$extension = substr($url, strrpos($url, '.') + 1);
// If these filetypes are not in the cache, they cannot be found via geSourceyIDByUrl method ( not in path DB ), so it's pointless to try. If they are offloaded, at some point the extra-info might load.
if ($extension == 'webp' || $extension == 'avif')
{
return false;
}
$source_id = $this->getSourceIDByURL($url);
}
else {
}
if ($source_id !== false)
{
return FileModel::$VIRTUAL_REMOTE;
}
else
{
return false;
}
}
protected function getSourceIDByURL($url)
{
$source_id = $this->sourceCache($url); // check cache first.
$cacheHit = false; // prevent a cache hit to be cached again.
$raw_url = $url; // keep raw. If resolved, add the raw url to the cache.
// If in cache, we are done.
if (! is_null($source_id))
{
return $source_id;
}
if (is_null($source_id)) // check on the raw url.
{
$class = $this->getMediaClass();
$parsedUrl = parse_url($url);
if (! isset($parsedUrl['scheme']) || ! in_array($parsedUrl['scheme'], array('http','https')))
{
$url = 'http://' . $url; //str_replace($parsedUrl['scheme'], 'https', $url);
}
$source_id = $this->sourceCache($url);
if(is_null($source_id))
{
$source = $class::get_item_source_by_remote_url($url);
$source2 = $class::get_item_source_by_remote_url($raw_url);
$source_id = isset($source['id']) ? intval($source['id']) : null;
}
else {
$cacheHit = true; // hit the cache. Yeah.
$this->sourceCache($raw_url, $source_id);
}
}
if (is_null($source_id)) // check now via the thumbnail hocus.
{
$pattern = '/(.*)-\d+[xX]\d+(\.\w+)/m';
$url = preg_replace($pattern, '$1$2', $url);
$source_id = $this->sourceCache($url); // check cache first.
if (is_null($source_id))
{
$source = $class::get_item_source_by_remote_url($url);
$source_id = isset($source['id']) ? intval($source['id']) : null;
}
else {
$cacheHit = true;
$this->sourceCache($raw_url , $source_id);
}
}
// Check issue with double extensions. If say double webp/avif is on, the double extension causes the URL not to be found (ie .jpg)
if (is_null($source_id))
{
if (substr_count($parsedUrl['path'], '.') > 1)
{
// Get extension
$ext = substr(strrchr($url, '.'), 1);
// Remove all extensions from the URL
$checkurl = substr($url, 0, strpos($url,'.')) ;
// Add back the last one.
$checkurl .= '.' . $ext;
// Retry
$source_id = $this->sourceCache($checkurl); // check cache first.
if (is_null($source_id))
{
$source = $class::get_item_source_by_remote_url($url);
$source_id = isset($source['id']) ? intval($source['id']) : null;
}
else {
$cacheHit = true;
$this->sourceCache($raw_url , $source_id);
}
}
}
if(is_null($source_id))
{
$source_id = false;
}
if (false === $cacheHit)
{
$this->sourceCache($url, $source_id); // cache it.
}
if ($source_id !== false && false === $cacheHit)
{
// get item
$item = $this->getItemById($source_id);
if (is_object($item) && method_exists($item, 'extra_info'))
{
$baseUrl = str_replace(basename($url),'', $url);
//$rawBaseUrl =
$extra_info = $item->extra_info();
if (isset($extra_info['objects']))
{
foreach($extra_info['objects'] as $extraItem)
{
if (is_array($extraItem) && isset($extraItem['source_file']))
{
// Add source stuff into cache.
$this->sourceCache($baseUrl . $extraItem['source_file'], $source_id);
}
}
}
}
}
return $source_id;
}
// @param s3 based URL that which is needed for finding local path
// @return String Filepath. Translated file path
public function getLocalPathByURL($url)
{
$source_id = $this->getSourceIDByURL($url);
if ($source_id === false)
{
return false;
}
$item = $this->getItemById($source_id);
$original_path = $item->original_source_path(); // $values['original_source_path'];
if (wp_basename($url) !== wp_basename($original_path)) // thumbnails translate to main file.
{
$original_path = str_replace(wp_basename($original_path), wp_basename($url), $original_path);
}
$fs = \wpSPIO()->filesystem();
$base = $fs->getWPUploadBase();
$file = $base . $original_path;
return $file;
}
/** Converted after png2jpg
*
* @param MediaItem Object SPIO
*/
public function image_converted($mediaItem)
{
$fs = \wpSPIO()->fileSystem();
$id = $mediaItem->get('id');
//$this->remove_remote($id);
$this->image_upload($mediaItem);
}
public function image_upload($mediaLibraryObject)
{
$id = $mediaLibraryObject->get('id');
$a3cfItem = $this->getItemById($id);
// Only medialibrary offloading supported.
if ('media' !== $mediaLibraryObject->get('type') )
{
return false;
}
if ( false === $a3cfItem)
{
return false;
}
$item = $this->getItemById($id, true);
if ( $item === false && ! $this->as3cf->get_setting( 'copy-to-s3' ) ) {
// abort if not already uploaded to provider and the copy setting is off
Log::addDebug('As3cf image upload is off and object not previously uploaded');
return false;
}
// Add Web/Avifs back under new method.
$this->shouldPrevent = false;
// The Handler doesn't work properly /w local removal if not the exact correct files are passed (?) . Offload does this probably via update metadata function, so let them sort it out with this . (until it breaks)
$meta = wp_get_attachment_metadata($id);
wp_update_attachment_metadata($id, $meta);
$this->shouldPrevent = true;
}
// WP Offload -for some reason - returns the same result of get_attached_file and wp_get_original_image_path , which are different files (one scaled) which then causes a wrong copy action after optimizing the image ( wrong destination download of the remote file ). This happens if offload with delete is on. Attempt to fix the URL to reflect the differences between -scaled and not.
public function checkScaledUrl($filepath, $id)
{
// Original filepath can never have a scaled in there.
// @todo This should probably check -scaled.<extension> as string end preventing issues.
if (strpos($filepath, '-scaled') !== false)
{
$filepath = str_replace('-scaled', '', $filepath);
}
return $filepath;
}
/** This function will cut out the initial upload to S3Offload . This cuts it off in the new handle area, leaving other updating in tact.
*/
public function preventInitialUploadHandler($bool, $as3cf_item, $options)
{
$fs = \wpSPIO()->filesystem();
$settings = \WPSPIO()->settings();
$post_id = $as3cf_item->source_id();
$quotaController = quotaController::getInstance();
if ($quotaController->hasQuota() === false)
{
return false;
}
if (! $this->offloading)
{
return false;
}
if ($this->shouldPrevent === false) // if false is returned, it's NOT prevented, so on-going.
{
return false;
}
if (isset(self::$offloadPrevented[$post_id]))
{
Log::addDebug('Offload Prevented via static for '. $post_id);
$error = new \WP_Error( 'upload-prevented', 'No offloading at this time, thanks' );
return $error;
}
Log::addDebug('Not preventing S3 Offload');
return $bool;
}
public function updateOriginalPath($imageModel)
{
$post_id = $imageModel->get('id');
$item = $this->getItemById($post_id);
if (false === $item) // item not offloaded.
{
return false;
}
$original_path = $item->original_path(); // Original path (non-scaled-)
$original_source_path = $item->original_source_path();
$path = $item->path();
$source_path = $item->source_path();
$wp_original = wp_get_original_image_path($post_id, apply_filters( 'emr_unfiltered_get_attached_file', true ));
$wp_original = apply_filters('emr/replace/original_image_path', $wp_original, $post_id);
$wp_source = trim(get_attached_file($post_id, apply_filters( 'emr_unfiltered_get_attached_file', true )));
$updated = false;
// If image is replaced with another name, the original soruce path will not match. This could also happen when an image is with -scaled as main is replaced by an image that doesn't have it. In all cases update the table to reflect proper changes.
if (wp_basename($wp_original) !== wp_basename($original_path))
{
$newpath = str_replace( wp_basename( $original_path ), wp_basename($wp_original), $original_path );
$item->set_original_path($newpath);
$newpath = str_replace( wp_basename( $original_source_path ), wp_basename($wp_original), $original_source_path );
$updated = true;
$item->set_original_source_path($newpath);
$item->save();
}
}
private function getWebpPaths($paths, $check_exists = true)
{
$newPaths = array();
$fs = \wpSPIO()->fileSystem();
foreach($paths as $size => $path)
{
$file = $fs->getFile($path);
$basedir = $file->getFileDir();
if (is_null($basedir)) // This could only happen if path is completely empty.
{
continue;
}
$basepath = $basedir->getPath();
$newPaths[$size] = $path;
$webpformat1 = $basepath . $file->getFileName() . '.webp';
$webpformat2 = $basepath . $file->getFileBase() . '.webp';
$avifformat = $basepath . $file->getFileName() . '.avif';
$avifformat2 = $basepath . $file->getFileBase() . '.avif';
if ($check_exists)
{
if (file_exists($webpformat1))
$newPaths[$size . '_webp'] = $webpformat1;
}
else {
$newPaths[$size . '_webp'] = $webpformat1;
}
if ($check_exists)
{
if(file_exists($webpformat2))
$newPaths[$size . '_webp2'] = $webpformat2;
}
else {
$newPaths[$size . '_webp2'] = $webpformat2;
}
if ($check_exists)
{
if (file_exists($avifformat))
{
$newPaths[$size . '_avif'] = $avifformat;
}
}
else {
$newPaths[$size . '_avif'] = $avifformat;
}
if ($check_exists)
{
if (file_exists($avifformat2))
{
$newPaths[$size . '_avif2'] = $avifformat2;
}
}
else {
$newPaths[$size . '_avif2'] = $avifformat2;
}
}
return $newPaths;
}
/** Get Webp Paths that might be generated and offload them as well.
* Paths - size : path values
*/
public function add_webp_paths($paths)
{
$paths = $this->getWebpPaths($paths, true);
//Log::addDebug('Add S3 Paths - ', array($paths));
return $paths;
}
public function remove_webp_paths($paths)
{
$paths = $this->getWebpPaths($paths, false);
//Log::addDebug('Remove S3 Paths', array($paths));
return $paths;
}
// GetbyURL can't find thumbnails, only the main image. Check via extrainfo method if we can find needed filetype
// @param $bool Boolean
// @param $fileObj FileModel The webp file we are searching for
// @param $url string The URL of the main file ( aka .jpg )
// @param $imagebaseDir DirectoryModel The remote path / path this all takes place at.
public function fixWebpRemotePath($bool, $fileObj, $url, $imagebaseDir)
{
$source_id = $this->getSourceIDByURL($url);
if (false === $source_id)
return false;
$item = $this->getItemById($source_id);
$extra_info = $item->extra_info();
if (! isset( $extra_info['objects'] ) || ! is_array( $extra_info['objects'] ) )
return false;
$bool = false;
foreach($extra_info['objects'] as $data)
{
$sourceFile = $data['source_file'];
if ($sourceFile == $fileObj->getFileName())
{
$bool = true;
return $fileObj;
break;
}
}
return $bool;
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace ShortPixel;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Pantheon {
public static $is_pantheon = false;
public function __construct()
{
add_action( 'shortpixel/image/optimised', array( $this, 'flush_image_caches' ), 10 );
if (! defined('SHORTPIXEL_TRUSTED_MODE'))
{
define('SHORTPIXEL_TRUSTED_MODE', true);
}
self::$is_pantheon = true;
}
public static function IsActive()
{
return self::$is_pantheon;
}
public function flush_image_caches( $imageItem )
{
$image_paths[] = $imageItem->getURL();
if ($imageItem->hasOriginal())
{
$image_paths[] = $imageItem->getOriginalFile()->getURL();
}
if (count($imageItem->get('thumbnails')) > 0)
{
foreach($imageItem->get('thumbnails') as $thumbObj)
{
$image_paths[] = $thumbObj->getURL();
}
}
$domain = get_site_url();
$image_paths = array_map(function($path) use ($domain)
{
return str_replace( $domain, '', $path);
},$image_paths);
if ( ! empty( $image_paths ) ) {
$image_paths = array_unique( $image_paths );
if ( function_exists( 'pantheon_wp_clear_edge_paths' ) ) {
// Do the flush
pantheon_wp_clear_edge_paths( $image_paths );
}
}
}
} // class
if ( ! empty( $_ENV['PANTHEON_ENVIRONMENT'] ) ) {
$p = new Pantheon(); // monitor hook.
}

View File

@@ -0,0 +1,33 @@
<?php
namespace ShortPixel;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class QueryMonitor
{
public function __construct()
{
if (false === \wpSPIO()->env()->is_debug)
return;
$this->hooks();
}
public function hooks()
{
add_action('qm/output/after', array($this, 'panelEnd'), 10, 2);
}
public function panelEnd($qmObj, $outputters)
{
}
}
$qm = new QueryMonitor();

View File

@@ -0,0 +1,44 @@
<?php
namespace ShortPixel;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
class Spai
{
public function __construct()
{
add_action('plugins_loaded', array($this, 'addHooks'));
}
public function addHooks()
{
if (\wpSPIO()->env()->plugin_active('spai'))
{
// Prevent SPAI doing its stuff to our JSON returns.
$hook_upon = array('shortpixel_image_processing', 'shortpixel_ajaxRequest');
if (wp_doing_ajax() &&
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- This is not a form
isset($_REQUEST['action']) &&
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- This is not a form
in_array($_REQUEST['action'], $hook_upon) )
{
$this->preventCache();
}
}
}
public function preventCache()
{
if (! defined('DONOTCDN'))
{
define('DONOTCDN', true);
}
}
}
$s = new Spai();

View File

@@ -0,0 +1,47 @@
<?php
namespace ShortPixel\External\Themes;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
class TotalTheme
{
public function __construct()
{
// do_action( 'totaltheme/resize-image/after_save_image', $attachment, $intermediate_size );
add_action( 'totaltheme/resize-image/after_save_image', array($this, 'resizeImage'), 10, 2);
}
public function resizeImage($attachment_id, $size)
{
$image = \wpSPIO()->filesystem()->getMediaImage($attachment_id);
if (! is_object($image))
{
return;
}
$changes = false;
$thumbObj = $image->getThumbnail($size);
if (is_object($thumbObj))
{
$thumbObj->onDelete(true);
$changes = true;
}
else {
}
if ( true === $changes)
{
$image->saveMeta();
}
}
} // class
$t = new TotalTheme();

View File

@@ -0,0 +1,48 @@
<?php
namespace ShortPixel;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
class UncodeController
{
function __construct()
{
$this->addHooks();
}
protected function addHooks()
{
add_action('uncode_delete_crop_image', array($this, 'removedMetaData'), 10, 2);
}
public function removedMetaData($attach_id, $filePath)
{
$fs = \wpSPIO()->filesystem();
$imageObj = $fs->getImage($attach_id, 'media', false);
$imageObj->saveMeta();
$fileObj = $fs->getFile($filePath);
if ($fileObj->hasBackup())
{
$backupObj = $fileObj->getBackupFile();
$backupObj->delete();
}
// Check Webp
$webpObj = $fs->getFile( (string) $fileObj->getFileDir() . $fileObj->getFileBase() . '.webp');
if ($webpObj->exists())
$webpObj->delete();
// Check Avif
$avifObj = $fs->getFile( (string) $fileObj->getFileDir() . $fileObj->getFileBase() . '.avif');
if ($avifObj->exists())
$avifObj->delete();
}
}
$u = new UncodeController();

View File

@@ -0,0 +1,27 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
// Visual Composer and compat class.
class visualComp
{
public function __construct()
{
add_filter('shortpixel/init/automedialibrary', array($this, 'check_vcinline'));
}
// autolibrary should not do things when VC is being inline somewhere.
public function check_vcinline($bool)
{
if ( function_exists( 'vc_action' ) && vc_action() == 'vc_inline' )
return false;
else
return $bool;
}
} // Class
$vc = new visualComp();

View File

@@ -0,0 +1,633 @@
<?php
namespace ShortPixel;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
use ShortPixel\Controller\OptimizeController as OptimizeController;
use ShortPixel\Controller\BulkController as BulkController;
use ShortPixel\Controller\Queue\Queue as Queue;
use ShortPixel\Controller\ApiController as ApiController;
use ShortPixel\Controller\ResponseController as ResponseController;
use ShortPixel\Helper\UiHelper as UiHelper;
class WpCliController
{
public static $instance;
protected static $ticks = 0;
protected static $emptyq = 0;
public function __construct()
{
$log = \ShortPixel\ShortPixelLogger\ShortPixelLogger::getInstance();
if (\ShortPixel\ShortPixelLogger\ShortPixelLogger::debugIsActive())
$log->setLogPath(SHORTPIXEL_BACKUP_FOLDER . "/shortpixel_log_wpcli");
$this->initCommands();
}
public static function getInstance()
{
if (is_null(self::$instance))
self::$instance = new WpCliController();
return self::$instance;
}
protected function initCommands()
{
\WP_CLI::add_command('spio', '\ShortPixel\SpioSingle');
\WP_CLI::add_command('spio bulk', '\ShortPixel\SpioBulk');
}
} // class WpCliController
/**
* ShortPixel Image Optimizer
*
*
*/
class SpioCommandBase
{
protected static $runs = 0;
protected $last_combinedStatus;
/**
* Adds a single item to the queue(s), then processes the queue(s).
*
* ## OPTIONS
*
* <id>
* : Media Library ID or Custom Media ID
*
*
* [--type=<type>]
* : media or custom
* ---
* default: media
* options:
* - media
* - custom
* ---
*
* [--halt]
* : Stops (does not process the queues) after the item is added.
*
*
* ## EXAMPLES
*
* wp spio [bulk] add 123
* wp spio [bulk] add 21 --type=custom --halt
*
* @when after_wp_load
*/
public function add($args, $assoc)
{
$controller = $this->getOptimizeController();
$type = isset($assoc['type']) ? sanitize_text_field($assoc['type']) : 'media';
if (! isset($args[0]))
{
\WP_CLI::Error(__('Specify an Media Library Item ID', 'shortpixel-image-optimiser'));
return;
}
$id = intval($args[0]);
$fs = \wpSPIO()->filesystem();
$imageObj = $fs->getImage($id, $type);
if ($imageObj === false)
{
\WP_CLI::Error(__('Image object not found / non-existing in database by this ID', 'shortpixel-image-optimiser'));
}
$result = $controller->addItemtoQueue($imageObj);
// $complete = isset($assoc['complete']) ? true : false;
if ($result->status == 1)
{
\WP_CLI::Success($result->result->message);
if (! isset($assoc['halt']))
{
$this->run($args, $assoc);
}
else {
\WP_CLI::Line (__('You can optimize images via the run command', 'shortpixel-image-optimiser'));
}
}
elseif ($result->status == 0)
{
\WP_CLI::Error(sprintf(__("while adding item: %s", 'shortpixel_image_optimiser'), $result->result->message) );
}
$this->status($args, $assoc);
}
/**
* Starts processing what has been added to the processing queue(s), optionally stopping after a specified number of "ticks".
*
* A tick (or cycle) means a request sent to the API, either to send an image to be processed or to check if the API has completed processing. Use the ticks (cycles) if you want to run the script regularly (every few minutes) want to run the script.
*
* If you do not define ticks, the queue will run until everything has been processed.
*
* ## OPTIONS
*
* [--ticks=<number>]
* : How often the queue runs (how many ticks/cycles)
* ---
*
* [--wait=<seconds>]
* : How many seconds the system waits for next tick (cycle).
* ---
* default: 3
* ---
*
* [--queue=<name>]
* : Either 'media' or 'custom'. Omit the parameter to run both queues.
* ---
* default: media,custom
* ---
* options:
* - media
* - custom
* ---
*
* ## EXAMPLES
*
* wp spio [bulk] run | Complete all processes
* wp spio [bulk] run --ticks=20 --wait=3 | Ticks and wait time.
* wp spio [bulk] run --queue=media | Only run a specific queue.
*
*
* @when after_wp_load
*/
public function run($args, $assoc)
{
if ( isset($assoc['ticks']))
$ticks = intval($assoc['ticks']);
if (isset($assoc['wait']))
$wait = intval($assoc['wait']);
else
$wait = 3;
// Prepare limit
if (isset($assoc['limit']))
{
$limit = intval($assoc['limit']);
}
else {
$limit = false;
}
$complete = false;
if (! isset($assoc['ticks']))
{
$ticks = -1;
$complete = true; // run until all is done.
}
$queue = $this->getQueueArgument($assoc);
while($ticks > 0 || $complete == true)
{
$bool = $this->runClick($queue);
if ($bool === false)
{
$this->status($args, $assoc);
break;
}
if (false !== $limit)
{
$status = $this->getStatus();
$total = $this->unFormatNumber($status->total->stats->total);
$is_preparing = $status->total->stats->is_preparing;
if ($total >= $limit && $is_preparing)
{
\WP_CLI::log(sprintf('Bulk Preparing is done. Limit reached of %s items (%s items). Use start command to signal ready. Use run to process after starting.', $limit, $status->total->stats->total));
$this->status($args, $assoc);
$bool = false;
break;
}
}
$ticks--;
if (ob_get_length() !== false)
{
ob_flush();
}
sleep($wait);
}
// Done.
$this->showResponses();
}
protected function runClick($queueTypes)
{
ResponseController::setOutput(ResponseController::OUTPUT_CLI);
$controller = $this->getOptimizeController();
$results = $controller->processQueue($queueTypes);
$totalStats = (property_exists($results, 'total') && property_exists($results->total, 'stats')) ? $results->total->stats : null;
// Trouble
if (is_object($results) && property_exists($results, 'status') && $results->status === false)
{
\WP_CLI::error($results->message);
}
foreach($queueTypes as $qname)
{
$qresult = $results->$qname; // qname really is type.
if (! is_null($qresult->message))
{
// Queue Empty not interesting for CLI.
if ($qresult->qstatus == Queue::RESULT_QUEUE_EMPTY || $qresult->qstatus == Queue::RESULT_EMPTY)
{
}
// Result / Results have more interesting information than how much was fetched here probably.
elseif (! property_exists($qresult, 'result') && ! property_exists($qresult, 'results'))
{
\WP_CLI::log( ucfirst($qname) . ' : ' . $qresult->message); // Single Response ( ie prepared, enqueued etc )
}
}
// Result after optimizing items and such.
if (property_exists($qresult, 'results') && is_array($qresult->results))
{
foreach($qresult->results as $item)
{
// Non-result results can happen ( ie. with PNG conversion ). Probably just ignore.
if (false === property_exists($item, 'result') || ! is_object($item->result))
{
continue;
}
$result = (true === property_exists($item, 'result')) ? $item->result : null;
$counts = (true === property_exists($item, 'counts')) ? $item->counts : null;
$apiStatus = property_exists($result, 'apiStatus') ? $result->apiStatus : null;
$this->displayResult($result, $qname, $counts);
// prevent spamming.
if (! is_null($totalStats) && $apiStatus == ApiController::STATUS_SUCCESS )
{
$this->displayStatsLine('Total', $totalStats);
}
}
}
if (property_exists($qresult, 'result') && is_object($qresult->result))
{
$this->displayResult($qresult->result);
}
}
// Combined Status. Implememented from shortpixel-processor.js
$mediaStatus = $customStatus = 100;
if (property_exists($results, 'media') && property_exists($results->media, 'qstatus') )
{
$mediaStatus = $results->media->qstatus;
}
if (property_exists($results, 'custom') && property_exists($results->custom, 'qstatus') )
{
$customStatus = $results->custom->qstatus;
}
// The lowest queue status (for now) equals earlier in process. Don't halt until both are done.
if ($mediaStatus <= $customStatus)
$combinedStatus = $mediaStatus;
else
$combinedStatus = $customStatus;
if ($combinedStatus == Queue::RESULT_QUEUE_EMPTY)
{
\WP_CLI::log('All Queues report processing has finished');
return false;
}
elseif($combinedStatus == Queue::RESULT_PREPARING_DONE)
{
\WP_CLI::log(sprintf('Bulk Preparing is done. %s items. Use start command to signal ready. Use run to process after starting.', $results->total->stats->total));
return false;
}
$this->last_combinedStatus = $combinedStatus;
return true;
}
// Function for Showing JSON output of Optimizer regarding the process.
protected function displayResult($result, $type, $counts = null)
{
$apiStatus = property_exists($result, 'apiStatus') ? $result->apiStatus : null;
if ($apiStatus === ApiController::STATUS_SUCCESS)
{
\WP_CLI::line(' ');
\WP_CLI::line('---------------------------------------');
\WP_CLI::line(' ');
\WP_CLI::line(' ' . $result->message); // testing
if (property_exists($result, 'improvements'))
{
$outputTable = array();
$improvements = $result->improvements;
if (isset($improvements['main']))
{
$outputTable[] = array('name' => 'main', 'improvement' => $improvements['main'][0] .'%');
}
// \WP_CLI::Success( sprintf(__('Image optimized by %d %% ', 'shortpixel-image-optimiser'), $improvements['main'][0]));
if (isset($improvements['thumbnails']))
{
foreach($improvements['thumbnails'] as $thumbName => $optData)
{
$outputTable[] = array('name' => $thumbName, 'improvement' => $optData[0] . '%');
}
}
$outputTable[] = array('name' => ' ', 'improvement' => ' ');
$outputTable[] = array('name' => __('Total', 'shortpixel-image-optimiser'), 'improvement' => $improvements['totalpercentage']. '%');
\WP_CLI\Utils\format_items('table', $outputTable, array('name', 'improvement'));
if (! is_null($counts))
{
$baseMsg = sprintf(' This job, %d credit(s) were used. %d for images ', $counts->creditCount,
$counts->baseCount);
if ($counts->webpCount > 0)
$baseMsg .= sprintf(', %d for webps ', $counts->webpCount);
if ( $counts->avifCount > 0)
$baseMsg .= sprintf(', %d for avifs ', $counts->avifCount);
\WP_CLI::line($baseMsg);
}
\WP_CLI::line(' ');
\WP_CLI::line('---------------------------------------');
\WP_CLI::line(' ');
}
} // success
elseif ($apiStatus === ApiController::STATUS_NOT_API)
{
$message = property_exists($result, 'message') ? $result->message : '';
\WP_CLI::line($message);
}
else
{
if ($result->is_error)
{
\WP_CLI::error($result->message, false);
}
else {
\WP_CLI::line($result->message);
}
}
}
protected function displayStatsLine($name, $stats)
{
$line = sprintf('Current Status for %s : (%s\%s) Done (%s%%), %s awaiting %s errors --', $name, ($stats->done + $stats->fatal_errors), $stats->total, $stats->percentage_done, ( $stats->awaiting ), $stats->fatal_errors);
\WP_CLI::line($line);
}
/**
* Displays the current status of the processing queue(s)
*
* [--show-debug]
* : Dumps more information for debugging purposes
* ---
*
* ## EXAMPLES
*
* wp spio [bulk] status [--show-debug]
*
*/
public function status($args, $assoc)
{
$queue = $this->getQueueArgument($assoc);
$startupData = $this->getStatus();
$items = array();
$fields = array('queue name', 'in queue', 'in process', 'fatal errors', 'done', 'total', 'preparing', 'running', 'finished');
foreach($queue as $queue_name)
{
//$Q = $optimizeController->getQueue($queue_name);
$stats = $startupData->$queue_name->stats;
$item = array(
'queue name' => $queue_name,
'in queue' => $stats->in_queue,
'in process' => $stats->in_process,
'fatal errors' => $stats->fatal_errors,
'done' => $stats->done,
'total' => $stats->total,
'preparing' => ($stats->is_preparing) ? __('Yes', 'shortpixel-image-optimiser') : __('No', 'shortpixel-image-optimiser'),
'running' => ($stats->is_running) ? __('Yes', 'shortpixel-image-optimiser') : __('No', 'shortpixel-image-optimiser'),
'finished' => ($stats->is_finished) ? __('Yes', 'shortpixel-image-optimiser') : __('No', 'shortpixel-image-optimiser'),
);
$items[] = $item;
if (isset($assoc['show-debug']))
{
print_r($stats);
}
}
\WP_CLI::Line("--- Current Status ---");
\WP_CLI\Utils\format_items('table', $items, $fields);
\WP_CLI::Line($this->displayStatsLine('Total', $startupData->total->stats));
}
/**
* Displays the key settings that are applied when executing commands with WP-CLI.
*
*
* ---
*
* ## EXAMPLES
*
* wp spio [bulk] settings
*
*/
public function settings()
{
$settings = \WPspio()->settings();
$items = array();
$fields = array('setting', 'value');
$items[] = array('setting' => 'Compression', 'value' => UiHelper::compressionTypeToText($settings->compressionType));
$items[] = array('setting' => 'Image Backup', 'value' => $this->textBoolean($settings->backupImages, true));
$items[] = array('setting' => 'Processed Thumbnails', 'value' => $this->textBoolean($settings->processThumbnails, true));
$items[] = array('setting' => ' ', 'value' => ' ');
$items[] = array('setting' => 'Creates Webp', 'value' => $this->textBoolean($settings->createWebp));
$items[] = array('setting' => 'Creates Avif', 'value' => $this->textBoolean($settings->createAvif));
\WP_CLI\Utils\format_items('table', $items, $fields);
}
/**
* Clears the Queue(s)
*
*
* [--queue=<name>]
* : Either 'media' or 'custom'. Omit the parameter to clear both queues.
* ---
* default: media,custom
* options:
* - media
* - custom
*
* ## EXAMPLES
*
* wp spio [bulk] clear
*/
public function clear($args, $assoc)
{
$queues = $this->getQueueArgument($assoc);
$optimizeController = $this->getOptimizeController();
foreach($queues as $type)
{
$queue = $optimizeController->getQueue($type);
$queue->resetQueue();
}
\WP_CLI::Success(__('Queue(s) cleared', 'shortpixel-image-optimiser'));
}
// Colored is buggy, so off for now -> https://github.com/wp-cli/php-cli-tools/issues/134
private function textBoolean($bool, $colored = false)
{
$colored = false;
$values = array('','');
if ($bool)
{
if ($colored)
{
$values = array('%g', '%n');
}
$res = vsprintf(__('%sYes%s', 'shortpixel-image-optimiser'), $values);
if ($colored)
$res = \WP_CLI::colorize($res);
}
else
{
if ($colored)
{
$values = array('%r', '');
}
$res = vsprintf(__('%sNo%s', 'shortpixel-image-optimiser'), $values);
if ($colored)
$res = \WP_CLI::colorize($res);
}
return $res;
}
protected function getStatus()
{
$optimizeController = $this->getOptimizeController();
$startupData = $optimizeController->getStartupData();
return $startupData;
}
protected function showResponses()
{
return false; // @todo Pending responseControl, offf.
/*$responses = ResponseController::getAll();
foreach ($responses as $response)
{
if ($response->is('error'))
\WP_CLI::Error($response->message, false);
elseif ($response->is('success'))
\WP_CLI::Success($response->message);
else
\WP_CLI::line($response->message);
} */
}
protected function getQueueArgument($assoc)
{
if (isset($assoc['queue']))
{
if (strpos($assoc['queue'], ',') !== false)
{
$queue = explode(',', $assoc['queue']);
$queue = array_map('sanitize_text_field', $queue);
}
else
$queue = array(sanitize_text_field($assoc['queue']));
}
else
$queue = array('media', 'custom');
return $queue;
}
// To ensure the bulk switch is ok.
protected function getOptimizeController()
{
$optimizeController = new OptimizeController();
return $optimizeController;
}
private function unFormatNumber($string)
{
$string = str_replace(',', '', $string);
$string = str_replace('.', '', $string);
return $string;
}
} // Class SpioCommandBase

View File

@@ -0,0 +1,296 @@
<?php
namespace ShortPixel;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
use ShortPixel\Controller\OptimizeController as OptimizeController;
use ShortPixel\Controller\BulkController as BulkController;
use ShortPixel\Controller\Queue\Queue as Queue;
use ShortPixel\Controller\ApiController as ApiController;
use ShortPixel\Controller\ResponseController as ResponseController;
/**
* Actions for running bulk operations from WP-CLI
*/
class SpioBulk extends SpioCommandBase
{
/**
* Starts the prepared queue(s). The bulk needs an express command to start processing.
* Once started, the queue(s) can be processed and finished with the run command.
*
* ## OPTIONS
* [--queue=<name>]
* : Either 'media' or 'custom'. Omit the parameter to start both queues.
* ---
* default: media,custom
* options:
* - media
* - custom
* ---
*
* ## EXAMPLES
*
* wp spio bulk start
*
*
* @when after_wp_load
*/
public function start($args, $assoc)
{
$bulkControl = BulkController::getInstance();
$queue = $this->getQueueArgument($assoc);
foreach($queue as $qname)
{
$result = $bulkControl->startBulk($qname);
}
\WP_CLI::Line('Start signal for Bulk Processing given.');
// $this->run($args, $assoc);
//$controller = new OptimizeController();
//$result = $controller->startBulk();
}
/**
* Automatically Bulk Processes everything that needs to be done.
*
* [--queue=<name>]
* : Either 'media' or 'custom'. Omit the parameter to process both queues.
* ---
* default: media,custom
* options:
* - media
* - custom
* ---
*
* [--limit=<num>]
* : Limit the amount of items being prepared.
*
* [--special=<migrate>]
* : Run the migration
*
* ## EXAMPLES
*
* wp spio bulk auto
*
*
*/
public function auto($args, $assoc)
{
$queue = $this->getQueueArgument($assoc);
$optimizeController = $this->getOptimizeController();
$bulkControl = BulkController::getInstance();
$running = true;
$created = false;
$this->settings();
sleep(2); // user can digest settings
while($running)
{
$data = $optimizeController->getStartupData();
$combined = $data->total->stats;
// Is_finished is no queue running.
if ($combined->is_preparing)
{
\WP_CLI::line('[Auto Bulk] Preparing .. ');
$this->prepare($args, $assoc);
$this->start($args, $assoc);
\WP_CLI::line('Preparing Run done');
}
elseif ($combined->is_running)
{
\WP_CLI::line('Bulk Running ...');
$this->run($args, $assoc); // Run All
}
elseif ($combined->total > 0 && $combined->done == 0 && $combined->is_running == false && $combined->is_preparing == false && $combined->is_finished == false)
{
\WP_CLI::line('[Auto Bulk] Starting to process');
$this->status($args, $assoc);
$this->start($args, $assoc);
}
elseif ($combined->is_finished)
{
if ($combined->done > 0 || $created == true) // means we already ran the whole thing once.
{
\WP_CLI::Line('[Auto Bulk] Seems finished and done running');
$running = false;
$this->finishBulk($args, $assoc);
break;
}
\WP_CLI::Line('[Auto Bulk] Creating New Queue');
$this->create($args, $assoc);
$created = true;
}
else
{
\WP_CLI::error("[Auto Bulk] : Encountered nothing to do", true);
$running = false; // extra fallback
}
}
\WP_CLI::log('Automatic Bulk ended');
}
/**
* Creates the queue(s) for bulk optimization of media library and/or custom media items.
*
* ## OPTIONS
*
* [--queue=<name>]
* : Either 'media' or 'custom'. Omit the parameter to create both queues.
* ---
* default: media,custom
* options:
* - media
* - custom
* [--special=<migrate>]
* : Run the migration
* ---
*
* ## EXAMPLES
*
* wp spio bulk create
*
*
* @when after_wp_load
*/
public function create($args, $assoc)
{
$bulkControl = BulkController::getInstance();
$json = new \stdClass;
$json->media = new \stdClass;
$json->custom = new \stdClass;
$queues = $this->getQueueArgument($assoc);
$operation = null;
if (isset($assoc['special']))
{
switch ($assoc['special'])
{
case 'migrate':
$operation = 'migrate';
$queues = array('media'); // can only have one bulk, this.
break;
}
}
foreach($queues as $qname)
{
$stats = $bulkControl->createNewBulk($qname, $operation);
$json->$qname->stats = $stats;
\WP_CLI::Line("Bulk $qname created. Ready to prepare");
}
$this->showResponses();
return $stats;
}
/**
* ## OPTIONS
*
* <start-id>
* : ID to start restore
*
* <end-id>
* : ID to stop restore
*
* [--type=<type>]
* : media or custom
* ---
* default: media
* options:
* - media
* - custom
* ---
*
* ## EXAMPLES
*
* wp spio bulk restore 0 100
*
*
* @when after_wp_load
*/
/*public function restore($args, $assoc)
{
\WP_CLI::Line('Not yet implemented');
} */
protected function finishBulk($args, $assoc)
{
$bulkControl = BulkController::getInstance();
$queues = $this->getQueueArgument($assoc);
foreach($queues as $queue_name)
{
$bulkControl->finishBulk($queue_name);
}
}
// To ensure the bulk switch is ok.
protected function getOptimizeController()
{
$optimizeController = new OptimizeController();
$optimizeController->setBulk(true);
return $optimizeController;
}
/**
* Prepares the items by adding them to the queue(s). It runs only when the queue is in the preparing phase and finishes when everything is prepared.
*
*
* [--queue=<name>]
* : Either 'media' or 'custom'. Omit the parameter to run both queues.
* ---
* default: media,custom
* options:
* - media
* - custom
* ---
*
* [--limit=<num>]
* : Limit the amount of items being prepared.
*
* ## EXAMPLES
*
* wp spio bulk prepare
*
*/
public function prepare($args, $assoc)
{
$queues = $this->getQueueArgument($assoc);
$optimizeController = $this->getOptimizeController();
$data = $optimizeController->getStartupData();
if (! $data->total->stats->is_preparing)
{
\WP_CLI::Error("No queues have status preparing, aborting");
}
else
{
$assoc['wait'] = 0.5;
$bool = $this->run($args, $assoc);
}
}
} // CLASS

View File

@@ -0,0 +1,95 @@
<?php
namespace ShortPixel;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
use ShortPixel\Controller\OptimizeController as OptimizeController;
use ShortPixel\Controller\BulkController as BulkController;
use ShortPixel\Controller\Queue\Queue as Queue;
use ShortPixel\Controller\ApiController as ApiController;
use ShortPixel\Controller\ResponseController as ResponseController;
/**
* Actions and operations for the ShortPixel Image Optimizer plugin
*/
class SpioSingle extends SpioCommandBase
{
/**
* Restores the optimized item to its original state (if backups are active).
*
* ## OPTIONS
*
* <id>
* : Media Library ID or Custom Media ID
*
* [--type=<type>]
* : media | custom
* ---
* default: media
* options:
* - media
* - custom
* ---
*
* ## EXAMPLES
*
* wp spio restore 123
* wp spio restore 21 --type=custom
*
* @when after_wp_load
*/
public function restore($args, $assoc_args)
{
$controller = new OptimizeController();
$fs = \wpSPIO()->filesystem();
if (! isset($args[0]))
{
\WP_CLI::Error(__('Specify an (Media Library) Item ID', 'shortpixel_image_optimiser'));
return;
}
if (! is_numeric($args[0]))
{
\WP_CLI::Error(__('Item ID needs to be a number', 'shortpixel-image-optimiser'));
return;
}
$id = intval($args[0]);
$type = $assoc_args['type'];
$image = $fs->getImage($id, $type);
if ($image === false)
{
\WP_CLI::Error(__('No Image returned. Please check if the number and type are correct and the image exists', 'shortpixel-image-optimiser'));
return;
}
$result = $controller->restoreItem($image);
$this->showResponses();
if (property_exists($result,'message') && ! is_null($result->message) && strlen($result->message) > 0)
$message = $result->message;
elseif (property_exists($result, 'result') && property_exists($result->result, 'message'))
$message = $result->result->message;
if ($result->status == 1)
{
\WP_CLI::Success($message);
}
elseif ($result->status == 0)
{
\WP_CLI::Error(sprintf(__("Restoring Item: %s", 'shortpixel_image_optimiser'), $message) );
}
}
} // CLASS