Merge pull request #16273 from owncloud/trash-fixrestoreuniquename
Fix restoring files from trash with unique name
This commit is contained in:
commit
02912aef58
|
@ -277,16 +277,16 @@ class Trashbin {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* restore files from trash bin
|
* Restore a file or folder from trash bin
|
||||||
*
|
*
|
||||||
* @param string $file path to the deleted file
|
* @param string $file path to the deleted file/folder relative to "files_trashbin/files/",
|
||||||
* @param string $filename name of the file
|
* including the timestamp suffix ".d12345678"
|
||||||
* @param int $timestamp time when the file was deleted
|
* @param string $filename name of the file/folder
|
||||||
|
* @param int $timestamp time when the file/folder was deleted
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool true on success, false otherwise
|
||||||
*/
|
*/
|
||||||
public static function restore($file, $filename, $timestamp) {
|
public static function restore($file, $filename, $timestamp) {
|
||||||
|
|
||||||
$user = \OCP\User::getUser();
|
$user = \OCP\User::getUser();
|
||||||
$view = new \OC\Files\View('/' . $user);
|
$view = new \OC\Files\View('/' . $user);
|
||||||
|
|
||||||
|
@ -311,6 +311,9 @@ class Trashbin {
|
||||||
|
|
||||||
$source = \OC\Files\Filesystem::normalizePath('files_trashbin/files/' . $file);
|
$source = \OC\Files\Filesystem::normalizePath('files_trashbin/files/' . $file);
|
||||||
$target = \OC\Files\Filesystem::normalizePath('files/' . $location . '/' . $uniqueFilename);
|
$target = \OC\Files\Filesystem::normalizePath('files/' . $location . '/' . $uniqueFilename);
|
||||||
|
if (!$view->file_exists($source)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
$mtime = $view->filemtime($source);
|
$mtime = $view->filemtime($source);
|
||||||
|
|
||||||
// restore file
|
// restore file
|
||||||
|
@ -762,6 +765,8 @@ class Trashbin {
|
||||||
$name = pathinfo($filename, PATHINFO_FILENAME);
|
$name = pathinfo($filename, PATHINFO_FILENAME);
|
||||||
$l = \OC::$server->getL10N('files_trashbin');
|
$l = \OC::$server->getL10N('files_trashbin');
|
||||||
|
|
||||||
|
$location = '/' . trim($location, '/');
|
||||||
|
|
||||||
// if extension is not empty we set a dot in front of it
|
// if extension is not empty we set a dot in front of it
|
||||||
if ($ext !== '') {
|
if ($ext !== '') {
|
||||||
$ext = '.' . $ext;
|
$ext = '.' . $ext;
|
||||||
|
|
|
@ -39,6 +39,11 @@ class Test_Trashbin extends \Test\TestCase {
|
||||||
private static $rememberRetentionObligation;
|
private static $rememberRetentionObligation;
|
||||||
private static $rememberAutoExpire;
|
private static $rememberAutoExpire;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private static $trashBinStatus;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \OC\Files\View
|
* @var \OC\Files\View
|
||||||
*/
|
*/
|
||||||
|
@ -47,6 +52,9 @@ class Test_Trashbin extends \Test\TestCase {
|
||||||
public static function setUpBeforeClass() {
|
public static function setUpBeforeClass() {
|
||||||
parent::setUpBeforeClass();
|
parent::setUpBeforeClass();
|
||||||
|
|
||||||
|
$appManager = \OC::$server->getAppManager();
|
||||||
|
self::$trashBinStatus = $appManager->isEnabledForUser('files_trashbin');
|
||||||
|
|
||||||
// reset backend
|
// reset backend
|
||||||
\OC_User::clearBackends();
|
\OC_User::clearBackends();
|
||||||
\OC_User::useBackend('database');
|
\OC_User::useBackend('database');
|
||||||
|
@ -89,12 +97,18 @@ class Test_Trashbin extends \Test\TestCase {
|
||||||
|
|
||||||
\OC\Files\Filesystem::getLoader()->removeStorageWrapper('oc_trashbin');
|
\OC\Files\Filesystem::getLoader()->removeStorageWrapper('oc_trashbin');
|
||||||
|
|
||||||
|
if (self::$trashBinStatus) {
|
||||||
|
\OC::$server->getAppManager()->enableApp('files_trashbin');
|
||||||
|
}
|
||||||
|
|
||||||
parent::tearDownAfterClass();
|
parent::tearDownAfterClass();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function setUp() {
|
protected function setUp() {
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
|
\OC::$server->getAppManager()->enableApp('files_trashbin');
|
||||||
|
|
||||||
$this->trashRoot1 = '/' . self::TEST_TRASHBIN_USER1 . '/files_trashbin';
|
$this->trashRoot1 = '/' . self::TEST_TRASHBIN_USER1 . '/files_trashbin';
|
||||||
$this->trashRoot2 = '/' . self::TEST_TRASHBIN_USER2 . '/files_trashbin';
|
$this->trashRoot2 = '/' . self::TEST_TRASHBIN_USER2 . '/files_trashbin';
|
||||||
$this->rootView = new \OC\Files\View();
|
$this->rootView = new \OC\Files\View();
|
||||||
|
@ -102,9 +116,18 @@ class Test_Trashbin extends \Test\TestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function tearDown() {
|
protected function tearDown() {
|
||||||
|
// disable trashbin to be able to properly clean up
|
||||||
|
\OC::$server->getAppManager()->disableApp('files_trashbin');
|
||||||
|
|
||||||
|
$this->rootView->deleteAll('/' . self::TEST_TRASHBIN_USER1 . '/files');
|
||||||
|
$this->rootView->deleteAll('/' . self::TEST_TRASHBIN_USER2 . '/files');
|
||||||
$this->rootView->deleteAll($this->trashRoot1);
|
$this->rootView->deleteAll($this->trashRoot1);
|
||||||
$this->rootView->deleteAll($this->trashRoot2);
|
$this->rootView->deleteAll($this->trashRoot2);
|
||||||
|
|
||||||
|
// clear trash table
|
||||||
|
$connection = \OC::$server->getDatabaseConnection();
|
||||||
|
$connection->executeUpdate('DELETE FROM `*PREFIX*files_trash`');
|
||||||
|
|
||||||
parent::tearDown();
|
parent::tearDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -294,6 +317,310 @@ class Test_Trashbin extends \Test\TestCase {
|
||||||
$this->assertSame('file1.txt', $element['name']);
|
$this->assertSame('file1.txt', $element['name']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test restoring a file
|
||||||
|
*/
|
||||||
|
public function testRestoreFileInRoot() {
|
||||||
|
$userFolder = \OC::$server->getUserFolder();
|
||||||
|
$file = $userFolder->newFile('file1.txt');
|
||||||
|
$file->putContent('foo');
|
||||||
|
|
||||||
|
$this->assertTrue($userFolder->nodeExists('file1.txt'));
|
||||||
|
|
||||||
|
$file->delete();
|
||||||
|
|
||||||
|
$this->assertFalse($userFolder->nodeExists('file1.txt'));
|
||||||
|
|
||||||
|
$filesInTrash = OCA\Files_Trashbin\Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime');
|
||||||
|
$this->assertCount(1, $filesInTrash);
|
||||||
|
|
||||||
|
/** @var \OCP\Files\FileInfo */
|
||||||
|
$trashedFile = $filesInTrash[0];
|
||||||
|
|
||||||
|
$this->assertTrue(
|
||||||
|
OCA\Files_Trashbin\Trashbin::restore(
|
||||||
|
'file1.txt.d' . $trashedFile->getMtime(),
|
||||||
|
$trashedFile->getName(),
|
||||||
|
$trashedFile->getMtime()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$file = $userFolder->get('file1.txt');
|
||||||
|
$this->assertEquals('foo', $file->getContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test restoring a file in subfolder
|
||||||
|
*/
|
||||||
|
public function testRestoreFileInSubfolder() {
|
||||||
|
$userFolder = \OC::$server->getUserFolder();
|
||||||
|
$folder = $userFolder->newFolder('folder');
|
||||||
|
$file = $folder->newFile('file1.txt');
|
||||||
|
$file->putContent('foo');
|
||||||
|
|
||||||
|
$this->assertTrue($userFolder->nodeExists('folder/file1.txt'));
|
||||||
|
|
||||||
|
$file->delete();
|
||||||
|
|
||||||
|
$this->assertFalse($userFolder->nodeExists('folder/file1.txt'));
|
||||||
|
|
||||||
|
$filesInTrash = OCA\Files_Trashbin\Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime');
|
||||||
|
$this->assertCount(1, $filesInTrash);
|
||||||
|
|
||||||
|
/** @var \OCP\Files\FileInfo */
|
||||||
|
$trashedFile = $filesInTrash[0];
|
||||||
|
|
||||||
|
$this->assertTrue(
|
||||||
|
OCA\Files_Trashbin\Trashbin::restore(
|
||||||
|
'file1.txt.d' . $trashedFile->getMtime(),
|
||||||
|
$trashedFile->getName(),
|
||||||
|
$trashedFile->getMtime()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$file = $userFolder->get('folder/file1.txt');
|
||||||
|
$this->assertEquals('foo', $file->getContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test restoring a folder
|
||||||
|
*/
|
||||||
|
public function testRestoreFolder() {
|
||||||
|
$userFolder = \OC::$server->getUserFolder();
|
||||||
|
$folder = $userFolder->newFolder('folder');
|
||||||
|
$file = $folder->newFile('file1.txt');
|
||||||
|
$file->putContent('foo');
|
||||||
|
|
||||||
|
$this->assertTrue($userFolder->nodeExists('folder'));
|
||||||
|
|
||||||
|
$folder->delete();
|
||||||
|
|
||||||
|
$this->assertFalse($userFolder->nodeExists('folder'));
|
||||||
|
|
||||||
|
$filesInTrash = OCA\Files_Trashbin\Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime');
|
||||||
|
$this->assertCount(1, $filesInTrash);
|
||||||
|
|
||||||
|
/** @var \OCP\Files\FileInfo */
|
||||||
|
$trashedFolder = $filesInTrash[0];
|
||||||
|
|
||||||
|
$this->assertTrue(
|
||||||
|
OCA\Files_Trashbin\Trashbin::restore(
|
||||||
|
'folder.d' . $trashedFolder->getMtime(),
|
||||||
|
$trashedFolder->getName(),
|
||||||
|
$trashedFolder->getMtime()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$file = $userFolder->get('folder/file1.txt');
|
||||||
|
$this->assertEquals('foo', $file->getContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test restoring a file from inside a trashed folder
|
||||||
|
*/
|
||||||
|
public function testRestoreFileFromTrashedSubfolder() {
|
||||||
|
$userFolder = \OC::$server->getUserFolder();
|
||||||
|
$folder = $userFolder->newFolder('folder');
|
||||||
|
$file = $folder->newFile('file1.txt');
|
||||||
|
$file->putContent('foo');
|
||||||
|
|
||||||
|
$this->assertTrue($userFolder->nodeExists('folder'));
|
||||||
|
|
||||||
|
$folder->delete();
|
||||||
|
|
||||||
|
$this->assertFalse($userFolder->nodeExists('folder'));
|
||||||
|
|
||||||
|
$filesInTrash = OCA\Files_Trashbin\Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime');
|
||||||
|
$this->assertCount(1, $filesInTrash);
|
||||||
|
|
||||||
|
/** @var \OCP\Files\FileInfo */
|
||||||
|
$trashedFile = $filesInTrash[0];
|
||||||
|
|
||||||
|
$this->assertTrue(
|
||||||
|
OCA\Files_Trashbin\Trashbin::restore(
|
||||||
|
'folder.d' . $trashedFile->getMtime() . '/file1.txt',
|
||||||
|
'file1.txt',
|
||||||
|
$trashedFile->getMtime()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$file = $userFolder->get('file1.txt');
|
||||||
|
$this->assertEquals('foo', $file->getContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test restoring a file whenever the source folder was removed.
|
||||||
|
* The file should then land in the root.
|
||||||
|
*/
|
||||||
|
public function testRestoreFileWithMissingSourceFolder() {
|
||||||
|
$userFolder = \OC::$server->getUserFolder();
|
||||||
|
$folder = $userFolder->newFolder('folder');
|
||||||
|
$file = $folder->newFile('file1.txt');
|
||||||
|
$file->putContent('foo');
|
||||||
|
|
||||||
|
$this->assertTrue($userFolder->nodeExists('folder/file1.txt'));
|
||||||
|
|
||||||
|
$file->delete();
|
||||||
|
|
||||||
|
$this->assertFalse($userFolder->nodeExists('folder/file1.txt'));
|
||||||
|
|
||||||
|
$filesInTrash = OCA\Files_Trashbin\Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime');
|
||||||
|
$this->assertCount(1, $filesInTrash);
|
||||||
|
|
||||||
|
/** @var \OCP\Files\FileInfo */
|
||||||
|
$trashedFile = $filesInTrash[0];
|
||||||
|
|
||||||
|
// delete source folder
|
||||||
|
$folder->delete();
|
||||||
|
|
||||||
|
$this->assertTrue(
|
||||||
|
OCA\Files_Trashbin\Trashbin::restore(
|
||||||
|
'file1.txt.d' . $trashedFile->getMtime(),
|
||||||
|
$trashedFile->getName(),
|
||||||
|
$trashedFile->getMtime()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$file = $userFolder->get('file1.txt');
|
||||||
|
$this->assertEquals('foo', $file->getContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test restoring a file in the root folder whenever there is another file
|
||||||
|
* with the same name in the root folder
|
||||||
|
*/
|
||||||
|
public function testRestoreFileDoesNotOverwriteExistingInRoot() {
|
||||||
|
$userFolder = \OC::$server->getUserFolder();
|
||||||
|
$file = $userFolder->newFile('file1.txt');
|
||||||
|
$file->putContent('foo');
|
||||||
|
|
||||||
|
$this->assertTrue($userFolder->nodeExists('file1.txt'));
|
||||||
|
|
||||||
|
$file->delete();
|
||||||
|
|
||||||
|
$this->assertFalse($userFolder->nodeExists('file1.txt'));
|
||||||
|
|
||||||
|
$filesInTrash = OCA\Files_Trashbin\Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime');
|
||||||
|
$this->assertCount(1, $filesInTrash);
|
||||||
|
|
||||||
|
/** @var \OCP\Files\FileInfo */
|
||||||
|
$trashedFile = $filesInTrash[0];
|
||||||
|
|
||||||
|
// create another file
|
||||||
|
$file = $userFolder->newFile('file1.txt');
|
||||||
|
$file->putContent('bar');
|
||||||
|
|
||||||
|
$this->assertTrue(
|
||||||
|
OCA\Files_Trashbin\Trashbin::restore(
|
||||||
|
'file1.txt.d' . $trashedFile->getMtime(),
|
||||||
|
$trashedFile->getName(),
|
||||||
|
$trashedFile->getMtime()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$anotherFile = $userFolder->get('file1.txt');
|
||||||
|
$this->assertEquals('bar', $anotherFile->getContent());
|
||||||
|
|
||||||
|
$restoredFile = $userFolder->get('file1 (restored).txt');
|
||||||
|
$this->assertEquals('foo', $restoredFile->getContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test restoring a file whenever there is another file
|
||||||
|
* with the same name in the source folder
|
||||||
|
*/
|
||||||
|
public function testRestoreFileDoesNotOverwriteExistingInSubfolder() {
|
||||||
|
$userFolder = \OC::$server->getUserFolder();
|
||||||
|
$folder = $userFolder->newFolder('folder');
|
||||||
|
$file = $folder->newFile('file1.txt');
|
||||||
|
$file->putContent('foo');
|
||||||
|
|
||||||
|
$this->assertTrue($userFolder->nodeExists('folder/file1.txt'));
|
||||||
|
|
||||||
|
$file->delete();
|
||||||
|
|
||||||
|
$this->assertFalse($userFolder->nodeExists('folder/file1.txt'));
|
||||||
|
|
||||||
|
$filesInTrash = OCA\Files_Trashbin\Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime');
|
||||||
|
$this->assertCount(1, $filesInTrash);
|
||||||
|
|
||||||
|
/** @var \OCP\Files\FileInfo */
|
||||||
|
$trashedFile = $filesInTrash[0];
|
||||||
|
|
||||||
|
// create another file
|
||||||
|
$file = $folder->newFile('file1.txt');
|
||||||
|
$file->putContent('bar');
|
||||||
|
|
||||||
|
$this->assertTrue(
|
||||||
|
OCA\Files_Trashbin\Trashbin::restore(
|
||||||
|
'file1.txt.d' . $trashedFile->getMtime(),
|
||||||
|
$trashedFile->getName(),
|
||||||
|
$trashedFile->getMtime()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$anotherFile = $userFolder->get('folder/file1.txt');
|
||||||
|
$this->assertEquals('bar', $anotherFile->getContent());
|
||||||
|
|
||||||
|
$restoredFile = $userFolder->get('folder/file1 (restored).txt');
|
||||||
|
$this->assertEquals('foo', $restoredFile->getContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test restoring a non-existing file from trashbin, returns false
|
||||||
|
*/
|
||||||
|
public function testRestoreUnexistingFile() {
|
||||||
|
$this->assertFalse(
|
||||||
|
OCA\Files_Trashbin\Trashbin::restore(
|
||||||
|
'unexist.txt.d123456',
|
||||||
|
'unexist.txt',
|
||||||
|
'123456'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test restoring a file into a read-only folder, will restore
|
||||||
|
* the file to root instead
|
||||||
|
*/
|
||||||
|
public function testRestoreFileIntoReadOnlySourceFolder() {
|
||||||
|
$userFolder = \OC::$server->getUserFolder();
|
||||||
|
$folder = $userFolder->newFolder('folder');
|
||||||
|
$file = $folder->newFile('file1.txt');
|
||||||
|
$file->putContent('foo');
|
||||||
|
|
||||||
|
$this->assertTrue($userFolder->nodeExists('folder/file1.txt'));
|
||||||
|
|
||||||
|
$file->delete();
|
||||||
|
|
||||||
|
$this->assertFalse($userFolder->nodeExists('folder/file1.txt'));
|
||||||
|
|
||||||
|
$filesInTrash = OCA\Files_Trashbin\Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime');
|
||||||
|
$this->assertCount(1, $filesInTrash);
|
||||||
|
|
||||||
|
/** @var \OCP\Files\FileInfo */
|
||||||
|
$trashedFile = $filesInTrash[0];
|
||||||
|
|
||||||
|
// delete source folder
|
||||||
|
list($storage, $internalPath) = $this->rootView->resolvePath('/' . self::TEST_TRASHBIN_USER1 . '/files/folder');
|
||||||
|
$folderAbsPath = $storage->getSourcePath($internalPath);
|
||||||
|
// make folder read-only
|
||||||
|
chmod($folderAbsPath, 0555);
|
||||||
|
|
||||||
|
$this->assertTrue(
|
||||||
|
OCA\Files_Trashbin\Trashbin::restore(
|
||||||
|
'file1.txt.d' . $trashedFile->getMtime(),
|
||||||
|
$trashedFile->getName(),
|
||||||
|
$trashedFile->getMtime()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$file = $userFolder->get('file1.txt');
|
||||||
|
$this->assertEquals('foo', $file->getContent());
|
||||||
|
|
||||||
|
chmod($folderAbsPath, 0755);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $user
|
* @param string $user
|
||||||
* @param bool $create
|
* @param bool $create
|
||||||
|
|
Loading…
Reference in New Issue