first commit
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace Duplicator\Addons\OneDriveAddon;
|
||||
|
||||
use Duplicator\Addons\OneDriveAddon\Models\OneDriveStorage;
|
||||
use Duplicator\Core\Addons\AbstractAddonCore;
|
||||
use Duplicator\Models\Storages\AbstractStorageEntity;
|
||||
|
||||
class OneDriveAddon extends AbstractAddonCore
|
||||
{
|
||||
const ADDON_PATH = __DIR__;
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
add_action('duplicator_pro_register_storage_types', [$this, 'registerStorages']);
|
||||
add_filter('duplicator_template_file', array(__CLASS__, 'getTemplateFile'), 10, 2);
|
||||
add_filter('duplicator_usage_stats_storages_infos', array(__CLASS__, 'getStorageUsageStats'), 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register storages
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function registerStorages()
|
||||
{
|
||||
OneDriveStorage::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, 'onedriveaddon/') === 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_onedrive_count'] = 0;
|
||||
|
||||
foreach ($storages as $storage) {
|
||||
if ($storage->getSType() === OneDriveStorage::getSType()) {
|
||||
$storageNums['storages_onedrive_count']++;
|
||||
}
|
||||
}
|
||||
|
||||
return $storageNums;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public static function getAddonPath()
|
||||
{
|
||||
return static::ADDON_PATH;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public static function getAddonFile()
|
||||
{
|
||||
return __FILE__;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
<?php
|
||||
|
||||
namespace Duplicator\Addons\OneDriveAddon;
|
||||
|
||||
use DUP_PRO_Log;
|
||||
use Duplicator\Libs\Snap\SnapLog;
|
||||
|
||||
class HttpClient
|
||||
{
|
||||
/** @var string Base URL */
|
||||
protected $baseUrl = '';
|
||||
/** @var string Access token */
|
||||
protected $accessToken = '';
|
||||
/** @var int Timeout in seconds */
|
||||
protected $timeout = 1000;
|
||||
/** @var string Path to the certificate */
|
||||
protected $sslCert = '';
|
||||
/** @var bool Should verify the SSL certificate */
|
||||
protected $sslVerify = true;
|
||||
/** @var array<string,string> Default headers */
|
||||
protected $headers = [];
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param string $baseUrl Base URL
|
||||
* @param string $accesToken Access token
|
||||
* @param bool $sslVerify If true, use SSL
|
||||
* @param string $sslCert If empty use server cert
|
||||
* @param int $timeout Timeout in seconds
|
||||
*/
|
||||
public function __construct($baseUrl, $accesToken, $sslVerify = true, $sslCert = '', $timeout = 1000)
|
||||
{
|
||||
$this->baseUrl = $baseUrl;
|
||||
$this->accessToken = $accesToken;
|
||||
$this->timeout = $timeout;
|
||||
$this->sslCert = $sslCert;
|
||||
$this->sslVerify = $sslVerify;
|
||||
|
||||
$this->headers['Authorization'] = 'Bearer ' . $this->accessToken;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $url The URL to request
|
||||
* @param array<string, scalar> $data The data to send
|
||||
* @param array<string, string> $headers The headers to send
|
||||
*
|
||||
* @return array{headers: array<string, string>, body: string, code: int}
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function get($url, $data = [], $headers = [])
|
||||
{
|
||||
return $this->request('GET', $url, $data, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url The URL to request
|
||||
* @param array<string, mixed>|string $data The data to send
|
||||
* @param array<string, string> $headers The headers to send
|
||||
*
|
||||
* @return array{headers: array<string, string>, body: string, code: int}
|
||||
*/
|
||||
public function post($url, $data = [], $headers = [])
|
||||
{
|
||||
return $this->request('POST', $url, $data, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url The URL to request
|
||||
* @param array<string, mixed>|string $data The data to send
|
||||
* @param array<string, string> $headers The headers to send
|
||||
*
|
||||
* @return array{headers: array<string, string>, body: string, code: int}
|
||||
*/
|
||||
public function put($url, $data = [], $headers = [])
|
||||
{
|
||||
return $this->request('PUT', $url, $data, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url The URL to request
|
||||
* @param array<string, string> $headers The headers to send
|
||||
*
|
||||
* @return array{headers: array<string, string>, body: string, code: int}
|
||||
*/
|
||||
public function delete($url, $headers = [])
|
||||
{
|
||||
return $this->request('DELETE', $url, [], $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a HTTP request
|
||||
*
|
||||
* @param string $method The request verb
|
||||
* @param string $url The URL to request
|
||||
* @param array<string, mixed>|string $data The data to send, arrays will be json encoded, strings will be sent as is
|
||||
* @param array<string, string> $headers The headers to send
|
||||
* @param array<int, scalar> $overrideOptions Override the curl options
|
||||
*
|
||||
* @return array{headers: array<string, string>, body: string, code: int}
|
||||
*/
|
||||
public function request($method, $url, $data = [], $headers = [], $overrideOptions = [])
|
||||
{
|
||||
$headers = array_merge($this->headers, $headers);
|
||||
if (!empty($this->baseUrl) && strpos($url, 'http') !== 0) {
|
||||
$url = rtrim($this->baseUrl, '/') . '/' . ltrim($url, '/');
|
||||
}
|
||||
|
||||
$curl = curl_init();
|
||||
|
||||
if (!$curl) {
|
||||
throw new \Exception('Could not initialize remote request using curl.');
|
||||
}
|
||||
|
||||
if ($method === 'GET' && !empty($data)) {
|
||||
$url .= '?' . http_build_query($data);
|
||||
$data = [];
|
||||
}
|
||||
|
||||
$options = [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_AUTOREFERER => true,
|
||||
CURLOPT_URL => $url,
|
||||
|
||||
CURLOPT_TIMEOUT => $this->timeout,
|
||||
CURLOPT_CUSTOMREQUEST => $method,
|
||||
CURLOPT_HEADER => true,
|
||||
CURLOPT_SSL_VERIFYHOST => ($this->sslVerify ? 2 : false),
|
||||
CURLOPT_SSL_VERIFYPEER => $this->sslVerify,
|
||||
];
|
||||
|
||||
if (!empty($this->sslCert)) {
|
||||
$options[CURLOPT_CAINFO] = $this->sslCert;
|
||||
}
|
||||
|
||||
// We are sending a json payload
|
||||
if (!empty($data) && is_array($data)) {
|
||||
$options[CURLOPT_POSTFIELDS] = json_encode($data);
|
||||
$headers['Content-Type'] = 'application/json';
|
||||
} elseif (!empty($data) && is_string($data)) {
|
||||
// We are sending a string payload
|
||||
$options[CURLOPT_POST] = true;
|
||||
$options[CURLOPT_POSTFIELDS] = $data;
|
||||
}
|
||||
|
||||
// Format the headers for curl and set the option
|
||||
$options[CURLOPT_HTTPHEADER] = $this->formatRequestHeaders($headers);
|
||||
|
||||
// Override the options if needed
|
||||
if (!empty($overrideOptions)) {
|
||||
$options = array_merge($options, $overrideOptions);
|
||||
}
|
||||
|
||||
// Set the options
|
||||
if (!curl_setopt_array($curl, $options)) {
|
||||
// curl will return immediately if it fails to set one of the options
|
||||
throw new \Exception('Could not set curl options.');
|
||||
}
|
||||
|
||||
$response = curl_exec($curl);
|
||||
|
||||
// check for any error
|
||||
$error = curl_error($curl);
|
||||
|
||||
if ($error || $response === false) {
|
||||
throw new \Exception("Curl error: {$error}");
|
||||
}
|
||||
|
||||
// Get the header size and the http code
|
||||
$headerSize = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
|
||||
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
||||
|
||||
// Get the header and the body
|
||||
$header = substr($response, 0, $headerSize);
|
||||
$body = substr($response, $headerSize);
|
||||
|
||||
curl_close($curl);
|
||||
|
||||
return [
|
||||
'headers' => $this->formatResponseHeaders($header),
|
||||
'body' => $body,
|
||||
'code' => (int) $httpCode,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the headers for curl
|
||||
*
|
||||
* @param array<string, string> $headers The headers to format
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected function formatRequestHeaders($headers)
|
||||
{
|
||||
return array_map(function ($key, $value) {
|
||||
return $key . ': ' . $value;
|
||||
}, array_keys($headers), $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the response headers
|
||||
*
|
||||
* @param string $headers The headers to format
|
||||
*
|
||||
* @return false|string[]
|
||||
*/
|
||||
protected function formatResponseHeaders($headers)
|
||||
{
|
||||
$headers = explode("\r\n", $headers);
|
||||
foreach ($headers as $index => $item) {
|
||||
$item = explode(': ', $item);
|
||||
if (count($item) === 2) {
|
||||
$headers[trim($item[0])] = trim($item[1]);
|
||||
}
|
||||
unset($headers[$index]);
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,651 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
*
|
||||
* @package Duplicator
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Addons\OneDriveAddon\Models;
|
||||
|
||||
use DUP_PRO_Global_Entity;
|
||||
use DUP_PRO_Log;
|
||||
use Duplicator\Addons\OneDriveAddon\OnedriveAdapter;
|
||||
use Duplicator\Addons\OneDriveAddon\OneDriveStoragePathInfo;
|
||||
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 Exception;
|
||||
|
||||
class OneDriveStorage extends AbstractStorageEntity implements StorageAuthInterface
|
||||
{
|
||||
const GRAPH_SCOPES = [
|
||||
'openid',
|
||||
'offline_access',
|
||||
'files.readwrite.appfolder',
|
||||
];
|
||||
|
||||
const CLIENT_ID = '15fa3a0d-b7ee-447c-8093-7bfcf30b0797';
|
||||
|
||||
const LOGOUT_REDIRECT_URI = 'https://snapcreek.com/misc/onedrive/redir3.php';
|
||||
|
||||
/**
|
||||
* @var null|OneDriveAdapter Storage adapter
|
||||
*/
|
||||
protected $adapter = null;
|
||||
|
||||
/**
|
||||
* Get default config
|
||||
*
|
||||
* @return array<string,scalar>
|
||||
*/
|
||||
protected static function getDefaultConfig()
|
||||
{
|
||||
$config = parent::getDefaultConfig();
|
||||
$config = array_merge(
|
||||
$config,
|
||||
[
|
||||
'endpoint_url' => '',
|
||||
'resource_id' => '',
|
||||
'access_token' => '',
|
||||
'refresh_token' => '',
|
||||
'token_obtained' => 0,
|
||||
'storage_folder_id' => '',
|
||||
'storage_folder_web_url' => '',
|
||||
'all_folders_perm' => false,
|
||||
'authorized' => false,
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize
|
||||
*
|
||||
* Wakeup method.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __wakeup()
|
||||
{
|
||||
parent::__wakeup();
|
||||
|
||||
if ($this->legacyEntity) {
|
||||
// Old storage entity
|
||||
$this->legacyEntity = false;
|
||||
// Make sure the storage type is right from the old entity
|
||||
$this->storage_type = $this->getSType();
|
||||
$this->config = [
|
||||
'endpoint_url' => $this->onedrive_endpoint_url,
|
||||
'resource_id' => $this->onedrive_resource_id,
|
||||
'access_token' => $this->onedrive_access_token,
|
||||
'refresh_token' => $this->onedrive_refresh_token,
|
||||
'token_obtained' => $this->onedrive_token_obtained,
|
||||
'storage_folder' => ltrim($this->onedrive_storage_folder, '/\\'),
|
||||
'max_packages' => $this->onedrive_max_files,
|
||||
'storage_folder_id' => $this->onedrive_storage_folder_id,
|
||||
'storage_folder_web_url' => $this->onedrive_storage_folder_web_url,
|
||||
'authorized' => ($this->onedrive_authorization_state == 1),
|
||||
];
|
||||
// reset old values
|
||||
$this->onedrive_endpoint_url = '';
|
||||
$this->onedrive_resource_id = '';
|
||||
$this->onedrive_access_token = '';
|
||||
$this->onedrive_refresh_token = '';
|
||||
$this->onedrive_token_obtained = 0;
|
||||
$this->onedrive_storage_folder = '';
|
||||
$this->onedrive_max_files = 10;
|
||||
$this->onedrive_storage_folder_id = '';
|
||||
$this->onedrive_authorization_state = 0;
|
||||
$this->onedrive_storage_folder_web_url = '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Will be called, automatically, when Serialize
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function __serialize() // phpcs:ignore PHPCompatibility.FunctionNameRestrictions.NewMagicMethods.__serializeFound
|
||||
{
|
||||
$data = parent::__serialize();
|
||||
unset($data['client']);
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the storage type
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function getSType()
|
||||
{
|
||||
return 7;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the storage type icon.
|
||||
*
|
||||
* @return string Returns the storage icon
|
||||
*/
|
||||
public static function getStypeIcon()
|
||||
{
|
||||
$imgUrl = DUPLICATOR_PRO_IMG_URL . '/onedrive.svg';
|
||||
return '<img src="' . esc_url($imgUrl) . '" class="dup-storage-icon" alt="' . esc_attr(static::getStypeName()) . '" />';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the storage type name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getStypeName()
|
||||
{
|
||||
return __('OneDrive', 'duplicator-pro');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get storage location string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLocationString()
|
||||
{
|
||||
if (!$this->isAuthorized()) {
|
||||
return __("Not Authenticated", "duplicator-pro");
|
||||
} else {
|
||||
return $this->config['storage_folder_web_url'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an html anchor tag of location
|
||||
*
|
||||
* @return string Returns an html anchor tag with the storage location as a hyperlink.
|
||||
*
|
||||
* @example
|
||||
* OneDrive Example return
|
||||
* <a target="_blank" href="https://1drv.ms/f/sAFrQtasdrewasyghg">https://1drv.ms/f/sAFrQtasdrewasyghg</a>
|
||||
*/
|
||||
public function getHtmlLocationLink()
|
||||
{
|
||||
if ($this->isAuthorized()) {
|
||||
return '<a href="' . esc_url($this->getLocationString()) . '" target="_blank" >' . esc_html($this->getLocationString()) . '</a>';
|
||||
} else {
|
||||
return $this->getLocationString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if storage is supported
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isSupported()
|
||||
{
|
||||
return SnapUtil::isCurlEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get supported notice, displayed if storage isn't supported
|
||||
*
|
||||
* @return string html string or empty if storage is supported
|
||||
*/
|
||||
public static function getNotSupportedNotice()
|
||||
{
|
||||
if (static::isSupported()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return esc_html__('OneDrive requires the PHP CURL extension enabled.', 'duplicator-pro');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if storage is valid
|
||||
*
|
||||
* @return bool Return true if storage is valid and ready to use, false otherwise
|
||||
*/
|
||||
public function isValid()
|
||||
{
|
||||
return $this->isAuthorized();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is autorized
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isAuthorized()
|
||||
{
|
||||
return $this->config['authorized'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the chunk size bytes
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getUploadChunkSize()
|
||||
{
|
||||
return DynamicGlobalEntity::getInstance()->getVal('onedrive_upload_chunksize_in_kb') * KB_IN_BYTES;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get upload chunk timeout in seconds
|
||||
*
|
||||
* @return int timeout in microseconds, 0 unlimited
|
||||
*/
|
||||
public function getUploadChunkTimeout()
|
||||
{
|
||||
$global = DUP_PRO_Global_Entity::getInstance();
|
||||
return (int) ($global->php_max_worker_time_in_sec <= 0 ? 0 : $global->php_max_worker_time_in_sec * SECONDS_IN_MICROSECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Authorized from HTTP request
|
||||
*
|
||||
* @param string $message Message
|
||||
*
|
||||
* @return bool True if authorized, false if failed
|
||||
*/
|
||||
public function authorizeFromRequest(&$message = '')
|
||||
{
|
||||
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(self::getSType(), ['refresh_token' => $refreshToken]));
|
||||
if (! $token->refresh(true)) {
|
||||
throw new Exception(__('Failed to fetch information from OneDrive. Make sure the token is valid.', 'duplicator-pro'));
|
||||
}
|
||||
|
||||
$this->config['access_token'] = $token->getAccessToken();
|
||||
$this->config['refresh_token'] = $token->getRefreshToken();
|
||||
$this->config['token_obtained'] = $token->getCreated();
|
||||
$this->config['endpoint_url'] = $this->config['resource_id'] = '';
|
||||
$this->config['authorized'] = true;
|
||||
|
||||
|
||||
DUP_PRO_Log::traceObject("OneDrive App folder: ", $storageFolder = $this->getOneDriveStorageFolder());
|
||||
|
||||
if (! $storageFolder) {
|
||||
throw new Exception("Failed to fetch information from OneDrive. Make sure the token is valid.");
|
||||
}
|
||||
|
||||
// Get the storage folder id
|
||||
$this->config['storage_folder_id'] = $storageFolder->id;
|
||||
$this->config['storage_folder_web_url'] = $storageFolder->webUrl;
|
||||
} catch (Exception $e) {
|
||||
DUP_PRO_Log::trace("Problem authorizing OneDrive access token msg: " . $e->getMessage());
|
||||
$message = $e->getMessage();
|
||||
return false;
|
||||
}
|
||||
|
||||
$message = __('OneDrive 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 = '')
|
||||
{
|
||||
if (!$this->isAuthorized()) {
|
||||
$message = __('Onedrive isn\'t authorized.', 'duplicator-pro');
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->config['endpoint_url'] = '';
|
||||
$this->config['resource_id'] = '';
|
||||
$this->config['access_token'] = '';
|
||||
$this->config['refresh_token'] = '';
|
||||
$this->config['token_obtained'] = 0;
|
||||
$this->config['storage_folder_id'] = '';
|
||||
$this->config['storage_folder_web_url'] = '';
|
||||
$this->config['authorized'] = false;
|
||||
|
||||
$message = __('Onedrive is disconnected successfully.', 'duplicator-pro');
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get external revoke url
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getExternalRevokeUrl()
|
||||
{
|
||||
$base_url = "https://login.microsoftonline.com/common/oauth2/v2.0/logout";
|
||||
$fields_arr = [
|
||||
"client_id" => self::CLIENT_ID,
|
||||
"post_logout_redirect_uri" => self::LOGOUT_REDIRECT_URI,
|
||||
];
|
||||
$query = http_build_query($fields_arr);
|
||||
|
||||
return $base_url . "?$query";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get authorization URL
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAuthorizationUrl()
|
||||
{
|
||||
return (new TokenService(static::getSType()))->getRedirectUri();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get action key text
|
||||
*
|
||||
* @param string $key Key name (action, pending, failed, cancelled, success)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getActionKeyText($key)
|
||||
{
|
||||
switch ($key) {
|
||||
case 'action':
|
||||
return sprintf(
|
||||
__('Transferring to %1$s folder:<br/> <i>%2$s</i>', "duplicator-pro"),
|
||||
$this->getStypeName(),
|
||||
$this->getStorageFolder()
|
||||
);
|
||||
case 'pending':
|
||||
return sprintf(
|
||||
__('Transfer to %1$s folder %2$s is pending', "duplicator-pro"),
|
||||
$this->getStypeName(),
|
||||
$this->getStorageFolder()
|
||||
);
|
||||
case 'failed':
|
||||
return sprintf(
|
||||
__('Failed to transfer to %1$s folder %2$s', "duplicator-pro"),
|
||||
$this->getStypeName(),
|
||||
$this->getStorageFolder()
|
||||
);
|
||||
case 'cancelled':
|
||||
return sprintf(
|
||||
__('Cancelled before could transfer to %1$s folder %2$s', "duplicator-pro"),
|
||||
$this->getStypeName(),
|
||||
$this->getStorageFolder()
|
||||
);
|
||||
case 'success':
|
||||
return sprintf(
|
||||
__('Transferred package to %1$s folder %2$s', "duplicator-pro"),
|
||||
$this->getStypeName(),
|
||||
$this->getStorageFolder()
|
||||
);
|
||||
default:
|
||||
throw new Exception('Invalid key');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update all permissions
|
||||
*
|
||||
* @param bool $allFoldersPerm All folders permission
|
||||
*
|
||||
* @return bool True if success, false otherwise
|
||||
*/
|
||||
public function setAllPermissions($allFoldersPerm)
|
||||
{
|
||||
$this->config['all_folders_perm'] = $allFoldersPerm;
|
||||
return $this->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render form config fields
|
||||
*
|
||||
* @param bool $echo Echo or return
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function renderConfigFields($echo = true)
|
||||
{
|
||||
$hasError = $this->isAuthorized() && !$this->getAdapter()->isValid();
|
||||
|
||||
return TplMng::getInstance()->render(
|
||||
'onedriveaddon/configs/onedrive',
|
||||
[
|
||||
'storage' => $this,
|
||||
'storageFolder' => $this->config['storage_folder'],
|
||||
'maxPackages' => $this->config['max_packages'],
|
||||
'allFolderPers' => $this->config['all_folders_perm'],
|
||||
'accountInfo' => $this->getAccountInfo(),
|
||||
'hasError' => $hasError,
|
||||
'externalRevokeUrl' => $this->getExternalRevokeUrl(),
|
||||
],
|
||||
$echo
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = '')
|
||||
{
|
||||
if ((parent::updateFromHttpRequest($message) === false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->config['max_packages'] = SnapUtil::sanitizeIntInput(SnapUtil::INPUT_REQUEST, 'onedrive_msgraph_max_files', 10);
|
||||
$oldFolder = $this->config['storage_folder'];
|
||||
$this->config['storage_folder'] = self::getSanitizedInputFolder('_onedrive_msgraph_storage_folder', 'remove');
|
||||
|
||||
if ($this->isAuthorized() && $oldFolder != $this->config['storage_folder']) {
|
||||
$this->config['storage_folder_id'] = '';
|
||||
// Create new folder
|
||||
$folder = $this->getOneDriveStorageFolder();
|
||||
$this->config['storage_folder_id'] = $folder->id;
|
||||
$this->config['storage_folder_web_url'] = $folder->webUrl;
|
||||
$this->save();
|
||||
}
|
||||
|
||||
$message = sprintf(
|
||||
__('OneDrive Storage Updated.', 'duplicator-pro'),
|
||||
$this->getStorageFolder()
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get account info
|
||||
*
|
||||
* @return false|object
|
||||
*/
|
||||
protected function getAccountInfo()
|
||||
{
|
||||
if (! $this->isAuthorized()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$storageFolder = $this->getOneDriveStorageFolder();
|
||||
|
||||
if (!$storageFolder || empty($storageFolder->user)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (object) $storageFolder->user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get onedrive storage folder
|
||||
*
|
||||
* @return OneDriveStoragePathInfo|false
|
||||
*/
|
||||
protected function getOneDriveStorageFolder()
|
||||
{
|
||||
$adapter = $this->getAdapter();
|
||||
|
||||
if (! $adapter->isValid()) {
|
||||
DUP_PRO_Log::trace("OneDrive adapter is not valid, can't get storage folder.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$this->config['storage_folder_id']) {
|
||||
if (! $adapter->initialize($error)) {
|
||||
DUP_PRO_Log::trace("Failed to initialize OneDrive adapter: $error");
|
||||
return false;
|
||||
}
|
||||
$folder = $adapter->getPathInfo('/');
|
||||
$this->config['storage_folder_id'] = $folder->id;
|
||||
$this->save();
|
||||
} else {
|
||||
$folder = $adapter->getPathInfo('/');
|
||||
}
|
||||
|
||||
return $folder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get stoage adapter
|
||||
*
|
||||
* @return OnedriveAdapter
|
||||
*/
|
||||
protected function getAdapter()
|
||||
{
|
||||
if (!$this->adapter) {
|
||||
$global = DUP_PRO_Global_Entity::getInstance();
|
||||
$token = $this->getTokenFromConfig();
|
||||
$this->adapter = new OneDriveAdapter(
|
||||
$token,
|
||||
$this->config['storage_folder'],
|
||||
$this->config['storage_folder_id'],
|
||||
!$global->ssl_disableverify,
|
||||
($global->ssl_useservercerts ? '' : DUPLICATOR_PRO_CERT_PATH)
|
||||
);
|
||||
if (! $this->adapter->initialize($error)) {
|
||||
DUP_PRO_Log::trace("Failed to initialize OneDrive adapter: $error");
|
||||
}
|
||||
}
|
||||
return $this->adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the token entity using config values
|
||||
*
|
||||
* @return TokenEntity|false
|
||||
*/
|
||||
public function getTokenFromConfig()
|
||||
{
|
||||
$token = new TokenEntity(self::getSType(), [
|
||||
'created' => $this->config['token_obtained'],
|
||||
'expires_in' => 3600,
|
||||
'scope' => self::GRAPH_SCOPES,
|
||||
'access_token' => $this->config['access_token'],
|
||||
'refresh_token' => $this->config['refresh_token'],
|
||||
]);
|
||||
|
||||
if ($token->isAboutToExpire()) {
|
||||
$token->refresh(true);
|
||||
if (!$token->isValid()) {
|
||||
return false;
|
||||
}
|
||||
$this->config['token_obtained'] = $token->getCreated();
|
||||
$this->config['refresh_token'] = $token->getRefreshToken();
|
||||
$this->config['access_token'] = $token->getAccessToken();
|
||||
$this->save();
|
||||
}
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function registerType()
|
||||
{
|
||||
parent::registerType();
|
||||
|
||||
add_action('duplicator_update_global_storage_settings', function () {
|
||||
$dGlobal = DynamicGlobalEntity::getInstance();
|
||||
|
||||
foreach (static::getDefaultSettings() as $key => $default) {
|
||||
$value = SnapUtil::sanitizeIntInput(SnapUtil::INPUT_REQUEST, $key, $default);
|
||||
$dGlobal->setVal($key, $value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default settings
|
||||
*
|
||||
* @return array<string, scalar>
|
||||
*/
|
||||
protected static function getDefaultSettings()
|
||||
{
|
||||
return ['onedrive_upload_chunksize_in_kb' => DUPLICATOR_PRO_ONEDRIVE_UPLOAD_CHUNK_DEFAULT_SIZE_IN_KB];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function renderGlobalOptions()
|
||||
{
|
||||
$values = static::getDefaultSettings();
|
||||
$dGlobal = DynamicGlobalEntity::getInstance();
|
||||
foreach ($values as $key => $default) {
|
||||
$values[$key] = $dGlobal->getVal($key, $default);
|
||||
}
|
||||
?>
|
||||
<h3 class="title"><?php echo esc_html(static::getStypeName()) ?></h3>
|
||||
<hr size="1" />
|
||||
<table class="form-table">
|
||||
<tr valign="top">
|
||||
<th scope="row"><label><?php esc_html_e("Upload Chunk Size", 'duplicator-pro'); ?></label></th>
|
||||
<td>
|
||||
<input
|
||||
type="number"
|
||||
name="onedrive_upload_chunksize_in_kb"
|
||||
id="onedrive_upload_chunksize_in_kb"
|
||||
class="dup-narrow-input text-right"
|
||||
step="320"
|
||||
min="<?php echo intval(DUPLICATOR_PRO_ONEDRIVE_UPLOAD_CHUNK_MIN_SIZE_IN_KB); ?>"
|
||||
data-parsley-required
|
||||
data-parsley-type="number"
|
||||
data-parsley-errors-container="#onedrive_upload_chunksize_in_kb_error_container"
|
||||
value="<?php echo (int) $values['onedrive_upload_chunksize_in_kb']; ?>"
|
||||
> <b>KB</b>
|
||||
<div id="onedrive_upload_chunksize_in_kb_error_container" class="duplicator-error-container"></div>
|
||||
<p class="description">
|
||||
<?php printf(esc_html__(
|
||||
'How much should be uploaded to OneDrive per attempt. It should be multiple of %dkb. Higher=faster but less reliable.',
|
||||
'duplicator-pro'
|
||||
), (int) DUPLICATOR_PRO_ONEDRIVE_UPLOAD_CHUNK_MIN_SIZE_IN_KB);
|
||||
// https://docs.microsoft.com/en-us/onedrive/developer/rest-api/api/driveitem_createuploadsession?view=odsp-graph-online#upload-bytes-to-the-upload-session
|
||||
printf(
|
||||
esc_html__('Default size %1$dkb. Min size %2$dkb.', 'duplicator-pro'),
|
||||
(int) DUPLICATOR_PRO_ONEDRIVE_UPLOAD_CHUNK_DEFAULT_SIZE_IN_KB,
|
||||
(int) DUPLICATOR_PRO_ONEDRIVE_UPLOAD_CHUNK_MIN_SIZE_IN_KB
|
||||
); ?>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Duplicator\Addons\OneDriveAddon;
|
||||
|
||||
use Duplicator\Models\Storages\StoragePathInfo;
|
||||
|
||||
class OneDriveStoragePathInfo extends StoragePathInfo
|
||||
{
|
||||
/** @var string */
|
||||
public $id = '';
|
||||
|
||||
/** @var string */
|
||||
public $name = '';
|
||||
|
||||
/** @var string */
|
||||
public $webUrl = '';
|
||||
|
||||
/**
|
||||
* @var ?array{mimeType: string, hashes: array{sha1Hash: string, quickXorHash: string, sha256Hash: string}}
|
||||
*/
|
||||
public $file = null;
|
||||
|
||||
/**
|
||||
* @var array{id: string, displayName: string}|null
|
||||
*/
|
||||
public $user = null;
|
||||
}
|
||||
@@ -0,0 +1,760 @@
|
||||
<?php
|
||||
|
||||
namespace Duplicator\Addons\OneDriveAddon;
|
||||
|
||||
use DUP_PRO_Log;
|
||||
use Duplicator\Models\Storages\AbstractStorageAdapter;
|
||||
use Duplicator\Models\Storages\StoragePathInfo;
|
||||
use Duplicator\Utils\OAuth\TokenEntity;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* @method OneDriveStoragePathInfo getPathInfo($path)
|
||||
*/
|
||||
class OnedriveAdapter extends AbstractStorageAdapter
|
||||
{
|
||||
/** @var TokenEntity The token object to use for authentication */
|
||||
protected $token = null;
|
||||
/** @var HttpClient */
|
||||
protected $http = null;
|
||||
/** @var string The ID of the folder to use for storage */
|
||||
protected $storageFolderId = '';
|
||||
/** @var string The name of the folder to use for storage */
|
||||
protected $storageFolderName = '';
|
||||
/** @var ?OneDriveStoragePathInfo The app folder object */
|
||||
protected $appFolder = null;
|
||||
/** @var string Base URL for API requests */
|
||||
protected $baseUrl = 'https://graph.microsoft.com/v1.0';
|
||||
/** @var bool */
|
||||
protected $sslVerify = true;
|
||||
/** @var string If empty use server cert else use custom cert path */
|
||||
protected $sslCert = '';
|
||||
/** @var int The microsecond at which the current operation started */
|
||||
protected $startTime = 0;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param TokenEntity $token The token object to use for authentication
|
||||
* @param string $storageFolder The folder to use for storage
|
||||
* @param string $storageFolderId The ID of the folder to use for storage
|
||||
* @param bool $sslVerify If true, use SSL
|
||||
* @param string $sslCert If empty use server cert
|
||||
*/
|
||||
public function __construct(
|
||||
TokenEntity $token,
|
||||
$storageFolder,
|
||||
$storageFolderId = '',
|
||||
$sslVerify = true,
|
||||
$sslCert = ''
|
||||
) {
|
||||
$this->token = $token;
|
||||
$this->storageFolderName = trim($storageFolder, '/');
|
||||
$this->storageFolderId = $storageFolderId;
|
||||
$this->sslVerify = $sslVerify;
|
||||
$this->sslCert = $sslCert;
|
||||
$this->http = new HttpClient($this->baseUrl, $token->getAccessToken(), $this->sslVerify, $this->sslCert);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the storage adapter
|
||||
*
|
||||
* @param string $errorMsg The error message to modify if initialization fails
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function initialize(&$errorMsg = '')
|
||||
{
|
||||
if (! $this->token->isValid()) {
|
||||
$errorMsg = __('Invalid token supplied for OneDrive', 'duplicator-pro');
|
||||
return false;
|
||||
}
|
||||
if (! $this->exists('/') && ! $this->createDir('/')) {
|
||||
$errorMsg = __('Unable to create root directory for OneDrive', 'duplicator-pro');
|
||||
return false;
|
||||
}
|
||||
if (empty($this->storageFolderId)) {
|
||||
$root = $this->getPathInfo('/');
|
||||
if (! $root || ! $root->exists) {
|
||||
$errorMsg = 'OneDrive root folder does not exist.';
|
||||
return false;
|
||||
}
|
||||
$this->storageFolderId = $root->id;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the storage adapter
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function destroy()
|
||||
{
|
||||
$storageFolder = $this->getPathInfo('/');
|
||||
if (! $storageFolder || ! $storageFolder->exists) {
|
||||
return true; // nothing to delete
|
||||
}
|
||||
|
||||
return $this->delete('/', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the storage adapter is valid
|
||||
*
|
||||
* @param string $errorMsg The error message to modify if validation fails
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid(&$errorMsg = '')
|
||||
{
|
||||
if (!$this->token->isValid() && !$this->token->refresh()) {
|
||||
$errorMsg = 'Invalid token supplied or token refresh failed.';
|
||||
return false;
|
||||
}
|
||||
$root = $this->getPathInfo('/');
|
||||
if (! $root || ! $root->exists) {
|
||||
$errorMsg = 'OneDrive root folder does not exist.';
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a directory in the storage
|
||||
* If the given path is a nested path, it will create all the parent directories
|
||||
*
|
||||
* @param string $path The path to create
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function realCreateDir($path)
|
||||
{
|
||||
if (empty($this->storageFolderId)) {
|
||||
$parentFolder = $this->getAppFolder()->id;
|
||||
$path = trim($this->storageFolderName . '/' . trim($path, '/'), '/');
|
||||
} else {
|
||||
$parentFolder = $this->storageFolderId;
|
||||
$path = trim($path, '/');
|
||||
}
|
||||
|
||||
$folders = explode('/', $this->formatPath($path));
|
||||
|
||||
$completed = '';
|
||||
foreach ($folders as $folder) {
|
||||
$item = $this->getItemDetailsByPath($completed . '/' . $folder);
|
||||
if (!isset($item['id'])) {
|
||||
try {
|
||||
$item = $this->createDriveDirectory($parentFolder, $folder);
|
||||
if (!isset($item['id'])) {
|
||||
return false;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$completed .= '/' . $folder;
|
||||
$parentFolder = $item['id'];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a file which is less that 4MB in the storage
|
||||
*
|
||||
* @see https://github.com/OneDrive/onedrive-api-docs/blob/live/docs/rest-api/api/driveitem_put_content.md
|
||||
*
|
||||
* @param string $path The path in which the file will be created
|
||||
* @param string $content The content of the file
|
||||
*
|
||||
* @return false|int
|
||||
*/
|
||||
protected function realCreateFile($path, $content)
|
||||
{
|
||||
// maximum content length is 4MB
|
||||
if (strlen($content) > 4 * 1024 * 1024) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$file = ltrim($this->formatPath($path), '/');
|
||||
|
||||
try {
|
||||
$response = $this->http->put("/me/drive/items/{$this->storageFolderId}:/{$file}:/content", $content, ['Content-Type' => 'text/plain']);
|
||||
} catch (Exception $e) {
|
||||
// Request failed from curl
|
||||
DUP_PRO_Log::infoTrace("Failed to create file in OneDrive: {$e->getMessage()}");
|
||||
return false;
|
||||
}
|
||||
$response = json_decode($response['body'], true);
|
||||
|
||||
if (!isset($response['id'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (int) $response['size'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete relative path from storage root.
|
||||
*
|
||||
* @see https://github.com/OneDrive/onedrive-api-docs/blob/live/docs/rest-api/api/driveitem_delete.md
|
||||
*
|
||||
* @param string $path The path or drive item id to delete
|
||||
* @param bool $recursive Whether to delete recursively
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function realDelete($path, $recursive = false)
|
||||
{
|
||||
if (! $this->exists($path)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$info = $this->getItemDetailsByPath($path);
|
||||
|
||||
if (! $recursive && isset($info['folder']) && $info['folder']['childCount'] > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
$response = $this->http->delete("/me/drive/items/{$info['id']}");
|
||||
} catch (Exception $e) {
|
||||
// Request failed from curl
|
||||
DUP_PRO_Log::infoTrace("Failed to delete file in OneDrive: {$e->getMessage()}");
|
||||
return false;
|
||||
}
|
||||
|
||||
return $response['code'] === 204;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contents of a file
|
||||
*
|
||||
* @param string $path The path to the file
|
||||
*
|
||||
* @return false|string
|
||||
*/
|
||||
public function getFileContent($path)
|
||||
{
|
||||
$item = $this->getItemDetailsByPath($path);
|
||||
if (!isset($item['@microsoft.graph.downloadUrl'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return file_get_contents($item['@microsoft.graph.downloadUrl']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get path info and cache it, is path not exists return path info with exists property set to false.
|
||||
*
|
||||
* @param string $path Relative storage path, if empty, return root path info.
|
||||
*
|
||||
* @return OneDriveStoragePathInfo|false The path info or false on error.
|
||||
*/
|
||||
protected function getRealPathInfo($path)
|
||||
{
|
||||
$path = '/' . ltrim($path, '/');
|
||||
|
||||
$item = $this->getItemDetailsByPath($path);
|
||||
|
||||
return $this->buildStoragePathInfo($item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a file or directory. The destination path must not exist.
|
||||
*
|
||||
* @see https://github.com/OneDrive/onedrive-api-docs/blob/live/docs/rest-api/api/driveitem_move.md
|
||||
*
|
||||
* @param string $oldPath The path to the file or directory to move
|
||||
* @param string $newPath The destination path
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function realMove($oldPath, $newPath)
|
||||
{
|
||||
$oldItem = $this->getPathInfo($oldPath);
|
||||
$newDirectoryItem = $this->getPathInfo(dirname($newPath));
|
||||
if (!$oldItem || !$newDirectoryItem) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
$response = $this->http->request('PATCH', "/me/drive/items/{$oldItem->id}", [
|
||||
'parentReference' => [
|
||||
'id' => $newDirectoryItem->id,
|
||||
],
|
||||
'name' => basename($newPath),
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
// Request failed error from curl
|
||||
DUP_PRO_Log::infoTrace("Failed to move file in OneDrive: {$e->getMessage()}");
|
||||
return false;
|
||||
}
|
||||
$response = json_decode($response['body'], true);
|
||||
|
||||
return isset($response['id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path The path to the directory to scan
|
||||
* @param bool $files Whether to include files
|
||||
* @param bool $folders Whether to include folders
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function scanDir($path, $files = true, $folders = true)
|
||||
{
|
||||
$path = '/' . ltrim($path, '/');
|
||||
if ($path !== '/') {
|
||||
// Paths under the storage folder must be prefixed with a colon, no need to do this for the storage folder itself
|
||||
$path = ":{$path}:";
|
||||
}
|
||||
try {
|
||||
$response = $this->http->get("/me/drive/items/{$this->storageFolderId}{$path}/children");
|
||||
} catch (Exception $e) {
|
||||
DUP_PRO_Log::infoTrace("Failed to scan dir in OneDrive: {$e->getMessage()}");
|
||||
return [];
|
||||
}
|
||||
$items = json_decode($response['body'], true);
|
||||
|
||||
if (!isset($items['value'])) {
|
||||
return [];
|
||||
} else {
|
||||
$items = $items['value'];
|
||||
}
|
||||
|
||||
foreach ($items as $index => $item) {
|
||||
$item = $this->buildStoragePathInfo($item);
|
||||
$items[$index] = $item->name;
|
||||
if (!$folders && $item->isDir) {
|
||||
unset($items[$index]);
|
||||
}
|
||||
if (!$files && !$item->isDir) {
|
||||
unset($items[$index]);
|
||||
}
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a directory is empty
|
||||
*
|
||||
* @param string $path The path to the directory
|
||||
* @param string[] $filters An array of filters to apply
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isDirEmpty($path, $filters = [])
|
||||
{
|
||||
$item = $this->getItemDetailsByPath($path);
|
||||
if (!isset($item['folder'])) {
|
||||
return false;
|
||||
}
|
||||
if ($item['folder']['childCount'] === 0) {
|
||||
return true;
|
||||
} elseif (empty($filters)) {
|
||||
// we have no filters, and the folder is not empty, so it must contain something
|
||||
return false;
|
||||
}
|
||||
$regexFilters = $normalFilters = [];
|
||||
|
||||
foreach ($filters as $filter) {
|
||||
if ($filter[0] === '/' && substr($filter, -1) === '/') {
|
||||
$regexFilters[] = $filter; // It's a regex filter as it starts and ends with a slash
|
||||
} else {
|
||||
$normalFilters[] = $filter;
|
||||
}
|
||||
}
|
||||
|
||||
$contents = $this->scanDir($path);
|
||||
foreach ($contents as $item) {
|
||||
if (in_array($item, $normalFilters)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($regexFilters as $regexFilter) {
|
||||
if (preg_match($regexFilter, $item) === 1) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start tracking the time for the current operation
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function startTrackingTime()
|
||||
{
|
||||
$this->startTime = (int) (microtime(true) * SECONDS_IN_MICROSECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the elapsed time since the start of the current operation
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
protected function getElapsedTime()
|
||||
{
|
||||
return (int) (microtime(true) * SECONDS_IN_MICROSECONDS) - $this->startTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the operation has reached the timeout
|
||||
*
|
||||
* @param int $timeout The timeout in microseconds
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasReachedTimeout($timeout)
|
||||
{
|
||||
return $timeout > 0 && $this->getElapsedTime() > $timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy local file to storage, partial copy is supported.
|
||||
* If destination file exists, it will be overwritten.
|
||||
* If offset is less than the destination file size, the file will be truncated.
|
||||
*
|
||||
* @param string $sourceFile The source file full path
|
||||
* @param string $storageFile Storage destination path
|
||||
* @param int<0,max> $offset The offset where the data starts.
|
||||
* @param int $length The maximum number of bytes read. Default to -1 (read all the remaining buffer).
|
||||
* @param int $timeout The timeout for the copy operation in microseconds. Default to 0 (no timeout).
|
||||
* @param array<string,mixed> $extraData Extra data to pass to copy function and updated during copy.
|
||||
* Used for storages that need to maintain persistent data during copy intra-session.
|
||||
*
|
||||
* @return false|int The number of bytes that were written to the file, or false on failure.
|
||||
*/
|
||||
protected function realCopyToStorage($sourceFile, $storageFile, $offset = 0, $length = -1, $timeout = 0, &$extraData = [])
|
||||
{
|
||||
$this->startTrackingTime();
|
||||
|
||||
if (! isset($extraData['uploadSession'])) {
|
||||
$extraData['uploadSession'] = $this->createUploadSession($storageFile);
|
||||
}
|
||||
$uploadSession = $extraData['uploadSession'];
|
||||
if (! $uploadSession) {
|
||||
DUP_PRO_Log::infoTrace("Failed to create upload session for {$storageFile}, try uploading again.");
|
||||
return false;
|
||||
}
|
||||
$expiration = strtotime($uploadSession['expirationDateTime']);
|
||||
$fileSize = filesize($sourceFile);
|
||||
$defaultChunkSize = MB_IN_BYTES; // 1MB
|
||||
$chunkSize = $length > 0 ? $length : $defaultChunkSize;
|
||||
$stream = fopen($sourceFile, 'rb');
|
||||
$bytesRemaining = $fileSize - $offset;
|
||||
$bytesUploaded = $offset;
|
||||
fseek($stream, $bytesUploaded);
|
||||
|
||||
// We stop uploading if we have reached the timeout or if we have uploaded the entire file.
|
||||
while ($bytesRemaining > 0 && ! $this->hasReachedTimeout($timeout)) {
|
||||
if (time() > $expiration) {
|
||||
DUP_PRO_Log::infoTrace("OneDrive Upload session expired for {$storageFile}, try uploading again.");
|
||||
unset($extraData['uploadSession']);
|
||||
return false;
|
||||
}
|
||||
$chunkSize = min($chunkSize, $bytesRemaining);
|
||||
$chunk = fread($stream, $chunkSize);
|
||||
try {
|
||||
//$this->http->setTimeout($timeout / SECONDS_IN_MICROSECONDS);
|
||||
$response = $this->http->put($uploadSession['uploadUrl'], $chunk, [
|
||||
'Content-Length' => $chunkSize,
|
||||
'Content-Range' => sprintf('bytes %d-%d/%d', $bytesUploaded, $bytesUploaded + $chunkSize - 1, $fileSize),
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
DUP_PRO_Log::infoTrace("Failed to copy file to OneDrive: {$e->getMessage()}");
|
||||
return false;
|
||||
}
|
||||
|
||||
$bytesUploaded += $chunkSize;
|
||||
$bytesRemaining -= $chunkSize;
|
||||
|
||||
if (in_array($response['code'], [200, 201])) {
|
||||
// We have finished uploading the file
|
||||
unset($extraData['uploadSession']);
|
||||
return $length > 0 ? $length : $fileSize; // We return the original requested chunksize for historical reasons.
|
||||
}
|
||||
// At this point only 202 is expected, which means we have to continue uploading
|
||||
if ($response['code'] !== 202) {
|
||||
// 4XX means we cannot resume uploading. 5XX means we can retry later.
|
||||
DUP_PRO_Log::infoTrace("OneDrive Upload error for {$storageFile}, try uploading again.");
|
||||
unset($extraData['uploadSession']);
|
||||
return false;
|
||||
}
|
||||
|
||||
// A specific length was requested, we have uploaded the requested length.
|
||||
if ($length > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// We return the original requested chunksize for historical reasons.
|
||||
// @todo: This SHOULD be the number of bytes that were written to the file.
|
||||
return $length > 0 ? $length : $fileSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the app folder object
|
||||
*
|
||||
* @see https://github.com/OneDrive/onedrive-api-docs/blob/live/docs/rest-api/api/drive_get_specialfolder.md
|
||||
*
|
||||
* @return OneDriveStoragePathInfo|false
|
||||
*/
|
||||
protected function getAppFolder()
|
||||
{
|
||||
if ($this->appFolder !== null) {
|
||||
return $this->appFolder;
|
||||
}
|
||||
try {
|
||||
$response = $this->http->get('/me/drive/special/approot');
|
||||
} catch (Exception $e) {
|
||||
DUP_PRO_Log::infoTrace("Failed to get app folder in OneDrive: {$e->getMessage()}");
|
||||
return false;
|
||||
}
|
||||
$item = json_decode($response['body'], true);
|
||||
|
||||
return $this->appFolder = $this->buildStoragePathInfo($item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the details of an item by path
|
||||
*
|
||||
* @see https://github.com/OneDrive/onedrive-api-docs/blob/live/docs/rest-api/api/driveitem_get.md#http-request
|
||||
*
|
||||
* @param string $path The path to the item
|
||||
*
|
||||
* @return array<string, mixed>|false
|
||||
*/
|
||||
public function getItemDetailsByPath($path)
|
||||
{
|
||||
$path = '/' . ltrim($this->formatPath($path), '/');
|
||||
if (empty($this->storageFolderId)) {
|
||||
$parent = $this->getAppFolder()->id;
|
||||
$path = '/' . trim($this->storageFolderName . $path, '/');
|
||||
} else {
|
||||
$parent = $this->storageFolderId;
|
||||
}
|
||||
try {
|
||||
$response = $this->http->get("me/drive/items/{$parent}:{$path}");
|
||||
} catch (Exception $e) {
|
||||
DUP_PRO_Log::infoTrace("Failed to get item details in OneDrive: {$e->getMessage()}");
|
||||
return false;
|
||||
}
|
||||
|
||||
return json_decode($response['body'], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a directory in the storage
|
||||
*
|
||||
* @see https://github.com/OneDrive/onedrive-api-docs/blob/live/docs/rest-api/api/driveitem_post_children.md
|
||||
*
|
||||
* @param string $parent The ID of the parent directory
|
||||
* @param string $directory The name of the directory to create
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
protected function createDriveDirectory($parent, $directory)
|
||||
{
|
||||
try {
|
||||
$response = $this->http->post('me/drive/items/' . $parent . '/children', [
|
||||
'name' => $this->formatPath($directory),
|
||||
'folder' => new \stdClass(),
|
||||
'@microsoft.graph.conflictBehavior' => 'fail',
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
DUP_PRO_Log::infoTrace("Failed to create directory in OneDrive: {$e->getMessage()}");
|
||||
return [];
|
||||
}
|
||||
|
||||
return json_decode($response['body'], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new upload session
|
||||
*
|
||||
* @see https://github.com/OneDrive/onedrive-api-docs/blob/live/docs/rest-api/api/driveitem_createuploadsession.md
|
||||
*
|
||||
* @param string $targetFile The path to the destination file
|
||||
*
|
||||
* @return array{uploadUrl: string, expirationDateTime: string}|false
|
||||
*/
|
||||
protected function createUploadSession($targetFile)
|
||||
{
|
||||
$targetFile = $this->formatPath($targetFile);
|
||||
$parent = $this->storageFolderId;
|
||||
$file = basename($targetFile);
|
||||
if ($file !== $targetFile) {
|
||||
$directory = dirname($targetFile);
|
||||
$this->createDir($directory);
|
||||
$targetDir = $this->getPathInfo($directory);
|
||||
$parent = $targetDir->id;
|
||||
}
|
||||
|
||||
try {
|
||||
$response = $this->http->post("me/drive/items/{$parent}:/{$file}:/createUploadSession", [
|
||||
'item' => ["name" => $file],
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
// Request failed from curl
|
||||
DUP_PRO_Log::infoTrace("Failed to create upload session in OneDrive: {$e->getMessage()}");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($response['code'] !== 200) {
|
||||
// Failed to create the upload session, error exists in the response body
|
||||
return false;
|
||||
}
|
||||
return json_decode($response['body'], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a StoragePathInfo object from the given array
|
||||
*
|
||||
* @param array<string, mixed> $item The array to build the object from
|
||||
*
|
||||
* @return OneDriveStoragePathInfo|false
|
||||
*/
|
||||
protected function buildStoragePathInfo($item)
|
||||
{
|
||||
$info = new OneDriveStoragePathInfo();
|
||||
|
||||
if (!isset($item['id'])) {
|
||||
return $info;
|
||||
}
|
||||
$info->exists = true;
|
||||
$info->id = $item['id'];
|
||||
$info->name = $item['name'];
|
||||
$info->isDir = isset($item['folder']);
|
||||
$info->created = isset($item['createdDateTime']) ? strtotime($item['createdDateTime']) : 0;
|
||||
$info->modified = isset($item['lastModifiedDateTime']) ? strtotime($item['lastModifiedDateTime']) : 0;
|
||||
$info->size = isset($item['size']) ? $item['size'] : 0;
|
||||
$info->webUrl = isset($item['webUrl']) ? $item['webUrl'] : '';
|
||||
if (isset($item['file'])) {
|
||||
$info->file = $item['file'];
|
||||
}
|
||||
if (isset($item['createdBy']['user'])) {
|
||||
$info->user = $item['createdBy']['user'];
|
||||
}
|
||||
if (isset($item['parentReference']['path'])) {
|
||||
// path can be different from the name, e.g. when the file is in a subdirectory
|
||||
$fullPath = $item['parentReference']['path'] . '/' . $info->name;
|
||||
$storagePosition = strpos($fullPath, $this->storageFolderName); // calculate the position of the storage folder name
|
||||
$filePathStartPosition = $storagePosition + strlen($this->storageFolderName) + 1; // file path starts after the storage folder name & the slash
|
||||
$info->path = substr($fullPath, $filePathStartPosition);
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate info on create dir
|
||||
*
|
||||
* @param string $path Dir path
|
||||
*
|
||||
* @return StoragePathInfo
|
||||
*/
|
||||
protected function generateCreateDirInfo($path)
|
||||
{
|
||||
return $this->getRealPathInfo($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate info on delete item
|
||||
*
|
||||
* @param string $path Item path
|
||||
*
|
||||
* @return StoragePathInfo
|
||||
*/
|
||||
protected function generateDeleteInfo($path)
|
||||
{
|
||||
$info = new OneDriveStoragePathInfo();
|
||||
$info->path = $path;
|
||||
$info->exists = false;
|
||||
$info->isDir = false;
|
||||
$info->size = 0;
|
||||
$info->created = 0;
|
||||
$info->modified = 0;
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a path for OneDrive. This handles special characters in the path
|
||||
*
|
||||
* @param string $path The path to format
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function formatPath($path)
|
||||
{
|
||||
return implode('/', array_map('rawurlencode', explode('/', $path)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy storage file to local file, partial copy is supported.
|
||||
* If destination file exists, it will be overwritten.
|
||||
* If offset is less than the destination file size, the file will be truncated.
|
||||
*
|
||||
* @param string $storageFile The storage file path
|
||||
* @param string $destFile The destination local file full path
|
||||
* @param int<0,max> $offset The offset where the data starts.
|
||||
* @param int $length The maximum number of bytes read. Default to -1 (read all the remaining buffer).
|
||||
* @param int $timeout The timeout for the copy operation in microseconds. Default to 0 (no timeout).
|
||||
* @param array<string,mixed> $extraData Extra data to pass to copy function and updated during copy.
|
||||
* Used for storages that need to maintain persistent data during copy intra-session.
|
||||
*
|
||||
* @return false|int The number of bytes that were written to the file, or false on failure.
|
||||
*/
|
||||
public function copyFromStorage($storageFile, $destFile, $offset = 0, $length = -1, $timeout = 0, &$extraData = [])
|
||||
{
|
||||
$this->startTrackingTime();
|
||||
//$this->http->setTimeout($timeout / SECONDS_IN_MICROSECONDS);
|
||||
if ($length < 0) {
|
||||
// We can use the download URL to download the file in one go
|
||||
$content = $this->getFileContent($storageFile);
|
||||
if ($content === false) {
|
||||
return false;
|
||||
}
|
||||
$content = substr($content, $offset);
|
||||
if (file_put_contents($destFile, $content) === false) {
|
||||
return false;
|
||||
}
|
||||
return strlen($content);
|
||||
}
|
||||
|
||||
if (! isset($extraData['downloadUrl'])) {
|
||||
$item = $this->getItemDetailsByPath($storageFile);
|
||||
if (!isset($item['@microsoft.graph.downloadUrl'])) {
|
||||
return false;
|
||||
}
|
||||
$extraData['downloadUrl'] = $item['@microsoft.graph.downloadUrl'];
|
||||
if (file_put_contents($destFile, '') === false) {
|
||||
DUP_PRO_Log::infoTrace("[OnedriveAddon] Failed to open file for writing: {$destFile}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$downloadUrl = $extraData['downloadUrl'];
|
||||
|
||||
$range = "bytes={$offset}-" . ($offset + $length - 1);
|
||||
try {
|
||||
$response = $this->http->get($downloadUrl, [], ['Range' => $range]);
|
||||
} catch (Exception $e) {
|
||||
DUP_PRO_Log::infoTrace("[OnedriveAddon] Failed to download file: {$storageFile}. Error: {$e->getMessage()}");
|
||||
return false;
|
||||
}
|
||||
if ($response['code'] !== 206 && $response['code'] !== 200) {
|
||||
DUP_PRO_Log::infoTrace("[OnedriveAddon] Failed to download file: {$storageFile}. Response " . $response['body']);
|
||||
return false;
|
||||
}
|
||||
file_put_contents($destFile, $response['body'], FILE_APPEND);
|
||||
|
||||
return $length;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,304 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Duplicator messages sections
|
||||
*
|
||||
* @package Duplicator
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
use Duplicator\Addons\OneDriveAddon\Models\OneDriveStorage;
|
||||
|
||||
defined("ABSPATH") or die("");
|
||||
|
||||
/**
|
||||
* Variables
|
||||
*
|
||||
* @var \Duplicator\Core\Controllers\ControllersManager $ctrlMng
|
||||
* @var \Duplicator\Core\Views\TplMng $tplMng
|
||||
* @var array<string, mixed> $tplData
|
||||
* @var OneDriveStorage $storage
|
||||
*/
|
||||
$storage = $tplData["storage"];
|
||||
/** @var string */
|
||||
$storageFolder = $tplData["storageFolder"];
|
||||
/** @var int */
|
||||
$maxPackages = $tplData["maxPackages"];
|
||||
/** @var bool */
|
||||
$allFolderPers = $tplData["allFolderPers"];
|
||||
/** @var false|object */
|
||||
$accountInfo = $tplData["accountInfo"];
|
||||
/** @var false|object */
|
||||
$hasError = $tplData["hasError"];
|
||||
/** @var string */
|
||||
$externalRevokeUrl = $tplData["externalRevokeUrl"];
|
||||
|
||||
$tplMng->render('admin_pages/storages/parts/provider_head');
|
||||
?>
|
||||
<tr>
|
||||
<th scope="row"><label><?php esc_html_e("Authorization", 'duplicator-pro'); ?></label></th>
|
||||
<td class="onedrive-authorize">
|
||||
<?php if (!$storage->isAuthorized()) : ?>
|
||||
<div class='onedrive-msgraph-authorization-state' id="onedrive-msgraph-state-unauthorized">
|
||||
<div id="dup-all-onedrive-allperms-wrapper" >
|
||||
<?php esc_html_e('All folders read write permission:', 'duplicator-pro'); ?>
|
||||
<label class="switch">
|
||||
<input
|
||||
id="onedrive_msgraph_all_folders_read_write_perm"
|
||||
name="onedrive_msgraph_all_folders_read_write_perm"
|
||||
type="checkbox"
|
||||
value="1"
|
||||
<?php checked($allFolderPers); ?>
|
||||
>
|
||||
<span class="slider round"></span>
|
||||
</label>
|
||||
<div class="auth-code-popup-note" style="margin-top:1px; margin-left: 0;">
|
||||
<?php
|
||||
echo esc_html__('There is only Apps folder permission scope by default.', 'duplicator-pro') . ' ' .
|
||||
esc_html__('If your OneDrive Business is not working, Please switch on this option.', 'duplicator-pro'); ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CONNECT -->
|
||||
<button
|
||||
id="dpro-onedrive-msgraph-connect-btn"
|
||||
type="button"
|
||||
class="button button-large"
|
||||
onclick="DupPro.Storage.OneDrive.GetAuthUrl(); return false;"
|
||||
>
|
||||
<i class="fa fa-plug"></i> <?php esc_html_e('Connect to OneDrive', 'duplicator-pro'); ?>
|
||||
<img
|
||||
src="<?php echo esc_url(DUPLICATOR_PRO_IMG_URL . '/onedrive.svg'); ?>"
|
||||
style='vertical-align: middle; margin:-2px 0 0 3px; height:18px; width:18px'
|
||||
>
|
||||
</button>
|
||||
|
||||
<div class='onedrive-msgraph-auth-container' style="display: none;">
|
||||
<!-- STEP 2 -->
|
||||
<b><?php esc_html_e("Step 1:", 'duplicator-pro'); ?></b>
|
||||
<?php esc_html_e(' Duplicator needs to authorize at OneDrive.', 'duplicator-pro'); ?>
|
||||
<div class="auth-code-popup-note" style="margin-top:1px">
|
||||
<?php
|
||||
esc_html_e(
|
||||
'Note: Clicking the button below will open a new tab/window.
|
||||
Please be sure your browser does not block popups.
|
||||
If a new tab/window does not open check your browsers address bar to allow popups from this URL.',
|
||||
'duplicator-pro'
|
||||
);
|
||||
?>
|
||||
</div>
|
||||
<button
|
||||
id="dpro-onedrive-msgraph-auth-btn"
|
||||
type="button"
|
||||
class="button button-large"
|
||||
data-auth-url="<?php echo esc_attr($storage->getAuthorizationUrl()); ?>"
|
||||
>
|
||||
<i class="fa fa-user"></i> <?php esc_html_e('Authorize Onedrive', 'duplicator-pro'); ?>
|
||||
</button>
|
||||
<br/><br/>
|
||||
|
||||
<div id="onedrive-msgraph-auth-container">
|
||||
<b><?php esc_html_e('Step 2:', 'duplicator-pro'); ?></b>
|
||||
<?php esc_html_e("Paste code from OneDrive authorization page.", 'duplicator-pro'); ?> <br/>
|
||||
<input style="width:400px" id="onedrive-msgraph-auth-code" name="onedrive-msgraph-auth-code" />
|
||||
</div>
|
||||
<br><br>
|
||||
<!-- STEP 3 -->
|
||||
<b><?php esc_html_e("Step 3:", 'duplicator-pro'); ?></b>
|
||||
<?php esc_html_e('Finalize OneDrive validation by clicking the "Finalize Setup" button.', 'duplicator-pro'); ?>
|
||||
<br/>
|
||||
<button
|
||||
type="button"
|
||||
id="onedrive-msgraph-finalize-setup"
|
||||
class="button"
|
||||
>
|
||||
<i class="fa fa-check-square"></i> <?php esc_html_e('Finalize Setup', 'duplicator-pro'); ?>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="onedrive-msgraph-authorization-state" id="onedrive-msgraph-state-authorized">
|
||||
<?php if ($storage->isAuthorized()) : ?>
|
||||
<h3>
|
||||
<?php esc_html_e('OneDrive Account', 'duplicator-pro'); ?><br/>
|
||||
<i class="dpro-edit-info">
|
||||
<?php esc_html_e('Duplicator has been authorized to access this user\'s OneDrive account', 'duplicator-pro'); ?>
|
||||
</i>
|
||||
</h3>
|
||||
|
||||
<?php
|
||||
if ($accountInfo !== false) {
|
||||
?>
|
||||
<div id="onedrive-account-info">
|
||||
<label><?php esc_html_e('Name', 'duplicator-pro'); ?>:</label>
|
||||
<?php echo esc_html($accountInfo->displayName); ?> <br/>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
} elseif ($hasError) {
|
||||
?>
|
||||
<div class="error-txt">
|
||||
<?php
|
||||
echo '<strong>';
|
||||
esc_html_e('Please click on the "Cancel Authorization" button and reauthorize the OneDrive storage', 'duplicator-pro');
|
||||
echo '</strong>';
|
||||
?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<br/>
|
||||
<button type="button" class="button" onclick='DupPro.Storage.OneDrive.CancelAuthorization();'>
|
||||
<?php esc_html_e('Cancel Authorization', 'duplicator-pro'); ?>
|
||||
</button><br/>
|
||||
<i class="dpro-edit-info">
|
||||
<?php
|
||||
esc_html_e(
|
||||
'Disassociates storage provider with the OneDrive account. Will require re-authorization.',
|
||||
'duplicator-pro'
|
||||
);
|
||||
?>
|
||||
</i>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="_onedrive_msgraph_storage_folder"><?php esc_html_e("Storage Folder", 'duplicator-pro'); ?></label></th>
|
||||
<td>
|
||||
<b>//OneDrive/Apps/Duplicator Pro/</b>
|
||||
<input
|
||||
id="_onedrive_msgraph_storage_folder"
|
||||
name="_onedrive_msgraph_storage_folder"
|
||||
type="text"
|
||||
value="<?php echo esc_attr($storageFolder); ?>"
|
||||
class="dpro-storeage-folder-path" data-parsley-pattern="^((?!\:).)*[^\.\:]$"
|
||||
data-parsley-errors-container="#onedrive_msgraph_storage_folder_error_container"
|
||||
data-parsley-pattern-message="<?php
|
||||
echo esc_attr__(
|
||||
'The folder path shouldn\'t include the special character colon(":") or shouldn\'t end with a dot(".").',
|
||||
'duplicator-pro'
|
||||
); ?>"
|
||||
>
|
||||
<p>
|
||||
<i>
|
||||
<?php
|
||||
esc_html_e(
|
||||
"Folder where packages will be stored. This should be unique for each web-site using Duplicator.",
|
||||
'duplicator-pro'
|
||||
); ?>
|
||||
</i>
|
||||
</p>
|
||||
<div id="onedrive_msgraph_storage_folder_error_container" class="duplicator-error-container"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for=""><?php esc_html_e("Max Packages", 'duplicator-pro'); ?></label></th>
|
||||
<td>
|
||||
<label for="onedrive_msgraph_max_files">
|
||||
<input
|
||||
data-parsley-errors-container="#onedrive_msgraph_max_files_error_container"
|
||||
id="onedrive_msgraph_max_files"
|
||||
name="onedrive_msgraph_max_files"
|
||||
type="text"
|
||||
value="<?php echo absint($maxPackages); ?>" maxlength="4"
|
||||
>
|
||||
<?php esc_html_e("Number of packages to keep in folder.", 'duplicator-pro'); ?> <br/>
|
||||
<i><?php esc_html_e("When this limit is exceeded, the oldest package will be deleted. Set to 0 for no limit.", 'duplicator-pro'); ?></i>
|
||||
</label>
|
||||
<div id="onedrive_msgraph_max_files_error_container" class="duplicator-error-container"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php $tplMng->render('admin_pages/storages/parts/provider_foot');
|
||||
|
||||
// Alerts for OneDrive
|
||||
$alertConnStatus = new DUP_PRO_UI_Dialog();
|
||||
$alertConnStatus->title = __('OneDrive Connection Status', 'duplicator-pro');
|
||||
$alertConnStatus->message = ''; // javascript inserted message
|
||||
$alertConnStatus->initAlert();
|
||||
?>
|
||||
<script>
|
||||
jQuery(document).ready(function ($) {
|
||||
let storageId = <?php echo (int) $storage->getId(); ?>;
|
||||
|
||||
DupPro.Storage.OneDrive.GetAuthUrl = function ()
|
||||
{
|
||||
$("#dpro-onedrive-msgraph-connect-btn").hide();
|
||||
$(".onedrive-msgraph-auth-container").show();
|
||||
};
|
||||
|
||||
$('#dpro-onedrive-msgraph-auth-btn').click(function() {
|
||||
let authUrl = $(this).data('auth-url');
|
||||
// console.log(DupPro.Storage.OneDrive.AuthUrl);
|
||||
window.open(authUrl, '_blank');
|
||||
});
|
||||
|
||||
DupPro.Storage.OneDrive.CancelAuthorization = function ()
|
||||
{
|
||||
window.open(<?php echo json_encode($externalRevokeUrl); ?>, '_blank');
|
||||
DupPro.Storage.RevokeAuth(storageId);
|
||||
}
|
||||
|
||||
DupPro.Storage.OneDrive.FinalizeSetup = function () {
|
||||
if ($('#onedrive-msgraph-auth-code').val().length > 5) {
|
||||
$("#dup-storage-form").submit();
|
||||
} else {
|
||||
<?php $alertConnStatus->showAlert(); ?>
|
||||
let alertMsg = "<i class='fas fa-exclamation-triangle'></i> " +
|
||||
"<?php esc_html_e('Please enter your OneDrive authorization code!', 'duplicator-pro'); ?>";
|
||||
<?php $alertConnStatus->updateMessage("alertMsg"); ?>
|
||||
}
|
||||
}
|
||||
|
||||
$('#onedrive_msgraph_all_folders_read_write_perm').change(function (event) {
|
||||
event.stopPropagation();
|
||||
let allPerms = $(this).is(':checked');
|
||||
Duplicator.Util.ajaxWrapper(
|
||||
{
|
||||
action: 'duplicator_pro_onedrive_all_perms_update',
|
||||
storage_id: storageId,
|
||||
all_perms: allPerms,
|
||||
nonce: '<?php echo esc_js(wp_create_nonce('duplicator_pro_onedrive_all_perms_update')); ?>'
|
||||
},
|
||||
function (result, data, funcData, textStatus, jqXHR) {
|
||||
if (funcData.success) {
|
||||
$('#dpro-onedrive-msgraph-auth-btn').data('auth-url', funcData.auth_url);
|
||||
} else {
|
||||
DupPro.addAdminMessage(funcData.message, 'error');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
$('#onedrive-msgraph-finalize-setup').click(function (event) {
|
||||
event.stopPropagation();
|
||||
|
||||
if ($('#onedrive-msgraph-auth-code').val().length > 5) {
|
||||
DupPro.Storage.PrepareForSubmit();
|
||||
|
||||
//$("#dup-storage-form").submit();
|
||||
|
||||
DupPro.Storage.Authorize(
|
||||
<?php echo (int) $storage->getId(); ?>,
|
||||
<?php echo (int) $storage->getSType(); ?>,
|
||||
{
|
||||
'name': $('#name').val(),
|
||||
'notes': $('#notes').val(),
|
||||
'storage_folder': $('#_onedrive_msgraph_storage_folder').val(),
|
||||
'max_packages': $('#onedrive_msgraph_max_files').val(),
|
||||
'auth_code' : $('#onedrive-msgraph-auth-code').val()
|
||||
}
|
||||
);
|
||||
} else {
|
||||
<?php $alertConnStatus->showAlert(); ?>
|
||||
let alertMsg = "<i class='fas fa-exclamation-triangle'></i> " +
|
||||
"<?php esc_html_e('Please enter your Onedrive authorization code!', 'duplicator-pro'); ?>";
|
||||
<?php $alertConnStatus->updateMessage("alertMsg"); ?>
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user