Browse Source

Merge pull request #44643 from nextcloud/feat/trashbin-deleted-by

feat(trashbin): Add deleted by properties
pull/45000/head
Pytal 1 week ago
parent
commit
4618e28b18
No account linked to committer's email address

+ 1
- 0
apps/files_trashbin/composer/composer/autoload_classmap.php View File

@@ -26,6 +26,7 @@ return array(
'OCA\\Files_Trashbin\\Listeners\\LoadAdditionalScripts' => $baseDir . '/../lib/Listeners/LoadAdditionalScripts.php',
'OCA\\Files_Trashbin\\Listeners\\SyncLivePhotosListener' => $baseDir . '/../lib/Listeners/SyncLivePhotosListener.php',
'OCA\\Files_Trashbin\\Migration\\Version1010Date20200630192639' => $baseDir . '/../lib/Migration/Version1010Date20200630192639.php',
'OCA\\Files_Trashbin\\Migration\\Version1020Date20240403003535' => $baseDir . '/../lib/Migration/Version1020Date20240403003535.php',
'OCA\\Files_Trashbin\\Sabre\\AbstractTrash' => $baseDir . '/../lib/Sabre/AbstractTrash.php',
'OCA\\Files_Trashbin\\Sabre\\AbstractTrashFile' => $baseDir . '/../lib/Sabre/AbstractTrashFile.php',
'OCA\\Files_Trashbin\\Sabre\\AbstractTrashFolder' => $baseDir . '/../lib/Sabre/AbstractTrashFolder.php',

+ 1
- 0
apps/files_trashbin/composer/composer/autoload_static.php View File

@@ -41,6 +41,7 @@ class ComposerStaticInitFiles_Trashbin
'OCA\\Files_Trashbin\\Listeners\\LoadAdditionalScripts' => __DIR__ . '/..' . '/../lib/Listeners/LoadAdditionalScripts.php',
'OCA\\Files_Trashbin\\Listeners\\SyncLivePhotosListener' => __DIR__ . '/..' . '/../lib/Listeners/SyncLivePhotosListener.php',
'OCA\\Files_Trashbin\\Migration\\Version1010Date20200630192639' => __DIR__ . '/..' . '/../lib/Migration/Version1010Date20200630192639.php',
'OCA\\Files_Trashbin\\Migration\\Version1020Date20240403003535' => __DIR__ . '/..' . '/../lib/Migration/Version1020Date20240403003535.php',
'OCA\\Files_Trashbin\\Sabre\\AbstractTrash' => __DIR__ . '/..' . '/../lib/Sabre/AbstractTrash.php',
'OCA\\Files_Trashbin\\Sabre\\AbstractTrashFile' => __DIR__ . '/..' . '/../lib/Sabre/AbstractTrashFile.php',
'OCA\\Files_Trashbin\\Sabre\\AbstractTrashFolder' => __DIR__ . '/..' . '/../lib/Sabre/AbstractTrashFolder.php',

+ 4
- 3
apps/files_trashbin/lib/Helper.php View File

@@ -60,7 +60,7 @@ class Helper {
$absoluteDir = $view->getAbsolutePath($dir);
$internalPath = $mount->getInternalPath($absoluteDir);

$originalLocations = \OCA\Files_Trashbin\Trashbin::getLocations($user);
$extraData = \OCA\Files_Trashbin\Trashbin::getExtraData($user);
$dirContent = $storage->getCache()->getFolderContents($mount->getInternalPath($view->getAbsolutePath($dir)));
foreach ($dirContent as $entry) {
$entryName = $entry->getName();
@@ -76,8 +76,8 @@ class Helper {
}
$originalPath = '';
$originalName = substr($entryName, 0, -strlen($timestamp) - 2);
if (isset($originalLocations[$originalName][$timestamp])) {
$originalPath = $originalLocations[$originalName][$timestamp];
if (isset($extraData[$originalName][$timestamp]['location'])) {
$originalPath = $extraData[$originalName][$timestamp]['location'];
if (substr($originalPath, -1) === '/') {
$originalPath = substr($originalPath, 0, -1);
}
@@ -101,6 +101,7 @@ class Helper {
$i['extraData'] = $originalName;
}
}
$i['deletedBy'] = $extraData[$originalName][$timestamp]['deletedBy'] ?? null;
$result[] = new FileInfo($absoluteDir . '/' . $i['name'], $storage, $internalPath . '/' . $i['name'], $i, $mount);
}


+ 56
- 0
apps/files_trashbin/lib/Migration/Version1020Date20240403003535.php View File

@@ -0,0 +1,56 @@
<?php

declare(strict_types=1);

/**
* @copyright 2024 Christopher Ng <chrng8@gmail.com>
*
* @author Christopher Ng <chrng8@gmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

namespace OCA\Files_Trashbin\Migration;

use Closure;
use OCP\DB\ISchemaWrapper;
use OCP\DB\Types;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;

class Version1020Date20240403003535 extends SimpleMigrationStep {

/**
* @param Closure(): ISchemaWrapper $schemaClosure
*/
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();

if (!$schema->hasTable('files_trash')) {
return null;
}

$table = $schema->getTable('files_trash');
$table->addColumn('deleted_by', Types::STRING, [
'notnull' => false,
'length' => 64,
]);

return $schema;
}
}

+ 5
- 0
apps/files_trashbin/lib/Sabre/AbstractTrash.php View File

@@ -28,6 +28,7 @@ namespace OCA\Files_Trashbin\Sabre;
use OCA\Files_Trashbin\Trash\ITrashItem;
use OCA\Files_Trashbin\Trash\ITrashManager;
use OCP\Files\FileInfo;
use OCP\IUser;

abstract class AbstractTrash implements ITrash {
/** @var ITrashItem */
@@ -89,6 +90,10 @@ abstract class AbstractTrash implements ITrash {
return $this->data->getTitle();
}

public function getDeletedBy(): ?IUser {
return $this->data->getDeletedBy();
}

public function delete() {
$this->trashManager->removeItem($this->data);
}

+ 3
- 0
apps/files_trashbin/lib/Sabre/ITrash.php View File

@@ -27,6 +27,7 @@ declare(strict_types=1);
namespace OCA\Files_Trashbin\Sabre;

use OCP\Files\FileInfo;
use OCP\IUser;

interface ITrash {
public function restore(): bool;
@@ -39,6 +40,8 @@ interface ITrash {

public function getDeletionTime(): int;

public function getDeletedBy(): ?IUser;

public function getSize(): int|float;

public function getFileId(): int;

+ 10
- 0
apps/files_trashbin/lib/Sabre/TrashbinPlugin.php View File

@@ -41,6 +41,8 @@ class TrashbinPlugin extends ServerPlugin {
public const TRASHBIN_ORIGINAL_LOCATION = '{http://nextcloud.org/ns}trashbin-original-location';
public const TRASHBIN_DELETION_TIME = '{http://nextcloud.org/ns}trashbin-deletion-time';
public const TRASHBIN_TITLE = '{http://nextcloud.org/ns}trashbin-title';
public const TRASHBIN_DELETED_BY_ID = '{http://nextcloud.org/ns}trashbin-deleted-by-id';
public const TRASHBIN_DELETED_BY_DISPLAY_NAME = '{http://nextcloud.org/ns}trashbin-deleted-by-display-name';

/** @var Server */
private $server;
@@ -83,6 +85,14 @@ class TrashbinPlugin extends ServerPlugin {
return $node->getDeletionTime();
});

$propFind->handle(self::TRASHBIN_DELETED_BY_ID, function () use ($node) {
return $node->getDeletedBy()?->getUID();
});

$propFind->handle(self::TRASHBIN_DELETED_BY_DISPLAY_NAME, function () use ($node) {
return $node->getDeletedBy()?->getDisplayName();
});

$propFind->handle(FilesPlugin::SIZE_PROPERTYNAME, function () use ($node) {
return $node->getSize();
});

+ 5
- 0
apps/files_trashbin/lib/Trash/ITrashItem.php View File

@@ -77,5 +77,10 @@ interface ITrashItem extends FileInfo {
*/
public function getUser(): IUser;

/**
* @since 30.0.0
*/
public function getDeletedBy(): ?IUser;

public function getTitle(): string;
}

+ 9
- 6
apps/files_trashbin/lib/Trash/LegacyTrashBackend.php View File

@@ -33,16 +33,16 @@ use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use OCP\Files\Storage\IStorage;
use OCP\IUser;
use OCP\IUserManager;

class LegacyTrashBackend implements ITrashBackend {
/** @var array */
private $deletedFiles = [];

/** @var IRootFolder */
private $rootFolder;

public function __construct(IRootFolder $rootFolder) {
$this->rootFolder = $rootFolder;
public function __construct(
private IRootFolder $rootFolder,
private IUserManager $userManager,
) {
}

/**
@@ -59,6 +59,8 @@ class LegacyTrashBackend implements ITrashBackend {
if (!$originalLocation) {
$originalLocation = $file->getName();
}
/** @psalm-suppress UndefinedInterfaceMethod */
$deletedBy = $this->userManager->get($file['deletedBy']) ?? $parent?->getDeletedBy();
$trashFilename = Trashbin::getTrashFilename($file->getName(), $file->getMtime());
return new TrashItem(
$this,
@@ -66,7 +68,8 @@ class LegacyTrashBackend implements ITrashBackend {
$file->getMTime(),
$parentTrashPath . '/' . ($isRoot ? $trashFilename : $file->getName()),
$file,
$user
$user,
$deletedBy,
);
}, $items);
}

+ 12
- 25
apps/files_trashbin/lib/Trash/TrashItem.php View File

@@ -27,33 +27,16 @@ use OCP\Files\FileInfo;
use OCP\IUser;

class TrashItem implements ITrashItem {
/** @var ITrashBackend */
private $backend;
/** @var string */
private $orignalLocation;
/** @var int */
private $deletedTime;
/** @var string */
private $trashPath;
/** @var FileInfo */
private $fileInfo;
/** @var IUser */
private $user;

public function __construct(
ITrashBackend $backend,
string $originalLocation,
int $deletedTime,
string $trashPath,
FileInfo $fileInfo,
IUser $user
private ITrashBackend $backend,
private string $originalLocation,
private int $deletedTime,
private string $trashPath,
private FileInfo $fileInfo,
private IUser $user,
private ?IUser $deletedBy,
) {
$this->backend = $backend;
$this->orignalLocation = $originalLocation;
$this->deletedTime = $deletedTime;
$this->trashPath = $trashPath;
$this->fileInfo = $fileInfo;
$this->user = $user;
}

public function getTrashBackend(): ITrashBackend {
@@ -61,7 +44,7 @@ class TrashItem implements ITrashItem {
}

public function getOriginalLocation(): string {
return $this->orignalLocation;
return $this->originalLocation;
}

public function getDeletedTime(): int {
@@ -192,6 +175,10 @@ class TrashItem implements ITrashItem {
return $this->fileInfo->getParentId();
}

public function getDeletedBy(): ?IUser {
return $this->deletedBy;
}

/**
* @inheritDoc
* @return array<string, int|string|bool|float|string[]|int[]>

+ 12
- 11
apps/files_trashbin/lib/Trashbin.php View File

@@ -126,24 +126,23 @@ class Trashbin {
}

/**
* get original location of files for user
* get original location and deleted by of files for user
*
* @param string $user
* @return array (filename => array (timestamp => original location))
* @return array<string, array<string, array{location: string, deletedBy: string}>>
*/
public static function getLocations($user) {
public static function getExtraData($user) {
$query = \OC::$server->getDatabaseConnection()->getQueryBuilder();
$query->select('id', 'timestamp', 'location')
$query->select('id', 'timestamp', 'location', 'deleted_by')
->from('files_trash')
->where($query->expr()->eq('user', $query->createNamedParameter($user)));
$result = $query->executeQuery();
$array = [];
while ($row = $result->fetch()) {
if (isset($array[$row['id']])) {
$array[$row['id']][$row['timestamp']] = $row['location'];
} else {
$array[$row['id']] = [$row['timestamp'] => $row['location']];
}
$array[$row['id']][$row['timestamp']] = [
'location' => (string)$row['location'],
'deletedBy' => (string)$row['deleted_by'],
];
}
$result->closeCursor();
return $array;
@@ -228,7 +227,8 @@ class Trashbin {
->setValue('id', $query->createNamedParameter($targetFilename))
->setValue('timestamp', $query->createNamedParameter($timestamp))
->setValue('location', $query->createNamedParameter($targetLocation))
->setValue('user', $query->createNamedParameter($user));
->setValue('user', $query->createNamedParameter($user))
->setValue('deleted_by', $query->createNamedParameter($user));
$result = $query->executeStatement();
if (!$result) {
\OC::$server->get(LoggerInterface::class)->error('trash bin database couldn\'t be updated for the files owner', ['app' => 'files_trashbin']);
@@ -358,7 +358,8 @@ class Trashbin {
->setValue('id', $query->createNamedParameter($filename))
->setValue('timestamp', $query->createNamedParameter($timestamp))
->setValue('location', $query->createNamedParameter($location))
->setValue('user', $query->createNamedParameter($owner));
->setValue('user', $query->createNamedParameter($owner))
->setValue('deleted_by', $query->createNamedParameter($user));
$result = $query->executeStatement();
if (!$result) {
\OC::$server->get(LoggerInterface::class)->error('trash bin database couldn\'t be updated', ['app' => 'files_trashbin']);

+ 9
- 1
apps/files_trashbin/lib/UserMigration/TrashbinMigrator.php View File

@@ -96,7 +96,15 @@ class TrashbinMigrator implements IMigrator, ISizeEstimationMigrator {
}
$output->writeln("Exporting trashbin files…");
$exportDestination->copyFolder($trashbinFolder, static::PATH_FILES_FOLDER);
$originalLocations = \OCA\Files_Trashbin\Trashbin::getLocations($uid);
$originalLocations = [];
// TODO Export all extra data and bump migrator to v2
foreach (\OCA\Files_Trashbin\Trashbin::getExtraData($uid) as $filename => $extraData) {
$locationData = [];
foreach ($extraData as $timestamp => ['location' => $location]) {
$locationData[$timestamp] = $location;
}
$originalLocations[$filename] = $locationData;
}
$exportDestination->addFileContents(static::PATH_LOCATIONS_FILE, json_encode($originalLocations));
} catch (NotFoundException $e) {
$output->writeln("No trashbin to export…");

Loading…
Cancel
Save