364 lines
14 KiB
PHP
364 lines
14 KiB
PHP
<?php
|
|
namespace ShortPixel\Controller;
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit; // Exit if accessed directly.
|
|
}
|
|
|
|
use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
|
|
|
|
class QuotaController
|
|
{
|
|
protected static $instance;
|
|
const CACHE_NAME = 'quotaData';
|
|
|
|
protected $quotaData;
|
|
|
|
/** Singleton instance
|
|
* @return Object QuotaController object
|
|
*/
|
|
public static function getInstance()
|
|
{
|
|
if (is_null(self::$instance))
|
|
self::$instance = new QuotaController();
|
|
|
|
return self::$instance;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return if account has any quota left
|
|
* @return boolean Has quota left
|
|
*/
|
|
public function hasQuota()
|
|
{
|
|
$settings = \wpSPIO()->settings();
|
|
|
|
if ($settings->quotaExceeded)
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
|
|
}
|
|
|
|
/**
|
|
* Retrieves QuotaData object from cache or from remote source
|
|
* @return array The quotadata array (remote format)
|
|
*/
|
|
protected function getQuotaData()
|
|
{
|
|
if (! is_null($this->quotaData))
|
|
return $this->quotaData;
|
|
|
|
$cache = new CacheController();
|
|
|
|
$cacheData = $cache->getItem(self::CACHE_NAME);
|
|
|
|
if (! $cacheData->exists() )
|
|
{
|
|
$quotaData = $this->getRemoteQuota();
|
|
if (! $this->hasQuota())
|
|
$timeout = MINUTE_IN_SECONDS;
|
|
else {
|
|
$timeout = HOUR_IN_SECONDS;
|
|
}
|
|
$cache->storeItem(self::CACHE_NAME, $quotaData, $timeout);
|
|
}
|
|
else
|
|
{
|
|
$quotaData = $cacheData->getValue();
|
|
}
|
|
|
|
return $quotaData;
|
|
}
|
|
|
|
/**
|
|
* Retrieve quota information for this account
|
|
* @return object quotadata SPIO format
|
|
*/
|
|
public function getQuota()
|
|
{
|
|
|
|
$quotaData = $this->getQuotaData();
|
|
$DateNow = time();
|
|
|
|
$DateSubscription = strtotime($quotaData['APILastRenewalDate']);
|
|
$DaysToReset = 30 - ( (int) ( ( $DateNow - $DateSubscription) / 84600) % 30);
|
|
|
|
$quota = (object) [
|
|
'unlimited' => isset($quotaData['Unlimited']) ? $quotaData['Unlimited'] : false,
|
|
'monthly' => (object) [
|
|
'text' => sprintf(__('%s/month', 'shortpixel-image-optimiser'), $quotaData['APICallsQuota']),
|
|
'total' => $quotaData['APICallsQuotaNumeric'],
|
|
'consumed' => $quotaData['APICallsMadeNumeric'],
|
|
'remaining' => max($quotaData['APICallsQuotaNumeric'] - $quotaData['APICallsMadeNumeric'], 0),
|
|
'renew' => $DaysToReset,
|
|
],
|
|
'onetime' => (object) [
|
|
'text' => $quotaData['APICallsQuotaOneTime'],
|
|
'total' => $quotaData['APICallsQuotaOneTimeNumeric'],
|
|
'consumed' => $quotaData['APICallsMadeOneTimeNumeric'],
|
|
'remaining' => $quotaData['APICallsQuotaOneTimeNumeric'] - $quotaData['APICallsMadeOneTimeNumeric'],
|
|
],
|
|
];
|
|
|
|
$quota->total = (object) [
|
|
'total' => $quota->monthly->total + $quota->onetime->total,
|
|
'consumed' => $quota->monthly->consumed + $quota->onetime->consumed,
|
|
'remaining' =>$quota->monthly->remaining + $quota->onetime->remaining,
|
|
];
|
|
|
|
|
|
return $quota;
|
|
}
|
|
|
|
/**
|
|
* Get available remaining - total - credits
|
|
* @return int Total remaining credits
|
|
*/
|
|
public function getAvailableQuota()
|
|
{
|
|
$quota = $this->getQuota();
|
|
return $quota->total->remaining;
|
|
}
|
|
|
|
/**
|
|
* Force to get quotadata from API, even if cache is still active ( use very sparingly )
|
|
* Does not actively fetches it, but invalidates cache, so next call will trigger a remote call
|
|
* @return void
|
|
*/
|
|
public function forceCheckRemoteQuota()
|
|
{
|
|
$cache = new CacheController();
|
|
$cacheData = $cache->getItem(self::CACHE_NAME);
|
|
$cacheData->delete();
|
|
$this->quotaData = null;
|
|
}
|
|
|
|
/**
|
|
* Validate account key in the API via quota check
|
|
* @param string $key User account key
|
|
* @return array Quotadata array (remote format) with validated key
|
|
*/
|
|
public function remoteValidateKey($key)
|
|
{
|
|
// Remove the cache before checking.
|
|
$this->forceCheckRemoteQuota();
|
|
return $this->getRemoteQuota($key, true);
|
|
}
|
|
|
|
/**
|
|
* Called when plugin detects the remote quota has been exceeded.
|
|
* Triggers various call to actions to customer
|
|
*/
|
|
public function setQuotaExceeded()
|
|
{
|
|
$settings = \wpSPIO()->settings();
|
|
$settings->quotaExceeded = 1;
|
|
$this->forceCheckRemoteQuota(); // remove the previous cache.
|
|
}
|
|
|
|
/**
|
|
* When quota is detected again via remote check, reset all call to actions
|
|
*/
|
|
private function resetQuotaExceeded()
|
|
{
|
|
$settings = \wpSPIO()->settings();
|
|
|
|
AdminNoticesController::resetAPINotices();
|
|
|
|
// Only reset after a quotaExceeded situation, otherwise it keeps popping.
|
|
if ($settings->quotaExceeded == 1)
|
|
{
|
|
AdminNoticesController::resetQuotaNotices();
|
|
}
|
|
// Log::addDebug('Reset Quota Exceeded and reset Notices');
|
|
$settings->quotaExceeded = 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* [getRemoteQuota description]
|
|
* @param string $apiKey User account key
|
|
* @param boolean $validate Api should also validate key or not
|
|
* @return array Quotadata array (remote format) [with validated key]
|
|
*/
|
|
private function getRemoteQuota($apiKey = false, $validate = false)
|
|
{
|
|
if (! $apiKey && ! $validate) // validation is done by apikeymodel, might result in a loop.
|
|
{
|
|
$keyControl = ApiKeyController::getInstance();
|
|
$apiKey = $keyControl->forceGetApiKey();
|
|
}
|
|
|
|
|
|
$settings = \wpSPIO()->settings();
|
|
|
|
if($settings->httpProto != 'https' && $settings->httpProto != 'http') {
|
|
$settings->httpProto = 'https';
|
|
}
|
|
|
|
$requestURL = $settings->httpProto . '://' . SHORTPIXEL_API . '/v2/api-status.php';
|
|
$args = array(
|
|
'timeout'=> 15, // wait for 15 secs.
|
|
'body' => array('key' => $apiKey)
|
|
);
|
|
$argsStr = "?key=".$apiKey;
|
|
|
|
$serverAgent = isset($_SERVER['HTTP_USER_AGENT']) ? urlencode(sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT']))) : '';
|
|
$args['body']['useragent'] = "Agent" . $serverAgent;
|
|
$argsStr .= "&useragent=Agent".$args['body']['useragent'];
|
|
|
|
// Only used for keyValidation
|
|
if($validate) {
|
|
|
|
$statsController = StatsController::getInstance();
|
|
$imageCount = $statsController->find('media', 'itemsTotal');
|
|
$thumbsCount = $statsController->find('media', 'thumbsTotal');
|
|
|
|
$args['body']['DomainCheck'] = get_site_url();
|
|
$args['body']['Info'] = get_bloginfo('version') . '|' . phpversion();
|
|
$args['body']['ImagesCount'] = $imageCount; //$imageCount['mainFiles'];
|
|
$args['body']['ThumbsCount'] = $thumbsCount; // $imageCount['totalFiles'] - $imageCount['mainFiles'];
|
|
$argsStr .= "&DomainCheck={$args['body']['DomainCheck']}&Info={$args['body']['Info']}&ImagesCount=$imageCount&ThumbsCount=$thumbsCount";
|
|
|
|
|
|
}
|
|
|
|
$args['body']['host'] = parse_url(get_site_url(),PHP_URL_HOST);
|
|
$argsStr .= "&host={$args['body']['host']}";
|
|
if (defined('SHORTPIXEL_HTTP_AUTH_USER') && defined('SHORTPIXEL_HTTP_AUTH_PASSWORD'))
|
|
{
|
|
$args['body']['user'] = stripslashes(SHORTPIXEL_HTTP_AUTH_USER);
|
|
$args['body']['pass'] = stripslashes(SHORTPIXEL_HTTP_AUTH_PASSWORD);
|
|
$argsStr .= '&user=' . urlencode($args['body']['user']) . '&pass=' . urlencode($args['body']['pass']);
|
|
}
|
|
elseif(! is_null($settings->siteAuthUser) && strlen($settings->siteAuthUser)) {
|
|
|
|
$args['body']['user'] = stripslashes($settings->siteAuthUser);
|
|
$args['body']['pass'] = stripslashes($settings->siteAuthPass);
|
|
$argsStr .= '&user=' . urlencode($args['body']['user']) . '&pass=' . urlencode($args['body']['pass']);
|
|
}
|
|
if($settings !== false) {
|
|
$args['body']['Settings'] = $settings;
|
|
}
|
|
|
|
$time = microtime(true);
|
|
$comm = array();
|
|
|
|
//Try first HTTPS post. add the sslverify = false if https
|
|
if($settings->httpProto === 'https') {
|
|
$args['sslverify'] = apply_filters('shortpixel/system/sslverify', true);
|
|
}
|
|
|
|
$response = wp_remote_post($requestURL, $args);
|
|
|
|
$comm['A: ' . (number_format(microtime(true) - $time, 2))] = array("sent" => "POST: " . $requestURL, "args" => $args, "received" => $response);
|
|
|
|
//some hosting providers won't allow https:// POST connections so we try http:// as well
|
|
if(is_wp_error( $response )) {
|
|
|
|
$requestURL = $settings->httpProto == 'https' ?
|
|
str_replace('https://', 'http://', $requestURL) :
|
|
str_replace('http://', 'https://', $requestURL);
|
|
// add or remove the sslverify
|
|
if($settings->httpProto === 'https') {
|
|
$args['sslverify'] = apply_filters('shortpixel/system/sslverify', true);
|
|
} else {
|
|
unset($args['sslverify']);
|
|
}
|
|
$response = wp_remote_post($requestURL, $args);
|
|
$comm['B: ' . (number_format(microtime(true) - $time, 2))] = array("sent" => "POST: " . $requestURL, "args" => $args, "received" => $response);
|
|
|
|
if(!is_wp_error( $response )){
|
|
$settings->httpProto = ($settings->httpProto == 'https' ? 'http' : 'https');
|
|
} else {
|
|
}
|
|
}
|
|
//Second fallback to HTTP get
|
|
if(is_wp_error( $response )){
|
|
$args['body'] = null;
|
|
$requestURL .= $argsStr;
|
|
$response = wp_remote_get($requestURL, $args);
|
|
$comm['C: ' . (number_format(microtime(true) - $time, 2))] = array("sent" => "POST: " . $requestURL, "args" => $args, "received" => $response);
|
|
}
|
|
Log::addInfo("API STATUS COMM: " . json_encode($comm));
|
|
|
|
$defaultData = array(
|
|
"APIKeyValid" => false,
|
|
"Message" => __('API Key could not be validated due to a connectivity error.<BR>Your firewall may be blocking us. Please contact your hosting provider and ask them to allow connections from your site to api.shortpixel.com (IP 176.9.21.94).<BR> If you still cannot validate your API Key after this, please <a href="https://shortpixel.com/contact" target="_blank">contact us</a> and we will try to help. ','shortpixel-image-optimiser'),
|
|
"APICallsMade" => __('Information unavailable. Please check your API key.','shortpixel-image-optimiser'),
|
|
"APICallsQuota" => __('Information unavailable. Please check your API key.','shortpixel-image-optimiser'),
|
|
"APICallsMadeOneTime" => 0,
|
|
"APICallsQuotaOneTime" => 0,
|
|
"APICallsMadeNumeric" => 0,
|
|
"APICallsQuotaNumeric" => 0,
|
|
"APICallsMadeOneTimeNumeric" => 0,
|
|
"APICallsQuotaOneTimeNumeric" => 0,
|
|
"APICallsRemaining" => 0,
|
|
"APILastRenewalDate" => 0,
|
|
"DomainCheck" => 'NOT Accessible');
|
|
$defaultData = is_array($settings->currentStats) ? array_merge( $settings->currentStats, $defaultData) : $defaultData;
|
|
|
|
if(is_object($response) && get_class($response) == 'WP_Error') {
|
|
|
|
$urlElements = parse_url($requestURL);
|
|
$portConnect = @fsockopen($urlElements['host'],8,$errno,$errstr,15);
|
|
if(!$portConnect) {
|
|
$defaultData['Message'] .= "<BR>Debug info: <i>$errstr</i>";
|
|
}
|
|
return $defaultData;
|
|
}
|
|
|
|
if($response['response']['code'] != 200) {
|
|
return $defaultData;
|
|
}
|
|
|
|
$data = $response['body'];
|
|
$data = json_decode($data);
|
|
|
|
if(empty($data)) { return $defaultData; }
|
|
|
|
if($data->Status->Code != 2) {
|
|
$defaultData['Message'] = $data->Status->Message;
|
|
return $defaultData;
|
|
}
|
|
|
|
$dataArray = array(
|
|
"APIKeyValid" => true,
|
|
"APICallsMade" => number_format($data->APICallsMade) . __(' credits','shortpixel-image-optimiser'),
|
|
"APICallsQuota" => number_format($data->APICallsQuota) . __(' credits','shortpixel-image-optimiser'),
|
|
"APICallsMadeOneTime" => number_format($data->APICallsMadeOneTime) . __(' credits','shortpixel-image-optimiser'),
|
|
"APICallsQuotaOneTime" => number_format($data->APICallsQuotaOneTime) . __(' credits','shortpixel-image-optimiser'),
|
|
"APICallsMadeNumeric" => (int) max($data->APICallsMade, 0),
|
|
"APICallsQuotaNumeric" => (int) max($data->APICallsQuota, 0),
|
|
"APICallsMadeOneTimeNumeric" => (int) max($data->APICallsMadeOneTime, 0),
|
|
"APICallsQuotaOneTimeNumeric" => (int) max($data->APICallsQuotaOneTime, 0),
|
|
|
|
"Unlimited" => (property_exists($data, 'Unlimited') && $data->Unlimited == 'true') ? true : false,
|
|
|
|
"APILastRenewalDate" => $data->DateSubscription,
|
|
"DomainCheck" => (isset($data->DomainCheck) ? $data->DomainCheck : null)
|
|
);
|
|
// My Eyes! Basically : ApiCalls - ApiCalls used, both for monthly and onetime. Max of each is 0. Negative quota seems possible, but should not be substracted from one or the other.
|
|
$dataArray["APICallsRemaining"] = max($dataArray['APICallsQuotaNumeric'] - $dataArray['APICallsMadeNumeric'], 0) + max($dataArray['APICallsQuotaOneTimeNumeric'] - $dataArray['APICallsMadeOneTimeNumeric'],0);
|
|
|
|
//reset quota exceeded flag -> user is allowed to process more images.
|
|
|
|
if ( $dataArray['APICallsRemaining'] > 0 || $dataArray['Unlimited'])
|
|
{
|
|
$this->resetQuotaExceeded();
|
|
}
|
|
else
|
|
{
|
|
//activate quota limiting
|
|
$this->setQuotaExceeded();
|
|
}
|
|
|
|
// Log::addDebug('GetQuotaInformation Result ', $dataArray);
|
|
return $dataArray;
|
|
}
|
|
|
|
}
|