Skip to content

Commit

Permalink
Merge pull request #2161 from nextcloud/feat/soft-deletion
Browse files Browse the repository at this point in the history
New attempt to implement recycle bin
  • Loading branch information
marcelklehr authored Jun 1, 2024
2 parents 5f4567c + b6a6b48 commit 5d34364
Show file tree
Hide file tree
Showing 49 changed files with 1,617 additions and 538 deletions.
7 changes: 7 additions & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
['name' => 'web_view#index', 'url' => '/duplicated', 'verb' => 'GET', 'postfix' => 'duplicated'],
['name' => 'web_view#index', 'url' => '/shared', 'verb' => 'GET', 'postfix' => 'shared'],
['name' => 'web_view#index', 'url' => '/bookmarklet', 'verb' => 'GET', 'postfix' => 'bookmarklet'],
['name' => 'web_view#index', 'url' => '/trashbin', 'verb' => 'GET', 'postfix' => 'trashbin'],
['name' => 'web_view#service_worker', 'url' => '/service-worker.js', 'verb' => 'GET'],
['name' => 'web_view#manifest', 'url' => '/manifest.webmanifest', 'verb' => 'GET'],

Expand All @@ -45,6 +46,7 @@
['name' => 'internal_bookmark#count_unavailable', 'url' => '/bookmark/unavailable', 'verb' => 'GET'],
['name' => 'internal_bookmark#count_archived', 'url' => '/bookmark/archived', 'verb' => 'GET'],
['name' => 'internal_bookmark#count_duplicated', 'url' => '/bookmark/duplicated', 'verb' => 'GET'],
['name' => 'internal_bookmark#get_deleted_bookmarks', 'url' => '/bookmark/deleted', 'verb' => 'GET'],
['name' => 'internal_bookmark#edit_bookmark', 'url' => '/bookmark/{id}', 'verb' => 'PUT'],
['name' => 'internal_bookmark#get_single_bookmark', 'url' => '/bookmark/{id}', 'verb' => 'GET'],
['name' => 'internal_bookmark#delete_bookmark', 'url' => '/bookmark/{id}', 'verb' => 'DELETE'],
Expand All @@ -62,17 +64,20 @@
['name' => 'internal_tags#delete_tag', 'url' => '/tag/{old_name}', 'verb' => 'DELETE'],
['name' => 'internal_folders#get_folders', 'url' => '/folder', 'verb' => 'GET'],
['name' => 'internal_folders#find_shared_folders', 'url' => '/folder/shared', 'verb' => 'GET'],
['name' => 'internal_folders#get_deleted_folders', 'url' => '/folder/deleted', 'verb' => 'GET'],
['name' => 'internal_folders#get_folder', 'url' => '/folder/{folderId}', 'verb' => 'GET'],
['name' => 'internal_folders#add_folder', 'url' => '/folder', 'verb' => 'POST'],
['name' => 'internal_folders#edit_folder', 'url' => '/folder/{folderId}', 'verb' => 'PUT'],
['name' => 'internal_folders#delete_folder', 'url' => '/folder/{folderId}', 'verb' => 'DELETE'],
['name' => 'internal_folders#hash_folder', 'url' => '/folder/{folderId}/hash', 'verb' => 'GET'],
['name' => 'internal_bookmark#import_bookmark', 'url' => '/folder/{folder}/import', 'verb' => 'POST'],
['name' => 'internal_folders#undelete_folder', 'url' => '/folder/{folderId}/undelete', 'verb' => 'POST'],
['name' => 'internal_folders#get_folder_children', 'url' => '/folder/{folderId}/children', 'verb' => 'GET'],
['name' => 'internal_folders#get_folder_children_order', 'url' => '/folder/{folderId}/childorder', 'verb' => 'GET'],
['name' => 'internal_folders#set_folder_children_order', 'url' => '/folder/{folderId}/childorder', 'verb' => 'PATCH'],
['name' => 'internal_folders#add_to_folder', 'url' => '/folder/{folderId}/bookmarks/{bookmarkId}', 'verb' => 'POST'],
['name' => 'internal_folders#remove_from_folder', 'url' => '/folder/{folderId}/bookmarks/{bookmarkId}', 'verb' => 'DELETE'],
['name' => 'internal_folders#undelete_from_folder', 'url' => '/folder/{folderId}/bookmarks/{bookmarkId}/undelete', 'verb' => 'POST'],
['name' => 'internal_folders#get_folder_public_token', 'url' => '/folder/{folderId}/publictoken', 'verb' => 'GET'],
['name' => 'internal_folders#create_folder_public_token', 'url' => '/folder/{folderId}/publictoken', 'verb' => 'POST'],
['name' => 'internal_folders#delete_folder_public_token', 'url' => '/folder/{folderId}/publictoken', 'verb' => 'DELETE'],
Expand Down Expand Up @@ -110,12 +115,14 @@
['name' => 'folders#edit_folder', 'url' => '/public/rest/v2/folder/{folderId}', 'verb' => 'PUT'],
['name' => 'folders#delete_folder', 'url' => '/public/rest/v2/folder/{folderId}', 'verb' => 'DELETE'],
['name' => 'folders#hash_folder', 'url' => '/public/rest/v2/folder/{folderId}/hash', 'verb' => 'GET'],
['name' => 'folders#undelete_folder', 'url' => '/public/rest/v2/folder/{folderId}/undelete', 'verb' => 'POST'],
['name' => 'bookmark#import_bookmark', 'url' => '/public/rest/v2/folder/{folder}/import', 'verb' => 'POST'],
['name' => 'folders#get_folder_children', 'url' => '/public/rest/v2/folder/{folderId}/children', 'verb' => 'GET'],
['name' => 'folders#get_folder_children_order', 'url' => '/public/rest/v2/folder/{folderId}/childorder', 'verb' => 'GET'],
['name' => 'folders#set_folder_children_order', 'url' => '/public/rest/v2/folder/{folderId}/childorder', 'verb' => 'PATCH'],
['name' => 'folders#add_to_folder', 'url' => '/public/rest/v2/folder/{folderId}/bookmarks/{bookmarkId}', 'verb' => 'POST'],
['name' => 'folders#remove_from_folder', 'url' => '/public/rest/v2/folder/{folderId}/bookmarks/{bookmarkId}', 'verb' => 'DELETE'],
['name' => 'folders#undelete_from_folder', 'url' => '/public/rest/v2/folder/{folderId}/bookmarks/{bookmarkId}/undelete', 'verb' => 'POST'],
['name' => 'bookmark#preflighted_cors', 'url' => '/public/rest/v2/{path}',
'verb' => 'OPTIONS', 'requirements' => ['path' => '.+']],
['name' => 'folders#get_folder_public_token', 'url' => '/public/rest/v2/folder/{folderId}/publictoken', 'verb' => 'GET'],
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"require-dev": {
"phpunit/phpunit": "^9.5.26",
"nextcloud/coding-standard": "^1.0.0",
"vimeo/psalm": "^4",
"vimeo/psalm": "5.x",
"nextcloud/ocp": "dev-master"
},
"config": {
Expand Down
11 changes: 7 additions & 4 deletions lib/Activity/ActivityPublisher.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
use OCA\Bookmarks\Db\SharedFolder;
use OCA\Bookmarks\Db\SharedFolderMapper;
use OCA\Bookmarks\Db\TreeMapper;
use OCA\Bookmarks\Events\BeforeDeleteEvent;
use OCA\Bookmarks\Events\BeforeSoftDeleteEvent;
use OCA\Bookmarks\Events\ChangeEvent;
use OCA\Bookmarks\Events\CreateEvent;
use OCA\Bookmarks\Events\MoveEvent;
Expand All @@ -26,6 +26,9 @@
use OCP\EventDispatcher\IEventListener;
use OCP\IL10N;

/**
* @psalm-implements IEventListener<ChangeEvent>
*/
class ActivityPublisher implements IEventListener {
/**
* @var IManager
Expand Down Expand Up @@ -117,7 +120,7 @@ public function publishShare(ChangeEvent $event): void {

if ($event instanceof CreateEvent) {
$activity->setSubject('share_created', ['folder' => $sharedFolder->getTitle(), 'sharee' => $sharedFolder->getUserId()]);
} elseif ($event instanceof BeforeDeleteEvent) {
} elseif ($event instanceof BeforeSoftDeleteEvent) {
$activity->setSubject('share_deleted', ['folder' => $sharedFolder->getTitle(), 'sharee' => $sharedFolder->getUserId()]);
} else {
return;
Expand Down Expand Up @@ -151,7 +154,7 @@ public function publishFolder(ChangeEvent $event): void {
$activity->setObject(TreeMapper::TYPE_FOLDER, $folder->getId());
if ($event instanceof CreateEvent) {
$activity->setSubject('folder_created', ['folder' => $folder->getTitle()]);
} elseif ($event instanceof BeforeDeleteEvent) {
} elseif ($event instanceof BeforeSoftDeleteEvent) {
$activity->setSubject('folder_deleted', ['folder' => $folder->getTitle()]);
} elseif ($event instanceof MoveEvent) {
$activity->setSubject('folder_moved', ['folder' => $folder->getTitle()]);
Expand Down Expand Up @@ -203,7 +206,7 @@ public function publishBookmark(ChangeEvent $event): void {

if ($event instanceof CreateEvent) {
$activity->setSubject('bookmark_created', ['bookmark' => $bookmark->getTitle()]);
} elseif ($event instanceof BeforeDeleteEvent) {
} elseif ($event instanceof BeforeSoftDeleteEvent) {
$activity->setSubject('bookmark_deleted', ['bookmark' => $bookmark->getTitle()]);
} else {
return;
Expand Down
4 changes: 4 additions & 0 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
use OCA\Bookmarks\Dashboard\Frequent;
use OCA\Bookmarks\Dashboard\Recent;
use OCA\Bookmarks\Events\BeforeDeleteEvent;
use OCA\Bookmarks\Events\BeforeSoftDeleteEvent;
use OCA\Bookmarks\Events\BeforeSoftUndeleteEvent;
use OCA\Bookmarks\Events\CreateEvent;
use OCA\Bookmarks\Events\MoveEvent;
use OCA\Bookmarks\Events\UpdateEvent;
Expand Down Expand Up @@ -79,6 +81,8 @@ public function register(IRegistrationContext $context): void {
$context->registerEventListener(UpdateEvent::class, TreeCacheManager::class);
$context->registerEventListener(BeforeDeleteEvent::class, TreeCacheManager::class);
$context->registerEventListener(MoveEvent::class, TreeCacheManager::class);
$context->registerEventListener(BeforeSoftDeleteEvent::class, TreeCacheManager::class);
$context->registerEventListener(BeforeSoftUndeleteEvent::class, TreeCacheManager::class);

$context->registerEventListener(CreateEvent::class, ActivityPublisher::class);
$context->registerEventListener(UpdateEvent::class, ActivityPublisher::class);
Expand Down
7 changes: 6 additions & 1 deletion lib/AugmentedTemplateResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,15 @@
use OC;
use OCP\AppFramework\Http\TemplateResponse;

/**
* @psalm-template S of int
* @psalm-template H of array<string, mixed>
* @psalm-implements TemplateResponse<S,H>
*/
class AugmentedTemplateResponse extends TemplateResponse {
public function render() {
$return = parent::render();
$return = preg_replace('/<link rel="manifest" href="(.*?)">/i', '<link rel="manifest" href="'. OC::$server->getURLGenerator()->linkToRouteAbsolute('bookmarks.web_view.manifest').'">', $return);
preg_replace('/<link rel="manifest" href="(.*?)">/i', '<link rel="manifest" href="'. OC::$server->getURLGenerator()->linkToRouteAbsolute('bookmarks.web_view.manifest').'">', $return);
return $return;
}
}
33 changes: 33 additions & 0 deletions lib/BackgroundJobs/EmptyTrashbinJob.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php
/*
* Copyright (c) 2020-2024. The Nextcloud Bookmarks contributors.
*
* This file is licensed under the Affero General Public License version 3 or later. See the COPYING file.
*/

namespace OCA\Bookmarks\BackgroundJobs;

use OCA\Bookmarks\Db\TreeMapper;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\TimedJob;

class EmptyTrashbinJob extends TimedJob {
public const BATCH_SIZE = 1000; // 40 items
public const INTERVAL = 5 * 60; // 5 minutes
public const TRASHBIN_TTL = 2 * 4 * 4 * 7 * 24 * 60 * 60; // Two months


public function __construct(
ITimeFactory $timeFactory,
private TreeMapper $treeMapper,
) {
parent::__construct($timeFactory);

$this->setInterval(self::INTERVAL);
$this->setTimeSensitivity(self::TIME_INSENSITIVE);
}

protected function run($argument) {
$this->treeMapper->deleteOldTrashbinItems(self::BATCH_SIZE, self::TRASHBIN_TTL);
}
}
34 changes: 32 additions & 2 deletions lib/Controller/BookmarkController.php
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ public function __construct(
*
* @return ((int|mixed)[]|mixed)[]
*
* @psalm-return array{folders: array<array-key, int>, tags: array|mixed}
* @psalm-return array{folders: array<array-key, int>, tags: array<array-key, mixed>|mixed, archivedFilePath?: mixed|string, archivedFileType?: mixed|string, ...<array-key, mixed>}
*/
private function _returnBookmarkAsArray(Bookmark $bookmark): array {
$array = $bookmark->toArray();
Expand Down Expand Up @@ -265,6 +265,8 @@ public function getSingleBookmark($id): JSONResponse {
* @param bool|null $unavailable
* @param bool|null $archived
* @param bool|null $duplicated
* @param bool|null $recursive
* @param bool|null $deleted
* @return DataResponse
*
* @NoAdminRequired
Expand All @@ -286,6 +288,7 @@ public function getBookmarks(
?bool $archived = null,
?bool $duplicated = null,
bool $recursive = false,
bool $deleted = false,
): DataResponse {
$this->registerResponder('rss', function (DataResponse $res) {
if ($res->getData()['status'] === 'success') {
Expand All @@ -302,7 +305,9 @@ public function getBookmarks(
'description' => $description,
'bookmarks' => $bookmarks,
], '');
$response->setHeaders($res->getHeaders());
/** @var array<string, mixed> $headers */
$headers = $res->getHeaders();
$response->setHeaders($headers);
$response->setStatus($res->getStatus());
if (stripos($this->request->getHeader('accept'), 'application/rss+xml') !== false) {
$response->addHeader('Content-Type', 'application/rss+xml');
Expand Down Expand Up @@ -343,6 +348,10 @@ public function getBookmarks(
if ($duplicated !== null) {
$params->setDuplicated($duplicated);
}
// search soft deleted bookmarks
$params->setSoftDeleted($deleted);
// search bookmarks only in soft-deleted folders
$params->setSoftDeletedFolders($deleted);
$params->setTags($filterTag);
$params->setSearch($search);
$params->setConjunction($conjunction);
Expand Down Expand Up @@ -852,4 +861,25 @@ public function releaseLock(): JSONResponse {
}
return new JSONResponse(['status' => 'success']);
}

/**
* @return Http\DataResponse
* @NoAdminRequired
* @NoCSRFRequired
*
* @PublicPage
*/
public function getDeletedBookmarks(): DataResponse {
$this->authorizer->setCredentials($this->request);
if ($this->authorizer->getUserId() === null) {
return new Http\DataResponse(['status' => 'error', 'data' => 'Unauthorized'], Http::STATUS_FORBIDDEN);
}
try {
$bookmarks = $this->treeMapper->getSoftDeletedRootItems($this->authorizer->getUserId(), TreeMapper::TYPE_BOOKMARK);
} catch (UrlParseError|\OCP\DB\Exception $e) {
return new Http\DataResponse(['status' => 'error', 'data' => 'Internal error'], Http::STATUS_INTERNAL_SERVER_ERROR);
}

return new Http\DataResponse(['status' => 'success', 'data' => array_map(fn ($bookmark) => $this->_returnBookmarkAsArray($bookmark), $bookmarks)]);
}
}
Loading

0 comments on commit 5d34364

Please sign in to comment.