first commit
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Google Drive ADDON
|
||||
*
|
||||
* Name: Google Drive ADDON
|
||||
* Version: 1
|
||||
* Author: Duplicator
|
||||
* Author URI: https://duplicator.com/
|
||||
*
|
||||
* PHP version 5.6
|
||||
*
|
||||
* @category Duplicator
|
||||
* @package Plugin
|
||||
* @author Duplicator
|
||||
* @copyright 2011-2021 Snapcreek LLC
|
||||
* @license https://www.gnu.org/licenses/gpl-3.0.html GPLv3
|
||||
* @version GIT: $Id$
|
||||
* @link https://duplicator.com/
|
||||
*/
|
||||
|
||||
namespace Duplicator\Addons\GDriveAddon;
|
||||
|
||||
use Duplicator\Addons\GDriveAddon\Models\GDriveStorage;
|
||||
use Duplicator\Addons\GDriveAddon\Utils\Autoloader;
|
||||
use Duplicator\Core\Addons\AbstractAddonCore;
|
||||
use Duplicator\Models\Storages\AbstractStorageEntity;
|
||||
|
||||
class GDriveAddon extends AbstractAddonCore
|
||||
{
|
||||
const ADDON_PATH = __DIR__;
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function init(): void
|
||||
{
|
||||
Autoloader::register();
|
||||
|
||||
add_action('duplicator_register_storage_types', [$this, 'registerStorages']);
|
||||
add_filter('duplicator_template_file', [self::class, 'getTemplateFile'], 10, 2);
|
||||
add_filter('duplicator_usage_stats_storages_infos', [self::class, 'getStorageUsageStats'], 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function registerStorages(): void
|
||||
{
|
||||
GDriveStorage::registerType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return template file path
|
||||
*
|
||||
* @param string $path path to the template file
|
||||
* @param string $slugTpl slug of the template
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getTemplateFile($path, $slugTpl)
|
||||
{
|
||||
if (strpos($slugTpl, 'gdriveaddon/') === 0) {
|
||||
return self::getAddonPath() . '/template/' . $slugTpl . '.php';
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get storage usage stats
|
||||
*
|
||||
* @param array<string,int> $storageNums Storages num
|
||||
*
|
||||
* @return array<string,int>
|
||||
*/
|
||||
public static function getStorageUsageStats($storageNums)
|
||||
{
|
||||
if (($storages = AbstractStorageEntity::getAll()) === false) {
|
||||
$storages = [];
|
||||
}
|
||||
|
||||
$storageNums['storages_gdrive_count'] = 0;
|
||||
|
||||
foreach ($storages as $index => $storage) {
|
||||
if ($storage->getSType() === GDriveStorage::getSType()) {
|
||||
$storageNums['storages_gdrive_count']++;
|
||||
}
|
||||
}
|
||||
|
||||
return $storageNums;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getAddonPath(): string
|
||||
{
|
||||
return __DIR__;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getAddonFile(): string
|
||||
{
|
||||
return __FILE__;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,585 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
*
|
||||
* @package Duplicator
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Addons\GDriveAddon\Models;
|
||||
|
||||
use Duplicator\Models\GlobalEntity;
|
||||
use Duplicator\Utils\Logging\DupLog;
|
||||
use Duplicator\Core\Views\TplMng;
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
use Duplicator\Models\DynamicGlobalEntity;
|
||||
use Duplicator\Models\Storages\AbstractStorageEntity;
|
||||
use Duplicator\Models\Storages\StorageAuthInterface;
|
||||
use Duplicator\Utils\OAuth\TokenEntity;
|
||||
use Duplicator\Utils\OAuth\TokenService;
|
||||
use Duplicator\Views\AdminNotices;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* @property GDriveAdapter $adapter
|
||||
*/
|
||||
class GDriveStorage extends AbstractStorageEntity implements StorageAuthInterface
|
||||
{
|
||||
// These numbers represent clients created in Google Cloud Console
|
||||
const GDRIVE_CLIENT_NATIVE = 1; // Native client 1
|
||||
const GDRIVE_CLIENT_WEB0722 = 2; // Web client 07/2022
|
||||
const GDRIVE_CLIENT_LATEST = 2; // Latest out of these above
|
||||
|
||||
const REQUIRED_SCOPES = [
|
||||
"openid",
|
||||
"https://www.googleapis.com/auth/userinfo.profile",
|
||||
"https://www.googleapis.com/auth/userinfo.email",
|
||||
// The drive.file scope limits access to just those files created by the plugin
|
||||
"https://www.googleapis.com/auth/drive.file",
|
||||
];
|
||||
|
||||
/**
|
||||
* Get default config
|
||||
*
|
||||
* @return array<string,scalar>
|
||||
*/
|
||||
protected static function getDefaultConfig(): array
|
||||
{
|
||||
$config = parent::getDefaultConfig();
|
||||
return array_merge(
|
||||
$config,
|
||||
[
|
||||
'storage_folder_id' => '',
|
||||
'storage_folder_web_url' => '',
|
||||
'token_json' => '',
|
||||
'refresh_token' => '',
|
||||
'client_number' => -1,
|
||||
'authorized' => false,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get priority, used to sort storages.
|
||||
* 100 is neutral value, 0 is the highest priority
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function getPriority(): int
|
||||
{
|
||||
return 300;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the storage type
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function getSType(): int
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the storage type icon URL
|
||||
*
|
||||
* @return string Returns the storage icon URL
|
||||
*/
|
||||
public static function getStypeIconURL(): string
|
||||
{
|
||||
return DUPLICATOR_IMG_URL . '/google-drive.svg';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the storage type name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getStypeName(): string
|
||||
{
|
||||
return __('Google Drive', 'duplicator-pro');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get storage location string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLocationString(): string
|
||||
{
|
||||
if ($this->isAuthorized()) {
|
||||
return (string) $this->config['storage_folder_web_url'];
|
||||
} else {
|
||||
return __('Not Authenticated', 'duplicator-pro');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if storage is supported
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isSupported(): bool
|
||||
{
|
||||
return (SnapUtil::isCurlEnabled() || SnapUtil::isUrlFopenEnabled());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get supported notice, displayed if storage isn't supported
|
||||
*
|
||||
* @return string html string or empty if storage is supported
|
||||
*/
|
||||
public static function getNotSupportedNotice(): string
|
||||
{
|
||||
if (static::isSupported()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (!SnapUtil::isCurlEnabled() && !SnapUtil::isUrlFopenEnabled()) {
|
||||
return esc_html__(
|
||||
'Google Drive requires either the PHP CURL extension enabled or the allow_url_fopen runtime configuration to be enabled.',
|
||||
'duplicator-pro'
|
||||
);
|
||||
} elseif (!SnapUtil::isCurlEnabled()) {
|
||||
return esc_html__('Google Drive requires the PHP CURL extension enabled.', 'duplicator-pro');
|
||||
} else {
|
||||
return esc_html__('Google Drive requires the allow_url_fopen runtime configuration to be enabled.', 'duplicator-pro');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get upload chunk size in bytes
|
||||
*
|
||||
* @return int bytes
|
||||
*/
|
||||
public function getUploadChunkSize(): int
|
||||
{
|
||||
$dGlobal = DynamicGlobalEntity::getInstance();
|
||||
$chunkSizeKb = $dGlobal->getValInt('gdrive_upload_chunksize_in_kb', 256);
|
||||
|
||||
return $chunkSizeKb * KB_IN_BYTES;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get download chunk size in bytes
|
||||
*
|
||||
* @return int bytes
|
||||
*/
|
||||
public function getDownloadChunkSize(): int
|
||||
{
|
||||
$dGlobal = DynamicGlobalEntity::getInstance();
|
||||
$chunkSizeKb = $dGlobal->getValInt('gdrive_download_chunksize_in_kb', 10 * 1024);
|
||||
|
||||
return $chunkSizeKb * KB_IN_BYTES;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get upload chunk timeout in seconds
|
||||
*
|
||||
* @return int timeout in microseconds, 0 unlimited
|
||||
*/
|
||||
public function getUploadChunkTimeout(): int
|
||||
{
|
||||
// @todo: fixed to 10 seconds for historical reasons, make it configurable.
|
||||
return 10 * 1000000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if storage is valid
|
||||
*
|
||||
* @param ?string $errorMsg Reference to store error message
|
||||
* @param bool $force Force the storage to be revalidated
|
||||
*
|
||||
* @return bool Return true if storage is valid and ready to use, false otherwise
|
||||
*/
|
||||
public function isValid(?string &$errorMsg = '', bool $force = false): bool
|
||||
{
|
||||
if (!$this->isAuthorized()) {
|
||||
$errorMsg = __('Google Drive isn\'t authorized.', 'duplicator-pro');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is autorized
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isAuthorized(): bool
|
||||
{
|
||||
return (bool) ($this->config['authorized'] ?? false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an HTML anchor tag of location
|
||||
*
|
||||
* @return string Returns an HTML anchor tag with the storage location as a hyperlink.
|
||||
*/
|
||||
public function getHtmlLocationLink(): string
|
||||
{
|
||||
if (! $this->isAuthorized() || empty($this->config['storage_folder_web_url'])) {
|
||||
return '<span>' . esc_html($this->getStorageFolder()) . '</span>';
|
||||
}
|
||||
|
||||
return sprintf("<a href=\"%s\" target=\"_blank\">%s</a>", esc_url($this->config['storage_folder_web_url']), esc_html($this->getStorageFolder()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Authorized from HTTP request
|
||||
*
|
||||
* @param string $message Message
|
||||
*
|
||||
* @return bool True if authorized, false if failed
|
||||
*/
|
||||
public function authorizeFromRequest(&$message = ''): bool
|
||||
{
|
||||
$tokenPairString = '';
|
||||
try {
|
||||
if (($refreshToken = SnapUtil::sanitizeTextInput(SnapUtil::INPUT_REQUEST, 'auth_code')) === '') {
|
||||
throw new Exception(__('Authorization code is empty', 'duplicator-pro'));
|
||||
}
|
||||
|
||||
$this->name = SnapUtil::sanitizeTextInput(SnapUtil::INPUT_REQUEST, 'name', '');
|
||||
$this->notes = SnapUtil::sanitizeDefaultInput(SnapUtil::INPUT_REQUEST, 'notes', '');
|
||||
$this->config['max_packages'] = SnapUtil::sanitizeIntInput(SnapUtil::INPUT_REQUEST, 'max_packages', 10);
|
||||
$this->config['storage_folder'] = self::getSanitizedInputFolder('storage_folder', 'remove');
|
||||
|
||||
$this->revokeAuthorization();
|
||||
|
||||
$token = (new TokenEntity(static::getSType(), ['refresh_token' => $refreshToken]));
|
||||
if (!$token->refresh(true)) {
|
||||
throw new Exception(__('Failed to fetch information from Google Drive. Make sure the token is valid.', 'duplicator-pro'));
|
||||
}
|
||||
|
||||
if (empty($token->getScope())) {
|
||||
throw new Exception(__("Couldn't connect. Google Drive scopes not found.", 'duplicator-pro'));
|
||||
}
|
||||
|
||||
if (! $token->hasScopes(static::REQUIRED_SCOPES)) {
|
||||
throw new Exception(
|
||||
__(
|
||||
"Authorization failed. You did not allow all required permissions. Try again and make sure that you checked all checkboxes.",
|
||||
'duplicator-pro'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$this->config['refresh_token'] = $token->getRefreshToken();
|
||||
$this->config['token_json'] = wp_json_encode([
|
||||
'created' => $token->getCreated(),
|
||||
'access_token' => $token->getAccessToken(),
|
||||
'refresh_token' => $token->getRefreshToken(),
|
||||
'expires_in' => $token->getExpiresIn(),
|
||||
'scope' => $token->getScope(),
|
||||
]);
|
||||
$this->config['client_number'] = self::GDRIVE_CLIENT_LATEST;
|
||||
|
||||
$this->config['authorized'] = $token->isValid();
|
||||
} catch (Exception $e) {
|
||||
DupLog::traceException($e, "Problem authorizing Google Drive access token");
|
||||
DupLog::traceObject('Token pair string from authorization:', $tokenPairString);
|
||||
$message = $e->getMessage();
|
||||
return false;
|
||||
}
|
||||
$this->save();
|
||||
|
||||
$message = __('Google Drive is connected successfully and Storage Provider Updated.', 'duplicator-pro');
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Revokes authorization
|
||||
*
|
||||
* @param string $message Message
|
||||
*
|
||||
* @return bool True if authorized, false if failed
|
||||
*/
|
||||
public function revokeAuthorization(&$message = ''): bool
|
||||
{
|
||||
if (!$this->isAuthorized()) {
|
||||
$message = __('Google Drive isn\'t authorized.', 'duplicator-pro');
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
$client = $this->getAdapter()->getService()->getClient();
|
||||
|
||||
if (!empty($this->config['refresh_token'])) {
|
||||
$client->revokeToken($this->config['refresh_token']);
|
||||
}
|
||||
|
||||
$accessTokenObj = json_decode($this->config['token_json']);
|
||||
if (is_object($accessTokenObj) && property_exists($accessTokenObj, 'access_token')) {
|
||||
$gdrive_access_token = $accessTokenObj->access_token;
|
||||
} else {
|
||||
$gdrive_access_token = false;
|
||||
}
|
||||
|
||||
if (!empty($gdrive_access_token)) {
|
||||
$client->revokeToken($gdrive_access_token);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
DupLog::trace("Problem revoking Google Drive access token msg: " . $e->getMessage());
|
||||
$message = $e->getMessage();
|
||||
return false;
|
||||
} finally {
|
||||
$this->config['token_json'] = '';
|
||||
$this->config['refresh_token'] = '';
|
||||
$this->config['client_number'] = -1;
|
||||
$this->config['authorized'] = false;
|
||||
}
|
||||
|
||||
$message = __('Google Drive is disconnected successfully.', 'duplicator-pro');
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get authorization URL
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAuthorizationUrl(): string
|
||||
{
|
||||
return (new TokenService(static::getSType()))->getRedirectUri();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get storage adapter
|
||||
*
|
||||
* @return GDriveAdapter
|
||||
*/
|
||||
protected function getAdapter(): GDriveAdapter
|
||||
{
|
||||
$global = GlobalEntity::getInstance();
|
||||
$token = $this->getTokenFromConfig();
|
||||
|
||||
if (! $this->adapter) {
|
||||
$storageFolderId = '';
|
||||
if (! empty($this->config['storage_folder_id'])) {
|
||||
$storageFolderId = $this->config['storage_folder_id'];
|
||||
}
|
||||
$this->adapter = new GDriveAdapter(
|
||||
$token,
|
||||
$this->config['storage_folder'],
|
||||
$storageFolderId,
|
||||
!$global->ssl_disableverify,
|
||||
($global->ssl_useservercerts ? '' : DUPLICATOR_CERT_PATH),
|
||||
$global->ipv4_only
|
||||
);
|
||||
$this->adapter->initialize();
|
||||
if ($token->isValid() && empty($this->config['storage_folder_id'])) {
|
||||
$storageFolder = $this->adapter->getPathInfo('/');
|
||||
$this->config['storage_folder_id'] = $storageFolder->id;
|
||||
$this->config['storage_folder_web_url'] = $storageFolder->webUrl;
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
if (! $token->isValid()) {
|
||||
return $this->adapter;
|
||||
}
|
||||
|
||||
// This check is only needed if we have a valid storage.
|
||||
$storageFolder = $this->adapter->getPathInfo('/');
|
||||
if ($storageFolder->name !== basename($this->getStorageFolder())) {
|
||||
// root folder id & storage folder name is different.
|
||||
$this->adapter = new GDriveAdapter(
|
||||
$token,
|
||||
$this->config['storage_folder'],
|
||||
'',
|
||||
!$global->ssl_disableverify,
|
||||
($global->ssl_useservercerts ? '' : DUPLICATOR_CERT_PATH),
|
||||
$global->ipv4_only
|
||||
);
|
||||
$this->adapter->initialize();
|
||||
$storageFolder = $this->adapter->getPathInfo('/');
|
||||
$this->config['storage_folder_id'] = $storageFolder->id;
|
||||
$this->config['storage_folder_web_url'] = $storageFolder->webUrl;
|
||||
$this->save();
|
||||
}
|
||||
|
||||
return $this->adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the config fields template data
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
protected function getConfigFieldsData(): array
|
||||
{
|
||||
$userInfo = false;
|
||||
$quotaString = '';
|
||||
$adapter = $this->getAdapter();
|
||||
|
||||
if ($this->isAuthorized() && $adapter->isValid()) {
|
||||
try {
|
||||
$serviceDrive = $adapter->getService();
|
||||
$optParams = ['fields' => '*'];
|
||||
$about = $serviceDrive->about->get($optParams);
|
||||
$storageQuota = $about->getStorageQuota();
|
||||
$quota_total = max($storageQuota->getLimit(), 1);
|
||||
$quota_used = $storageQuota->getUsage();
|
||||
$userInfo = $about->getUser();
|
||||
|
||||
if (is_numeric($quota_total) && is_numeric($quota_used)) {
|
||||
$available_quota = $quota_total - $quota_used;
|
||||
$used_perc = round($quota_used * 100 / $quota_total, 1);
|
||||
$quotaString = sprintf(
|
||||
__('%1$s%% used, %2$s available', 'duplicator-pro'),
|
||||
$used_perc,
|
||||
size_format($available_quota)
|
||||
);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
DupLog::info("Problem getting Google Drive user info and quota: " . $e->getMessage());
|
||||
$userInfo = $quotaString = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Adapter is invalid, but we have a refresh token, so it has been revoked
|
||||
if (! $adapter->isValid() && ! empty($this->config['refresh_token'])) {
|
||||
$errorMessage = __('Google Drive has been disconnected. Please connect to Google Drive again.', 'duplicator-pro');
|
||||
AdminNotices::displayGeneralAdminNotice($errorMessage, AdminNotices::GEN_ERROR_NOTICE);
|
||||
}
|
||||
|
||||
return array_merge($this->getDefaultConfigFieldsData(), [
|
||||
'userInfo' => $userInfo,
|
||||
'quotaString' => $quotaString,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default config fields template data
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
protected function getDefaultConfigFieldsData(): array
|
||||
{
|
||||
return [
|
||||
'storage' => $this,
|
||||
'storageFolder' => $this->config['storage_folder'],
|
||||
'maxPackages' => $this->config['max_packages'],
|
||||
'userInfo' => null,
|
||||
'quotaString' => null,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the config fields template path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getConfigFieldsTemplatePath(): string
|
||||
{
|
||||
return 'gdriveaddon/configs/google_drive';
|
||||
}
|
||||
|
||||
/**
|
||||
* Update data from http request, this method don't save data, just update object properties
|
||||
*
|
||||
* @param string $message Message
|
||||
*
|
||||
* @return bool True if success and all data is valid, false otherwise
|
||||
*/
|
||||
public function updateFromHttpRequest(&$message = ''): bool
|
||||
{
|
||||
if ((parent::updateFromHttpRequest($message) === false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$previousStorageFolder = $this->config['storage_folder'];
|
||||
$this->config['max_packages'] = SnapUtil::sanitizeIntInput(SnapUtil::INPUT_REQUEST, 'gdrive_max_files', 10);
|
||||
$this->config['storage_folder'] = self::getSanitizedInputFolder('_gdrive_storage_folder', 'remove');
|
||||
|
||||
if ($previousStorageFolder !== $this->config['storage_folder']) {
|
||||
$this->config['storage_folder_id'] = '';
|
||||
$this->config['storage_folder_web_url'] = '';
|
||||
}
|
||||
|
||||
$message = sprintf(
|
||||
__('Google Drive Storage Updated.', 'duplicator-pro'),
|
||||
$this->config['server'],
|
||||
$this->getStorageFolder()
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the token entity from config
|
||||
*
|
||||
* @return TokenEntity
|
||||
*/
|
||||
protected function getTokenFromConfig()
|
||||
{
|
||||
$token = new TokenEntity(static::getSType(), $this->config['token_json']);
|
||||
if ($token->isValid() && $token->isAboutToExpire()) {
|
||||
try {
|
||||
if (! $token->refresh(true)) {
|
||||
$token->updateProperties(['access_token' => '']); // clear access token
|
||||
$this->config['authorized'] = false;
|
||||
$this->save();
|
||||
return $token;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
DupLog::traceException($e, "Problem refreshing Google Drive access token");
|
||||
}
|
||||
$this->config['token_json'] = wp_json_encode([
|
||||
'created' => $token->getCreated(),
|
||||
'access_token' => $token->getAccessToken(),
|
||||
'refresh_token' => $token->getRefreshToken(),
|
||||
'expires_in' => $token->getExpiresIn(),
|
||||
'scope' => $token->getScope(),
|
||||
]);
|
||||
$this->save();
|
||||
}
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function registerType(): void
|
||||
{
|
||||
parent::registerType();
|
||||
|
||||
add_action('duplicator_update_global_storage_settings', function (): void {
|
||||
$dGlobal = DynamicGlobalEntity::getInstance();
|
||||
|
||||
foreach (static::getDefaultSettings() as $key => $default) {
|
||||
$value = SnapUtil::sanitizeIntInput(SnapUtil::INPUT_REQUEST, $key, $default);
|
||||
$dGlobal->setValInt($key, $value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default settings
|
||||
*
|
||||
* @return array<string, scalar>
|
||||
*/
|
||||
protected static function getDefaultSettings(): array
|
||||
{
|
||||
return [
|
||||
'gdrive_upload_chunksize_in_kb' => 1024,
|
||||
'gdrive_download_chunksize_in_kb' => 10 * 1024,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function renderGlobalOptions(): void
|
||||
{
|
||||
$dGlobal = DynamicGlobalEntity::getInstance();
|
||||
|
||||
TplMng::getInstance()->render(
|
||||
'gdriveaddon/configs/global_options',
|
||||
[
|
||||
'uploadChunkSize' => $dGlobal->getValInt('gdrive_upload_chunksize_in_kb', 1024),
|
||||
'downloadChunkSize' => $dGlobal->getValInt('gdrive_download_chunksize_in_kb', 10 * 1024),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
*
|
||||
* @package Duplicator
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Addons\GDriveAddon\Models;
|
||||
|
||||
use Duplicator\Models\Storages\StoragePathInfo;
|
||||
use VendorDuplicator\Google\Service\Drive\DriveFile;
|
||||
|
||||
class GDriveStoragePathInfo extends StoragePathInfo
|
||||
{
|
||||
/** @var string */
|
||||
public $id = '';
|
||||
|
||||
/** @var string */
|
||||
public $name = '';
|
||||
|
||||
/** @var string */
|
||||
public $mimeType = '';
|
||||
|
||||
/** @var string */
|
||||
public $webUrl = '';
|
||||
|
||||
/** @var string */
|
||||
public $md5Checksum = '';
|
||||
|
||||
/** @var DriveFile */
|
||||
public $file;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Duplicator\Addons\GDriveAddon\Utils;
|
||||
|
||||
use Duplicator\Addons\GDriveAddon\GDriveAddon;
|
||||
use Duplicator\Utils\AbstractAutoloader;
|
||||
|
||||
class Autoloader extends AbstractAutoloader
|
||||
{
|
||||
const VENDOR_PATH = GDriveAddon::ADDON_PATH . '/vendor-prefixed/';
|
||||
/**
|
||||
* Register autoloader function
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function register(): void
|
||||
{
|
||||
spl_autoload_register([self::class, 'load']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load class
|
||||
*
|
||||
* @param string $className class name
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function load($className): void
|
||||
{
|
||||
if (strpos($className, self::ROOT_VENDOR) === 0) {
|
||||
foreach (self::getNamespacesVendorMapping() as $namespace => $mappedPath) {
|
||||
if (strpos($className, (string) $namespace) !== 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$filepath = self::getFilenameFromClass($className, $namespace, $mappedPath);
|
||||
if (file_exists($filepath)) {
|
||||
include $filepath;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return namespace mapping
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected static function getNamespacesVendorMapping(): array
|
||||
{
|
||||
$googleNS = 'Google\\'; // This to avoid StringClassNameToClassConstantRector replace rules
|
||||
return [
|
||||
self::ROOT_VENDOR . 'Firebase\\JWT' => self::VENDOR_PATH . 'firebase/php-jwt/src/',
|
||||
self::ROOT_VENDOR . $googleNS . 'Service' => self::VENDOR_PATH . 'google/apiclient-services/src',
|
||||
self::ROOT_VENDOR . $googleNS . 'Auth' => self::VENDOR_PATH . 'google/auth/src',
|
||||
self::ROOT_VENDOR . 'Google' => self::VENDOR_PATH . 'google/apiclient/src',
|
||||
self::ROOT_VENDOR . 'Monolog' => self::VENDOR_PATH . 'monolog/monolog/src/Monolog',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Duplicator\Addons\GDriveAddon\Utils;
|
||||
|
||||
use VendorDuplicator\Google\Client;
|
||||
use VendorDuplicator\GuzzleHttp\Client as GuzzleClient;
|
||||
|
||||
class GoogleClient extends Client
|
||||
{
|
||||
/** @var array<string,mixed> */
|
||||
protected $customHttpOptions = [];
|
||||
|
||||
/**
|
||||
* Set http client options
|
||||
*
|
||||
* @param array<string,mixed> $options options
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setHttpClientOptions(array $options): void
|
||||
{
|
||||
$this->customHttpOptions = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new http client.
|
||||
*
|
||||
* @return GuzzleClient
|
||||
*/
|
||||
protected function createDefaultHttpClient()
|
||||
{
|
||||
$options = [
|
||||
'base_uri' => $this->getConfig('base_path'),
|
||||
'http_errors' => false,
|
||||
];
|
||||
$options = array_merge($options, $this->customHttpOptions);
|
||||
|
||||
return new GuzzleClient($options);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Duplicator messages sections
|
||||
*
|
||||
* @package Duplicator
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
use Duplicator\Addons\GDriveAddon\Models\GDriveStorage;
|
||||
use Duplicator\Libs\Snap\SnapServer;
|
||||
|
||||
defined("ABSPATH") or die("");
|
||||
|
||||
/**
|
||||
* Variables
|
||||
*
|
||||
* @var \Duplicator\Core\Controllers\ControllersManager $ctrlMng
|
||||
* @var \Duplicator\Core\Views\TplMng $tplMng
|
||||
* @var array<string, mixed> $tplData
|
||||
*/
|
||||
?>
|
||||
<div class="dup-accordion-wrapper display-separators close">
|
||||
<div class="accordion-header">
|
||||
<h3 class="title"><?php echo esc_html(GDriveStorage::getStypeName()); ?></h3>
|
||||
</div>
|
||||
<div class="accordion-content">
|
||||
<label class="lbl-larger">
|
||||
<?php esc_html_e("Upload Chunk Size", 'duplicator-pro'); ?>
|
||||
</label>
|
||||
<div class="margin-bottom-1">
|
||||
<input
|
||||
class="text-right inline-display width-small margin-bottom-0"
|
||||
name="gdrive_upload_chunksize_in_kb"
|
||||
id="gdrive_upload_chunksize_in_kb"
|
||||
type="number"
|
||||
min="256"
|
||||
step="256"
|
||||
data-parsley-required
|
||||
data-parsley-type="number"
|
||||
data-parsley-errors-container="#gdrive_upload_chunksize_in_kb_error_container"
|
||||
value="<?php echo (int) $tplData['uploadChunkSize']; ?>"> <b>KB</b>
|
||||
<div id="gdrive_upload_chunksize_in_kb_error_container" class="duplicator-error-container"></div>
|
||||
<p class="description">
|
||||
<?php esc_html_e(
|
||||
'How much should be uploaded to Google Drive per attempt. Higher=faster but less reliable. It should be multiple of 256.',
|
||||
'duplicator-pro'
|
||||
); ?>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<label class="lbl-larger">
|
||||
<?php esc_html_e("Download Chunk Size", 'duplicator-pro'); ?>
|
||||
</label>
|
||||
<div class="margin-bottom-1">
|
||||
<input
|
||||
class="text-right inline-display width-small margin-bottom-0"
|
||||
name="gdrive_download_chunksize_in_kb"
|
||||
id="gdrive_download_chunksize_in_kb"
|
||||
type="number"
|
||||
min="256"
|
||||
step="256"
|
||||
data-parsley-required
|
||||
data-parsley-type="number"
|
||||
data-parsley-errors-container="#gdrive_download_chunksize_in_kb_error_container"
|
||||
value="<?php echo (int) $tplData['downloadChunkSize']; ?>"> <b>KB</b>
|
||||
<div id="gdrive_download_chunksize_in_kb_error_container" class="duplicator-error-container"></div>
|
||||
<p class="description">
|
||||
<?php esc_html_e(
|
||||
'How much should be downloaded from Google Drive per attempt.',
|
||||
'duplicator-pro'
|
||||
); ?>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,271 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Duplicator messages sections
|
||||
*
|
||||
* @package Duplicator
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
use Duplicator\Addons\GDriveAddon\Models\GDriveStorage;
|
||||
use Duplicator\Views\UI\UiDialog;
|
||||
|
||||
defined("ABSPATH") or die("");
|
||||
|
||||
/**
|
||||
* Variables
|
||||
*
|
||||
* @var \Duplicator\Core\Controllers\ControllersManager $ctrlMng
|
||||
* @var \Duplicator\Core\Views\TplMng $tplMng
|
||||
* @var array<string, mixed> $tplData
|
||||
* @var GDriveStorage $storage
|
||||
*/
|
||||
$storage = $tplData["storage"];
|
||||
/** @var string */
|
||||
$storageFolder = $tplData["storageFolder"];
|
||||
/** @var int */
|
||||
$maxPackages = $tplData["maxPackages"];
|
||||
/** @var \VendorDuplicator\Google\Service\Drive\User $userInfo */
|
||||
$userInfo = $tplData["userInfo"];
|
||||
/** @var string */
|
||||
$quotaString = $tplData["quotaString"];
|
||||
|
||||
$tplMng->render('admin_pages/storages/parts/provider_head');
|
||||
?>
|
||||
<tr>
|
||||
<th scope="row"><label for=""><?php esc_html_e("Authorization", 'duplicator-pro'); ?></label></th>
|
||||
<td>
|
||||
<?php if (!$storage->isAuthorized()) : ?>
|
||||
<div class='gdrive-authorization-state' id="gdrive-state-unauthorized">
|
||||
<!-- CONNECT -->
|
||||
<div id="dupli-gdrive-connect-btn-area">
|
||||
<button
|
||||
id="dupli-gdrive-connect-btn"
|
||||
type="button"
|
||||
class="button secondary hollow"
|
||||
onclick="DupliJs.Storage.GDrive.GoogleGetAuthUrl();">
|
||||
<i class="fa fa-plug"></i> <?php esc_html_e('Connect to Google Drive', 'duplicator-pro'); ?>
|
||||
<img
|
||||
src="<?php echo esc_url(DUPLICATOR_IMG_URL . '/google-drive.svg'); ?>"
|
||||
style='vertical-align: middle; margin:-2px 0 0 3px; height:18px; width:18px' />
|
||||
</button>
|
||||
</div>
|
||||
<div class="authorization-state" id="dupli-gdrive-connect-progress">
|
||||
<div style="padding:10px">
|
||||
<i class="fas fa-circle-notch fa-spin"></i> <?php esc_html_e('Getting Google Drive Request Token...', 'duplicator-pro'); ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- STEPS -->
|
||||
<div id="dupli-gdrive-steps">
|
||||
<div class="storage-auth-step">
|
||||
<p>
|
||||
<b><?php esc_html_e('Step 1:', 'duplicator-pro'); ?></b>
|
||||
<?php
|
||||
esc_html_e(
|
||||
"Duplicator needs to authorize Google Drive. Make sure to allow all required permissions.",
|
||||
'duplicator-pro'
|
||||
); ?>
|
||||
</p>
|
||||
<div class="auth-code-popup-note">
|
||||
<?php esc_html_e(
|
||||
'Note: Clicking the button below will open a new tab/window. Please be sure your browser does not block popups.',
|
||||
'duplicator-pro'
|
||||
); ?>
|
||||
<?php esc_html_e(
|
||||
'If a new tab/window does not open check your browsers address bar to allow popups from this URL.',
|
||||
'duplicator-pro'
|
||||
); ?>
|
||||
</div>
|
||||
<button
|
||||
id="gdrive-auth-window-button"
|
||||
class="button secondary hollow margin-bottom-0"
|
||||
onclick="DupliJs.Storage.GDrive.OpenAuthPage(); return false;">
|
||||
<i class="fa fa-user"></i> <?php esc_html_e("Authorize Google Drive", 'duplicator-pro'); ?>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="storage-auth-step">
|
||||
<p>
|
||||
<b><?php esc_html_e('Step 2:', 'duplicator-pro'); ?></b>
|
||||
<?php esc_html_e("Paste code from Google authorization page.", 'duplicator-pro'); ?>
|
||||
</p>
|
||||
<input style="width:400px" id="gdrive-auth-code" name="gdrive-auth-code" type="text" />
|
||||
</div>
|
||||
|
||||
<div class="storage-auth-step">
|
||||
<p>
|
||||
<b><?php esc_html_e('Step 3:', 'duplicator-pro'); ?></b>
|
||||
<?php esc_html_e('Finalize Google Drive setup by clicking the "Finalize Setup" button.', 'duplicator-pro') ?>
|
||||
</p>
|
||||
<button
|
||||
id="gdrive-finalize-setup"
|
||||
type="button"
|
||||
class="button secondary margin-bottom-0">
|
||||
<i class="fa fa-check-square"></i> <?php esc_html_e('Finalize Setup', 'duplicator-pro'); ?>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php else : ?>
|
||||
<div class='gdrive-authorization-state' id="gdrive-state-authorized" style="margin-top:-10px">
|
||||
<?php if ($userInfo != null) : ?>
|
||||
<h3>
|
||||
<?php esc_html_e('Google Drive Account', 'duplicator-pro'); ?><br />
|
||||
<i class="dupli-edit-info">
|
||||
<?php esc_html_e('Duplicator has been authorized to access this user\'s Google Drive account', 'duplicator-pro'); ?>
|
||||
</i>
|
||||
</h3>
|
||||
<div id="gdrive-account-info">
|
||||
<label><?php esc_html_e('Name', 'duplicator-pro'); ?>:</label>
|
||||
<?php echo esc_html($userInfo->getDisplayName()); ?><br />
|
||||
|
||||
<label><?php esc_html_e('Email', 'duplicator-pro'); ?>:</label> <?php echo esc_html($userInfo->getEmailAddress()); ?>
|
||||
<?php if (strlen($quotaString) > 0) { ?>
|
||||
<br>
|
||||
<label><?php esc_html_e('Quota', 'duplicator-pro'); ?>:</label> <?php echo esc_html($quotaString); ?>
|
||||
<?php } ?>
|
||||
</div><br />
|
||||
<?php else : ?>
|
||||
<div><?php esc_html_e('Error retrieving user information.', 'duplicator-pro'); ?></div>
|
||||
<?php endif ?>
|
||||
|
||||
<button
|
||||
id="dup-gdrive-cancel-authorization"
|
||||
type="button"
|
||||
class="button secondary hollow">
|
||||
<?php esc_html_e('Cancel Authorization', 'duplicator-pro'); ?>
|
||||
</button><br />
|
||||
<i class="dupli-edit-info">
|
||||
<?php
|
||||
esc_html_e(
|
||||
'Disassociates storage provider with the Google Drive account. Will require re-authorization.',
|
||||
'duplicator-pro'
|
||||
); ?>
|
||||
</i>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="_gdrive_storage_folder">
|
||||
<?php esc_html_e("Storage Folder", 'duplicator-pro'); ?>
|
||||
</label>
|
||||
</th>
|
||||
<td>
|
||||
<div class="horizontal-input-row">
|
||||
<label for="_gdrive_storage_folder">
|
||||
<b>//Google Drive/</b>
|
||||
</label>
|
||||
<input
|
||||
id="_gdrive_storage_folder"
|
||||
name="_gdrive_storage_folder"
|
||||
type="text"
|
||||
value="<?php echo esc_attr($storageFolder); ?>"
|
||||
class="dupli-storeage-folder-path">
|
||||
</div>
|
||||
<p>
|
||||
<i>
|
||||
<?php
|
||||
esc_html_e(
|
||||
"Folder where backups will be stored. This should be unique for each web-site using Duplicator.",
|
||||
'duplicator-pro'
|
||||
); ?>
|
||||
</i>
|
||||
<?php
|
||||
$tipContent = __(
|
||||
'If the directory path above is already in Google Drive before connecting then a duplicate folder name will be made in the same path.',
|
||||
'duplicator-pro'
|
||||
) . ' google_drive.php' . __('This is because the plugin only has rights to folders it creates.', 'duplicator-pro');
|
||||
?>
|
||||
<i
|
||||
class="fa-solid fa-question-circle fa-sm dark-gray-color"
|
||||
data-tooltip-title="<?php esc_attr_e("Storage Folder Notice", 'duplicator-pro'); ?>"
|
||||
data-tooltip="<?php esc_attr($tipContent); ?>">
|
||||
</i>
|
||||
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for=""><?php esc_html_e("Max Backups", 'duplicator-pro'); ?></label></th>
|
||||
<td>
|
||||
<div class="horizontal-input-row">
|
||||
<input
|
||||
id="gdrive_max_files"
|
||||
name="gdrive_max_files"
|
||||
type="number"
|
||||
value="<?php echo (int) $maxPackages; ?>"
|
||||
min="0"
|
||||
maxlength="4"
|
||||
data-parsley-errors-container="#gdrive_max_files_error_container"
|
||||
data-parsley-required="true"
|
||||
data-parsley-type="number"
|
||||
data-parsley-min="0">
|
||||
<label for="gdrive_max_files">
|
||||
<?php esc_html_e("Number of Backups to keep in folder.", 'duplicator-pro'); ?> <br />
|
||||
</label>
|
||||
</div>
|
||||
<?php $tplMng->render('admin_pages/storages/parts/max_backups_description'); ?>
|
||||
<div id="gdrive_max_files_error_container" class="duplicator-error-container"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
$tplMng->render('admin_pages/storages/parts/provider_foot');
|
||||
|
||||
// Alerts for Google Drive
|
||||
$alertConnStatus = new UiDialog();
|
||||
$alertConnStatus->title = __('Google Drive Authorization Error', 'duplicator-pro');
|
||||
$alertConnStatus->message = ''; // javascript inserted message
|
||||
$alertConnStatus->initAlert();
|
||||
|
||||
?>
|
||||
<script>
|
||||
jQuery(document).ready(function($) {
|
||||
DupliJs.Storage.GDrive = DupliJs.Storage.GDrive || {};
|
||||
|
||||
$('#dup-gdrive-cancel-authorization').click(function(event) {
|
||||
event.stopPropagation();
|
||||
DupliJs.Storage.RevokeAuth(<?php echo (int) $storage->getId(); ?>);
|
||||
});
|
||||
|
||||
DupliJs.Storage.GDrive.GoogleGetAuthUrl = function() {
|
||||
$('#dupli-gdrive-connect-btn-area').hide();
|
||||
$('#dupli-gdrive-steps').show();
|
||||
DupliJs.Storage.GDrive.AuthUrl = <?php echo json_encode($storage->getAuthorizationUrl()); ?>;
|
||||
}
|
||||
|
||||
DupliJs.Storage.GDrive.OpenAuthPage = function() {
|
||||
window.open(DupliJs.Storage.GDrive.AuthUrl, '_blank');
|
||||
}
|
||||
|
||||
$('#gdrive-finalize-setup').click(function(event) {
|
||||
event.stopPropagation();
|
||||
|
||||
if ($('#gdrive-auth-code').val().length > 5) {
|
||||
DupliJs.Storage.PrepareForSubmit();
|
||||
|
||||
//$("#dup-storage-form").submit();
|
||||
|
||||
DupliJs.Storage.Authorize(
|
||||
<?php echo (int) $storage->getId(); ?>,
|
||||
<?php echo (int) $storage->getSType(); ?>, {
|
||||
'name': $('#name').val(),
|
||||
'notes': $('#notes').val(),
|
||||
'storage_folder': $('#_gdrive_storage_folder').val(),
|
||||
'max_packages': $('#gdrive_max_files').val(),
|
||||
'auth_code': $('#gdrive-auth-code').val()
|
||||
}
|
||||
);
|
||||
} else {
|
||||
<?php $alertConnStatus->showAlert(); ?>
|
||||
let alertMsg = "<i class='fas fa-exclamation-triangle'></i> " +
|
||||
"<?php esc_html_e('Please enter your Google Drive authorization code!', 'duplicator-pro'); ?>";
|
||||
<?php $alertConnStatus->updateMessage("alertMsg"); ?>
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user