File: //proc/self/cwd/wp-content/plugins/shortpixel-image-optimiser/class/Controller/AjaxController.php
<?php
namespace ShortPixel\Controller;
if (! defined('ABSPATH')) {
exit; // Exit if accessed directly.
}
use ShortPixel\Controller\Api\RequestManager;
use ShortPixel\Controller\View\ListMediaViewController as ListMediaViewController;
use ShortPixel\Controller\View\OtherMediaViewController as OtherMediaViewController;
use ShortPixel\Controller\View\OtherMediaFolderViewController as OtherMediaFolderViewController;
use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
use ShortPixel\Notices\NoticeController as Notices;
//use ShortPixel\Controller\BulkController as BulkController;
use ShortPixel\Helper\UiHelper as UiHelper;
use ShortPixel\Helper\InstallHelper as InstallHelper;
use ShortPixel\Helper\UtilHelper;
use ShortPixel\Model\Image\ImageModel as ImageModel;
use ShortPixel\Model\AccessModel as AccessModel;
// @todo This should probably become settingscontroller, for saving
use ShortPixel\Controller\View\SettingsViewController as SettingsViewController;
use ShortPixel\Controller\Queue\QueueItems as QueueItems;
use ShortPixel\Model\AiDataModel;
use ShortPixel\Model\Queue\QueueItem;
use ShortPixel\ViewController;
// Class for containing all Ajax Related Actions.
class AjaxController
{
const PROCESSOR_ACTIVE = -1;
const NONCE_FAILED = -2;
const NO_ACTION = -3;
const APIKEY_FAILED = -4;
const NOQUOTA = -5;
const SERVER_ERROR = -6;
const NO_ACCESS = -7;
private static $instance;
public static function getInstance()
{
if (is_null(self::$instance))
self::$instance = new static();
return self::$instance;
}
// Support for JS Processor - also used by localize to get for init.
public function getProcessorKey()
{
// Get a Secret Key.
$cacheControl = new CacheController();
$bulkSecret = $cacheControl->getItem('bulk-secret');
$secretKey = $bulkSecret->getValue();
if (is_null($secretKey) || strlen($secretKey) == 0 || $secretKey === 'null') {
$secretKey = false;
}
return $secretKey;
}
protected function checkProcessorKey()
{
$processKey = $this->getProcessorKey();
// phpcs:ignore -- Nonce is checked
$bulkSecret = isset($_POST['bulk-secret']) ? sanitize_text_field(wp_unslash($_POST['bulk-secret'])) : false;
// phpcs:ignore -- Nonce is checked
$isBulk = isset($_POST['isBulk']) ? filter_var(sanitize_text_field(wp_unslash($_POST['isBulk'])), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) : false;
$is_processor = false;
if ($processKey == false && $bulkSecret !== false) {
$is_processor = true;
} elseif ($processKey == $bulkSecret) {
$is_processor = true;
} elseif ($isBulk) {
$is_processor = true;
}
// Save new ProcessorKey
if ($is_processor && $bulkSecret !== $processKey) {
$cacheControl = new CacheController();
$cachedObj = $cacheControl->getItem('bulk-secret');
$cachedObj->setValue($bulkSecret);
$cachedObj->setExpires(2 * MINUTE_IN_SECONDS);
$cachedObj->save();
}
if (false === $is_processor) {
$json = new \stdClass;
$json->message = __('Processor is active in another window', 'shortpixel-image-optimiser');
$json->status = false;
$json->error = self::PROCESSOR_ACTIVE; // processor active
$this->send($json);
}
}
protected function getItemView()
{
// phpcs:ignore -- Nonce is checked
$type = isset($_POST['type']) ? sanitize_text_field($_POST['type']) : 'media';
// phpcs:ignore -- Nonce is checked
$id = isset($_POST['id']) ? intval($_POST['id']) : false;
$result = '';
$item = \wpSPIO()->filesystem()->getImage($id, $type);
$this->checkImageAccess($item);
if ($id > 0) {
if ($type == 'media') {
ob_start();
$control = ListMediaViewController::getInstance();
$control->doColumn('wp-shortPixel', $id);
$result = ob_get_contents();
ob_end_clean();
}
if ($type == 'custom') {
ob_start();
$control = new OtherMediaViewController();
$control->doActionColumn($item);
$result = ob_get_contents();
ob_end_clean();
}
}
$json = new \stdClass;
$json->$type = new \stdClass;
$json->$type->itemView = $result;
$json->$type->is_optimizable = (false !== $item) ? $item->isProcessable() : false;
$json->$type->is_restorable = (false !== $item) ? $item->isRestorable() : false;
$json->$type->id = $id;
$json->$type->image = [
'width' => $item->get('width'),
'height' => $item->get('height'),
'extension' => $item->getExtension(),
];
$json->$type->results = null;
$json->$type->is_error = false;
$json->status = true;
return $json;
// $this->send($json);
}
public function ajax_processQueue()
{
$this->checkNonce('processing');
$this->checkActionAccess('processQueue', 'is_author');
$this->checkProcessorKey();
ErrorController::start(); // Capture fatal errors for us.
// Notice that POST variables are always string, so 'true', not true.
// phpcs:ignore -- Nonce is checked
$isBulk = (isset($_POST['isBulk']) && $_POST['isBulk'] == 'true') ? true : false;
// phpcs:ignore -- Nonce is checked
$queue = (isset($_POST['queues'])) ? sanitize_text_field($_POST['queues']) : 'media,custom';
$queues = array_filter(explode(',', $queue), 'trim');
$control = new QueueController(['is_bulk' => $isBulk]);
$result = $control->processQueue($queues);
$this->send($result);
}
/** Ajax function to recheck if something can be active. If client is doens't have the processor key, it will check later if the other client is 'done' or went away. */
protected function recheckActive()
{
// If not processor, this ends the processing and sends JSON.
$this->checkProcessorKey();
$json = new \stdClass;
$json->message = __('Became processor', 'shortpixel-image-optimiser');
$json->status = true;
$this->send($json);
}
public function ajaxRequest()
{
$this->checkNonce('ajax_request');
ErrorController::start(); // Capture fatal errors for us.
$this->checkActionAccess('ajax', 'is_author');
// phpcs:ignore -- Nonce is checked
$action = isset($_POST['screen_action']) ? sanitize_text_field($_POST['screen_action']) : false;
// phpcs:ignore -- Nonce is checked
$typeArray = isset($_POST['type']) ? array(sanitize_text_field($_POST['type'])) : array('media', 'custom');
// phpcs:ignore -- Nonce is checked
$id = isset($_POST['id']) ? intval($_POST['id']) : false;
$json = new \stdClass;
foreach ($typeArray as $type) {
$json->$type = new \stdClass;
// $json->$type->id = $id; // This one is off because item_id belongs to results -> itemResult.
$json->$type->results = null;
// $json->$type->is_error = false;
$json->status = false;
}
$data = array('id' => $id, 'typeArray' => $typeArray, 'action' => $action);
if (count($typeArray) == 1) // Actions which need specific type like optimize / restore.
{
$data['type'] = $typeArray[0];
unset($data['typeArray']);
}
// First Item action, alphabet. Second general actions, alpha.
switch ($action) {
case 'cancelOptimize':
$json = $this->cancelOptimize($json, $data);
break;
case 'getItemEditWarning': // Has to do with image editor
$json = $this->getItemEditWarning($json, $data);
break;
case 'getComparerData':
$json = $this->getComparerData($json, $data);
break;
case 'getItemView':
$json = $this->getItemView($json, $data);
break;
case 'markCompleted':
$json = $this->markCompleted($json, $data);
break;
case 'optimizeItem':
$json = $this->optimizeItem($json, $data);
break;
case "redoLegacy":
$this->redoLegacy($json, $data);
break;
case 'restoreItem':
$json = $this->restoreItem($json, $data);
break;
case 'reOptimizeItem':
$json = $this->reOptimizeItem($json, $data);
break;
case 'settings/purgecdncache':
$json = $this->purgeCDNCache($json, $data);
break;
case 'settings/importexport':
$json = $this->importexportSettings($json, $data);
break;
case 'ai/requestalt':
$json = $this->requestAlt($json, $data);
break;
case 'ai/getAltData':
$json = $this->getAltData($json, $data);
break;
case 'ai/undoAlt':
$json = $this->undoAltData($json, $data);
break;
case 'unMarkCompleted':
$json = $this->unMarkCompleted($json, $data);
break;
case 'applyBulkSelection':
$this->checkActionAccess($action, 'is_editor');
$json = $this->applyBulkSelection($json, $data);
break;
case 'createBulk':
$this->checkActionAccess($action, 'is_editor');
$json = $this->createBulk($json, $data);
break;
case 'finishBulk':
$this->checkActionAccess($action, 'is_editor');
$json = $this->finishBulk($json, $data);
break;
case 'startBulk':
$this->checkActionAccess($action, 'is_editor');
$json = $this->startBulk($json, $data);
break;
case 'startRestoreAll':
$this->checkActionAccess($action, 'is_admin_user');
$json = $this->startRestoreAll($json, $data);
break;
case 'startBulkUndoAI':
$this->checkActionAccess($action, 'is_admin_user');
$json = $this->startUndoAI($json, $data);
break;
case 'startMigrateAll':
$this->checkActionAccess($action, 'is_admin_user');
$json = $this->startMigrateAll($json, $data);
break;
case 'startRemoveLegacy':
$this->checkActionAccess($action, 'is_admin_user');
$json = $this->startRemoveLegacy($json, $data);
break;
case "toolsRemoveAll":
$this->checkActionAccess($action, 'is_admin_user');
$json = $this->removeAllData($json, $data);
break;
case "toolsRemoveBackup":
$this->checkActionAccess($action, 'is_admin_user');
$json = $this->removeBackup($json, $data);
break;
case 'request_new_api_key': // @todo Dunnoo why empty, should go if not here.
break;
case "loadLogFile":
$this->checkActionAccess($action, 'is_editor');
$data['logFile'] = isset($_POST['loadFile']) ? sanitize_text_field($_POST['loadFile']) : null;
$json = $this->loadLogFile($json, $data);
break;
case 'refreshFolder':
$this->checkActionAccess($action, 'is_editor');
$json = $this->refreshFolder($json, $data);
break;
// CUSTOM FOLDERS
case 'removeCustomFolder':
$this->checkActionAccess($action, 'is_editor');
$json = $this->removeCustomFolder($json, $data);
break;
case 'browseFolders':
$this->checkActionAccess($action, 'is_editor');
$json = $this->browseFolders($json, $data);
break;
case 'addCustomFolder':
$this->checkActionAccess($action, 'is_editor');
$json = $this->addCustomFolder($json, $data);
break;
case 'scanNextFolder':
$this->checkActionAccess($action, 'is_editor');
$json = $this->scanNextFolder($json, $data);
break;
case 'resetScanFolderChecked':
$this->checkActionAccess($action, 'is_editor');
$json = $this->resetScanFolderChecked($json, $data);
break;
case 'recheckActive':
$this->checkActionAccess($action, 'is_editor');
$json = $this->recheckActive($json, $data);
break;
case 'settings/changemode':
$this->handleChangeMode($data);
break;
case 'settings/getAiExample':
$this->checkActionAccess($action, 'is_admin_user');
$this->getSettingsAiExample($data);
break;
case 'settings/setAiImageId':
$this->checkActionAccess($action, 'is_admin_user');
$this->setSettingsAiImage($data);
break;
case 'settings/getNewAiImagePreview':
$this->getNewAiImagePreview($data);
break;
case 'media/getEditorPopup':
$this->getEditorPopup($data);
break;
case 'media/getEditorPreview':
$this->getEditorPreview($data);
break;
default:
$json->$type->message = __('Ajaxrequest - no action found', 'shorpixel-image-optimiser');
$json->error = self::NO_ACTION;
break;
}
$this->send($json);
}
public function settingsRequest()
{
$this->checkNonce('settings_request');
ErrorController::start(); // Capture fatal errors for us.
$action = isset($_POST['screen_action']) ? sanitize_text_field($_POST['screen_action']) : false;
$this->checkActionAccess($action, 'is_admin_user');
switch ($action) {
case 'form_submit':
case 'action_addkey':
case 'action_debug_redirectBulk':
case 'action_debug_removePrevented':
case 'action_debug_removeProcessorKey':
case 'action_debug_resetNotices':
case 'action_debug_resetQueue':
case 'action_debug_resetquota':
case 'action_debug_resetStats':
case 'action_debug_triggerNotice':
case 'action_request_new_key':
case 'action_debug_editSetting':
case 'action_end_quick_tour':
$this->settingsFormSubmit($action);
break;
default:
Log::addError('Issue with settingsRequest, not valid action');
exit('0');
break;
}
}
protected function settingsFormSubmit($action)
{
$viewController = new SettingsViewController();
$viewController->indicateAjaxSave(); // set ajax save method
$url = isset($_POST['request_url']) ? sanitize_text_field($_POST['request_url']) : null;
if (is_null($url)) {
Log::addError('Ajax : redirect URL not set!');
}
$viewController->setControllerURL($url); // set url for redirects, otherwise set by route / plugin
if (method_exists($viewController, $action)) {
$viewController->$action();
} else {
$viewController->load();
}
exit('ajaxcontroller - formsubmit');
}
protected function getEditorPopup($data)
{
$item_id = intval($_POST['id']);
$mediaItem = $this->getMediaItem($item_id, 'media');
$this->checkImageAccess($mediaItem);
$action_name = isset($_POST['action_name']) ? sanitize_text_field($_POST['action_name']) : 'replace';
$previewImage = UiHelper::findBestPreview($mediaItem, 800);
$json = new \stdClass;
$json->item_id = $item_id;
$post = get_post($item_id);
$originalImage = $mediaItem;
if ($mediaItem->isScaled())
{
$originalImage = $mediaItem->getOriginalFile();
}
$view = new ViewController();
$view->addData([
'previewImage' => $previewImage,
'originalImage' => $originalImage,
'placeholderImage' => \wpSPIO()->plugin_url('res/img/bulk/placeholder.svg'),
'item_id' => $item_id,
'post_title' => $post->post_title,
'action_name' => $action_name,
]
);
$json->popup = $view->returnView('snippets/media-popup');
$json->action_name = $action_name;
$this->send($json);
}
protected function getEditorPreview($data)
{
$item_id = $data['id'];
$is_preview = true; // default to no action
$is_preview = (isset($_POST['is_preview'])) ? filter_var(sanitize_text_field($_POST['is_preview']), FILTER_VALIDATE_BOOL) : $is_preview;
$action_name = isset($_POST['action_name']) ? sanitize_text_field($_POST['action_name']) : 'remove';
$mediaItem = $this->getMediaItem($item_id, 'media');
$this->checkImageAccess($mediaItem);
$qItem = QueueItems::getImageItem($mediaItem);
// General needed:
$opener = isset($_POST['opener']) ? sanitize_text_field($_POST['opener']) : '';
$attached_post_id = isset($_POST['attached_post_id']) ? intval($_POST['attached_post_id']) : 0;
$newFileName = isset($_POST['newFileName']) ? sanitize_file_name($_POST['newFileName']) : false;
$newPostTitle = isset($_POST['newPostTitle']) ? sanitize_text_field($_POST['newPostTitle']) : '';
$refresh = isset($_POST['refresh']) ? filter_var(sanitize_text_field($_POST['refresh']), FILTER_VALIDATE_BOOL) : false;
$args = [
'newFileName' => $newFileName,
'newPostTitle' => $newPostTitle,
'refresh' => $refresh,
'attached_post_id' => $attached_post_id,
];
// For remove background :
if ('remove' === $action_name)
{
$backgroundType = isset($_POST['background_type']) ? sanitize_text_field($_POST['background_type']) : 'transparent';
$backgroundColor = isset($_POST['background_color']) ? sanitize_text_field($_POST['background_color']) : false;
$backgroundTransparency = isset($_POST['background_transparency']) ? sanitize_text_field($_POST['background_transparency']) : '00';
if ('solid' == $backgroundType)
{
$args['replace_color'] = $backgroundColor;
$args['replace_transparency'] = $backgroundTransparency;
$args['do_transparent'] = false;
}
else
{
$args['do_transparent'] = true;
}
$optimizer = $qItem->getApiController('remove_background');
$qItem->newRemoveBackgroundAction(array_merge(['is_preview' => $is_preview], $args));
}
elseif ('scale' == $action_name) // For image scaling:
{
$args['scale'] = isset($_POST['scale']) ? intval($_POST['scale']) : 2;
$optimizer = $qItem->getApiController('scale_image');
$qItem->newScaleImageAction(array_merge(['is_preview' => $is_preview], $args));
}
//$args = [];
/*$args['do_transparent'] = ('transparent' == $backgroundType) ? true : false;
$args['newFileName'] = $newFileName;
$args['newPostTitle'] = $newPostTitle;
$args['refresh'] = $refresh;
$args['attached_post_id'] = $attached_post_id;
*/
$optimizer->sendToProcessing($qItem);
$optimizer->handleAPIResult($qItem);
$result = $qItem->result();
$qItem->data()->tries++;
// $state = 'requestAlt'; // mimic here the double task of the Ai gen.
$is_done = false;
$i = 0;
while (false === $is_done)
{
Log::addTemp('Result', $result);
if (false === property_exists($result, 'is_done') || $result->is_done === false)
{
// Any subsequent request *must* be hard refresh no or it hangs.
$optimizer->sendToProcessing($qItem);
$optimizer->handleAPIResult($qItem);
$qItem->data()->tries++;
$result = $qItem->result();
}
if (property_exists($result, 'is_done') && true === $result->is_done)
{
if ($result->apiStatus === RequestManager::STATUS_SUCCESS && false === $is_preview )
{
$new_attach_id = $qItem->result()->new_attach_id;
if ('edit' == $opener)
{
$redirect = admin_url('post.php?post=' . $new_attach_id . '&action=edit');
}
elseif ('gallery' == $opener)
{
$redirect = admin_url('upload.php?item=' . $new_attach_id);
}
elseif ('gutenberg' == $opener)
{
$redirect = 'gutenberg'; // overload for JS processing
$attachment = get_post($new_attach_id);
$js_attach = wp_prepare_attachment_for_js($attachment);
$qItem->addResult(['file' => $js_attach]);
}
if (isset($redirect))
{
$qItem->addResult([ 'redirect' => $redirect ]);
}
$result = $qItem->result();
}
$this->send($result);
exit();
}
if ($i >= 15) // safeguard.
{
//$this->send((object) $result_json);
$result = [
'is_error' => true,
'is_done' => true,
'message' => __('Limit of attempts exceeded. Possible connection issue. Try again later. ', 'shortpixel-image-optimiser'),
];
Log::addTemp('Timeout 15x');
$this->send((object)$result);
exit('Timeout');
break;
}
sleep(3); // prevent in case of fast connection hammering the API
$i++;
}
}
protected function getMediaItem($id, $type)
{
$fs = \wpSPIO()->filesystem();
return $fs->getImage($id, $type);
}
protected function getItemEditWarning($json, $data)
{
$id = intval($_POST['id']);
$mediaItem = $this->getMediaItem($id, 'media');
$this->checkImageAccess($mediaItem);
if (is_object($mediaItem)) {
$json = new \stdClass;
$json->id = $id;
$json->is_restorable = ($mediaItem->isRestorable()) ? 'true' : 'false';
$json->is_optimized = ($mediaItem->isOptimized()) ? 'true' : 'false';
} else {
}
return $json;
}
/** Adds a single Items to the Single queue */
protected function optimizeItem()
{
$id = intval($_POST['id']);
$type = isset($_POST['type']) ? sanitize_text_field($_POST['type']) : 'media';
$compressionType = isset($_POST['compressionType']) ? sanitize_text_field($_POST['compressionType']) : false;
$flags = isset($_POST['flags']) ? sanitize_text_field($_POST['flags']) : false;
$mediaItem = $this->getMediaItem($id, $type);
$this->checkImageAccess($mediaItem);
// if order is given, remove barrier and file away.
if ($mediaItem->isOptimizePrevented() !== false)
{
$mediaItem->resetPrevent();
}
$control = new QueueController();
$json = new \stdClass;
$json->$type = new \stdClass;
$args = [];
if ('force' === $flags) {
$args['forceExclusion'] = true;
}
if (false !== $compressionType)
{
$args['compressionType'] = $compressionType;
}
$json->$type->results = [$control->addItemToQueue($mediaItem, $args)];
$json->$type->qstatus = $control->getLastQueueStatus();
return $json;
}
protected function purgeCDNCache($json, $data)
{
$this->checkActionAccess('purge', 'is_admin_user');
$purge = isset($_POST['purge']) ? sanitize_text_field($_POST['purge']) : 'cssjs';
$CDNController = new \ShortPixel\Controller\Front\CDNController();
$result = $CDNController->purgeCDN(['purge' => $purge]);
$json->settings->results = $result;
$json->status = true;
return $json;
}
protected function importexportSettings($json, $data)
{
$action = (isset($_POST['actionType'])) ? sanitize_text_field($_POST['actionType']) : 'export';
$this->checkActionAccess($action, 'is_admin_user');
$settings = \wpSPIO()->settings();
if ('import' === $action)
{
$importdata = (isset($_POST['importData'])) ? sanitize_text_field(trim($_POST['importData'])) : false;
$importdata = stripslashes($importdata);
$importdata = trim($importdata);
if (false === $importdata || 0 == strlen($importdata))
{
$json->settings->results = ['is_error' => true, 'message' => __('Import contained empty field', 'shortpixel-image-optimiser')];
}
elseif (true === UtilHelper::validateJson($importdata) )
{
//$result = ['is_error' => false];
$messages = [];
$importjson = json_decode($importdata, true);
Log::addInfo('JSON Import: ', $importjson);
$counter = 0;
foreach($importjson as $name => $value )
{
if (false === $settings->exists($name))
{
$messages[] = sprintf(__('Field with name %s does not exist in current version', 'shortpixel-image-optimiser'), $name);
}
else
{
$settings->$name = $value;
$counter++;
}
}
$messages[] = sprintf(__('%s settings imported! Reload page to see changes', 'shortpixel-image-optimiser'), $counter);
$json->settings->results = ['is_error' => false, 'messages' => $messages];
}
else
{
$json->settings->results = ['is_error' => true,
'message' => sprintf(__('Invalid JSON sent: %s', 'shortpixel-image-optimiser'), json_last_error_msg())];
}
}
else
{
$data = $settings->getExport();
$json->settings->exportData = json_encode($data);
$json->settings->message = __('Export completed. Copy the string below', 'shortpixel-image-optimiser');
}
$json->status = true;
return $json;
}
protected function markCompleted($json, $data)
{
$id = intval($_POST['id']);
$type = isset($_POST['type']) ? sanitize_text_field($_POST['type']) : 'media';
$imageModel = $this->getMediaItem($id, $type);
$this->checkImageAccess($imageModel);
$imageModel->markCompleted(__('This item has been manually marked as completed', 'shortpixel-image-optimiser'), ImageModel::FILE_STATUS_MARKED_DONE);
$qItem = QueueItems::getImageItem($imageModel);
$qItem->addResult([
'fileStatus' => ImageModel::FILE_STATUS_SUCCESS,
'item_id' => $id,
'message' => __('Item marked as completed', 'shortpixel-image-optimiser'),
'is_done' => true,
'is_error' => false,
]);
/* $json->$type->fileStatus = ImageModel::FILE_STATUS_SUCCESS; // great success!
$json->$type->result = new \stdClass;
$json->$type->result->item_id = $id;
$json->$type->result->message = __('Item marked as completed', 'shortpixel-image-optimiser');
$json->$type->result->is_done = true;
$json->$type->result->is_error = false;
*/
$json->$type->results = [$qItem->result()];
$json->status = true;
return $json;
}
protected function unMarkCompleted($json, $data)
{
$id = intval($_POST['id']);
$type = isset($_POST['type']) ? sanitize_text_field($_POST['type']) : 'media';
$imageModel = $this->getMediaItem($id, $type);
$this->checkImageAccess($imageModel);
$imageModel->resetPrevent();
$qItem = QueueItems::getImageItem($imageModel);
$qItem->addResult([
'fileStatus' => ImageModel::FILE_STATUS_SUCCESS,
'item_id' => $id,
'message' => __('Item unmarked', 'shortpixel-image-optimiser'),
'is_done' => true,
'is_error' => false,
]);
$json->$type->results = [$qItem->result()];
$json->status = true;
return $json;
}
protected function cancelOptimize($json, $data)
{
$id = intval($_POST['id']);
$type = isset($_POST['type']) ? sanitize_text_field($_POST['type']) : 'media';
$mediaItem = $this->getMediaItem($id, $type);
$this->checkImageAccess($mediaItem);
$mediaItem->dropFromQueue();
$qItem = QueueItems::getImageItem($mediaItem);
$qItem->addResult([
'fileStatus' => ImageModel::FILE_STATUS_SUCCESS,
'item_id' => $id,
'message' => __('Item removed from queue', 'shortpixel-image-optimiser'),
'is_done' => true,
'is_error' => false,
]);
$json->$type->results = [$qItem->result()];
$json->status = true;
return $json;
}
/* Integration for WP /LR Sync plugin - https://meowapps.com/plugin/wplr-sync/
* @integration WP / LR Sync
*
*/
public function onWpLrUpdateMedia($imageId)
{
// Get and remove Meta
$mediaItem = \wpSPIO()->filesystem()->getImage($imageId, 'media');
$mediaItem->onDelete();
// Flush and reaquire image to make sure it doesn't stay previous state.
\wpSPIO()->filesystem()->flushImage($mediaItem);
$mediaItem = \wpSPIO()->filesystem()->getImage($imageId, 'media', false);
// Optimize
$control = new QueueController();
$json = $control->addItemToQueue($mediaItem);
return $json;
}
// @param Row of something in llr_sync table. This changed
public function onWpLrSyncMedia($row)
{
$attachment_id = $row->wp_id;
return $this->onWpLrUpdateMedia($attachment_id);
}
protected function restoreItem($json, $data)
{
$id = $data['id'];
$type = $data['type'];
$imageModel = $this->getMediaItem($id, $type);
$this->checkImageAccess($imageModel);
$queueController = new QueueController();
$result = $queueController->addItemToQueue($imageModel, ['action' => 'restore']);
$json->$type->results = [$result];
$json->status = true;
return $json;
}
protected function reOptimizeItem($json, $data)
{
$id = $data['id'];
$type = $data['type'];
$compressionType = isset($_POST['compressionType']) ? intval($_POST['compressionType']) : 0;
$actionType = isset($_POST['actionType']) ? intval($_POST['actionType']) : null;
$imageModel = $this->getMediaItem($id, $type);
$this->checkImageAccess($imageModel);
$args = ['action' => 'reoptimize', 'compressionType' => $compressionType];
// Smartcrop is not always passed, only add here when passed otherwise to defaults.
if ($actionType == ImageModel::ACTION_SMARTCROP || $actionType == ImageModel::ACTION_SMARTCROPLESS)
{
$args['smartcrop'] = $actionType;
}
// @todo Ideally this should go to QueueController - addItemToQueue, but issue with arguments. Leaving it for now.
$queueController = new QueueController();
$result = $queueController->addItemToQueue($imageModel, $args);
$json->$type->results = [$result];
$json->$type->qstatus = $queueController->getLastQueueStatus();
$json->status = true;
return $json;
}
protected function requestAlt($json, $data)
{
$id = $data['id'];
$type = $data['type'];
$preview_only = isset($_POST['preview_only']) ? true : false;
$imageModel = $this->getMediaItem($id, $type);
$queueController = new QueueController();
$args = [
'action' => 'requestAlt',
];
if (true === $preview_only)
{
$args['preview_only'] = true;
}
$result = $queueController->addItemToQueue($imageModel, $args);
$result->apiName = 'ai'; // prevent response leaking to media interface.
$json->$type->results = [$result];
$json->$type->qstatus = $queueController->getLastQueueStatus();
$json->status = true;
return $json;
}
protected function getAltData($json, $data)
{
$id = $data['id'];
$type = $data['type'];
$imageModel = $this->getMediaItem($id, $type);
$this->checkImageAccess($imageModel);
$queueItem = new QueueItem(['imageModel' => $imageModel]);
$queueItem->getAltDataAction();
$api = $queueItem->getApiController('getAltData');
$metadata = $api->getAltData($queueItem);
$json->$type = (object) $metadata;
$json->$type->results = null;
$json->status = true;
return $json;
}
protected function undoAltData($json, $data)
{
$id = $data['id'];
$type = $data['type'];
// undo or redo
$action_type = isset($_POST['action_type']) ? sanitize_text_field($_POST['action_type']) : 'undo';
$imageModel = $this->getMediaItem($id, $type);
$this->checkImageAccess($imageModel);
// @todo Should e.v be moved to QItem hop.
/*$queueController = new QueueController();
$action = ('redo' == $action_type) ? 'redoAI' : 'undoAI';
$result = $queueController->addItemToQueue($imageModel, ['action' => $action]);
*/
$queueItem = new QueueItem(['imageModel' => $imageModel]);
$queueItem->getAltDataAction();
$api = $queueItem->getApiController('getAltData');
$altData = $api->undoAltData($queueItem);
if ('redo' == $action_type)
{
return $this->requestAlt($json, $data);
}
$json->$type = $altData;
$json->status = true;
return $json;
}
protected function finishBulk($json, $data)
{
$bulkControl = BulkController::getInstance();
if (false !== $bulkControl->getAnyCustomOperation()) {
$json->redirect = add_query_arg(['page' => 'wp-shortpixel-settings', 'part' => 'tools'], admin_url('options-general.php'));
}
$bulkControl->finishBulk('media');
$bulkControl->finishBulk('custom');
$json->status = 1;
return $json;
}
protected function createBulk($json, $data)
{
$filters = [];
$has_filters = false;
if (isset($_POST['filter_startdate']))
{
$filters['start_date'] = sanitize_text_field($_POST['filter_startdate']);
$has_filters = true;
}
if (isset($_POST['filter_enddate']))
{
$filters['end_date'] = sanitize_text_field($_POST['filter_enddate']);
$has_filters = true;
}
$args = [];
if (true === $has_filters)
{
$args['filters'] = $filters;
Log::addTemp('Queue starting with filters: ', $filters);
}
$bulkControl = BulkController::getInstance();
// This is where the settings start to break and double. This info is also needs inside the process.
$doMedia = filter_var(sanitize_text_field($_POST['mediaActive']), FILTER_VALIDATE_BOOLEAN);
$doAi = filter_var(sanitize_text_field($_POST['aiActive']), FILTER_VALIDATE_BOOLEAN);
$mediaArgs = array_merge($args, ['doMedia' => $doMedia, 'doAi' => $doAi]);
$stats = $bulkControl->createNewBulk('media', $mediaArgs);
$json->media->stats = $stats;
$stats = $bulkControl->createNewBulk('custom', $args);
$json->custom->stats = $stats;
$json = $this->applyBulkSelection($json, $data);
return $json;
}
protected function applyBulkSelection($json, $data)
{
// These values should always be given!
$doMedia = filter_var(sanitize_text_field($_POST['mediaActive']), FILTER_VALIDATE_BOOLEAN);
$doCustom = filter_var(sanitize_text_field($_POST['customActive']), FILTER_VALIDATE_BOOLEAN);
$doWebp = filter_var(sanitize_text_field($_POST['webpActive']), FILTER_VALIDATE_BOOLEAN);
$doAvif = filter_var(sanitize_text_field($_POST['avifActive']), FILTER_VALIDATE_BOOLEAN);
$doAi = filter_var(sanitize_text_field($_POST['aiActive']), FILTER_VALIDATE_BOOLEAN);
$aiPreserve = isset($_POST['aiPreserve']) ? filter_var(sanitize_text_field($_POST['aiPreserve']), FILTER_VALIDATE_BOOLEAN) : null;
$backgroundProcess = filter_var(sanitize_text_field($_POST['backgroundProcess']), FILTER_VALIDATE_BOOLEAN);
// Can be hidden
if (isset($_POST['thumbsActive'])) {
$doThumbs = filter_var(sanitize_text_field($_POST['thumbsActive']), FILTER_VALIDATE_BOOLEAN);
\wpSPIO()->settings()->processThumbnails = $doThumbs;
}
\wpSPIO()->settings()->createWebp = $doWebp;
\wpSPIO()->settings()->createAvif = $doAvif;
\wpSPIO()->settings()->doBackgroundProcess = $backgroundProcess;
\wpSPIO()->settings()->autoAIBulk = $doAi;
if (false === is_null($aiPreserve))
{
\wpSPIO()->settings()->aiPreserve = $aiPreserve;
}
$bulkControl = BulkController::getInstance();
if (! $doMedia && ! $doAi) {
$bulkControl->finishBulk('media');
}
if (! $doCustom) {
$bulkControl->finishBulk('custom');
}
$queueController = new QueueController(['is_bulk' => true]);
$data = $queueController->getStartupData();
$json->media->stats = $data->media->stats;
$json->custom->stats = $data->custom->stats;
$json->total = $data->total;
$json->status = true;
return $json;
}
protected function startBulk($json, $data)
{
$bulkControl = BulkController::getInstance();
$types = array('media', 'custom');
$result = $bulkControl->startBulk($types);
$this->send($result);
}
protected function startRestoreAll($json, $data)
{
$bulkControl = BulkController::getInstance();
QueueController::resetQueues(); // prevent any weirdness
$queue = (isset($_POST['queues'])) ? sanitize_text_field($_POST['queues']) : false;
if ($queue === false) // safety first.
return $json;
$queues = array_filter(explode(',', $queue), 'trim');
if (in_array('media', $queues)) {
$stats = $bulkControl->createNewBulk('media', ['customOp' => 'bulk-restore']);
$json->media->stats = $stats;
}
if (in_array('custom', $queues)) {
$stats = $bulkControl->createNewBulk('custom', ['customOp' => 'bulk-restore']);
$json->custom->stats = $stats;
}
return $json;
}
protected function startUndoAI($json, $data)
{
$bulkControl = BulkController::getInstance();
QueueController::resetQueues(); // prevent any weirdness
$stats = $bulkControl->createNewBulk('media', ['customOp' => 'bulk-undoAI']);
$json->media->stats = $stats;
return $json;
}
protected function startMigrateAll($json, $data)
{
$bulkControl = BulkController::getInstance();
QueueController::resetQueues(); // prevent any weirdness
$stats = $bulkControl->createNewBulk('media', ['customOp' => 'migrate']);
$json->media->stats = $stats;
return $json;
}
protected function startRemoveLegacy($json, $data)
{
$bulkControl = BulkController::getInstance();
QueueController::resetQueues(); // prevent any weirdness
$stats = $bulkControl->createNewBulk('media', ['customOp' => 'removeLegacy']);
$json->media->stats = $stats;
return $json;
}
protected function redoLegacy($json, $data)
{
$id = $data['id'];
$type = $data['type'];
$mediaItem = $this->getMediaItem($id, $type);
$this->checkImageAccess($mediaItem);
// Changed since updated function should detect what is what.
$mediaItem->migrate();
$json->status = true;
$json->media->id = $id;
$json->media->type = 'media';
$this->send($json);
}
protected function handleChangeMode($data)
{
$user_id = get_current_user_id();
$new_mode = isset($_POST['new_mode']) ? sanitize_text_field($_POST['new_mode']) : false;
if (false === $new_mode) {
return false;
}
update_user_option($user_id, 'shortpixel-settings-mode', $new_mode);
}
protected function getNewAiImagePreview($data)
{
$item_id = $data['id'];
$settingsData = isset($_POST['settingsData']) ? $_POST['settingsData'] : null;
if (! is_null($settingsData))
{
$json = json_decode(stripslashes($settingsData), true);
$settings = \wpSPIO()->settings();
//$settingsData = array_map('sanitize_text_field', $json);
$settingsData = $settings->getSanitizedData($json, false);
}
else
{
$settingsData = []; // null - empty array
}
$result_json = [
'error' => __('Something went wrong', 'shortpixel-image-optimiser'),
'is_error' => true,
];
$imageModel = \wpSPIO()->filesystem()->getMediaImage($item_id);
if (false === $imageModel)
{
$result_json['message'] = __('This image could not be loaded', 'shortpixel-image-optimiser');
$this->send((object) $result_json);
}
$qItem = QueueItems::getImageItem($imageModel);
$optimizer = $qItem->getApiController('requestAlt');
$qItem->requestAltAction(array_merge(['preview_only' => true], $settingsData));
$optimizer->sendToProcessing($qItem);
$result = $qItem->result();
$state = 'requestAlt'; // mimic here the double task of the Ai gen.
$is_done = false;
$i = 0;
while (false === $is_done)
{
if (false === property_exists($result, 'is_done') || $result->is_done === false)
{
$optimizer->sendToProcessing($qItem);
$result = $qItem->result();
}
if (property_exists($result, 'is_done') && true === $result->is_done)
{
// If is done and is error, bail out.
if (true === $result->is_error)
{
$this->send($result);
}
if ('requestAlt' === $state)
{
$remote_id = $result->remote_id;
$result = $optimizer->enqueueItem($qItem, ['preview_only' => true, 'action' => 'retrieveAlt', 'remote_id' => $remote_id]);
$state = 'retrieveAlt';
}
if ('retrieveAlt' === $state)
{
Log::addTemp('Result', $result);
if (property_exists($result, 'aiData'))
{
$aiModel = AiDataModel::getModelByAttachment($qItem->item_id, 'media');
$aiData = $optimizer->formatResultData($result->aiData, $qItem);
list($items, $aiData) = $optimizer->formatGenerated($aiData, $aiModel->getCurrentData(), $aiModel->getOriginalData(), true);
$aiData['item_id'] = $qItem->item_id;
$aiData['time_generated'] = time();
set_transient('spio_settings_ai_example', $aiData, MONTH_IN_SECONDS);
set_transient('spio_settings_ai_example_id', $qItem->item_id, MONTH_IN_SECONDS);
$aiData['aiData'] = true; // for the JS check
$this->send((object) $aiData);
$is_done = true;
break; // safe guards.
}
if ($result->is_done)
{
$this->send($result);
break;
}
}
}
if ('retrieveAlt' === $state)
{
sleep(2); // prevent in case of fast connection hammering the API
}
if ($i >= 30) // safeguard.
{
$this->send((object) $result_json);
break;
}
$i++;
}
}
protected function getSettingsAiExample($data)
{
$id = get_transient('spio_settings_ai_example_id');
if (false === $id || ! is_numeric($id))
{
$item = AiDataModel::getMostRecent();
$attach_id = $item->getAttachId();
}
else
{
$item = AiDataModel::getModelByAttachment($id);
$attach_id = $id;
}
$imageModel = \wpSPIO()->fileSystem()->getMediaImage($attach_id);
if (is_null($attach_id) || false === $imageModel)
{
// make something up
$json = [
'preview_image' => '',
'item_id' => -1,
'generated' => ['alt' => __('Select an image for example', 'shortpixel-image-optimser')],
'original' => [],
];
$this->send((object) $json);
}
else
{
$transient = get_transient('spio_settings_ai_example');
if (is_array($transient) && $transient['item_id'] == $id)
{
$generated = $transient;
}
else
{
$generated = $item->getGeneratedData();
}
if ($item->isSomeThingGenerated())
{
$original = $item->getOriginalData();
}
else
{
$original = $item->getCurrentData();
}
}
$json = [
'preview_image' => UiHelper::findBestPreview($imageModel)->getURL(),
'item_id' => $attach_id,
'generated' => $generated,
'original' => $original,
];
$this->send((object) $json);
}
protected function setSettingsAiImage($data)
{
$id = $data['id'];
set_transient('spio_settings_ai_example_id', $id, MONTH_IN_SECONDS);
return $this->getSettingsAiExample($data);
}
/** Data for the compare function */
protected function getComparerData($json, $data)
{
$type = isset($_POST['type']) ? sanitize_text_field($_POST['type']) : 'media';
$id = isset($_POST['id']) ? intval($_POST['id']) : false;
if ($id === false || !current_user_can('upload_files') && !current_user_can('edit_posts')) {
$json->status = false;
$json->id = $id;
$json->message = __('Error - item to compare could not be found or no access', 'shortpixel-image-optimiser');
$this->send($json);
}
$ret = array();
$fs = \wpSPIO()->filesystem();
$imageObj = $fs->getImage($id, $type);
$this->checkImageAccess($imageObj);
if (false === $imageObj->isOptimized())
{
$imageObj = $imageObj->getSomethingOptimized();
}
// With PDF, the thumbnail called 'full' is the image, the main is the PDF file
if ($imageObj->getExtension() == 'pdf') {
$thumbImg = $imageObj->getThumbnail('full');
if ($thumbImg !== false) {
$imageObj = $thumbImg;
}
}
$backupFile = $imageObj->getBackupFile();
if (is_object($backupFile))
$backup_url = $fs->pathToUrl($backupFile);
else
$backup_url = '';
$ret['origUrl'] = $backup_url; // $backupUrl . $urlBkPath . $meta->getName();
$ret['optUrl'] = $imageObj->getURL();
$ret['width'] = $imageObj->getMeta('originalWidth');
$ret['height'] = $imageObj->getMeta('originalHeight');
if (is_null($ret['width']) || $ret['width'] == false) {
if (! $imageObj->is_virtual()) {
$ret['width'] = $imageObj->get('width');
$ret['height'] = $imageObj->get('height');
} else {
$size = getimagesize($backupFile->getFullPath());
if (is_array($size)) {
$ret['width'] = $size[0];
$ret['height'] = $size[1];
}
}
}
$this->send((object) $ret);
}
protected function refreshFolder($json, $data)
{
$otherMediaController = OtherMediaController::getInstance();
$folder_id = isset($_POST['id']) ? intval($_POST['id']) : false;
$json->folder->message = '';
if (false === $folder_id) {
$json->folder->is_error = true;
$json->folder->message = __('An error has occured: no folder id', 'shortpixel-image-optimiser');
}
$folderObj = $otherMediaController->getFolderByID($folder_id);
if (false === $folderObj) {
$json->folder->is_error = true;
$json->folder->message = __('An error has occured: no folder object', 'shortpixel-image-optimiser');
}
$result = $folderObj->refreshFolder(true);
if (false === $result) {
$json->folder->message = $folderObj->get('last_message');
} else { // result is stats
$stats = $result;
if ($stats['new'] > 0) {
$message = sprintf(__('%s new files found ( %s waiting %s optimized)', 'shortpixel-image-optimiser'), $stats['new'], $stats['waiting'], $stats['optimized']);
} else {
$message = sprintf(__('No new files found ( %s waiting %s optimized)', 'shortpixel-image-optimiser'), $stats['waiting'], $stats['optimized']);
}
$json->folder->message = $message;
}
$json->status = true;
$json->folder->fileCount = $folderObj->get('fileCount');
$json->folder->action = 'refresh';
$json->folder->updated = UiHelper::formatTS($folderObj->get('updated'));
$json->folder->id = $folder_id;
return $json;
}
protected function removeCustomFolder($json, $data)
{
$folder_id = isset($_POST['id']) ? intval($_POST['id']) : false;
$otherMedia = OtherMediaController::getInstance();
$dirObj = $otherMedia->getFolderByID($folder_id);
if ($dirObj === false) {
$json->folder->is_error = true;
$json->folder->message = __('An error has occured: no folder object', 'shortpixel-image-optimiser');
return;
}
$dirObj->delete();
$json->status = true;
$json->folder->message = __('Folder has been removed', 'shortpixel-image-optimiser');
$json->folder->is_done = true;
$json->folder->action = 'remove';
$json->folder->id = $folder_id;
return $json;
}
protected function addCustomFolder($json, $data)
{
$relpath = isset($_POST['relpath']) ? sanitize_text_field($_POST['relpath']) : null;
$fs = \wpSPIO()->filesystem();
$customFolderBase = $fs->getWPFileBase();
$basePath = $customFolderBase->getPath();
$path = trailingslashit($basePath) . $relpath;
$otherMedia = OtherMediaController::getInstance();
// Result is a folder object
$result = $otherMedia->addDirectory($path);
if (false === $result) {
$json->folder->is_error = true;
$json->folder->message = __('Failed to add Folder', 'shortpixel-image-optimiser');
} else {
$control = new OtherMediaFolderViewController();
$itemView = $control->singleItemView($result);
$json->folder->result = new \stdClass;
$json->folder->result->id = $result->get('id');
$json->folder->result->itemView = $itemView;
}
$noticeController = Notices::getInstance();
$json->notices = $noticeController->getNewNotices();
if (count($json->notices) > 0) {
$json->display_notices = [];
foreach ($json->notices as $notice) {
$json->display_notices[] = $notice->getForDisplay(['class' => 'is_ajax', 'is_removable' => false]);
}
}
$noticeController->update(); // dismiss one-time ponies
return $json;
}
protected function browseFolders($json, $data)
{
$relpath = isset($_POST['relPath']) ? sanitize_text_field($_POST['relPath']) : '';
$otherMediaController = OtherMediaController::getInstance();
$folders = $otherMediaController->browseFolder($relpath);
if (isset($folders['is_error']) && true == $folders['is_error']) {
$json->folder->is_error = true;
$json->folder->message = $folders['message'];
$folders = array();
}
$json->folder->folders = $folders;
$json->folder->relpath = $relpath;
$json->status = true;
return $json;
}
protected function resetScanFolderChecked($json, $data)
{
$otherMediaController = OtherMediaController::getInstance();
$otherMediaController->resetCheckedTimestamps();
return $json;
}
protected function scanNextFolder($json, $data)
{
$otherMediaController = OtherMediaController::getInstance();
$force = isset($_POST['force']) ? sanitize_text_field($_POST['force']) : null;
$args = array();
$args['force'] = $force;
$result = $otherMediaController->doNextRefreshableFolder($args);
if ($result === false) {
$json->folder->is_done = true;
$json->folder->result = new \stdClass;
$json->folder->result->message = __('All Folders have been scanned!', 'shortpixel_image_optimiser');
} else {
$json->folder->result = $result;
}
return $json;
}
/*
public function ajax_getBackupFolderSize()
{
$this->checkNonce('ajax_request');
$this->checkActionAccess($action, 'is_editor');
$dirObj = \wpSPIO()->filesystem()->getDirectory(SHORTPIXEL_BACKUP_FOLDER);
$size = $dirObj->getFolderSize();
echo UiHelper::formatBytes($size);
exit();
}
*/
public function ajax_proposeQuotaUpgrade()
{
$this->checkNonce('ajax_request');
$this->checkActionAccess('propose_upgrade', 'is_editor');
$notices = AdminNoticesController::getInstance();
$notices->proposeUpgradeRemote();
exit();
}
public function ajax_checkquota()
{
$this->checkNonce('ajax_request');
$action = 'check_quota';
$this->checkActionAccess($action, 'is_author');
$quotaController = QuotaController::getInstance();
$quotaController->forceCheckRemoteQuota();
$quota = $quotaController->getQuota();
$settings = \wpSPIO()->settings();
$sendback = wp_get_referer();
// sanitize the referring webpage location
$sendback = preg_replace('|[^a-z0-9-~+_.?#=&;,/:]|i', '', $sendback);
$result = array('status' => 'no-quota', 'redirect' => $sendback);
if (! $settings->quotaExceeded) {
$result['status'] = 'has-quota';
} else {
Notices::addWarning(__('You have no available image credits. If you just bought a package, please note that sometimes it takes a few minutes for the payment processor to send us the payment confirmation.', 'shortpixel-image-optimiser'));
}
wp_send_json($result);
}
protected function loadLogFile($json, $data)
{
$logFile = $data['logFile'] . '.log';
$type = $data['type'];
$fs = \wpSPIO()->filesystem();
if (is_null($logFile)) {
$json->$type->is_error = true;
$json->$type->result = __('Could not load log file', 'shortpixel-image-optimiser');
return $json;
}
$bulkController = BulkController::getInstance();
$log = $bulkController->getLog($logFile);
$logData = $bulkController->getLogData($logFile);
$logType = $logData['type']; // custom or media.
$json->$type->logType = $logType;
if (false === $log) {
$json->$type->is_error = true;
$json->$type->result = __('Log file does not exist', 'shortpixel-image-optimiser');
return $json;
}
$date = (isset($logData['date'])) ? UiHelper::formatTS($logData['date']) : false;
$content = trim($log->getContents());
$lines = array_filter(explode(';', $content));
$headers = [
__('Time', 'shortpixel-image-optimiser'),
__('Filename', 'shortpixel-image-optimiser'),
__('ID', 'shortpixel-image-optimiser'),
__('Error', 'shortpixel-image-optimiser'),
];
if ('custom' == $logType) {
array_splice($headers, 3, 0, __('Info', 'shortpixel-image-optimiser'));
}
foreach ($lines as $index => $line) {
$cells = array_filter(explode('|', $line));
$date = $cells[0];
$filename = $cells[1];
$id = isset($cells[2]) ? $cells[2] : false;
$error = isset($cells[3]) ? $cells[3] : false;
$line = ['date' => $date, 'filename' => $filename, 'id' => $id, 'error' => $error];
if ($id !== false && $logType !== 'custom') {
// replaces the image id with a link to image.
$line['link'] = esc_url(admin_url('post.php?post=' . trim($id) . '&action=edit'));
} elseif ($logType === 'custom') {
$base = esc_url(admin_url('upload.php?page=wp-short-pixel-custom'));
$line['link'] = add_query_arg('s', sanitize_text_field($filename), $base);
}
if ($error !== false) {
$line['kblink'] = UiHelper::getKBSearchLink($error);
}
if ('custom' == $logType && $id !== false) {
$imageObj = $fs->getImage($id, 'custom');
if (is_object($imageObj)) {
$dir = $imageObj->getFileDir();
if (is_object($dir)) {
$path = $dir->getRelativePath();
$line['path'] = $path;
}
}
}
$lines[$index] = $line;
}
$lines = array_values(array_filter($lines));
array_unshift($lines, $headers);
$json->$type->title = sprintf(__('Bulk ran on %s', 'shortpixel-image-optimiser'), $date);
$json->$type->results = $lines;
return $json;
}
protected function checkNonce($action)
{
if (! wp_verify_nonce($_POST['nonce'], $action)) {
$id = isset($_POST['id']) ? intval($_POST['id']) : false;
$action = isset($_POST['screen_action']) ? sanitize_text_field($_POST['screen_action']) : false;
$json = new \stdClass;
$json->message = __('Nonce is missing or wrong - Try to refresh the page', 'shortpixel-image-optimiser');
$json->item_id = $id;
$json->action = $action;
$json->status = false;
$json->error = self::NONCE_FAILED;
$this->send($json);
exit();
}
}
protected function checkActionAccess($action, $access)
{
$accessModel = AccessModel::getInstance();
$bool = $accessModel->userIsAllowed($access);
if ($bool === false) {
$json = new \stdClass;
$json->message = __('This user is not allowed to perform this action', 'shortpixel-image-optimiser');
$json->action = $action;
$json->status = false;
$json->error = self::NO_ACCESS;
$this->send($json);
exit();
}
return true;
}
protected function checkImageAccess($mediaItem)
{
// defaults
$message = __('This user is not allowed to edit this image', 'shortpixel-image-optimiser');
$accessModel = AccessModel::getInstance();
if (is_object($mediaItem)) {
$bool = $accessModel->imageIsEditable($mediaItem);
$id = $mediaItem->get('id');
} else {
$bool = false;
$id = false;
if (! is_object($mediaItem))
{
$message = __('Image does not exist or could not be loaded', 'shortpixel-image-optimiser');
}
}
if ($bool === false) {
$json = new \stdClass;
$json->message = $message;
$json->status = false;
$json->id = $id;
$json->error = self::NO_ACCESS;
$this->send($json);
exit();
}
return true;
}
protected function send($json)
{
$callback = isset($_POST['callback']) ? sanitize_text_field($_POST['callback']) : false;
if ($callback)
$json->callback = $callback; // which type of request we just fullfilled ( response processing )
$pKey = $this->getProcessorKey();
if ($pKey !== false)
$json->processorKey = $pKey;
wp_send_json($json);
exit();
}
private function removeAllData($json, $data)
{
if (1 === wp_verify_nonce($_POST['tools-nonce'], 'remove-all')) {
InstallHelper::hardUninstall();
$json->settings->results = __('All Data has been removed. The plugin has been deactivated', 'shortpixel-image-optimiser');
} else {
Log::addError('RemoveAll detected with wrong nonce');
}
$json->settings->redirect = admin_url('plugins.php');
return $json;
}
private function removeBackup($json, $data)
{
if (wp_verify_nonce($_POST['tools-nonce'], 'empty-backup')) {
$fs = \wpSPIO()->filesystem();
$fs->moveLogFiles();
$dir = \wpSPIO()->filesystem()->getDirectory(SHORTPIXEL_BACKUP_FOLDER);
$dir->recursiveDelete();
$fs->moveLogFiles(['to_temp' => false]);
$json->settings->results = __('The backups have been removed. You can close the window', 'shortpixel-image-optimiser');
} else {
$json->settings->results = __('Error: Invalid Nonce in empty backups', 'shortpixel-image-optimiser');
}
return $json;
}
}