bookmarks/lib/Controller/BookmarkController.php

855 lines
28 KiB
PHP

<?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\Controller;
use DateInterval;
use DateTime;
use Exception;
use OCA\Bookmarks\Contract\IImage;
use OCA\Bookmarks\Db\Bookmark;
use OCA\Bookmarks\Db\BookmarkMapper;
use OCA\Bookmarks\Db\Folder;
use OCA\Bookmarks\Db\FolderMapper;
use OCA\Bookmarks\Db\PublicFolder;
use OCA\Bookmarks\Db\PublicFolderMapper;
use OCA\Bookmarks\Db\TagMapper;
use OCA\Bookmarks\Db\TreeMapper;
use OCA\Bookmarks\Exception\AlreadyExistsError;
use OCA\Bookmarks\Exception\HtmlParseError;
use OCA\Bookmarks\Exception\UnauthorizedAccessError;
use OCA\Bookmarks\Exception\UnsupportedOperation;
use OCA\Bookmarks\Exception\UrlParseError;
use OCA\Bookmarks\Exception\UserLimitExceededError;
use OCA\Bookmarks\ExportResponse;
use OCA\Bookmarks\QueryParameters;
use OCA\Bookmarks\Service\Authorizer;
use OCA\Bookmarks\Service\BookmarkService;
use OCA\Bookmarks\Service\FolderService;
use OCA\Bookmarks\Service\HtmlExporter;
use OCP\AppFramework\ApiController;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataDisplayResponse;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Http\NotFoundResponse;
use OCP\AppFramework\Http\RedirectResponse;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Files\IRootFolder;
use OCP\IL10N;
use OCP\IURLGenerator;
use Psr\Log\LoggerInterface;
class BookmarkController extends ApiController {
private const IMAGES_CACHE_TTL = 7 * 24 * 60 * 60;
/**
* @var IL10N
*/
private $l10n;
/**
* @var LoggerInterface
*/
private $logger;
/**
* @var BookmarkMapper
*/
private $bookmarkMapper;
/**
* @var TagMapper
*/
private $tagMapper;
/**
* @var FolderMapper
*/
private $folderMapper;
/**
* @var ITimeFactory
*/
private $timeFactory;
/**
* @var IURLGenerator
*/
private $url;
/**
* @var HtmlExporter
*/
private $htmlExporter;
/**
* @var Authorizer
*/
private $authorizer;
/**
* @var TreeMapper
*/
private $treeMapper;
/**
* @var PublicFolderMapper
*/
private $publicFolderMapper;
private $rootFolderId;
/**
* @var BookmarkService
*/
private $bookmarks;
/**
* @var FolderService
*/
private $folders;
/**
* @var IRootFolder
*/
private $rootFolder;
/**
* @var \OCA\Bookmarks\Service\LockManager
*/
private $lockManager;
public function __construct(
$appName, $request, IL10N $l10n, BookmarkMapper $bookmarkMapper, TagMapper $tagMapper, FolderMapper $folderMapper, TreeMapper $treeMapper, PublicFolderMapper $publicFolderMapper, ITimeFactory $timeFactory, LoggerInterface $logger, IURLGenerator $url, HtmlExporter $htmlExporter, Authorizer $authorizer, BookmarkService $bookmarks, FolderService $folders, IRootFolder $rootFolder, \OCA\Bookmarks\Service\LockManager $lockManager
) {
parent::__construct($appName, $request);
$this->request = $request;
$this->l10n = $l10n;
$this->bookmarkMapper = $bookmarkMapper;
$this->tagMapper = $tagMapper;
$this->folderMapper = $folderMapper;
$this->treeMapper = $treeMapper;
$this->publicFolderMapper = $publicFolderMapper;
$this->timeFactory = $timeFactory;
$this->logger = $logger;
$this->url = $url;
$this->htmlExporter = $htmlExporter;
$this->authorizer = $authorizer;
$this->bookmarks = $bookmarks;
$this->folders = $folders;
$this->rootFolder = $rootFolder;
$this->lockManager = $lockManager;
$this->authorizer->setCORS(true);
}
/**
* @param Bookmark $bookmark
*
* @return ((int|mixed)[]|mixed)[]
*
* @psalm-return array{folders: array<array-key, int>, tags: array|mixed}
*/
private function _returnBookmarkAsArray(Bookmark $bookmark): array {
$array = $bookmark->toArray();
if (!isset($array['folders'])) {
$array['folders'] = array_map(function (Folder $folder) {
return $this->toExternalFolderId($folder->getId());
}, $this->treeMapper->findParentsOf(TreeMapper::TYPE_BOOKMARK, $bookmark->getId()));
} else {
$array['folders'] = array_map(function ($id) {
return $this->toExternalFolderId($id);
}, $array['folders']);
}
if (!isset($array['tags'])) {
$array['tags'] = $this->tagMapper->findByBookmark($bookmark->getId());
}
if ($array['archivedFile'] !== 0) {
$results = $this->rootFolder->getById($array['archivedFile']);
if (count($results)) {
$array['archivedFilePath'] = $results[0]->getPath();
$array['archivedFileType'] = $results[0]->getMimePart();
}
}
return $array;
}
/**
* @return int|null
*/
private function _getRootFolderId(): ?int {
if ($this->rootFolderId !== null) {
return $this->rootFolderId;
}
if ($this->authorizer->getUserId() !== null) {
$this->rootFolderId = $this->folderMapper->findRootFolder($this->authorizer->getUserId())->getId();
}
if ($this->authorizer->getToken() !== null) {
try {
/**
* @var $publicFolder PublicFolder
*/
$publicFolder = $this->publicFolderMapper->find($this->authorizer->getToken());
$this->rootFolderId = $publicFolder->getFolderId();
} catch (DoesNotExistException | MultipleObjectsReturnedException $e) {
$this->logger->error($e->getMessage()."\n".$e->getMessage());
}
}
return $this->rootFolderId;
}
/**
* @param int $external
*
* @return int|null
*/
private function toInternalFolderId(int $external): ?int {
if ($external === -1) {
return $this->_getRootFolderId();
}
return $external;
}
/**
* @param int $internal
* @return int
*/
private function toExternalFolderId(int $internal): int {
if ($internal === $this->_getRootFolderId()) {
return -1;
}
return $internal;
}
/**
* @param string $id
* @return JSONResponse
*
* @NoAdminRequired
* @NoCSRFRequired
*
* @PublicPage
*/
public function getSingleBookmark($id): JSONResponse {
if (!Authorizer::hasPermission(Authorizer::PERM_READ, $this->authorizer->getPermissionsForBookmark($id, $this->request))) {
return new JSONResponse(['status' => 'error', 'data' => 'Unauthorized'], Http::STATUS_FORBIDDEN);
}
try {
/**
* @var $bm Bookmark
*/
$bm = $this->bookmarkMapper->find($id);
} catch (DoesNotExistException $e) {
return new JSONResponse(['status' => 'error', 'data' => 'Not found'], Http::STATUS_NOT_FOUND);
} catch (MultipleObjectsReturnedException $e) {
return new JSONResponse(['status' => 'error', 'data' => 'Multiple objects found'], Http::STATUS_BAD_REQUEST);
}
return new JSONResponse(['item' => $this->_returnBookmarkAsArray($bm), 'status' => 'success']);
}
/**
* @param int $page
* @param string[] $tags
* @param string $conjunction
* @param string $sortby
* @param array $search
* @param int $limit
* @param bool $untagged
* @param int|null $folder
* @param string|null $url
* @param bool|null $unavailable
* @param bool|null $archived
* @param bool|null $duplicated
* @return DataResponse
*
* @NoAdminRequired
* @NoCSRFRequired
*
* @PublicPage
*/
public function getBookmarks(
$page = 0,
$tags = null,
$conjunction = 'or',
$sortby = '',
$search = [],
$limit = 10,
$untagged = false,
?int $folder = null,
?string $url = null,
?bool $unavailable = null,
?bool $archived = null,
?bool $duplicated = null,
bool $recursive = false,
): DataResponse {
$this->registerResponder('rss', function (DataResponse $res) {
if ($res->getData()['status'] === 'success') {
$bookmarks = $res->getData()['data'];
$description = '';
} else {
$bookmarks = [['id' => -1]];
$description = $res->getData()['data'];
}
$response = new TemplateResponse('bookmarks', 'rss', [
'rssLang' => $this->l10n->getLanguageCode(),
'rssPubDate' => date('r'),
'description' => $description,
'bookmarks' => $bookmarks,
], '');
$response->setHeaders($res->getHeaders());
$response->setStatus($res->getStatus());
if (stripos($this->request->getHeader('accept'), 'application/rss+xml') !== false) {
$response->addHeader('Content-Type', 'application/rss+xml');
} else {
$response->addHeader('Content-Type', 'text/xml; charset=UTF-8');
}
return $response;
});
$this->authorizer->setCredentials($this->request);
if ($this->authorizer->getUserId() === null && $this->authorizer->getToken() === null) {
$res = new DataResponse(['status' => 'error', 'data' => 'Please authenticate first'], Http::STATUS_UNAUTHORIZED);
$res->addHeader('WWW-Authenticate', 'Basic realm="Nextcloud", charset="UTF-8"');
return $res;
}
$userId = $this->authorizer->getUserId();
if (is_array($tags)) {
$filterTag = $tags;
} else {
$filterTag = [];
}
// set query params
$params = new QueryParameters();
if ($url !== null) {
$params->setUrl($url);
}
if ($unavailable !== null) {
$params->setUnavailable($unavailable);
}
if ($untagged !== null) {
$params->setUntagged($untagged);
}
if ($archived !== null) {
$params->setArchived($archived);
}
if ($duplicated !== null) {
$params->setDuplicated($duplicated);
}
$params->setTags($filterTag);
$params->setSearch($search);
$params->setConjunction($conjunction);
$params->setOffset($page * $limit);
$params->setLimit($limit);
if ($page === -1) {
$params->setLimit(-1);
$params->setOffset(0);
}
if ($sortby) {
$params->setSortBy($sortby);
} else {
$params->setSortBy('lastmodified');
}
if ($folder !== null) {
if (!Authorizer::hasPermission(Authorizer::PERM_READ, $this->authorizer->getPermissionsForFolder($folder, $this->request))) {
return new DataResponse(['status' => 'error', 'data' => ['Insufficient permissions']], Http::STATUS_FORBIDDEN);
}
try {
/** @var Folder $folderEntity */
$folderEntity = $this->folderMapper->find($this->toInternalFolderId($folder));
// IMPORTANT:
// If we have this user's permission to see the contents of their folder, simply set the userID
// to theirs
$userId = $folderEntity->getUserId();
} catch (DoesNotExistException | MultipleObjectsReturnedException $e) {
return new DataResponse(['status' => 'error', 'data' => 'Not found'], Http::STATUS_BAD_REQUEST);
}
$params->setFolder($this->toInternalFolderId($folder));
$params->setRecursive($recursive);
}
if ($userId !== null) {
try {
$result = $this->bookmarkMapper->findAll($userId, $params);
} catch (UrlParseError $e) {
return new DataResponse(['status' => 'error', 'data' => 'Failed to parse URL'], Http::STATUS_BAD_REQUEST);
}
} else {
try {
$result = $this->bookmarkMapper->findAllInPublicFolder($this->authorizer->getToken(), $params);
} catch (DoesNotExistException | MultipleObjectsReturnedException $e) {
return new DataResponse(['status' => 'error', 'data' => 'Not found'], Http::STATUS_BAD_REQUEST);
}
}
return new DataResponse([
'data' => array_map(
function ($bm) {
return $this->_returnBookmarkAsArray($bm);
}, $result
),
'status' => 'success',
]);
}
/**
* @param string $url
* @param string|null $title
* @param string $description
* @param array $tags
* @param array $folders
* @param string $target
* @return JSONResponse
*
* @NoAdminRequired
* @NoCSRFRequired
*
* @PublicPage
*/
public function newBookmark($url = '', $title = null, $description = null, $tags = null, $folders = [], $target = null): JSONResponse {
$permissions = Authorizer::PERM_ALL;
$this->authorizer->setCredentials($this->request);
foreach ($folders as $folder) {
$permissions &= $this->authorizer->getPermissionsForFolder($folder, $this->request);
}
if (!Authorizer::hasPermission(Authorizer::PERM_WRITE, $permissions) || $this->authorizer->getUserId() === null) {
return new JSONResponse(['status' => 'error', 'data' => 'Unauthorized'], Http::STATUS_FORBIDDEN);
}
try {
$folders = array_map(function ($folderId) {
return $this->toInternalFolderId($folderId);
}, $folders);
$bookmark = $this->bookmarks->create($this->authorizer->getUserId(), $target ?: $url, $title, $description, $tags, $folders);
return new JSONResponse(['item' => $this->_returnBookmarkAsArray($bookmark), 'status' => 'success']);
} catch (AlreadyExistsError $e) {
// This is really unlikely, as we make sure to use the existing one if it already exists
return new JSONResponse(['status' => 'error', 'data' => 'Bookmark already exists'], Http::STATUS_BAD_REQUEST);
} catch (UrlParseError $e) {
return new JSONResponse(['status' => 'error', 'data' => 'Invalid URL'], Http::STATUS_BAD_REQUEST);
} catch (UserLimitExceededError $e) {
return new JSONResponse(['status' => 'error', 'data' => 'User limit exceeded'], Http::STATUS_BAD_REQUEST);
} catch (DoesNotExistException $e) {
return new JSONResponse(['status' => 'error', 'data' => 'Could not add bookmark'], Http::STATUS_BAD_REQUEST);
} catch (MultipleObjectsReturnedException $e) {
return new JSONResponse(['status' => 'error', 'data' => 'Internal server error'], Http::STATUS_INTERNAL_SERVER_ERROR);
} catch (UnsupportedOperation $e) {
return new JSONResponse(['status' => 'error', 'data' => 'Internal server error'], Http::STATUS_INTERNAL_SERVER_ERROR);
}
}
/**
* @param int|null $id
* @param string|null $url
* @param string|null $title
* @param string|null $description
* @param array|null $tags
* @param array|null $folders
* @param string|null $target
* @return JSONResponse
*
* @NoAdminRequired
* @NoCSRFRequired
*
* @PublicPage
*/
public function editBookmark($id = null, $url = null, $title = null, $description = null, $tags = null, $folders = null, $target = null): JSONResponse {
if (!Authorizer::hasPermission(Authorizer::PERM_EDIT, $this->authorizer->getPermissionsForBookmark($id, $this->request))) {
return new JSONResponse(['status' => 'error', 'data' => 'Unauthorized'], Http::STATUS_FORBIDDEN);
}
try {
if (isset($folders)) {
$folders = array_map(function ($folderId) {
return $this->toInternalFolderId((int)$folderId);
}, $folders);
$permissions = Authorizer::PERM_ALL;
foreach ($folders as $folder) {
$permissions &= $this->authorizer->getPermissionsForFolder($folder, $this->request);
}
if (!Authorizer::hasPermission(Authorizer::PERM_WRITE, $permissions)) {
return new JSONResponse(['status' => 'error', 'data' => ['Insufficient permissions']], Http::STATUS_FORBIDDEN);
}
}
$bookmark = $this->bookmarks->update($this->authorizer->getUserId(), $id, $target ?: $url, $title, $description, $tags, $folders);
return new JSONResponse(['item' => $bookmark ? $this->_returnBookmarkAsArray($bookmark) : null, 'status' => 'success']);
} catch (AlreadyExistsError $e) {
// This is really unlikely, as we make sure to use the existing one if it already exists
return new JSONResponse(['status' => 'error', 'data' => 'Bookmark already exists'], Http::STATUS_BAD_REQUEST);
} catch (UrlParseError $e) {
$this->logger->error($e->getMessage(), ['exception' => $e]);
return new JSONResponse(['status' => 'error', 'data' => 'Invald URL'], Http::STATUS_BAD_REQUEST);
} catch (UserLimitExceededError $e) {
return new JSONResponse(['status' => 'error', 'data' => 'User limit exceeded'], Http::STATUS_BAD_REQUEST);
} catch (DoesNotExistException $e) {
return new JSONResponse(['status' => 'error', 'data' => 'Could not add bookmark'], Http::STATUS_BAD_REQUEST);
} catch (MultipleObjectsReturnedException $e) {
return new JSONResponse(['status' => 'error', 'data' => 'Internal server error'], Http::STATUS_INTERNAL_SERVER_ERROR);
} catch (UnsupportedOperation $e) {
return new JSONResponse(['status' => 'error', 'data' => 'Could not add bookmark'], Http::STATUS_INTERNAL_SERVER_ERROR);
}
}
/**
* @param int $id
* @return JSONResponse
*
* @NoAdminRequired
* @NoCSRFRequired
*
* @PublicPage
*/
public function deleteBookmark($id): JSONResponse {
try {
$this->bookmarkMapper->find($id);
} catch (DoesNotExistException | MultipleObjectsReturnedException $e) {
return new JSONResponse(['status' => 'success']);
}
if (!Authorizer::hasPermission(Authorizer::PERM_EDIT, $this->authorizer->getPermissionsForBookmark($id, $this->request))) {
return new JSONResponse(['status' => 'error', 'data' => ['Insufficient permissions']], Http::STATUS_FORBIDDEN);
}
try {
$this->bookmarks->delete($id);
} catch (UnsupportedOperation $e) {
return new JSONResponse(['status' => 'error', 'data' => ['Unsupported operation']], Http::STATUS_INTERNAL_SERVER_ERROR);
} catch (DoesNotExistException $e) {
return new JSONResponse(['status' => 'success']);
} catch (MultipleObjectsReturnedException $e) {
return new JSONResponse(['status' => 'error', 'data' => ['Multiple objects found']], Http::STATUS_INTERNAL_SERVER_ERROR);
}
return new JSONResponse(['status' => 'success']);
}
/**
*
* @param string $url
* @return JSONResponse
*
* @NoAdminRequired
* @NoCSRFRequired
*
* @PublicPage
*/
public function clickBookmark($url = ''): JSONResponse {
if ($this->authorizer->getUserId() === null) {
return new JSONResponse(['status' => 'error', 'data' => 'Unauthorized'], Http::STATUS_FORBIDDEN);
}
try {
$bookmark = $this->bookmarks->findByUrl($this->authorizer->getUserId(), $url);
} catch (DoesNotExistException $e) {
return new JSONResponse(['status' => 'error', 'data' => ['Not found']], Http::STATUS_BAD_REQUEST);
} catch (UrlParseError $e) {
return new JSONResponse(['status' => 'error', 'data' => ['Failed to parse URL']], Http::STATUS_BAD_REQUEST);
}
if ($bookmark->getUserId() !== $this->authorizer->getUserId()) {
return new JSONResponse(['status' => 'error', 'data' => ['Not found']], Http::STATUS_BAD_REQUEST);
}
try {
$this->bookmarks->click($bookmark->getId());
} catch (UrlParseError $e) {
return new JSONResponse(['status' => 'error', 'data' => ['Failed to parse URL']], Http::STATUS_BAD_REQUEST);
} catch (DoesNotExistException $e) {
return new JSONResponse(['status' => 'error', 'data' => ['Not found']], Http::STATUS_BAD_REQUEST);
} catch (MultipleObjectsReturnedException $e) {
return new JSONResponse(['status' => 'error', 'data' => ['Not found']], Http::STATUS_BAD_REQUEST);
}
return new JSONResponse(['status' => 'success'], Http::STATUS_OK);
}
/**
*
* @param int $id The id of the bookmark whose favicon should be returned
*
* @NoAdminRequired
* @NoCSRFRequired
*
* @PublicPage
* @return DataDisplayResponse|NotFoundResponse|RedirectResponse|DataResponse
*/
public function getBookmarkImage($id) {
if (!Authorizer::hasPermission(Authorizer::PERM_READ, $this->authorizer->getPermissionsForBookmark($id, $this->request))) {
return new DataResponse(['status' => 'error', 'data' => 'Unauthorized'], Http::STATUS_FORBIDDEN);
}
try {
$image = $this->bookmarks->getImage($id);
if ($image === null) {
return new DataResponse([], Http::STATUS_NOT_FOUND);
}
return $this->doImageResponse($image);
} catch (DoesNotExistException | MultipleObjectsReturnedException | Exception $e) {
return new NotFoundResponse();
}
}
/**
*
* @param int $id The id of the bookmark whose image should be returned
*
* @NoAdminRequired
* @NoCSRFRequired
*
* @PublicPage
* @return DataDisplayResponse|NotFoundResponse|DataResponse
*/
public function getBookmarkFavicon($id) {
if (!Authorizer::hasPermission(Authorizer::PERM_READ, $this->authorizer->getPermissionsForBookmark($id, $this->request))) {
return new DataResponse(['status' => 'error', 'data' => 'Unauthorized'], Http::STATUS_FORBIDDEN);
}
try {
$image = $this->bookmarks->getFavicon($id);
if ($image === null) {
return new NotFoundResponse();
}
return $this->doImageResponse($image);
} catch (DoesNotExistException | MultipleObjectsReturnedException | Exception $e) {
return new NotFoundResponse();
}
}
/**
* @param $image
*
* @return DataDisplayResponse|NotFoundResponse
*
* @throws Exception
*/
public function doImageResponse(?IImage $image) {
if ($image === null || $image->getData() === null) {
return new NotFoundResponse();
}
$response = new DataDisplayResponse($image->getData());
$response->addHeader('Content-Type', $image->getContentType());
$response->cacheFor(self::IMAGES_CACHE_TTL);
$expires = new DateTime();
$expires->setTimestamp($this->timeFactory->getTime());
$expires->add(new DateInterval('PT' . self::IMAGES_CACHE_TTL . 'S'));
$response->addHeader('Expires', $expires->format(DateTime::RFC1123));
$response->addHeader('Pragma', 'cache');
return $response;
}
/**
*
* @param int $folder The id of the folder to import into
* @return JSONResponse
*
* @NoAdminRequired
* @NoCSRFRequired
*
* @PublicPage
*/
public function importBookmark($folder = null): JSONResponse {
if (!Authorizer::hasPermission(Authorizer::PERM_WRITE, $this->authorizer->getPermissionsForFolder($folder ?? -1, $this->request))) {
return new JSONResponse(['status' => 'error', 'data' => 'Unauthorized'], Http::STATUS_FORBIDDEN);
}
$full_input = $this->request->getUploadedFile('bm_import');
$result = ['errors' => []];
if (empty($full_input)) {
$result['errors'][] = $this->l10n->t('No file provided for import');
return new JSONResponse(['status' => 'error', 'data' => $result['errors']]);
}
$file = $full_input['tmp_name'];
if ($full_input['type'] !== 'text/html') {
$result['errors'][] = $this->l10n->t('Unsupported file type for import');
return new JSONResponse(['status' => 'error', 'data' => $result['errors']]);
}
if ($folder !== null) {
$folder = $this->toInternalFolderId($folder);
} else {
$folder = $this->_getRootFolderId();
}
try {
$result = $this->folders->importFile($this->authorizer->getUserId(), $file, $folder);
} catch (UnauthorizedAccessError $e) {
return new JSONResponse(['status' => 'error', 'data' => 'Unauthorized access'], Http::STATUS_FORBIDDEN);
} catch (DoesNotExistException $e) {
return new JSONResponse(['status' => 'error', 'data' => 'Folder not found'], Http::STATUS_BAD_REQUEST);
} catch (MultipleObjectsReturnedException $e) {
return new JSONResponse(['status' => 'error', 'data' => 'Multiple objects found'], Http::STATUS_INTERNAL_SERVER_ERROR);
} catch (HtmlParseError $e) {
return new JSONResponse(['status' => 'error', 'data' => 'Parse error: Invalid HTML'], Http::STATUS_BAD_REQUEST);
} catch (UserLimitExceededError $e) {
return new JSONResponse(['status' => 'error', 'data' => 'Could not import all bookmarks: User limit Exceeded'], Http::STATUS_BAD_REQUEST);
} catch (AlreadyExistsError $e) {
return new JSONResponse(['status' => 'error', 'data' => 'Could not import all bookmarks: Already exists'], Http::STATUS_BAD_REQUEST);
} catch (UnsupportedOperation $e) {
return new JSONResponse(['status' => 'error', 'data' => 'Internal server error'], Http::STATUS_INTERNAL_SERVER_ERROR);
}
if (count($result['errors']) !== 0) {
$this->logger->warning(var_export($result['errors'], true), ['app' => 'bookmarks']);
return new JSONResponse(['status' => 'error', 'data' => $result['errors']]);
}
return new JSONResponse([
'status' => 'success',
'data' => $result['imported'],
]);
}
/**
* @return ExportResponse|JSONResponse
* @NoAdminRequired
* @NoCSRFRequired
*
* @PublicPage
*/
public function exportBookmark() {
$this->authorizer->setCredentials($this->request);
try {
$data = $this->htmlExporter->exportFolder($this->authorizer->getUserId(), $this->_getRootFolderId());
} catch (UnauthorizedAccessError $e) {
// Will probably never happen
return new JSONResponse(['status' => 'error', 'data' => 'Unauthorized access']);
} catch (DoesNotExistException $e) {
// Neither will this
return new JSONResponse(['status' => 'error', 'data' => 'Not found']);
} catch (MultipleObjectsReturnedException $e) {
return new JSONResponse(['status' => 'error', 'data' => 'Multiple objects found']);
}
return new ExportResponse($data);
}
/**
* @param int $folder
* @return JSONResponse
* @NoAdminRequired
* @NoCSRFRequired
*
* @PublicPage
*/
public function countBookmarks(int $folder): JSONResponse {
if (!Authorizer::hasPermission(Authorizer::PERM_READ, $this->authorizer->getPermissionsForFolder($folder, $this->request))) {
return new JSONResponse(['status' => 'error', 'data' => 'Unauthorized'], Http::STATUS_FORBIDDEN);
}
if ($folder === -1 && $this->authorizer->getUserId() !== null) {
$count = $this->bookmarkMapper->countBookmarksOfUser($this->authorizer->getUserId());
return new JSONResponse(['status' => 'success', 'item' => $count]);
}
$folder = $this->toInternalFolderId($folder);
$count = $this->treeMapper->countBookmarksInFolder($this->toInternalFolderId($folder));
return new JSONResponse(['status' => 'success', 'item' => $count]);
}
/**
* @return JSONResponse
* @NoAdminRequired
* @NoCSRFRequired
*
* @PublicPage
*/
public function countUnavailable(): JSONResponse {
if (!Authorizer::hasPermission(Authorizer::PERM_READ, $this->authorizer->getPermissionsForFolder(-1, $this->request))) {
return new JSONResponse(['status' => 'error', 'data' => 'Unauthorized'], Http::STATUS_FORBIDDEN);
}
$count = $this->bookmarkMapper->countUnavailable($this->authorizer->getUserId());
return new JSONResponse(['status' => 'success', 'item' => $count]);
}
/**
* @return JSONResponse
* @NoAdminRequired
* @NoCSRFRequired
*
* @PublicPage
*/
public function countArchived(): JSONResponse {
if (!Authorizer::hasPermission(Authorizer::PERM_READ, $this->authorizer->getPermissionsForFolder(-1, $this->request))) {
return new JSONResponse(['status' => 'error', 'data' => 'Unauthorized'], Http::STATUS_FORBIDDEN);
}
$count = $this->bookmarkMapper->countArchived($this->authorizer->getUserId());
return new JSONResponse(['status' => 'success', 'item' => $count]);
}
/**
* @return JSONResponse
* @NoAdminRequired
* @NoCSRFRequired
*
* @PublicPage
*/
public function countDuplicated(): JSONResponse {
if (!Authorizer::hasPermission(Authorizer::PERM_READ, $this->authorizer->getPermissionsForFolder(-1, $this->request))) {
return new JSONResponse(['status' => 'error', 'data' => 'Unauthorized'], Http::STATUS_FORBIDDEN);
}
$count = $this->bookmarkMapper->countDuplicated($this->authorizer->getUserId());
return new JSONResponse(['status' => 'success', 'item' => $count]);
}
/**
* @return JSONResponse
* @NoAdminRequired
* @NoCSRFRequired
*
* @PublicPage
*/
public function acquireLock(): JSONResponse {
if (!Authorizer::hasPermission(Authorizer::PERM_WRITE, $this->authorizer->getPermissionsForFolder(-1, $this->request))) {
return new JSONResponse(['status' => 'error', 'data' => 'Unauthorized'], Http::STATUS_FORBIDDEN);
}
try {
if ($this->lockManager->getLock($this->authorizer->getUserId()) === true) {
return new JSONResponse(['status' => 'error', 'data' => 'Resource is already locked'], Http::STATUS_LOCKED);
}
$this->lockManager->setLock($this->authorizer->getUserId(), true);
} catch (\Exception $e) {
$this->logger->error($e->getMessage());
return new JSONResponse(['status' => 'error'], Http::STATUS_INTERNAL_SERVER_ERROR);
}
return new JSONResponse(['status' => 'success']);
}
/**
* @return JSONResponse
* @NoAdminRequired
* @NoCSRFRequired
*
* @PublicPage
*/
public function releaseLock(): JSONResponse {
if (!Authorizer::hasPermission(Authorizer::PERM_WRITE, $this->authorizer->getPermissionsForFolder(-1, $this->request))) {
return new JSONResponse(['status' => 'error', 'data' => 'Unauthorized'], Http::STATUS_FORBIDDEN);
}
try {
if ($this->lockManager->getLock($this->authorizer->getUserId()) === false) {
return new JSONResponse(['status' => 'success']);
}
$this->lockManager->setLock($this->authorizer->getUserId(), false);
} catch (\Exception $e) {
$this->logger->error($e->getMessage());
return new JSONResponse(['status' => 'error'], Http::STATUS_INTERNAL_SERVER_ERROR);
}
return new JSONResponse(['status' => 'success']);
}
}