Version Description
- Dropbox API update to V2
- Code fixes and text changes
Download this release
Release Info
Developer | xcloner |
Plugin | XCloner – Backup and Restore |
Version | 4.0.5 |
Comparing to | |
See all releases |
Code changes from version 4.0.4 to 4.0.5
- README.txt +6 -2
- includes/class-xcloner-database.php +5 -0
- includes/class-xcloner-scheduler.php +9 -1
- includes/class-xcloner-settings.php +12 -0
- vendor/splitbrain/php-archive/src/Tar.php +1 -0
- vendor/srmklive/flysystem-dropbox-v2/src/Client/DropboxClient.php +241 -5
- vendor/srmklive/flysystem-dropbox-v2/src/DropboxUploadCounter.php +30 -0
- vendor/srmklive/flysystem-dropbox-v2/tests/ClientTest.php +243 -1
- xcloner.php +1 -1
README.txt
CHANGED
@@ -3,8 +3,8 @@ Contributors: xcloner
|
|
3 |
Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=AAPE8PLAE554S
|
4 |
Tags: backup plugin, restore plugin, database backup, duplicate, full site backup, website cloner, wordpress backup, database restore, webdav, azure, ftp, sftp, amazon s3, dropbox, google drive, differential backup
|
5 |
Requires at least: 3.0.1
|
6 |
-
Tested up to: 4.
|
7 |
-
Stable tag: 4.0.
|
8 |
|
9 |
Backup your site, restore to any web location, send your backups to Dropbox, Amazon S3, Azure, FTP, SFTP, WebDAV, Google Drive with XCloner plugin.
|
10 |
|
@@ -108,6 +108,10 @@ Of course, schedules can be adjusted accordingly to how often you update your si
|
|
108 |
|
109 |
== Changelog ==
|
110 |
|
|
|
|
|
|
|
|
|
111 |
= 4.0.4 =
|
112 |
* remote storage view fix
|
113 |
* added automatic backups option before WP automatic update
|
3 |
Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=AAPE8PLAE554S
|
4 |
Tags: backup plugin, restore plugin, database backup, duplicate, full site backup, website cloner, wordpress backup, database restore, webdav, azure, ftp, sftp, amazon s3, dropbox, google drive, differential backup
|
5 |
Requires at least: 3.0.1
|
6 |
+
Tested up to: 4.9
|
7 |
+
Stable tag: 4.0.5
|
8 |
|
9 |
Backup your site, restore to any web location, send your backups to Dropbox, Amazon S3, Azure, FTP, SFTP, WebDAV, Google Drive with XCloner plugin.
|
10 |
|
108 |
|
109 |
== Changelog ==
|
110 |
|
111 |
+
= 4.0.5 =
|
112 |
+
* Dropbox API update to V2
|
113 |
+
* Code fixes and text changes
|
114 |
+
|
115 |
= 4.0.4 =
|
116 |
* remote storage view fix
|
117 |
* added automatic backups option before WP automatic update
|
includes/class-xcloner-database.php
CHANGED
@@ -509,6 +509,11 @@ class Xcloner_Database extends wpdb{
|
|
509 |
|
510 |
foreach ($arr as $key => $value) {
|
511 |
$value = $this->_real_escape($value);
|
|
|
|
|
|
|
|
|
|
|
512 |
$buffer .= "'".$value."', ";
|
513 |
}
|
514 |
$buffer = rtrim($buffer, ', ') . ");\n";
|
509 |
|
510 |
foreach ($arr as $key => $value) {
|
511 |
$value = $this->_real_escape($value);
|
512 |
+
|
513 |
+
if(method_exists($this, 'remove_placeholder_escape')){
|
514 |
+
$value = $this->remove_placeholder_escape($value);
|
515 |
+
}
|
516 |
+
|
517 |
$buffer .= "'".$value."', ";
|
518 |
}
|
519 |
$buffer = rtrim($buffer, ', ') . ");\n";
|
includes/class-xcloner-scheduler.php
CHANGED
@@ -323,12 +323,20 @@ class Xcloner_Scheduler{
|
|
323 |
$subject = sprintf(__("%s - new backup generated %s") , $schedule['name'], $return['extra']['backup_parent']);
|
324 |
|
325 |
$this->archive_system->send_notification($to, $from, $subject, $return['extra']['backup_parent'], $schedule, "", $additional);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
326 |
}catch(Exception $e)
|
327 |
{
|
328 |
$this->logger->error($e->getMessage());
|
329 |
}
|
330 |
}
|
331 |
-
|
332 |
$this->xcloner_file_system->remove_tmp_filesystem();
|
333 |
|
334 |
$this->xcloner_file_system->backup_storage_cleanup();
|
323 |
$subject = sprintf(__("%s - new backup generated %s") , $schedule['name'], $return['extra']['backup_parent']);
|
324 |
|
325 |
$this->archive_system->send_notification($to, $from, $subject, $return['extra']['backup_parent'], $schedule, "", $additional);
|
326 |
+
|
327 |
+
//CHECK IF WE SHOULD DELETE BACKUP AFTER REMOTE TRANSFER IS DONE
|
328 |
+
if($schedule['remote_storage'] && $this->xcloner_settings->get_xcloner_option('xcloner_cleanup_delete_after_remote_transfer'))
|
329 |
+
{
|
330 |
+
$this->logger->info(sprintf("Deleting %s from local storage matching rule xcloner_cleanup_delete_after_remote_transfer",$return['extra']['backup_parent']));
|
331 |
+
$this->xcloner_file_system->delete_backup_by_name($return['extra']['backup_parent']);
|
332 |
+
|
333 |
+
}
|
334 |
}catch(Exception $e)
|
335 |
{
|
336 |
$this->logger->error($e->getMessage());
|
337 |
}
|
338 |
}
|
339 |
+
|
340 |
$this->xcloner_file_system->remove_tmp_filesystem();
|
341 |
|
342 |
$this->xcloner_file_system->backup_storage_cleanup();
|
includes/class-xcloner-settings.php
CHANGED
@@ -562,6 +562,18 @@ class Xcloner_Settings
|
|
562 |
__('Remove oldest backups if all created backups exceed the configured limit in Megabytes. 0 disables this option','xcloner-backup-and-restore')
|
563 |
)
|
564 |
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
565 |
|
566 |
//REGISTERING THE 'CRON SECTION' FIELDS
|
567 |
register_setting('xcloner_cron_settings_group', 'xcloner_cron_frequency');
|
562 |
__('Remove oldest backups if all created backups exceed the configured limit in Megabytes. 0 disables this option','xcloner-backup-and-restore')
|
563 |
)
|
564 |
);
|
565 |
+
|
566 |
+
register_setting('xcloner_cleanup_settings_group', 'xcloner_cleanup_delete_after_remote_transfer', array($this->xcloner_sanitization, "sanitize_input_as_int"));
|
567 |
+
add_settings_field(
|
568 |
+
'xcloner_cleanup_delete_after_remote_transfer',
|
569 |
+
__('Delete Backup After Remote Storage Transfer','xcloner-backup-and-restore'),
|
570 |
+
array($this, 'do_form_switch_field'),
|
571 |
+
'xcloner_cleanup_settings_page',
|
572 |
+
'xcloner_cleanup_settings_group',
|
573 |
+
array('xcloner_cleanup_delete_after_remote_transfer',
|
574 |
+
__('Remove backup created automatically from local storage after sending the backup to Remote Storage','xcloner-backup-and-restore')
|
575 |
+
)
|
576 |
+
);
|
577 |
|
578 |
//REGISTERING THE 'CRON SECTION' FIELDS
|
579 |
register_setting('xcloner_cron_settings_group', 'xcloner_cron_frequency');
|
vendor/splitbrain/php-archive/src/Tar.php
CHANGED
@@ -728,6 +728,7 @@ class Tar extends Archive
|
|
728 |
$return['checksum'] = OctDec(trim($header['checksum']));
|
729 |
|
730 |
if ($return['checksum'] != $chks) {
|
|
|
731 |
throw new ArchiveCorruptedException('Header does not match it\'s checksum for ');
|
732 |
}
|
733 |
|
728 |
$return['checksum'] = OctDec(trim($header['checksum']));
|
729 |
|
730 |
if ($return['checksum'] != $chks) {
|
731 |
+
//print_r($header);exit;
|
732 |
throw new ArchiveCorruptedException('Header does not match it\'s checksum for ');
|
733 |
}
|
734 |
|
vendor/srmklive/flysystem-dropbox-v2/src/Client/DropboxClient.php
CHANGED
@@ -6,6 +6,7 @@ use GuzzleHttp\Client as HttpClient;
|
|
6 |
use GuzzleHttp\Exception\ClientException as HttpClientException;
|
7 |
use GuzzleHttp\Psr7\StreamWrapper;
|
8 |
use Illuminate\Support\Collection;
|
|
|
9 |
use Srmklive\Dropbox\Exceptions\BadRequest;
|
10 |
|
11 |
class DropboxClient
|
@@ -19,6 +20,8 @@ class DropboxClient
|
|
19 |
const THUMBNAIL_SIZE_L = 'w640h480';
|
20 |
const THUMBNAIL_SIZE_XL = 'w1024h768';
|
21 |
|
|
|
|
|
22 |
/** @var \GuzzleHttp\Client */
|
23 |
protected $client;
|
24 |
|
@@ -343,11 +346,46 @@ class DropboxClient
|
|
343 |
'to_path' => $this->normalizePath($toPath),
|
344 |
]);
|
345 |
|
346 |
-
$this->apiEndpoint = 'files/
|
347 |
|
348 |
return $this->doDropboxApiRequest();
|
349 |
}
|
350 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
351 |
/**
|
352 |
* Create a new file with the contents provided in the request.
|
353 |
*
|
@@ -363,6 +401,10 @@ class DropboxClient
|
|
363 |
*/
|
364 |
public function upload($path, $contents, $mode = 'add')
|
365 |
{
|
|
|
|
|
|
|
|
|
366 |
$this->setupRequest([
|
367 |
'path' => $this->normalizePath($path),
|
368 |
'mode' => $mode,
|
@@ -374,12 +416,206 @@ class DropboxClient
|
|
374 |
|
375 |
$response = $this->doDropboxApiContentRequest();
|
376 |
|
377 |
-
$metadata =
|
378 |
$metadata['.tag'] = 'file';
|
379 |
|
380 |
return $metadata;
|
381 |
}
|
382 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
383 |
/**
|
384 |
* Set Dropbox API request data.
|
385 |
*
|
@@ -399,10 +635,10 @@ class DropboxClient
|
|
399 |
*/
|
400 |
protected function doDropboxApiRequest()
|
401 |
{
|
|
|
|
|
402 |
try {
|
403 |
-
$response = $this->client->post("{$this->apiUrl}{$this->apiEndpoint}",
|
404 |
-
'json' => $this->request->toArray(),
|
405 |
-
]);
|
406 |
} catch (HttpClientException $exception) {
|
407 |
throw $this->determineException($exception);
|
408 |
}
|
6 |
use GuzzleHttp\Exception\ClientException as HttpClientException;
|
7 |
use GuzzleHttp\Psr7\StreamWrapper;
|
8 |
use Illuminate\Support\Collection;
|
9 |
+
use Srmklive\Dropbox\DropboxUploadCounter;
|
10 |
use Srmklive\Dropbox\Exceptions\BadRequest;
|
11 |
|
12 |
class DropboxClient
|
20 |
const THUMBNAIL_SIZE_L = 'w640h480';
|
21 |
const THUMBNAIL_SIZE_XL = 'w1024h768';
|
22 |
|
23 |
+
const MAX_CHUNK_SIZE = 15728640; //150MB
|
24 |
+
|
25 |
/** @var \GuzzleHttp\Client */
|
26 |
protected $client;
|
27 |
|
346 |
'to_path' => $this->normalizePath($toPath),
|
347 |
]);
|
348 |
|
349 |
+
$this->apiEndpoint = 'files/move_v2';
|
350 |
|
351 |
return $this->doDropboxApiRequest();
|
352 |
}
|
353 |
|
354 |
+
/**
|
355 |
+
* The file should be uploaded in chunks if it size exceeds the 150 MB threshold
|
356 |
+
* or if the resource size could not be determined (eg. a popen() stream).
|
357 |
+
*
|
358 |
+
* @param string|resource $contents
|
359 |
+
*
|
360 |
+
* @return bool
|
361 |
+
*/
|
362 |
+
protected function shouldUploadChunk($contents)
|
363 |
+
{
|
364 |
+
$size = is_string($contents) ? strlen($contents) : fstat($contents)['size'];
|
365 |
+
|
366 |
+
if ($this->isPipe($contents)) {
|
367 |
+
return true;
|
368 |
+
}
|
369 |
+
|
370 |
+
if ($size === null) {
|
371 |
+
return true;
|
372 |
+
}
|
373 |
+
|
374 |
+
return $size > static::MAX_CHUNK_SIZE;
|
375 |
+
}
|
376 |
+
|
377 |
+
/**
|
378 |
+
* Check if the contents is a pipe stream (not seekable, no size defined).
|
379 |
+
*
|
380 |
+
* @param string|resource $contents
|
381 |
+
*
|
382 |
+
* @return bool
|
383 |
+
*/
|
384 |
+
protected function isPipe($contents)
|
385 |
+
{
|
386 |
+
return is_resource($contents) ? (fstat($contents)['mode'] & 010000) != 0 : false;
|
387 |
+
}
|
388 |
+
|
389 |
/**
|
390 |
* Create a new file with the contents provided in the request.
|
391 |
*
|
401 |
*/
|
402 |
public function upload($path, $contents, $mode = 'add')
|
403 |
{
|
404 |
+
if ($this->shouldUploadChunk($contents)) {
|
405 |
+
return $this->uploadChunk($path, $contents, $mode);
|
406 |
+
}
|
407 |
+
|
408 |
$this->setupRequest([
|
409 |
'path' => $this->normalizePath($path),
|
410 |
'mode' => $mode,
|
416 |
|
417 |
$response = $this->doDropboxApiContentRequest();
|
418 |
|
419 |
+
$metadata = json_decode($response->getBody(), true);
|
420 |
$metadata['.tag'] = 'file';
|
421 |
|
422 |
return $metadata;
|
423 |
}
|
424 |
|
425 |
+
/**
|
426 |
+
* Upload file split in chunks. This allows uploading large files, since
|
427 |
+
* Dropbox API v2 limits the content size to 150MB.
|
428 |
+
*
|
429 |
+
* The chunk size will affect directly the memory usage, so be careful.
|
430 |
+
* Large chunks tends to speed up the upload, while smaller optimizes memory usage.
|
431 |
+
*
|
432 |
+
* @param string $path
|
433 |
+
* @param string|resource $contents
|
434 |
+
* @param string $mode
|
435 |
+
* @param int $chunkSize
|
436 |
+
*
|
437 |
+
* @return array
|
438 |
+
*/
|
439 |
+
public function uploadChunk($path, $contents, $mode = 'add', $chunkSize = null)
|
440 |
+
{
|
441 |
+
$chunkSize = ($chunkSize) ? $chunkSize: static::MAX_CHUNK_SIZE;
|
442 |
+
$stream = $contents;
|
443 |
+
|
444 |
+
// This method relies on resources, so we need to convert strings to resource
|
445 |
+
if (is_string($contents)) {
|
446 |
+
$stream = fopen('php://memory', 'r+');
|
447 |
+
fwrite($stream, $contents);
|
448 |
+
rewind($stream);
|
449 |
+
}
|
450 |
+
|
451 |
+
$data = self::readChunk($stream, $chunkSize);
|
452 |
+
$cursor = null;
|
453 |
+
|
454 |
+
while (!((strlen($data) < $chunkSize) || feof($stream))) {
|
455 |
+
// Start upload session on first iteration, then just append on subsequent iterations
|
456 |
+
$cursor = isset($cursor) ? $this->appendContentToUploadSession($data, $cursor) : $this->startUploadSession($data);
|
457 |
+
$data = self::readChunk($stream, $chunkSize);
|
458 |
+
}
|
459 |
+
|
460 |
+
// If there's no cursor here, our stream is small enough to a single request
|
461 |
+
if (!isset($cursor)) {
|
462 |
+
$cursor = $this->startUploadSession($data);
|
463 |
+
$data = '';
|
464 |
+
}
|
465 |
+
|
466 |
+
return $this->finishUploadSession($data, $cursor, $path, $mode);
|
467 |
+
}
|
468 |
+
|
469 |
+
/**
|
470 |
+
* Upload sessions allow you to upload a single file in one or more requests,
|
471 |
+
* for example where the size of the file is greater than 150 MB.
|
472 |
+
* This call starts a new upload session with the given data.
|
473 |
+
*
|
474 |
+
* @link https://www.dropbox.com/developers/documentation/http/documentation#files-upload_session-start
|
475 |
+
*
|
476 |
+
* @param string $contents
|
477 |
+
* @param bool $close
|
478 |
+
*
|
479 |
+
* @return \Srmklive\Dropbox\DropboxUploadCounter
|
480 |
+
*/
|
481 |
+
public function startUploadSession($contents, $close = false)
|
482 |
+
{
|
483 |
+
$this->setupRequest(
|
484 |
+
compact('close')
|
485 |
+
);
|
486 |
+
|
487 |
+
$this->apiEndpoint = 'files/upload_session/start';
|
488 |
+
|
489 |
+
$this->content = $contents;
|
490 |
+
|
491 |
+
$response = json_decode(
|
492 |
+
$this->doDropboxApiContentRequest()->getBody(),
|
493 |
+
true
|
494 |
+
);
|
495 |
+
|
496 |
+
return new DropboxUploadCounter($response['session_id'], strlen($contents));
|
497 |
+
}
|
498 |
+
|
499 |
+
/**
|
500 |
+
* Append more data to an upload session.
|
501 |
+
* When the parameter close is set, this call will close the session.
|
502 |
+
* A single request should not upload more than 150 MB.
|
503 |
+
*
|
504 |
+
* @link https://www.dropbox.com/developers/documentation/http/documentation#files-upload_session-append_v2
|
505 |
+
*
|
506 |
+
* @param string $contents
|
507 |
+
* @param DropboxUploadCounter $cursor
|
508 |
+
* @param bool $close
|
509 |
+
*
|
510 |
+
* @return \Srmklive\Dropbox\DropboxUploadCounter
|
511 |
+
*/
|
512 |
+
public function appendContentToUploadSession($contents, DropboxUploadCounter $cursor, $close = false)
|
513 |
+
{
|
514 |
+
$this->setupRequest(compact('cursor', 'close'));
|
515 |
+
|
516 |
+
$this->apiEndpoint = 'files/upload_session/append_v2';
|
517 |
+
|
518 |
+
$this->content = $contents;
|
519 |
+
|
520 |
+
$this->doDropboxApiContentRequest()->getBody();
|
521 |
+
|
522 |
+
$cursor->offset += strlen($contents);
|
523 |
+
|
524 |
+
return $cursor;
|
525 |
+
}
|
526 |
+
|
527 |
+
/**
|
528 |
+
* Finish an upload session and save the uploaded data to the given file path.
|
529 |
+
* A single request should not upload more than 150 MB.
|
530 |
+
*
|
531 |
+
* @link https://www.dropbox.com/developers/documentation/http/documentation#files-upload_session-finish
|
532 |
+
*
|
533 |
+
* @param string $contents
|
534 |
+
* @param \Srmklive\Dropbox\DropboxUploadCounter $cursor
|
535 |
+
* @param string $path
|
536 |
+
* @param string|array $mode
|
537 |
+
* @param bool $autorename
|
538 |
+
* @param bool $mute
|
539 |
+
*
|
540 |
+
* @return array
|
541 |
+
*/
|
542 |
+
public function finishUploadSession($contents, DropboxUploadCounter $cursor, $path, $mode = 'add', $autorename = false, $mute = false)
|
543 |
+
{
|
544 |
+
$arguments = compact('cursor');
|
545 |
+
$arguments['commit'] = compact('path', 'mode', 'autorename', 'mute');
|
546 |
+
|
547 |
+
$this->setupRequest($arguments);
|
548 |
+
|
549 |
+
$this->apiEndpoint = 'files/upload_session/finish';
|
550 |
+
|
551 |
+
$this->content = $contents;
|
552 |
+
|
553 |
+
$response = $this->doDropboxApiContentRequest();
|
554 |
+
|
555 |
+
$metadata = json_decode($response->getBody(), true);
|
556 |
+
|
557 |
+
$metadata['.tag'] = 'file';
|
558 |
+
|
559 |
+
return $metadata;
|
560 |
+
}
|
561 |
+
|
562 |
+
/**
|
563 |
+
* Sometimes fread() returns less than the request number of bytes (for example, when reading
|
564 |
+
* from network streams). This function repeatedly calls fread until the requested number of
|
565 |
+
* bytes have been read or we've reached EOF.
|
566 |
+
*
|
567 |
+
* @param resource $stream
|
568 |
+
* @param int $chunkSize
|
569 |
+
*
|
570 |
+
* @throws \Exception
|
571 |
+
*
|
572 |
+
* @return string
|
573 |
+
*/
|
574 |
+
protected static function readChunk($stream, $chunkSize)
|
575 |
+
{
|
576 |
+
$chunk = '';
|
577 |
+
while (!feof($stream) && $chunkSize > 0) {
|
578 |
+
$part = fread($stream, $chunkSize);
|
579 |
+
|
580 |
+
if ($part === false) {
|
581 |
+
throw new \Exception('Error reading from $stream.');
|
582 |
+
}
|
583 |
+
|
584 |
+
$chunk .= $part;
|
585 |
+
$chunkSize -= strlen($part);
|
586 |
+
}
|
587 |
+
|
588 |
+
return $chunk;
|
589 |
+
}
|
590 |
+
|
591 |
+
/**
|
592 |
+
* Get Account Info for current authenticated user.
|
593 |
+
*
|
594 |
+
* @link https://www.dropbox.com/developers/documentation/http/documentation#users-get_current_account
|
595 |
+
*
|
596 |
+
* @return \Psr\Http\Message\ResponseInterface
|
597 |
+
*/
|
598 |
+
public function getAccountInfo()
|
599 |
+
{
|
600 |
+
$this->apiEndpoint = 'users/get_current_account';
|
601 |
+
|
602 |
+
return $this->doDropboxApiRequest();
|
603 |
+
}
|
604 |
+
|
605 |
+
/**
|
606 |
+
* Revoke current access token.
|
607 |
+
*
|
608 |
+
* @link https://www.dropbox.com/developers/documentation/http/documentation#auth-token-revoke
|
609 |
+
*
|
610 |
+
* @return \Psr\Http\Message\ResponseInterface
|
611 |
+
*/
|
612 |
+
public function revokeToken()
|
613 |
+
{
|
614 |
+
$this->apiEndpoint = 'auth/token/revoke';
|
615 |
+
|
616 |
+
return $this->doDropboxApiRequest();
|
617 |
+
}
|
618 |
+
|
619 |
/**
|
620 |
* Set Dropbox API request data.
|
621 |
*
|
635 |
*/
|
636 |
protected function doDropboxApiRequest()
|
637 |
{
|
638 |
+
$request = empty($this->request) ? [] : ['json' => $this->request->toArray()];
|
639 |
+
|
640 |
try {
|
641 |
+
$response = $this->client->post("{$this->apiUrl}{$this->apiEndpoint}", $request);
|
|
|
|
|
642 |
} catch (HttpClientException $exception) {
|
643 |
throw $this->determineException($exception);
|
644 |
}
|
vendor/srmklive/flysystem-dropbox-v2/src/DropboxUploadCounter.php
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Srmklive\Dropbox;
|
4 |
+
|
5 |
+
class DropboxUploadCounter
|
6 |
+
{
|
7 |
+
/**
|
8 |
+
* The upload session ID (returned by upload_session/start).
|
9 |
+
*
|
10 |
+
* @var string
|
11 |
+
*/
|
12 |
+
public $session_id;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* The amount of data that has been uploaded so far. We use this to make sure upload data isn't lost or duplicated in the event of a network error.
|
16 |
+
*
|
17 |
+
* @var int
|
18 |
+
*/
|
19 |
+
public $offset;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @param string $session_id
|
23 |
+
* @param int $offset
|
24 |
+
*/
|
25 |
+
public function __construct($session_id, $offset = 0)
|
26 |
+
{
|
27 |
+
$this->session_id = $session_id;
|
28 |
+
$this->offset = $offset;
|
29 |
+
}
|
30 |
+
}
|
vendor/srmklive/flysystem-dropbox-v2/tests/ClientTest.php
CHANGED
@@ -7,6 +7,7 @@ use PHPUnit\Framework\TestCase;
|
|
7 |
use Psr\Http\Message\ResponseInterface;
|
8 |
use Psr\Http\Message\StreamInterface;
|
9 |
use Srmklive\Dropbox\Client\DropboxClient as Client;
|
|
|
10 |
|
11 |
class ClientTest extends TestCase
|
12 |
{
|
@@ -223,7 +224,7 @@ class ClientTest extends TestCase
|
|
223 |
|
224 |
$mockHttpClient = $this->mock_http_request(
|
225 |
json_encode($expectedResponse),
|
226 |
-
'https://api.dropboxapi.com/2/files/
|
227 |
[
|
228 |
'json' => [
|
229 |
'from_path' => '/from/path/file.txt',
|
@@ -265,6 +266,206 @@ class ClientTest extends TestCase
|
|
265 |
);
|
266 |
}
|
267 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
268 |
private function mock_http_request($expectedResponse, $expectedEndpoint, $expectedParams)
|
269 |
{
|
270 |
$mockResponse = $this->getMockBuilder(ResponseInterface::class)
|
@@ -283,4 +484,45 @@ class ClientTest extends TestCase
|
|
283 |
|
284 |
return $mockHttpClient;
|
285 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
286 |
}
|
7 |
use Psr\Http\Message\ResponseInterface;
|
8 |
use Psr\Http\Message\StreamInterface;
|
9 |
use Srmklive\Dropbox\Client\DropboxClient as Client;
|
10 |
+
use Srmklive\Dropbox\DropboxUploadCounter as UploadSessionCursor;
|
11 |
|
12 |
class ClientTest extends TestCase
|
13 |
{
|
224 |
|
225 |
$mockHttpClient = $this->mock_http_request(
|
226 |
json_encode($expectedResponse),
|
227 |
+
'https://api.dropboxapi.com/2/files/move_v2',
|
228 |
[
|
229 |
'json' => [
|
230 |
'from_path' => '/from/path/file.txt',
|
266 |
);
|
267 |
}
|
268 |
|
269 |
+
/** @test */
|
270 |
+
public function it_can_start_upload_session()
|
271 |
+
{
|
272 |
+
$mockGuzzle = $this->mock_http_request(
|
273 |
+
json_encode(['session_id' => 'mockedUploadSessionId']),
|
274 |
+
'https://content.dropboxapi.com/2/files/upload_session/start',
|
275 |
+
[
|
276 |
+
'headers' => [
|
277 |
+
'Dropbox-API-Arg' => json_encode(
|
278 |
+
[
|
279 |
+
'close' => false,
|
280 |
+
]
|
281 |
+
),
|
282 |
+
'Content-Type' => 'application/octet-stream',
|
283 |
+
],
|
284 |
+
'body' => 'this text have 23 bytes',
|
285 |
+
]
|
286 |
+
);
|
287 |
+
|
288 |
+
$client = new Client('test_token', $mockGuzzle);
|
289 |
+
|
290 |
+
$uploadSessionCursor = $client->startUploadSession('this text have 23 bytes');
|
291 |
+
|
292 |
+
$this->assertInstanceOf(UploadSessionCursor::class, $uploadSessionCursor);
|
293 |
+
$this->assertEquals('mockedUploadSessionId', $uploadSessionCursor->session_id);
|
294 |
+
$this->assertEquals(23, $uploadSessionCursor->offset);
|
295 |
+
}
|
296 |
+
|
297 |
+
/** @test */
|
298 |
+
public function it_can_append_to_upload_session()
|
299 |
+
{
|
300 |
+
$mockGuzzle = $this->mock_http_request(
|
301 |
+
null,
|
302 |
+
'https://content.dropboxapi.com/2/files/upload_session/append_v2',
|
303 |
+
[
|
304 |
+
'headers' => [
|
305 |
+
'Dropbox-API-Arg' => json_encode(
|
306 |
+
[
|
307 |
+
'cursor' => [
|
308 |
+
'session_id' => 'mockedUploadSessionId',
|
309 |
+
'offset' => 10,
|
310 |
+
],
|
311 |
+
'close' => false,
|
312 |
+
]
|
313 |
+
),
|
314 |
+
'Content-Type' => 'application/octet-stream',
|
315 |
+
],
|
316 |
+
'body' => 'this text has 32 bytes',
|
317 |
+
]
|
318 |
+
);
|
319 |
+
|
320 |
+
$client = new Client('test_token', $mockGuzzle);
|
321 |
+
|
322 |
+
$oldUploadSessionCursor = new UploadSessionCursor('mockedUploadSessionId', 10);
|
323 |
+
|
324 |
+
$uploadSessionCursor = $client->appendContentToUploadSession('this text has 32 bytes', $oldUploadSessionCursor);
|
325 |
+
|
326 |
+
$this->assertInstanceOf(UploadSessionCursor::class, $uploadSessionCursor);
|
327 |
+
$this->assertEquals('mockedUploadSessionId', $uploadSessionCursor->session_id);
|
328 |
+
$this->assertEquals(32, $uploadSessionCursor->offset);
|
329 |
+
}
|
330 |
+
|
331 |
+
/** @test */
|
332 |
+
public function it_can_upload_a_file_string_chunk()
|
333 |
+
{
|
334 |
+
$content = 'chunk0chunk1chunk2rest';
|
335 |
+
$mockClient = $this->mock_chunk_upload_client($content, 6);
|
336 |
+
|
337 |
+
$this->assertEquals(
|
338 |
+
['name' => 'answers.txt'],
|
339 |
+
$mockClient->uploadChunk('Homework/math/answers.txt', $content, 'add', 6)
|
340 |
+
);
|
341 |
+
}
|
342 |
+
|
343 |
+
/** @test */
|
344 |
+
public function it_can_upload_a_file_resource_chunk()
|
345 |
+
{
|
346 |
+
$content = 'chunk0chunk1chunk2rest';
|
347 |
+
$resource = fopen('php://memory', 'r+');
|
348 |
+
fwrite($resource, $content);
|
349 |
+
rewind($resource);
|
350 |
+
|
351 |
+
$mockClient = $this->mock_chunk_upload_client($content, 6);
|
352 |
+
|
353 |
+
$this->assertEquals(
|
354 |
+
['name' => 'answers.txt'],
|
355 |
+
$mockClient->uploadChunk('Homework/math/answers.txt', $resource, 'add', 6)
|
356 |
+
);
|
357 |
+
}
|
358 |
+
|
359 |
+
/** @test */
|
360 |
+
public function it_can_upload_a_tiny_file_chunk()
|
361 |
+
{
|
362 |
+
$content = 'smallerThenChunkSize';
|
363 |
+
$resource = fopen('php://memory', 'r+');
|
364 |
+
fwrite($resource, $content);
|
365 |
+
rewind($resource);
|
366 |
+
|
367 |
+
$mockClient = $this->mock_chunk_upload_client($content, 21);
|
368 |
+
|
369 |
+
$this->assertEquals(
|
370 |
+
['name' => 'answers.txt'],
|
371 |
+
$mockClient->uploadChunk('Homework/math/answers.txt', $resource, 'add', 21)
|
372 |
+
);
|
373 |
+
}
|
374 |
+
|
375 |
+
/** @test */
|
376 |
+
public function it_can_finish_an_upload_session()
|
377 |
+
{
|
378 |
+
$mockGuzzle = $this->mock_http_request(
|
379 |
+
json_encode([
|
380 |
+
'name' => 'answers.txt',
|
381 |
+
]),
|
382 |
+
'https://content.dropboxapi.com/2/files/upload_session/finish',
|
383 |
+
[
|
384 |
+
'headers' => [
|
385 |
+
'Dropbox-API-Arg' => json_encode([
|
386 |
+
'cursor' => [
|
387 |
+
'session_id' => 'mockedUploadSessionId',
|
388 |
+
'offset' => 10,
|
389 |
+
],
|
390 |
+
'commit' => [
|
391 |
+
'path' => 'Homework/math/answers.txt',
|
392 |
+
'mode' => 'add',
|
393 |
+
'autorename' => false,
|
394 |
+
'mute' => false,
|
395 |
+
],
|
396 |
+
]),
|
397 |
+
'Content-Type' => 'application/octet-stream',
|
398 |
+
],
|
399 |
+
'body' => 'this text has 32 bytes',
|
400 |
+
]
|
401 |
+
);
|
402 |
+
|
403 |
+
$client = new Client('test_token', $mockGuzzle);
|
404 |
+
|
405 |
+
$oldUploadSessionCursor = new UploadSessionCursor('mockedUploadSessionId', 10);
|
406 |
+
|
407 |
+
$response = $client->finishUploadSession(
|
408 |
+
'this text has 32 bytes',
|
409 |
+
$oldUploadSessionCursor,
|
410 |
+
'Homework/math/answers.txt'
|
411 |
+
);
|
412 |
+
|
413 |
+
$this->assertEquals([
|
414 |
+
'.tag' => 'file',
|
415 |
+
'name' => 'answers.txt',
|
416 |
+
], $response);
|
417 |
+
}
|
418 |
+
|
419 |
+
/** @test */
|
420 |
+
public function it_can_get_account_info()
|
421 |
+
{
|
422 |
+
$expectedResponse = [
|
423 |
+
'account_id' => 'dbid:AAH4f99T0taONIb-OurWxbNQ6ywGRopQngc',
|
424 |
+
'name' => [
|
425 |
+
'given_name' => 'Franz',
|
426 |
+
'surname' => 'Ferdinand',
|
427 |
+
'familiar_name' => 'Franz',
|
428 |
+
'display_name' => 'Franz Ferdinand (Personal)',
|
429 |
+
'abbreviated_name' => 'FF',
|
430 |
+
],
|
431 |
+
'email' => 'franz@gmail.com',
|
432 |
+
'email_verified' => false,
|
433 |
+
'disabled' => false,
|
434 |
+
'locale' => 'en',
|
435 |
+
'referral_link' => 'https://db.tt/ZITNuhtI',
|
436 |
+
'is_paired' => false,
|
437 |
+
'account_type' => [
|
438 |
+
'.tag' => 'basic',
|
439 |
+
],
|
440 |
+
'profile_photo_url' => 'https://dl-web.dropbox.com/account_photo/get/dbid%3AAAH4f99T0taONIb-OurWxbNQ6ywGRopQngc?vers=1453416673259&size=128x128',
|
441 |
+
'country' => 'US',
|
442 |
+
];
|
443 |
+
|
444 |
+
$mockGuzzle = $this->mock_http_request(
|
445 |
+
json_encode($expectedResponse),
|
446 |
+
'https://api.dropboxapi.com/2/users/get_current_account',
|
447 |
+
[]
|
448 |
+
);
|
449 |
+
|
450 |
+
$client = new Client('test_token', $mockGuzzle);
|
451 |
+
|
452 |
+
$this->assertEquals($expectedResponse, $client->getAccountInfo());
|
453 |
+
}
|
454 |
+
|
455 |
+
/** @test */
|
456 |
+
public function it_can_revoke_token()
|
457 |
+
{
|
458 |
+
$mockGuzzle = $this->mock_http_request(
|
459 |
+
null,
|
460 |
+
'https://api.dropboxapi.com/2/auth/token/revoke',
|
461 |
+
[]
|
462 |
+
);
|
463 |
+
|
464 |
+
$client = new Client('test_token', $mockGuzzle);
|
465 |
+
|
466 |
+
$client->revokeToken();
|
467 |
+
}
|
468 |
+
|
469 |
private function mock_http_request($expectedResponse, $expectedEndpoint, $expectedParams)
|
470 |
{
|
471 |
$mockResponse = $this->getMockBuilder(ResponseInterface::class)
|
484 |
|
485 |
return $mockHttpClient;
|
486 |
}
|
487 |
+
|
488 |
+
private function mock_chunk_upload_client($content, $chunkSize)
|
489 |
+
{
|
490 |
+
$chunks = str_split($content, $chunkSize);
|
491 |
+
|
492 |
+
$mockClient = $this->getMockBuilder(Client::class)
|
493 |
+
->setConstructorArgs(['test_token'])
|
494 |
+
->setMethodsExcept(['uploadChunk', 'upload'])
|
495 |
+
->getMock();
|
496 |
+
|
497 |
+
$mockClient->expects($this->once())
|
498 |
+
->method('startUploadSession')
|
499 |
+
->with(array_shift($chunks))
|
500 |
+
->willReturn(new UploadSessionCursor('mockedSessionId', $chunkSize));
|
501 |
+
|
502 |
+
$mockClient->expects($this->once())
|
503 |
+
->method('finishUploadSession')
|
504 |
+
->with(array_pop($chunks), $this->anything(), 'Homework/math/answers.txt', 'add')
|
505 |
+
->willReturn(['name' => 'answers.txt']);
|
506 |
+
|
507 |
+
$remainingChunks = count($chunks);
|
508 |
+
$offset = $chunkSize;
|
509 |
+
|
510 |
+
if ($remainingChunks) {
|
511 |
+
$withs = [];
|
512 |
+
$returns = [];
|
513 |
+
|
514 |
+
foreach ($chunks as $chunk) {
|
515 |
+
$offset += $chunkSize;
|
516 |
+
$withs[] = [$chunk, $this->anything()];
|
517 |
+
$returns[] = new UploadSessionCursor('mockedSessionId', $offset);
|
518 |
+
}
|
519 |
+
|
520 |
+
$mockClient->expects($this->exactly($remainingChunks))
|
521 |
+
->method('appendContentToUploadSession')
|
522 |
+
->withConsecutive(...$withs)
|
523 |
+
->willReturn(...$returns);
|
524 |
+
}
|
525 |
+
|
526 |
+
return $mockClient;
|
527 |
+
}
|
528 |
}
|
xcloner.php
CHANGED
@@ -15,7 +15,7 @@
|
|
15 |
* Plugin Name: XCloner - Site Backup and Restore
|
16 |
* Plugin URI: http://www.xcloner.com
|
17 |
* Description: XCloner is a tool that will help you manage your website backups, generate/restore/move so your website will be always secured! With XCloner you will be able to clone your site to any other location with just a few clicks, as well as transfer the backup archives to remote FTP, SFTP, DropBox, Amazon S3, Google Drive, WebDAV, Backblaze, Azure accounts.
|
18 |
-
* Version: 4.0.
|
19 |
* Author: Liuta Ovidiu
|
20 |
* Author URI: http://www.thinkovi.com
|
21 |
* License: GPL-2.0+
|
15 |
* Plugin Name: XCloner - Site Backup and Restore
|
16 |
* Plugin URI: http://www.xcloner.com
|
17 |
* Description: XCloner is a tool that will help you manage your website backups, generate/restore/move so your website will be always secured! With XCloner you will be able to clone your site to any other location with just a few clicks, as well as transfer the backup archives to remote FTP, SFTP, DropBox, Amazon S3, Google Drive, WebDAV, Backblaze, Azure accounts.
|
18 |
+
* Version: 4.0.5
|
19 |
* Author: Liuta Ovidiu
|
20 |
* Author URI: http://www.thinkovi.com
|
21 |
* License: GPL-2.0+
|