XCloner – Backup and Restore - Version 4.0.5

Version Description

  • Dropbox API update to V2
  • Code fixes and text changes
Download this release

Release Info

Developer xcloner
Plugin Icon 128x128 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 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.8
7
- Stable tag: 4.0.4
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/move';
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 = \GuzzleHttp\json_decode($response->getBody(), true);
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/move',
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.4
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+