first
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
<?php
|
||||
namespace ShortPixel\Helper;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
|
||||
use ShortPixel\Controller\ResponseController as ResponseController;
|
||||
|
||||
class DownloadHelper
|
||||
{
|
||||
private static $instance;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->checkEnv();
|
||||
}
|
||||
|
||||
public static function getInstance()
|
||||
{
|
||||
if (is_null(self::$instance))
|
||||
{
|
||||
self::$instance = new DownloadHelper();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
protected function checkEnv()
|
||||
{
|
||||
if ( ! function_exists( 'download_url' ) ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/file.php';
|
||||
}
|
||||
}
|
||||
|
||||
public function downloadFile($url, $args = array())
|
||||
{
|
||||
$defaults = array(
|
||||
'expectedSize' => null,
|
||||
'destinationPath' => null,
|
||||
);
|
||||
|
||||
$args = wp_parse_args($args, $defaults);
|
||||
$success = false;
|
||||
|
||||
Log::addDebug('Downloading file :' . $url, $args);
|
||||
|
||||
$methods = array(
|
||||
"download_url" => array(array($this, 'downloadURLMethod'), $url, false),
|
||||
"download_url_force" => array(array($this, 'downloadURLMethod'), true),
|
||||
"remote_get" => array(array($this, 'remoteGetMethod'), $url)
|
||||
);
|
||||
|
||||
foreach($methods as $name => $data)
|
||||
{
|
||||
$function = $data[0];
|
||||
if (is_callable($function))
|
||||
{
|
||||
$result = call_user_func_array($function, array_slice($data, 1) );
|
||||
|
||||
if (false !== $result)
|
||||
{
|
||||
$tempFile = $result;
|
||||
$success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (false === $success)
|
||||
{
|
||||
Log::addError('Failed to download File', $result);
|
||||
ResponseController::addData('is_error', true);
|
||||
//Responsecontroller::addData('message', $tempFile->get_error_message());
|
||||
return false;
|
||||
}
|
||||
|
||||
$fs = \wpSPIO()->filesystem();
|
||||
$file = $fs->getFile($tempFile);
|
||||
|
||||
if (! is_null($args['destinationPath']))
|
||||
{
|
||||
$result = $this->moveDownload($file, $args['destinationPath']);
|
||||
if (false === $result)
|
||||
{
|
||||
Log::addError('Failed to move Download', $args);
|
||||
ResponseController::addData('is_error', true);
|
||||
Responsecontroller::addData('message', __('Failed to move download to destination!', 'shortpixel-image-optimiser'));
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
$file = $result;
|
||||
}
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
protected function moveDownload($fileObj, $destinationPath)
|
||||
{
|
||||
$fs = \wpSPIO()->filesystem();
|
||||
|
||||
$destinationFile = $fs->getFile($destinationPath);
|
||||
// If file is non-existing, check directory and write-permissions.
|
||||
if (false == $destinationFile->exists())
|
||||
{
|
||||
$dirObj = $destinationFile->getFileDir();
|
||||
$dirObj->check(true);
|
||||
}
|
||||
|
||||
$result = $fileObj->copy($destinationFile);
|
||||
|
||||
if ($result === false)
|
||||
return false;
|
||||
|
||||
return $destinationFile;
|
||||
|
||||
}
|
||||
|
||||
private function downloadURLMethod($url, $force = false)
|
||||
{
|
||||
$executionTime = ini_get('max_execution_time');
|
||||
if (! is_numeric($executionTime)) // edge case
|
||||
{
|
||||
$executionTime = 0;
|
||||
}
|
||||
$downloadTimeout = max($executionTime - 10, 15);
|
||||
|
||||
$url = $this->setPreferredProtocol(urldecode($url), $force);
|
||||
$tempFile = \download_url($url, $downloadTimeout);
|
||||
|
||||
if (is_wp_error($tempFile))
|
||||
{
|
||||
Log::addError('Failed to Download File ', $tempFile);
|
||||
Responsecontroller::addData('message', $tempFile->get_error_message());
|
||||
return false;
|
||||
}
|
||||
|
||||
return $tempFile;
|
||||
}
|
||||
|
||||
private function remoteGetMethod($url)
|
||||
{
|
||||
//get_temp_dir
|
||||
$tmpfname = tempnam(get_temp_dir(), 'spiotmp');
|
||||
$downloadTimeout = max(ini_get('max_execution_time') - 10, 15);
|
||||
|
||||
$args_for_get = array(
|
||||
'stream' => true,
|
||||
'filename' => $tmpfname,
|
||||
'timeout' => $downloadTimeout,
|
||||
);
|
||||
|
||||
$response = wp_remote_get( $url, $args_for_get );
|
||||
|
||||
if (wp_remote_retrieve_response_code($response) == 200 && isset($response['filename']))
|
||||
{
|
||||
$filepath = $response['filename'];
|
||||
return $filepath; // body is the full image is all went well.
|
||||
}
|
||||
else {
|
||||
Log::addError('Wp Remote Get failed', $response);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function setPreferredProtocol($url, $reset = false) {
|
||||
//switch protocol based on the formerly detected working protocol
|
||||
$settings = \wpSPIO()->settings();
|
||||
|
||||
if($settings->downloadProto == '' || $reset) {
|
||||
//make a test to see if the http is working
|
||||
$testURL = 'http://' . SHORTPIXEL_API . '/img/connection-test-image.png';
|
||||
$result = download_url($testURL, 10);
|
||||
$settings->downloadProto = is_wp_error( $result ) ? 'https' : 'http';
|
||||
}
|
||||
return $settings->downloadProto == 'http' ?
|
||||
str_replace('https://', 'http://', $url) :
|
||||
str_replace('http://', 'https://', $url);
|
||||
}
|
||||
}
|
@@ -0,0 +1,315 @@
|
||||
<?php
|
||||
namespace ShortPixel\Helper;
|
||||
|
||||
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\FileSystemController as FileSystemController;
|
||||
use ShortPixel\Controller\AdminNoticesController as AdminNoticesController;
|
||||
use ShortPixel\Controller\StatsController as StatsController;
|
||||
use ShortPixel\Controller\ApiKeyController as ApiKeyController;
|
||||
use ShortPixel\Helper\UtilHelper as UtilHelper;
|
||||
|
||||
|
||||
class InstallHelper
|
||||
{
|
||||
|
||||
public static function activatePlugin()
|
||||
{
|
||||
self::deactivatePlugin();
|
||||
$settings = \wpSPIO()->settings();
|
||||
|
||||
$env = wpSPIO()->env();
|
||||
|
||||
if(\WPShortPixelSettings::getOpt('deliverWebp') == 3 && ! $env->is_nginx) {
|
||||
UtilHelper::alterHtaccess(true,true); //add the htaccess lines. Both are true because even if one option is now off in the past both fileformats could have been generated.
|
||||
}
|
||||
|
||||
self::checkTables();
|
||||
|
||||
AdminNoticesController::resetOldNotices();
|
||||
\WPShortPixelSettings::onActivate();
|
||||
|
||||
$optimizeController = new OptimizeController();
|
||||
$q = $optimizeController->getQueue('media');
|
||||
$q->getShortQ()->install(); // create table.
|
||||
|
||||
$settings->currentVersion = SHORTPIXEL_IMAGE_OPTIMISER_VERSION;
|
||||
}
|
||||
|
||||
public static function deactivatePlugin()
|
||||
{
|
||||
|
||||
$settings = \wpSPIO()->settings();
|
||||
$settings::onDeactivate();
|
||||
|
||||
$env = wpSPIO()->env();
|
||||
|
||||
if (! $env->is_nginx)
|
||||
{
|
||||
UtilHelper::alterHtaccess(false, false);
|
||||
}
|
||||
|
||||
// save remove.
|
||||
$fs = new FileSystemController();
|
||||
$log = $fs->getFile(SHORTPIXEL_BACKUP_FOLDER . "/shortpixel_log");
|
||||
|
||||
if ($log->exists())
|
||||
$log->delete();
|
||||
|
||||
global $wpdb;
|
||||
$sql = "delete from " . $wpdb->options . " where option_name like '%_transient_shortpixel%'";
|
||||
$wpdb->query($sql); // remove transients.
|
||||
|
||||
// saved in settings object, reset all stats.
|
||||
StatsController::getInstance()->reset();
|
||||
}
|
||||
|
||||
public static function uninstallPlugin()
|
||||
{
|
||||
OptimizeController::uninstallPlugin();
|
||||
ApiKeyController::uninstallPlugin();
|
||||
|
||||
delete_transient('bulk-secret');
|
||||
delete_transient('othermedia_refresh_folder_delay');
|
||||
delete_transient('avif_server_check');
|
||||
delete_transient('quotaData');
|
||||
|
||||
}
|
||||
|
||||
// Removes everything of SPIO 5.x . Not recommended.
|
||||
public static function hardUninstall()
|
||||
{
|
||||
$env = \wpSPIO()->env();
|
||||
$settings = \wpSPIO()->settings();
|
||||
|
||||
|
||||
$nonce = (isset($_POST['tools-nonce'])) ? sanitize_key($_POST['tools-nonce']) : null;
|
||||
if ( ! wp_verify_nonce( $nonce, 'remove-all' ) ) {
|
||||
wp_nonce_ays( '' );
|
||||
}
|
||||
|
||||
self::deactivatePlugin(); // deactivate
|
||||
self::uninstallPlugin(); // uninstall
|
||||
|
||||
// Bulk Log
|
||||
BulkController::uninstallPlugin();
|
||||
|
||||
$settings::resetOptions();
|
||||
|
||||
if (! $env->is_nginx)
|
||||
{
|
||||
insert_with_markers( get_home_path() . '.htaccess', 'ShortPixelWebp', '');
|
||||
}
|
||||
|
||||
self::removeTables();
|
||||
|
||||
// Remove Backups
|
||||
$dir = \wpSPIO()->filesystem()->getDirectory(SHORTPIXEL_BACKUP_FOLDER);
|
||||
$dir->recursiveDelete();
|
||||
|
||||
$plugin = basename(SHORTPIXEL_PLUGIN_DIR) . '/' . basename(SHORTPIXEL_PLUGIN_FILE);
|
||||
deactivate_plugins($plugin);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static function deactivateConflictingPlugin()
|
||||
{
|
||||
if ( ! isset($_GET['_wpnonce']) || ! wp_verify_nonce( sanitize_key($_GET['_wpnonce']), 'sp_deactivate_plugin_nonce' ) ) {
|
||||
wp_nonce_ays( 'Nononce' );
|
||||
}
|
||||
|
||||
$referrer_url = wp_get_referer();
|
||||
$url = wp_get_referer();
|
||||
$plugin = (isset($_GET['plugin'])) ? sanitize_text_field(wp_unslash($_GET['plugin'])) : null; // our target.
|
||||
|
||||
if (! is_null($plugin))
|
||||
deactivate_plugins($plugin);
|
||||
|
||||
wp_safe_redirect($url);
|
||||
die();
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if TableName exists
|
||||
* @param $tableName The Name of the Table without Prefix.
|
||||
*/
|
||||
public static function checkTableExists($tableName)
|
||||
{
|
||||
global $wpdb;
|
||||
$tableName = $wpdb->prefix . $tableName;
|
||||
$sql = $wpdb->prepare("
|
||||
SHOW TABLES LIKE %s
|
||||
", $tableName);
|
||||
|
||||
$result = intval($wpdb->query($sql));
|
||||
|
||||
if ($result == 0)
|
||||
return false;
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function checkTables()
|
||||
{
|
||||
global $wpdb;
|
||||
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
|
||||
|
||||
dbDelta(self::getFolderTableSQL());
|
||||
dbDelta(self::getMetaTableSQL());
|
||||
dbDelta(self::getPostMetaSQL());
|
||||
|
||||
self::checkIndexes();
|
||||
}
|
||||
|
||||
private static function checkIndexes()
|
||||
{
|
||||
global $wpdb;
|
||||
|
||||
$definitions = array(
|
||||
'shortpixel_meta' => array(
|
||||
'path' => 'path'
|
||||
),
|
||||
'shortpixel_folders' => array(
|
||||
'path' => 'path'
|
||||
),
|
||||
'shortpixel_postmeta' => array(
|
||||
'attach_id' => 'attach_id',
|
||||
'parent' => 'parent',
|
||||
'size' => 'size',
|
||||
'status' => 'status',
|
||||
'compression_type' => 'compression_type'
|
||||
)
|
||||
);
|
||||
|
||||
foreach($definitions as $raw_tableName => $indexes)
|
||||
{
|
||||
$tableName = $wpdb->prefix . $raw_tableName;
|
||||
foreach($indexes as $indexName => $fieldName)
|
||||
{
|
||||
// Check exists
|
||||
$sql = 'SHOW INDEX FROM ' . $tableName . ' WHERE Key_name = %s';
|
||||
$sql = $wpdb->prepare($sql, $indexName);
|
||||
|
||||
$res = $wpdb->get_row($sql);
|
||||
|
||||
if (is_null($res))
|
||||
{
|
||||
// can't prepare for those, also not any user data here.
|
||||
$sql = 'CREATE INDEX ' . $indexName . ' ON ' . $tableName . ' ( ' . $fieldName . ')';
|
||||
$res = $wpdb->query($sql);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static function removeTables()
|
||||
{
|
||||
global $wpdb;
|
||||
if (self::checkTableExists('shortpixel_folders') === true)
|
||||
{
|
||||
$sql = 'DROP TABLE ' . $wpdb->prefix . 'shortpixel_folders';
|
||||
$wpdb->query($sql);
|
||||
}
|
||||
if (self::checkTableExists('shortpixel_meta') === true)
|
||||
{
|
||||
$sql = 'DROP TABLE ' . $wpdb->prefix . 'shortpixel_meta';
|
||||
$wpdb->query($sql);
|
||||
}
|
||||
if (self::checkTableExists('shortpixel_postmeta') === true)
|
||||
{
|
||||
$sql = 'DROP TABLE ' . $wpdb->prefix . 'shortpixel_postmeta';
|
||||
error_log('Dropping postmeta' . $sql);
|
||||
$wpdb->query($sql);
|
||||
}
|
||||
}
|
||||
|
||||
public static function getFolderTableSQL() {
|
||||
global $wpdb;
|
||||
$charsetCollate = $wpdb->get_charset_collate();
|
||||
$prefix = $wpdb->prefix;
|
||||
|
||||
return "CREATE TABLE {$prefix}shortpixel_folders (
|
||||
id mediumint(9) NOT NULL AUTO_INCREMENT,
|
||||
path varchar(512),
|
||||
name varchar(150),
|
||||
path_md5 char(32),
|
||||
file_count int,
|
||||
status SMALLINT NOT NULL DEFAULT 0,
|
||||
parent SMALLINT DEFAULT 0,
|
||||
ts_checked timestamp,
|
||||
ts_updated timestamp,
|
||||
ts_created timestamp,
|
||||
PRIMARY KEY id (id)
|
||||
) $charsetCollate;";
|
||||
|
||||
}
|
||||
|
||||
public static function getMetaTableSQL() {
|
||||
global $wpdb;
|
||||
$charsetCollate = $wpdb->get_charset_collate();
|
||||
$prefix = $wpdb->prefix;
|
||||
|
||||
return "CREATE TABLE {$prefix}shortpixel_meta (
|
||||
id mediumint(10) NOT NULL AUTO_INCREMENT,
|
||||
folder_id mediumint(9) NOT NULL,
|
||||
ext_meta_id int(10),
|
||||
path varchar(512),
|
||||
name varchar(150),
|
||||
path_md5 char(32),
|
||||
compressed_size int(10) NOT NULL DEFAULT 0,
|
||||
compression_type tinyint,
|
||||
keep_exif tinyint DEFAULT 0,
|
||||
cmyk2rgb tinyint DEFAULT 0,
|
||||
resize tinyint,
|
||||
resize_width smallint,
|
||||
resize_height smallint,
|
||||
backup tinyint DEFAULT 0,
|
||||
status SMALLINT NOT NULL DEFAULT 0,
|
||||
retries tinyint NOT NULL DEFAULT 0,
|
||||
message varchar(255),
|
||||
ts_added timestamp,
|
||||
ts_optimized timestamp,
|
||||
extra_info LONGTEXT,
|
||||
PRIMARY KEY sp_id (id)
|
||||
) $charsetCollate;";
|
||||
|
||||
}
|
||||
|
||||
public static function getPostMetaSQL()
|
||||
{
|
||||
global $wpdb;
|
||||
$charsetCollate = $wpdb->get_charset_collate();
|
||||
$prefix = $wpdb->prefix;
|
||||
|
||||
$sql = "CREATE TABLE {$prefix}shortpixel_postmeta (
|
||||
id bigint unsigned NOT NULL AUTO_INCREMENT ,
|
||||
attach_id bigint unsigned NOT NULL,
|
||||
parent bigint unsigned NOT NULL,
|
||||
image_type tinyint default 0,
|
||||
size varchar(200),
|
||||
status tinyint default 0,
|
||||
compression_type tinyint,
|
||||
compressed_size int,
|
||||
original_size int,
|
||||
tsAdded timestamp,
|
||||
tsOptimized timestamp,
|
||||
extra_info LONGTEXT,
|
||||
PRIMARY KEY id (id)
|
||||
) $charsetCollate;";
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
|
||||
} // InstallHelper
|
@@ -0,0 +1,885 @@
|
||||
<?php
|
||||
namespace ShortPixel\Helper;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
use ShortPixel\Model\Image\ImageModel as ImageModel;
|
||||
use ShortPixel\Controller\ApiKeyController as ApiKeyController;
|
||||
use ShortPixel\Controller\QuotaController as QuotaController;
|
||||
use ShortPixel\Controller\OptimizeController as OptimizeController;
|
||||
|
||||
use ShortPixel\Model\AccessModel as AccessModel;
|
||||
|
||||
class UiHelper
|
||||
{
|
||||
|
||||
private static $outputMode = 'admin';
|
||||
|
||||
private static $knowledge_url = 'https://shortpixel.com/knowledge-base/search?query='; // the URL of all knowledge.
|
||||
|
||||
public static function setOutputHandler($name)
|
||||
{
|
||||
self::$outputMode = $name;
|
||||
}
|
||||
|
||||
public static function renderBurgerList($actions, $imageObj)
|
||||
{
|
||||
$output = "";
|
||||
$id = $imageObj->get('id');
|
||||
$primary = isset($actions['optimizethumbs']) ? 'button-primary' : '';
|
||||
|
||||
$output .= "<div class='sp-column-actions '>
|
||||
<div class='sp-dropdown'>
|
||||
<button onclick='ShortPixel.openImageMenu(event);' class='sp-dropbtn button dashicons dashicons-menu $primary' title='ShortPixel Actions'></button>";
|
||||
$output .= "<div id='sp-dd-$id' class='sp-dropdown-content'>";
|
||||
|
||||
foreach($actions as $actionName => $actionData)
|
||||
{
|
||||
$link = ($actionData['type'] == 'js') ? 'javascript:' . $actionData['function'] : $actionData['function'];
|
||||
$output .= "<a href='" . $link . "' class='" . esc_attr($actionName) . "' >" . esc_html($actionData['text']) . "</a>";
|
||||
|
||||
}
|
||||
|
||||
$output .= "</div> <!--sp-dropdown-content--> </div> <!--sp-dropdown--> </div> <!--sp-column-actions--> ";
|
||||
return $output;
|
||||
}
|
||||
|
||||
public static function renderSuccessText($imageObj)
|
||||
{
|
||||
$output = '';
|
||||
//$percent = $imageObj->getMeta('improvement');
|
||||
$percent = $imageObj->getImprovement();
|
||||
|
||||
if($percent == 999) return ;
|
||||
|
||||
if ($percent == 999 )
|
||||
$output .= __("Reduced by X%(unknown)", 'shortpixel-image-optimiser');
|
||||
|
||||
if ($percent && $percent > 0)
|
||||
{
|
||||
$output .= __('Reduced by','shortpixel-image-optimiser') . ' <strong>' . self::formatNumber($percent,2) . '%</strong> ';
|
||||
}
|
||||
if (intval($percent) < 5)
|
||||
$output .= __('Bonus processing','shortpixel-image-optimiser');
|
||||
|
||||
$type = $imageObj->getMeta('compressionType');
|
||||
$output .= ' ('. self::compressionTypeToText($type) .')';
|
||||
|
||||
$thumbs = $imageObj->get('thumbnails');
|
||||
$thumbsDone = $retinasDone = 0;
|
||||
$thumbsTotal = ($thumbs) ? count($thumbs) : 0;
|
||||
|
||||
$retinas = $imageObj->get('retinas');
|
||||
|
||||
$webpsTotal = $imageObj->count('webps');
|
||||
$avifsTotal = $imageObj->count('avifs');
|
||||
|
||||
if($retinas)
|
||||
{
|
||||
foreach($retinas as $retinaObj)
|
||||
{
|
||||
if ($retinaObj->isOptimized())
|
||||
{
|
||||
$retinasDone++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$improvements = $imageObj->getImprovements();
|
||||
$thumbTotal = $thumbsDone = 0;
|
||||
if ($imageObj->get('thumbnails'))
|
||||
{
|
||||
$thumbsTotal = count($imageObj->get('thumbnails')); //
|
||||
//$thumbsDone = (isset($improvements['thumbnails'])) ? count($improvements['thumbnails']) : 0;
|
||||
$thumbsDone = $imageObj->count('optimized', array('thumbs_only' => true));
|
||||
$excludedThumbs = $imageObj->count('user_excluded', array('thumbs_only' => true));
|
||||
}
|
||||
|
||||
if (isset($improvements['thumbnails']))
|
||||
{
|
||||
$excluded = ($excludedThumbs > 0) ? sprintf(__('(%s excluded)', 'shortpixel-image-optimiser'), $excludedThumbs) : '';
|
||||
|
||||
$output .= '<div class="thumbnails optimized">';
|
||||
if ($thumbsTotal > $thumbsDone)
|
||||
$output .= '<div class="totals">' . sprintf(__('+%s of %s thumbnails optimized','shortpixel-image-optimiser'), self::formatNumber($thumbsDone,0), self::formatNumber($thumbsTotal,0)) . ' ' . $excluded . '</div>';
|
||||
|
||||
elseif ($thumbsDone > 0)
|
||||
$output .= '<div class="totals">' . sprintf(__('+%s thumbnails optimized','shortpixel-image-optimiser'), self::formatNumber($thumbsDone, 0)) . ' ' . $excluded . '</div>';
|
||||
|
||||
$improvs = array();
|
||||
|
||||
uasort($improvements['thumbnails'], function ($a, $b) {
|
||||
//return $b[0] <=> $a[0]; // @todo Efficient code to use once PHP 5 support is done.
|
||||
if ($a == $b) {
|
||||
return 0;
|
||||
}
|
||||
return ($b < $a) ? -1 : 1;
|
||||
});
|
||||
|
||||
$cutoff = false;
|
||||
$thumbCount = count($improvements['thumbnails']);
|
||||
if ($thumbCount > 20)
|
||||
{
|
||||
$improvements['thumbnails'] = array_slice($improvements['thumbnails'], 0, 15, true);
|
||||
$cutoff = true;
|
||||
}
|
||||
|
||||
|
||||
// Quality Check
|
||||
foreach($improvements['thumbnails'] as $thumbName => $thumbStat)
|
||||
{
|
||||
$stat = $thumbStat[0];
|
||||
if (is_numeric($stat) && $stat >= 0)
|
||||
{
|
||||
$improvs[$thumbName] = $stat; //self::formatNumber($stat,2);
|
||||
}
|
||||
}
|
||||
|
||||
if (count($improvs) > 0)
|
||||
{
|
||||
$output .= "<div class='thumb-wrapper'>";
|
||||
$lowrating = 0;
|
||||
foreach($improvs as $thumbName => $stat)
|
||||
{
|
||||
$statText = self::formatNumber($stat, 2);
|
||||
$title = sprintf(__('%s : %s', 'shortpixel-image-optimiser'), $thumbName, $statText . '%');
|
||||
$rating = ceil( round($stat) / 10);
|
||||
if (0 == $rating)
|
||||
{
|
||||
$lowrating++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$blocks_on = str_repeat('<span class="point checked"> </span>', $rating);
|
||||
$blocks_off = str_repeat('<span class="point"> </span>', (10- $rating));
|
||||
|
||||
$output .= "<div class='thumb " . $thumbName . "' title='" . $title . "'>"
|
||||
. "<span class='thumb-name'>" . $thumbName . '</span>' .
|
||||
"<span class='optimize-bar'>" . $blocks_on . $blocks_off . "</span>
|
||||
</div>";
|
||||
}
|
||||
|
||||
if ($lowrating > 0)
|
||||
{
|
||||
$blocks_off = str_repeat('<span class="point"> </span>', 10);
|
||||
|
||||
$output .= "<div class='thumb'>"
|
||||
. "<span class='thumb-name'>" . sprintf(__('+ %d thumbnails ', 'shortpixel-image-optimiser'), $lowrating) . '</span>' .
|
||||
"<span class='optimize-bar'>" . $blocks_off . "</span>
|
||||
</div>";
|
||||
}
|
||||
|
||||
if (true === $cutoff)
|
||||
{
|
||||
$output .= '<div class="thumb"><span class="cutoff">' . sprintf(__('+ %d more', 'shortpixel-image-optimiser'), ($thumbCount - 15)) . '</span></div>';
|
||||
}
|
||||
|
||||
|
||||
$output .= "</div> <!-- /thumb-wrapper -->";
|
||||
}
|
||||
$output .= "</div> <!-- /thumb optimized -->";
|
||||
}
|
||||
|
||||
if ($retinasDone > 0)
|
||||
{
|
||||
$output .= '<div class="filetype retina">' . sprintf(__('+%s Retina images optimized','shortpixel-image-optimiser') , $retinasDone) . '</div>';
|
||||
}
|
||||
if ($webpsTotal > 0)
|
||||
{
|
||||
$output .= '<div class="filetype webp">' . sprintf(__('+%s Webp images ','shortpixel-image-optimiser') , $webpsTotal) . '</div>';
|
||||
}
|
||||
if ($avifsTotal > 0)
|
||||
{
|
||||
$output .= '<div class="filetype avif">' . sprintf(__('+%s Avif images ','shortpixel-image-optimiser') , $avifsTotal) . '</div>';
|
||||
}
|
||||
|
||||
if ($imageObj->isSomethingOptimized() && $imageObj->isProcessable())
|
||||
{
|
||||
list($urls, $optimizable) = $imageObj->getCountOptimizeData('thumbnails');
|
||||
list($webpUrls, $webpCount) = $imageObj->getCountOptimizeData('webp');
|
||||
list($avifUrls, $avifCount) = $imageObj->getCountOptimizeData('avif');
|
||||
|
||||
|
||||
$maxList = 10;
|
||||
|
||||
if (count($urls) > $maxList)
|
||||
{
|
||||
$urls = array_slice($urls, 0, $maxList, true);
|
||||
$urls[] = '...';
|
||||
}
|
||||
if (count($webpUrls) > $maxList)
|
||||
{
|
||||
$webpUrls = array_slice($webpUrls, 0, $maxList, true);
|
||||
$webpUrls[] = '...';
|
||||
}
|
||||
if (count($avifUrls) > $maxList)
|
||||
{
|
||||
$avifUrls = array_slice($avifUrls, 0, $maxList, true);
|
||||
$avifUrls[] = '...';
|
||||
}
|
||||
|
||||
if ($optimizable > 0)
|
||||
{
|
||||
$output .= '<div class="thumbs-todo"><h4>' . sprintf(__('%d images to optimize', 'shortpixel-image-optimiser'), $optimizable) . '</h4>';
|
||||
$output .= "<span>";
|
||||
foreach($urls as $optObj)
|
||||
{
|
||||
if ($optObj === '...')
|
||||
$output .= $optObj;
|
||||
else
|
||||
$output .= substr($optObj, strrpos($optObj, '/')+1) . '<br>';
|
||||
}
|
||||
$output .= "</span>";
|
||||
$output .= '</div>';
|
||||
}
|
||||
|
||||
if ($webpCount > 0 )
|
||||
{
|
||||
|
||||
$output .= '<div class="thumbs-todo"><h4>' . sprintf(__('%d Webp files to create', 'shortpixel-image-optimiser'), $webpCount) . '</h4>';
|
||||
$output .= "<span>";
|
||||
foreach($webpUrls as $optObj)
|
||||
{
|
||||
if ($optObj === '...')
|
||||
$output .= $optObj;
|
||||
else
|
||||
$output .= self::convertImageTypeName(substr($optObj, strrpos($optObj, '/')+1), 'webp') . '<br>';
|
||||
}
|
||||
$output .= "</span>";
|
||||
$output .= '</div>';
|
||||
}
|
||||
if ($avifCount > 0)
|
||||
{
|
||||
$output .= '<div class="thumbs-todo"><h4>' . sprintf(__('%d Avif files to create', 'shortpixel-image-optimiser'), $avifCount) . '</h4>';
|
||||
$output .= "<span>";
|
||||
foreach($avifUrls as $optObj)
|
||||
{
|
||||
$output .= self::convertImageTypeName(substr($optObj, strrpos($optObj, '/')+1), 'avif') . '<br>';
|
||||
}
|
||||
$output .= "</span>";
|
||||
$output .= '</div>';
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
|
||||
}
|
||||
|
||||
public static function compressionTypeToText($type)
|
||||
{
|
||||
if ($type == ImageModel::COMPRESSION_LOSSLESS )
|
||||
return __('Lossless', 'shortpixel-image-optimiser');
|
||||
|
||||
if ($type == ImageModel::COMPRESSION_LOSSY )
|
||||
return __('Lossy', 'shortpixel-image-optimiser');
|
||||
|
||||
if ($type == ImageModel::COMPRESSION_GLOSSY )
|
||||
return __('Glossy', 'shortpixel-image-optimiser');
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
public static function getListActions($mediaItem)
|
||||
{
|
||||
$list_actions = array();
|
||||
$id = $mediaItem->get('id');
|
||||
|
||||
$keyControl = ApiKeyController::getInstance();
|
||||
if (! $keyControl->keyIsVerified())
|
||||
{
|
||||
return array(); // nothing
|
||||
}
|
||||
|
||||
$quotaControl = QuotaController::getInstance();
|
||||
|
||||
$access = AccessModel::getInstance();
|
||||
if (! $access->imageIsEditable($mediaItem))
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
if ($id === 0)
|
||||
return array();
|
||||
|
||||
if ($mediaItem->isSomethingOptimized() )
|
||||
{
|
||||
list($u, $optimizable) = $mediaItem->getCountOptimizeData('thumbnails');
|
||||
list($u, $optimizableWebp) = $mediaItem->getCountOptimizeData('webp');
|
||||
list($u, $optimizableAvif) = $mediaItem->getCountOptimizeData('avif');
|
||||
|
||||
if ($mediaItem->isProcessable() && ! $mediaItem->isOptimizePrevented())
|
||||
{
|
||||
$action = self::getAction('optimizethumbs', $id);
|
||||
if ($optimizable > 0)
|
||||
{
|
||||
$total = $optimizable + $optimizableWebp + $optimizableAvif;
|
||||
if ($optimizableWebp > 0 || $optimizableAvif > 0)
|
||||
$itemText = __('items', 'shortpixel-image-optimiser');
|
||||
else {
|
||||
$itemText = __('thumbnails', 'shortpixel-image-optimiser');
|
||||
}
|
||||
$action['text'] = sprintf(__('Optimize %s %s','shortpixel-image-optimiser'),$total, $itemText);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($optimizableWebp > 0 && $optimizableAvif > 0)
|
||||
$text = sprintf(__('Optimize %s webps and %s avif','shortpixel-image-optimiser'),$optimizableWebp, $optimizableAvif);
|
||||
elseif ($optimizableWebp > 0)
|
||||
$text = sprintf(__('Optimize %s webps','shortpixel-image-optimiser'),$optimizableWebp);
|
||||
else
|
||||
$text = sprintf(__('Optimize %s avifs','shortpixel-image-optimiser'),$optimizableAvif);
|
||||
$action['text'] = $text;
|
||||
}
|
||||
$list_actions['optimizethumbs'] = $action;
|
||||
}
|
||||
|
||||
|
||||
if ($mediaItem->isRestorable())
|
||||
{
|
||||
if ($mediaItem->get('type') == 'custom')
|
||||
{
|
||||
if ($mediaItem->getExtension() !== 'pdf') // no support for this
|
||||
$list_actions['comparer'] = self::getAction('compare-custom', $id);
|
||||
}
|
||||
else
|
||||
{
|
||||
// PDF without thumbnail can't be compared.
|
||||
$showCompare = true;
|
||||
if ($mediaItem->getExtension() == 'pdf')
|
||||
{
|
||||
if (! $mediaItem->getThumbnail('full'))
|
||||
$showCompare = false;
|
||||
elseif(! $mediaItem->getThumbnail('full')->hasBackup())
|
||||
$showCompare = false;
|
||||
}
|
||||
|
||||
if ($showCompare)
|
||||
$list_actions['comparer'] = self::getAction('compare', $id);
|
||||
}
|
||||
if ($mediaItem->isRestorable())
|
||||
{
|
||||
|
||||
$compressionType = $mediaItem->getMeta('compressionType');
|
||||
switch($compressionType)
|
||||
{
|
||||
case ImageModel::COMPRESSION_LOSSLESS:
|
||||
$list_actions['reoptimize-lossy'] = self::getAction('reoptimize-lossy', $id);
|
||||
$list_actions['reoptimize-glossy'] = self::getAction('reoptimize-glossy', $id);
|
||||
|
||||
break;
|
||||
case ImageModel::COMPRESSION_LOSSY:
|
||||
$list_actions['reoptimize-lossless'] = self::getAction('reoptimize-lossless', $id);
|
||||
$list_actions['reoptimize-glossy'] = self::getAction('reoptimize-glossy', $id);
|
||||
|
||||
break;
|
||||
case ImageModel::COMPRESSION_GLOSSY:
|
||||
$list_actions['reoptimize-lossy'] = self::getAction('reoptimize-lossy', $id);
|
||||
$list_actions['reoptimize-lossless'] = self::getAction('reoptimize-lossless', $id);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($mediaItem->get('type') === 'media')
|
||||
{
|
||||
$list_actions['reoptimize-smartcrop'] = self::getAction('reoptimize-smartcrop', $id, array('compressionType' => $compressionType));
|
||||
$list_actions['reoptimize-smartcropless'] = self::getAction('reoptimize-smartcropless', $id, array('compressionType' => $compressionType));
|
||||
}
|
||||
$list_actions['restore'] = self::getAction('restore', $id);
|
||||
} // isRestorable
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
} // hasBackup
|
||||
|
||||
if (\wpSPIO()->env()->is_debug && $mediaItem->get('type') == 'media')
|
||||
{
|
||||
$list_actions['redo_legacy'] = self::getAction('redo_legacy', $id);
|
||||
}
|
||||
} //isOptimized
|
||||
|
||||
if(! $quotaControl->hasQuota())
|
||||
{
|
||||
$remove = array('reoptimize-lossy' => '', 'reoptimize-glossy' => '', 'reoptimize-lossless' => '', 'optimizethumbs' => '');
|
||||
$list_actions = array_diff_key($list_actions, $remove);
|
||||
|
||||
}
|
||||
return $list_actions;
|
||||
}
|
||||
|
||||
public static function getActions($mediaItem)
|
||||
{
|
||||
$actions = array();
|
||||
$id = $mediaItem->get('id');
|
||||
$quotaControl = QuotaController::getInstance();
|
||||
$optimizeController = new OptimizeController();
|
||||
|
||||
$keyControl = ApiKeyController::getInstance();
|
||||
if (! $keyControl->keyIsVerified())
|
||||
{
|
||||
return array(); // nothing
|
||||
}
|
||||
|
||||
$access = AccessModel::getInstance();
|
||||
if (! $access->imageIsEditable($mediaItem))
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
if ($id === 0)
|
||||
return array();
|
||||
|
||||
if(! $quotaControl->hasQuota())
|
||||
{
|
||||
$actions['extendquota'] = self::getAction('extendquota', $id);
|
||||
$actions['checkquota'] = self::getAction('checkquota', $id);
|
||||
}
|
||||
elseif($mediaItem->isProcessable() && ! $mediaItem->isSomethingOptimized() && ! $mediaItem->isOptimizePrevented() && ! $optimizeController->isItemInQueue($mediaItem))
|
||||
{
|
||||
$actions['optimize'] = self::getAction('optimize', $id);
|
||||
$actions['markCompleted'] = self::getAction('markCompleted', $id);
|
||||
}
|
||||
elseif ($mediaItem->isUserExcluded() && false === $mediaItem->isSomethingOptimized())
|
||||
{
|
||||
$actions['optimize'] = self::getAction('forceOptimize', $id);
|
||||
}
|
||||
|
||||
|
||||
return $actions;
|
||||
}
|
||||
|
||||
public static function getStatusText($mediaItem)
|
||||
{
|
||||
$keyControl = ApiKeyController::getInstance();
|
||||
$quotaControl = QuotaController::getInstance();
|
||||
$optimizeController = new OptimizeController();
|
||||
$settings = \wpSPIO()->settings();
|
||||
|
||||
$text = '';
|
||||
|
||||
if (! $keyControl->keyIsVerified())
|
||||
{
|
||||
$text = __('Invalid API Key. <a href="options-general.php?page=wp-shortpixel-settings">Check your Settings</a>','shortpixel-image-optimiser');
|
||||
}
|
||||
// This basically happens when a NextGen gallery is not added to Custom Media.
|
||||
elseif ($mediaItem->get('id') === 0)
|
||||
{
|
||||
if ($mediaItem->isProcessable(true) === false)
|
||||
{
|
||||
$text = __('Not Processable: ','shortpixel_image_optimiser');
|
||||
$text .= $mediaItem->getProcessableReason();
|
||||
}
|
||||
else {
|
||||
if (\wpSPIO()->env()->has_nextgen && false == $settings->includeNextGen)
|
||||
{
|
||||
$text = __('This image was not found in our database. Enable "Optimize nextgen galleries" in the settings, or add this folder manually. ', 'shortpixel-image-optimiser');
|
||||
}
|
||||
else {
|
||||
$text = __('This image was not found in our database. Refresh folders, or add this gallery', 'shortpixel-image-optimiser');
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif ($mediaItem->isSomethingOptimized())
|
||||
{
|
||||
$text = UiHelper::renderSuccessText($mediaItem);
|
||||
}
|
||||
elseif (false === $mediaItem->isProcessable() )
|
||||
{
|
||||
$text = __('Not Processable: ','shortpixel_image_optimiser');
|
||||
$text .= $mediaItem->getProcessableReason();
|
||||
}
|
||||
elseif (! $mediaItem->exists())
|
||||
{
|
||||
$text = __('File does not exist.','shortpixel-image-optimiser');
|
||||
}
|
||||
elseif ($mediaItem->getMeta('status') < 0)
|
||||
{
|
||||
$text = $mediaItem->getMeta('errorMessage');
|
||||
}
|
||||
elseif( $optimizeController->isItemInQueue($mediaItem) === true)
|
||||
{
|
||||
$text = '<p>' . __('This item is waiting to be processed', 'shortpixel-image-optimiser') . '</p>';
|
||||
$action = self::getAction('cancelOptimize', $mediaItem->get('id'));
|
||||
|
||||
$text .= '<p><a href="javascript:' . $action['function'] . '">' . $action['text'] . '</a></p>';
|
||||
}
|
||||
|
||||
if ($mediaItem->isOptimizePrevented() !== false)
|
||||
{
|
||||
$retry = self::getAction('retry', $mediaItem->get('id'));
|
||||
$unmark = self::getAction('unMarkCompleted', $mediaItem->get('id'));
|
||||
$redo_legacy = false;
|
||||
|
||||
if ($mediaItem->get('type') == 'media')
|
||||
{
|
||||
$was_converted = get_post_meta($mediaItem->get('id'), '_shortpixel_was_converted', true);
|
||||
$updateTs = 1656892800; // July 4th 2022 - 00:00 GMT
|
||||
|
||||
if ($was_converted < $updateTs)
|
||||
{
|
||||
$meta = $mediaItem->getWPMetaData();
|
||||
if (is_array($meta) && isset($meta['ShortPixel']))
|
||||
{
|
||||
$redo_legacy = self::getAction('redo_legacy', $mediaItem->get('id'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$status = $mediaItem->getMeta('status');
|
||||
$text = ''; // reset text
|
||||
|
||||
if (ImageModel::FILE_STATUS_MARKED_DONE == $status)
|
||||
{
|
||||
$text .= "<div class='shortpixel-image-notice'>" . esc_html($mediaItem->getReason('processable'));
|
||||
|
||||
$text .= "<p class='shortpixel-error-reset'>" . sprintf(__('%s Click to unmark as completed %s', 'shortpixel-image-optimiser'), '<a href="javascript:' . $unmark['function'] . '">', '</a>') . '</p>';
|
||||
$text .= '</div>';
|
||||
}
|
||||
else {
|
||||
$text .= "<div class='shortpixel-image-error'>" . esc_html($mediaItem->getReason('processable'));
|
||||
$text .= "<span class='shortpixel-error-reset'>" . sprintf(__('After you have fixed this issue, you can %s click here to retry %s', 'shortpixel-image-optimiser'), '<a href="javascript:' . $retry['function'] . '">', '</a>') . '</span>';
|
||||
$text .= '</div>';
|
||||
}
|
||||
|
||||
|
||||
|
||||
if ($redo_legacy !== false)
|
||||
{
|
||||
$text .= "<div class='shortpixel-image-error'><span class='shortpixel-error-reset'>";
|
||||
|
||||
$text .= sprintf(esc_html__('It seems you have older converted legacy data, which might cause this issue. You can try to %s %s %s . If nothing changes, this is not the cause. ','shortpixel-image-optimiser'), '<a href="javascript:' . $redo_legacy['function'] . '">', $redo_legacy['text'], '</a>');
|
||||
$text .= "</span></div>";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
// Defines all possible actions in the Ui
|
||||
public static function getAction($name, $id, $args = array())
|
||||
{
|
||||
$action = array('function' => '', 'type' => '', 'text' => '', 'display' => '');
|
||||
$keyControl = ApiKeyController::getInstance();
|
||||
|
||||
$compressionType = isset($args['compressionType']) ? $args['compressionType'] : null;
|
||||
|
||||
switch($name)
|
||||
{
|
||||
case 'optimize':
|
||||
$action['function'] = 'window.ShortPixelProcessor.screen.Optimize(' . $id . ')';
|
||||
$action['type'] = 'js';
|
||||
$action['text'] = __('Optimize Now', 'shortpixel-image-optimiser');
|
||||
$action['display'] = 'button';
|
||||
break;
|
||||
case 'forceOptimize':
|
||||
$action['function'] = 'window.ShortPixelProcessor.screen.Optimize(' . $id . ', true)';
|
||||
$action['type'] = 'js';
|
||||
$action['text'] = __('Override exclusions and optimize now', 'shortpixel-image-optimiser');
|
||||
$action['display'] = 'button';
|
||||
break;
|
||||
case 'cancelOptimize':
|
||||
$action['function'] = 'window.ShortPixelProcessor.screen.CancelOptimizeItem(' . $id . ')';
|
||||
$action['type'] = 'js';
|
||||
$action['text'] = __('Cancel item optimization', 'shortpixel-image-optimiser');
|
||||
$action['display'] = 'button';
|
||||
break;
|
||||
case 'markCompleted':
|
||||
$action['function'] = 'window.ShortPixelProcessor.screen.MarkCompleted(' . $id . ')';
|
||||
$action['type'] = 'js';
|
||||
$action['text'] = __('Mark as Completed', 'shortpixel-image-optimiser');
|
||||
$action['display'] = 'button-secondary';
|
||||
$action['layout'] = 'paragraph';
|
||||
$action['title'] = __('This will cause the plugin to skip this image for optimization', 'shortpixel-image-optimiser');
|
||||
break;
|
||||
case 'unMarkCompleted':
|
||||
$action['function'] = 'window.ShortPixelProcessor.screen.UnMarkCompleted(' . $id . ')';
|
||||
$action['type'] = 'js';
|
||||
$action['text'] = __('Click to unmark this item as done', 'shortpixel-image-optimiser');
|
||||
$action['display'] = 'js';
|
||||
|
||||
break;
|
||||
case 'optimizethumbs':
|
||||
$action['function'] = 'window.ShortPixelProcessor.screen.Optimize(' . $id . ');';
|
||||
$action['type'] = 'js';
|
||||
$action['text'] = '';
|
||||
$action['display'] = 'inline';
|
||||
break;
|
||||
|
||||
case 'retry':
|
||||
$action['function'] = 'window.ShortPixelProcessor.screen.Optimize(' . $id . ');';
|
||||
$action['type'] = 'js';
|
||||
$action['text'] = __('Retry', 'shortpixel-image-optimiser') ;
|
||||
$action['display'] = 'button';
|
||||
break;
|
||||
case 'redo_legacy':
|
||||
$action['function'] = 'window.ShortPixelProcessor.screen.RedoLegacy(' . $id . ');';
|
||||
$action['type'] = 'js';
|
||||
$action['text'] = __('Redo Conversion', 'shortpixel-image-optimiser') ;
|
||||
$action['display'] = 'button';
|
||||
break;
|
||||
|
||||
case 'restore':
|
||||
$action['function'] = 'window.ShortPixelProcessor.screen.RestoreItem(' . $id . ');';
|
||||
$action['type'] = 'js';
|
||||
$action['text'] = __('Restore backup','shortpixel-image-optimiser');
|
||||
$action['display'] = 'inline';
|
||||
break;
|
||||
|
||||
case 'compare':
|
||||
$action['function'] = 'ShortPixel.loadComparer(' . $id . ')';
|
||||
$action['type'] = 'js';
|
||||
$action['text'] = __('Compare', 'shortpixel-image-optimiser');
|
||||
$action['display'] = 'inline';
|
||||
break;
|
||||
case 'compare-custom':
|
||||
$action['function'] = 'ShortPixel.loadComparer(' . $id . ',"custom")';
|
||||
$action['type'] = 'js';
|
||||
$action['text'] = __('Compare', 'shortpixel-image-optimiser');
|
||||
$action['display'] = 'inline';
|
||||
break;
|
||||
case 'reoptimize-glossy':
|
||||
$action['function'] = 'window.ShortPixelProcessor.screen.ReOptimize(' . $id . ',' . ImageModel::COMPRESSION_GLOSSY . ')';
|
||||
$action['type'] = 'js';
|
||||
$action['text'] = __('Re-optimize Glossy','shortpixel-image-optimiser') ;
|
||||
$action['display'] = 'inline';
|
||||
break;
|
||||
case 'reoptimize-lossy':
|
||||
$action['function'] = 'window.ShortPixelProcessor.screen.ReOptimize(' . $id . ',' . ImageModel::COMPRESSION_LOSSY . ')';
|
||||
$action['type'] = 'js';
|
||||
$action['text'] = __('Re-optimize Lossy','shortpixel-image-optimiser');
|
||||
$action['display'] = 'inline';
|
||||
break;
|
||||
|
||||
case 'reoptimize-lossless':
|
||||
$action['function'] = 'window.ShortPixelProcessor.screen.ReOptimize(' . $id . ',' . ImageModel::COMPRESSION_LOSSLESS . ')';
|
||||
$action['type'] = 'js';
|
||||
$action['text'] = __('Re-optimize Lossless','shortpixel-image-optimiser');
|
||||
$action['display'] = 'inline';
|
||||
break;
|
||||
case 'reoptimize-smartcrop':
|
||||
$action['function'] = 'window.ShortPixelProcessor.screen.ReOptimize(' . $id . ',' . $compressionType . ',' . ImageModel::ACTION_SMARTCROP . ')';
|
||||
$action['type'] = 'js';
|
||||
$action['text'] = __('Re-optimize with SmartCrop','shortpixel-image-optimiser');
|
||||
$action['display'] = 'inline';
|
||||
break;
|
||||
case 'reoptimize-smartcropless':
|
||||
$action['function'] = 'window.ShortPixelProcessor.screen.ReOptimize(' . $id . ',' . $compressionType . ',' . ImageModel::ACTION_SMARTCROPLESS . ')';
|
||||
$action['type'] = 'js';
|
||||
$action['text'] = __('Re-optimize without SmartCrop','shortpixel-image-optimiser');
|
||||
$action['display'] = 'inline';
|
||||
break;
|
||||
case 'extendquota':
|
||||
$action['function'] = 'https://shortpixel.com/login/'. $keyControl->getKeyForDisplay();
|
||||
$action['type'] = 'button';
|
||||
$action['text'] = __('Extend Quota','shortpixel-image-optimiser');
|
||||
$action['display'] = 'button';
|
||||
break;
|
||||
case 'checkquota':
|
||||
$action['function'] = 'ShortPixel.checkQuota()';
|
||||
$action['type'] = 'js';
|
||||
$action['display'] = 'button';
|
||||
$action['text'] = __('Check Quota','shortpixel-image-optimiser');
|
||||
break;
|
||||
}
|
||||
|
||||
return $action;
|
||||
}
|
||||
|
||||
public static function getConvertErrorReason($error)
|
||||
{
|
||||
switch($error)
|
||||
{
|
||||
case -1: //ERROR_LIBRARY:
|
||||
$reason = __('PNG Library is not present or not working', 'shortpixel-image-optimiser');
|
||||
break;
|
||||
case -2: //ERROR_PATHFAIL:
|
||||
$reason = __('Could not create path', 'shortpixel-image-optimiser');
|
||||
break;
|
||||
case -3: //ERROR_RESULTLARGER:
|
||||
$reason = __('Result file is larger','shortpixel-image-optimiser');
|
||||
break;
|
||||
case -4: // ERROR_WRITEERROR
|
||||
$reason = __('Could not write result file', 'shortpixel-image-optimiser');
|
||||
break;
|
||||
case -5: // ERROR_BACKUPERROR
|
||||
$reason = __('Could not create backup', 'shortpixel-image-optimiser');
|
||||
break;
|
||||
case -6: // ERROR_TRANSPARENT
|
||||
$reason = __('Image is transparent', 'shortpixel-image-optimiser');
|
||||
break;
|
||||
default:
|
||||
$reason = sprintf(__('Unknown error %s', 'shortpixel-image-optimiser'), $error);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
$message = sprintf(__('Not converted: %s ', 'shortpixel-image-optimiser'), $reason);
|
||||
return $message;
|
||||
}
|
||||
|
||||
public static function getKBSearchLink($subject)
|
||||
{
|
||||
return esc_url(self::$knowledge_url . sanitize_text_field($subject));
|
||||
}
|
||||
|
||||
// @param MediaLibraryModel Object $imageItem
|
||||
// @param String $size Preferred size
|
||||
// @param String Preload The preloader tries to guess what the preview might be for a more smooth process. Ignore optimize / backup
|
||||
public static function findBestPreview($imageItem, $size = 800, $preload = false)
|
||||
{
|
||||
$closestObj = $imageItem;
|
||||
|
||||
// set the standard.
|
||||
if ($imageItem->getExtension() == 'pdf') // try not to select non-showable extensions.
|
||||
$bestdiff = 0;
|
||||
else
|
||||
$bestdiff = abs($imageItem->get('width') - $size);
|
||||
|
||||
$thumbnails = $imageItem->get('thumbnails');
|
||||
|
||||
if (! is_array($thumbnails))
|
||||
{
|
||||
return $closestObj; // nothing more to do.
|
||||
}
|
||||
|
||||
foreach($thumbnails as $thumbnail)
|
||||
{
|
||||
if (! $preload && (! $thumbnail->isOptimized() || ! $thumbnail->hasBackup()))
|
||||
continue;
|
||||
|
||||
$diff = abs($thumbnail->get('width') - $size);
|
||||
if ($diff < $bestdiff)
|
||||
{
|
||||
$closestObj = $thumbnail;
|
||||
$bestdiff = $diff;
|
||||
}
|
||||
}
|
||||
|
||||
return $closestObj;
|
||||
}
|
||||
|
||||
public static function formatTS($ts)
|
||||
{
|
||||
//$format = get_option('date_format') .' @ ' . date_i18n(get_option('time_format');
|
||||
if (function_exists('wp_date'))
|
||||
{
|
||||
$date = wp_date(get_option('date_format'), $ts);
|
||||
$date .= ' @ ' . wp_date(get_option('time_format'), $ts);
|
||||
}
|
||||
else
|
||||
{
|
||||
$date = date_i18n(get_option('date_format'), $ts);
|
||||
$date .= ' @ ' . date_i18n(get_option('time_format'), $ts);
|
||||
|
||||
}
|
||||
return $date;
|
||||
}
|
||||
|
||||
public static function formatBytes($bytes, $precision = 2) {
|
||||
$units = array('B', 'KB', 'MB', 'GB', 'TB');
|
||||
|
||||
$bytes = max($bytes, 0);
|
||||
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
|
||||
$pow = min($pow, count($units) - 1);
|
||||
|
||||
$bytes /= pow(1024, $pow);
|
||||
|
||||
return number_format_i18n(round($bytes, $precision), $precision) . ' ' . $units[$pow];
|
||||
}
|
||||
|
||||
public static function formatNumber($number, $precision = 2)
|
||||
{
|
||||
global $wp_locale;
|
||||
$decimalpoint = isset($wp_locale->number_format['decimal_point']) ? $wp_locale->number_format['decimal_point'] : false;
|
||||
$number = number_format_i18n( (float) $number, $precision);
|
||||
|
||||
$hasDecimal = (strpos($number, $decimalpoint) === false) ? false : true;
|
||||
|
||||
// Don't show trailing zeroes if number is a whole unbroken number. -> string comparison because number_format_i18n returns string.
|
||||
if ($decimalpoint !== false && $hasDecimal && substr($number, strpos($number, $decimalpoint) + 1) === '00')
|
||||
{
|
||||
$number = substr($number, 0, strpos($number, $decimalpoint));
|
||||
}
|
||||
// Some locale's have no-breaking-space as thousands separator. This doesn't work well in JS / Cron Shell so replace with space.
|
||||
$number = str_replace(' ', ' ', $number);
|
||||
return $number;
|
||||
}
|
||||
|
||||
public static function formatDate( $date ) {
|
||||
|
||||
if ( '0000-00-00 00:00:00' === $date->format('Y-m-d ') ) {
|
||||
$h_time = '';
|
||||
} else {
|
||||
$time = $date->format('U'); //get_post_time( 'G', true, $post, false );
|
||||
if ( ( abs( $t_diff = time() - $time ) ) < DAY_IN_SECONDS ) {
|
||||
if ( $t_diff < 0 ) {
|
||||
$h_time = sprintf( __( '%s from now' ), human_time_diff( $time ) );
|
||||
} else {
|
||||
$h_time = sprintf( __( '%s ago' ), human_time_diff( $time ) );
|
||||
}
|
||||
} else {
|
||||
$h_time = $date->format( 'Y/m/d' );
|
||||
}
|
||||
}
|
||||
|
||||
return $h_time;
|
||||
}
|
||||
|
||||
protected static function convertImageTypeName($name, $type)
|
||||
{
|
||||
if ($type == 'webp')
|
||||
{
|
||||
$is_double = \wpSPIO()->env()->useDoubleWebpExtension();
|
||||
}
|
||||
if ($type == 'avif')
|
||||
{
|
||||
$is_double = \wpSPIO()->env()->useDoubleAvifExtension();
|
||||
}
|
||||
|
||||
if ($is_double)
|
||||
{
|
||||
return $name . '.' . $type;
|
||||
}
|
||||
else
|
||||
{
|
||||
return substr($name, 0, strrpos($name, '.')) . '.' . $type;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Strings on settings page that need to be available for both JS and PHP */
|
||||
public static function getSettingsStrings($name = false)
|
||||
{
|
||||
|
||||
$strings = array(
|
||||
);
|
||||
|
||||
$exclusion_types = array(
|
||||
'name' => __('Name', 'shortpixel-image-optimiser'),
|
||||
'path' => __('Path', 'shortpixel-image-optimiser'),
|
||||
'size' => __('Size', 'shortpixel-image-optimiser'),
|
||||
);
|
||||
|
||||
$exclusion_apply = array(
|
||||
'all' => __('All', 'shortpixel-image-optimiser'),
|
||||
'only-thumbs' => __('Only Thumbnails', 'shortpixel-image-optimiser'),
|
||||
'only-custom' => __('Only Custom Media Images', 'shortpixel-image-optimiser'),
|
||||
'selected-thumbs' => __('Selected Images', 'shortpixel-image-optimiser'),
|
||||
);
|
||||
|
||||
$strings['exclusion_types'] = $exclusion_types;
|
||||
$strings['exclusion_apply'] = $exclusion_apply;
|
||||
|
||||
if ($name !== false && isset($strings[$name]))
|
||||
{
|
||||
return $strings[$name];
|
||||
}
|
||||
|
||||
return $strings;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // class
|
@@ -0,0 +1,277 @@
|
||||
<?php
|
||||
namespace ShortPixel\Helper;
|
||||
|
||||
use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
|
||||
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
// Our newest Tools class
|
||||
class UtilHelper
|
||||
{
|
||||
|
||||
public static function getPostMetaTable()
|
||||
{
|
||||
global $wpdb;
|
||||
|
||||
return $wpdb->prefix . 'shortpixel_postmeta';
|
||||
}
|
||||
|
||||
public static function shortPixelIsPluginActive($plugin) {
|
||||
$activePlugins = apply_filters( 'active_plugins', get_option( 'active_plugins', array()));
|
||||
if ( is_multisite() ) {
|
||||
$activePlugins = array_merge($activePlugins, get_site_option( 'active_sitewide_plugins'));
|
||||
}
|
||||
return in_array( $plugin, $activePlugins);
|
||||
}
|
||||
|
||||
static public function timestampToDB($timestamp)
|
||||
{
|
||||
return date("Y-m-d H:i:s", $timestamp);
|
||||
}
|
||||
|
||||
static public function DBtoTimestamp($date)
|
||||
{
|
||||
return strtotime($date);
|
||||
}
|
||||
|
||||
public static function getWordPressImageSizes()
|
||||
{
|
||||
global $_wp_additional_image_sizes;
|
||||
|
||||
$sizes_names = get_intermediate_image_sizes();
|
||||
$sizes = array();
|
||||
foreach ( $sizes_names as $size ) {
|
||||
$sizes[ $size ][ 'width' ] = intval( get_option( "{$size}_size_w" ) );
|
||||
$sizes[ $size ][ 'height' ] = intval( get_option( "{$size}_size_h" ) );
|
||||
$sizes[ $size ][ 'crop' ] = get_option( "{$size}_crop" ) ? get_option( "{$size}_crop" ) : false;
|
||||
$sizes[ $size ][ 'nice-name'] = ucfirst($size);
|
||||
}
|
||||
if(function_exists('wp_get_additional_image_sizes')) {
|
||||
$sizes = array_merge($sizes, wp_get_additional_image_sizes());
|
||||
} elseif(is_array($_wp_additional_image_sizes)) {
|
||||
$sizes = array_merge($sizes, $_wp_additional_image_sizes);
|
||||
}
|
||||
|
||||
$sizes = apply_filters('shortpixel/settings/image_sizes', $sizes);
|
||||
return $sizes;
|
||||
}
|
||||
|
||||
// wp_normalize_path doesn't work for windows installs in some situations, so we can use it, but we still want some of the functions.
|
||||
public static function spNormalizePath($path)
|
||||
{
|
||||
$path = preg_replace( '|(?<=.)/+|', '/', $path );
|
||||
return $path;
|
||||
}
|
||||
|
||||
// Copy of private https://developer.wordpress.org/reference/functions/_wp_relative_upload_path/
|
||||
public static function getRelativeUploadPath($path)
|
||||
{
|
||||
$new_path = $path;
|
||||
$uploads = wp_get_upload_dir();
|
||||
if ( 0 === strpos( $new_path, $uploads['basedir'] ) ) {
|
||||
$new_path = str_replace( $uploads['basedir'], '', $new_path );
|
||||
$new_path = ltrim( $new_path, '/' );
|
||||
}
|
||||
return $new_path;
|
||||
}
|
||||
|
||||
public static function getExclusions($args = array())
|
||||
{
|
||||
$defaults = array(
|
||||
'filter' => false,
|
||||
'thumbname' => null,
|
||||
'is_thumbnail' => false,
|
||||
'is_custom' => false,
|
||||
);
|
||||
|
||||
$args = wp_parse_args($args, $defaults);
|
||||
|
||||
$patterns = \wpSPIO()->settings()->excludePatterns;
|
||||
$matches = array();
|
||||
|
||||
if (false === is_array($patterns))
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
foreach($patterns as $index => $pattern)
|
||||
{
|
||||
if (! isset($pattern['apply']))
|
||||
{
|
||||
$patterns[$index]['apply'] = 'all';
|
||||
}
|
||||
|
||||
if (true === $args['filter'])
|
||||
{
|
||||
if (true === self::matchExclusion($patterns[$index], $args))
|
||||
{
|
||||
$matches[] = $pattern;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (true === $args['filter'])
|
||||
{
|
||||
return $matches;
|
||||
}
|
||||
else
|
||||
return $patterns;
|
||||
}
|
||||
|
||||
protected static function matchExclusion($pattern, $options)
|
||||
{
|
||||
$apply = $pattern['apply'];
|
||||
$thumblist = isset($pattern['thumblist']) ? $pattern['thumblist'] : array();
|
||||
$bool = false;
|
||||
|
||||
if ($apply === 'all')
|
||||
{
|
||||
$bool = true;
|
||||
}
|
||||
elseif ($apply == 'only-thumbs' && true === $options['is_thumbnail'])
|
||||
{
|
||||
$bool = true;
|
||||
}
|
||||
elseif ($apply == 'only-custom' && true === $options['is_custom'])
|
||||
{
|
||||
$bool = true;
|
||||
}
|
||||
elseif (count($thumblist) > 0 && ! is_null($options['thumbname']))
|
||||
{
|
||||
$thumbname = $options['thumbname'];
|
||||
if (in_array($thumbname, $thumblist))
|
||||
{
|
||||
$bool = true;
|
||||
}
|
||||
}
|
||||
return $bool;
|
||||
}
|
||||
|
||||
|
||||
public static function alterHtaccess($webp = false, $avif = false)
|
||||
{
|
||||
// [BS] Backward compat. 11/03/2019 - remove possible settings from root .htaccess
|
||||
/* Plugin init is before loading these admin scripts. So it can happen misc.php is not yet loaded */
|
||||
if (! function_exists('insert_with_markers'))
|
||||
{
|
||||
Log::addWarn('AlterHtaccess Called before WP init');
|
||||
return;
|
||||
//require_once( ABSPATH . 'wp-admin/includes/misc.php' );
|
||||
}
|
||||
$upload_dir = wp_upload_dir();
|
||||
$upload_base = trailingslashit($upload_dir['basedir']);
|
||||
|
||||
if (false === $webp && false === $avif ) {
|
||||
insert_with_markers( get_home_path() . '.htaccess', 'ShortPixelWebp', '');
|
||||
|
||||
// Only empty these tags if the file actually exist, they are created by SPIO.
|
||||
if (file_exists($upload_base . '.htaccess'))
|
||||
{
|
||||
insert_with_markers( $upload_base . '.htaccess', 'ShortPixelWebp', '');
|
||||
}
|
||||
|
||||
|
||||
if (file_exists(trailingslashit(WP_CONTENT_DIR) . '.htaccess'))
|
||||
{
|
||||
insert_with_markers( trailingslashit(WP_CONTENT_DIR) . '.htaccess', 'ShortPixelWebp', '');
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
$avif_rules = '
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
##### Directives for delivering AVIF files, if they exist #####
|
||||
# Does the browser support avif?
|
||||
RewriteCond %{HTTP_ACCEPT} image/avif
|
||||
# AND is the request a jpg or png? (also grab the basepath %1 to match in the next rule)
|
||||
RewriteCond %{REQUEST_URI} ^(.+)\.(?:jpe?g|png|gif)$
|
||||
# AND does a .avif image exist?
|
||||
RewriteCond %{DOCUMENT_ROOT}/%1.avif -f
|
||||
# THEN send the avif image and set the env var avif
|
||||
RewriteRule (.+)\.(?:jpe?g|png)$ $1.avif [NC,T=image/avif,E=avif,L]
|
||||
|
||||
# Does the browser support avif?
|
||||
RewriteCond %{HTTP_ACCEPT} image/avif
|
||||
# AND is the request a jpg or png? (also grab the basepath %1 to match in the next rule)
|
||||
RewriteCond %{REQUEST_URI} ^(.+)\.(?:jpe?g|png|gif)$
|
||||
# AND does a .jpg.avif image exist?
|
||||
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.avif -f
|
||||
# THEN send the avif image and set the env var avif
|
||||
RewriteRule ^(.+)$ $1.avif [NC,T=image/avif,E=avif,L]
|
||||
|
||||
</IfModule>
|
||||
<IfModule mod_headers.c>
|
||||
# If REDIRECT_avif env var exists, append Accept to the Vary header
|
||||
Header append Vary Accept env=REDIRECT_avif
|
||||
</IfModule>
|
||||
<IfModule mod_mime.c>
|
||||
AddType image/avif .avif
|
||||
</IfModule>
|
||||
';
|
||||
|
||||
$webp_rules = '
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
##### TRY FIRST the file appended with .webp (ex. test.jpg.webp) #####
|
||||
# Is the browser Chrome?
|
||||
RewriteCond %{HTTP_USER_AGENT} Chrome [OR]
|
||||
# OR Is request from Page Speed
|
||||
RewriteCond %{HTTP_USER_AGENT} "Google Page Speed Insights" [OR]
|
||||
# OR does this browser explicitly support webp
|
||||
RewriteCond %{HTTP_ACCEPT} image/webp
|
||||
# AND NOT MS EDGE 42/17 - doesnt work.
|
||||
RewriteCond %{HTTP_USER_AGENT} !Edge/17
|
||||
# AND is the request a jpg, png or gif?
|
||||
RewriteCond %{REQUEST_URI} ^(.+)\.(?:jpe?g|png|gif)$
|
||||
# AND does a .ext.webp image exist?
|
||||
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.webp -f
|
||||
# THEN send the webp image and set the env var webp
|
||||
RewriteRule ^(.+)$ $1.webp [NC,T=image/webp,E=webp,L]
|
||||
##### IF NOT, try the file with replaced extension (test.webp) #####
|
||||
RewriteCond %{HTTP_USER_AGENT} Chrome [OR]
|
||||
RewriteCond %{HTTP_USER_AGENT} "Google Page Speed Insights" [OR]
|
||||
RewriteCond %{HTTP_ACCEPT} image/webp
|
||||
RewriteCond %{HTTP_USER_AGENT} !Edge/17
|
||||
# AND is the request a jpg, png or gif? (also grab the basepath %1 to match in the next rule)
|
||||
RewriteCond %{REQUEST_URI} ^(.+)\.(?:jpe?g|png|gif)$
|
||||
# AND does a .webp image exist?
|
||||
RewriteCond %{DOCUMENT_ROOT}/%1.webp -f
|
||||
# THEN send the webp image and set the env var webp
|
||||
RewriteRule (.+)\.(?:jpe?g|png|gif)$ $1.webp [NC,T=image/webp,E=webp,L]
|
||||
</IfModule>
|
||||
<IfModule mod_headers.c>
|
||||
# If REDIRECT_webp env var exists, append Accept to the Vary header
|
||||
Header append Vary Accept env=REDIRECT_webp
|
||||
</IfModule>
|
||||
<IfModule mod_mime.c>
|
||||
AddType image/webp .webp
|
||||
</IfModule>
|
||||
' ;
|
||||
|
||||
$rules = '';
|
||||
// if ($avif)
|
||||
$rules .= $avif_rules;
|
||||
// if ($webp)
|
||||
$rules .= $webp_rules;
|
||||
|
||||
insert_with_markers( get_home_path() . '.htaccess', 'ShortPixelWebp', $rules);
|
||||
|
||||
/** In uploads and on, it needs Inherit. Otherwise things such as the 404 error page will not be loaded properly
|
||||
* since the WP rewrite will not be active at that point (overruled) **/
|
||||
$rules = str_replace('RewriteEngine On', 'RewriteEngine On' . PHP_EOL . 'RewriteOptions Inherit', $rules);
|
||||
|
||||
// Can shortcircuit (return false) the creation of subdirectory Htaccess files if this causes issues and is not needed.
|
||||
$bool = apply_filters('shortpixel/install/write_deep_htaccess', true);
|
||||
|
||||
if (true === $bool)
|
||||
{
|
||||
insert_with_markers( $upload_base . '.htaccess', 'ShortPixelWebp', $rules);
|
||||
insert_with_markers( trailingslashit(WP_CONTENT_DIR) . '.htaccess', 'ShortPixelWebp', $rules);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // class
|
Reference in New Issue
Block a user