2019-10-22 20:01:09 +00:00
|
|
|
<?php
|
2020-09-21 12:25:50 +00:00
|
|
|
/*
|
2024-02-02 18:27:50 +00:00
|
|
|
* Copyright (c) 2020-2024. The Nextcloud Bookmarks contributors.
|
2020-09-21 12:25:50 +00:00
|
|
|
*
|
|
|
|
* This file is licensed under the Affero General Public License version 3 or later. See the COPYING file.
|
|
|
|
*/
|
2019-10-22 20:01:09 +00:00
|
|
|
|
|
|
|
namespace OCA\Bookmarks\Service;
|
|
|
|
|
|
|
|
use OCA\Bookmarks\Db\Bookmark;
|
|
|
|
use OCA\Bookmarks\Db\BookmarkMapper;
|
|
|
|
use OCA\Bookmarks\Db\Folder;
|
|
|
|
use OCA\Bookmarks\Db\FolderMapper;
|
|
|
|
use OCA\Bookmarks\Db\TagMapper;
|
2020-03-20 13:51:03 +00:00
|
|
|
use OCA\Bookmarks\Db\TreeMapper;
|
2019-12-28 16:41:31 +00:00
|
|
|
use OCA\Bookmarks\Exception\AlreadyExistsError;
|
2020-03-20 13:51:03 +00:00
|
|
|
use OCA\Bookmarks\Exception\HtmlParseError;
|
2019-10-22 20:01:09 +00:00
|
|
|
use OCA\Bookmarks\Exception\UnauthorizedAccessError;
|
2020-10-08 12:46:14 +00:00
|
|
|
use OCA\Bookmarks\Exception\UnsupportedOperation;
|
2019-10-22 20:01:09 +00:00
|
|
|
use OCA\Bookmarks\Exception\UrlParseError;
|
2019-12-28 16:41:31 +00:00
|
|
|
use OCA\Bookmarks\Exception\UserLimitExceededError;
|
2019-10-22 20:01:09 +00:00
|
|
|
use OCP\AppFramework\Db\DoesNotExistException;
|
|
|
|
use OCP\AppFramework\Db\Entity;
|
|
|
|
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Class HtmlImporter
|
|
|
|
*
|
|
|
|
* @package OCA\Bookmarks\Service
|
|
|
|
*/
|
|
|
|
class HtmlImporter {
|
|
|
|
/**
|
|
|
|
* @var BookmarkMapper
|
|
|
|
*/
|
|
|
|
protected $bookmarkMapper;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var FolderMapper
|
|
|
|
*/
|
|
|
|
protected $folderMapper;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var TagMapper
|
|
|
|
*/
|
|
|
|
protected $tagMapper;
|
|
|
|
|
|
|
|
/** @var BookmarksParser */
|
|
|
|
private $bookmarksParser;
|
2020-03-20 13:51:03 +00:00
|
|
|
/**
|
|
|
|
* @var TreeMapper
|
|
|
|
*/
|
|
|
|
private $treeMapper;
|
2020-10-12 19:51:51 +00:00
|
|
|
/**
|
2021-01-22 11:48:51 +00:00
|
|
|
* @var TreeCacheManager
|
2020-10-12 19:51:51 +00:00
|
|
|
*/
|
|
|
|
private $hashManager;
|
2019-10-22 20:01:09 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* ImportService constructor.
|
|
|
|
*
|
|
|
|
* @param BookmarkMapper $bookmarkMapper
|
|
|
|
* @param FolderMapper $folderMapper
|
|
|
|
* @param TagMapper $tagMapper
|
2020-09-21 12:17:46 +00:00
|
|
|
* @param TreeMapper $treeMapper
|
2019-10-22 20:01:09 +00:00
|
|
|
* @param BookmarksParser $bookmarksParser
|
2021-01-22 11:48:51 +00:00
|
|
|
* @param TreeCacheManager $hashManager
|
2019-10-22 20:01:09 +00:00
|
|
|
*/
|
2021-01-22 11:48:51 +00:00
|
|
|
public function __construct(BookmarkMapper $bookmarkMapper, FolderMapper $folderMapper, TagMapper $tagMapper, TreeMapper $treeMapper, BookmarksParser $bookmarksParser, \OCA\Bookmarks\Service\TreeCacheManager $hashManager) {
|
2019-10-22 20:01:09 +00:00
|
|
|
$this->bookmarkMapper = $bookmarkMapper;
|
|
|
|
$this->folderMapper = $folderMapper;
|
|
|
|
$this->tagMapper = $tagMapper;
|
2020-03-20 13:51:03 +00:00
|
|
|
$this->treeMapper = $treeMapper;
|
2019-10-22 20:01:09 +00:00
|
|
|
$this->bookmarksParser = $bookmarksParser;
|
2020-10-12 19:51:51 +00:00
|
|
|
$this->hashManager = $hashManager;
|
2019-10-22 20:01:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Import Bookmarks from html formatted file
|
2020-10-23 10:20:50 +00:00
|
|
|
*
|
2019-10-22 20:01:09 +00:00
|
|
|
* @param int $userId
|
2020-10-23 10:20:50 +00:00
|
|
|
* @param string $file
|
2020-09-21 12:17:46 +00:00
|
|
|
* @param int|null $rootFolder
|
2020-10-23 10:20:50 +00:00
|
|
|
*
|
2019-10-22 20:01:09 +00:00
|
|
|
* @return array
|
2020-10-23 10:20:50 +00:00
|
|
|
*
|
2019-10-22 20:01:09 +00:00
|
|
|
* @throws DoesNotExistException
|
|
|
|
* @throws MultipleObjectsReturnedException
|
|
|
|
* @throws UnauthorizedAccessError
|
2019-12-28 16:41:31 +00:00
|
|
|
* @throws AlreadyExistsError
|
|
|
|
* @throws UserLimitExceededError
|
2020-03-20 13:51:03 +00:00
|
|
|
* @throws HtmlParseError
|
2019-10-22 20:01:09 +00:00
|
|
|
*/
|
2020-03-20 13:51:03 +00:00
|
|
|
public function importFile($userId, string $file, int $rootFolder = null): array {
|
2019-10-22 20:01:09 +00:00
|
|
|
$content = file_get_contents($file);
|
|
|
|
return $this->import($userId, $content, $rootFolder);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Import Bookmarks from html
|
2021-01-20 14:42:41 +00:00
|
|
|
*
|
2019-10-22 20:01:09 +00:00
|
|
|
* @param int $userId
|
|
|
|
* @param string $content
|
2020-03-20 13:51:03 +00:00
|
|
|
* @param int|null $rootFolderId
|
2021-01-20 14:42:41 +00:00
|
|
|
*
|
|
|
|
* @return (array|mixed|string)[][]
|
|
|
|
*
|
2020-03-20 13:51:03 +00:00
|
|
|
* @throws AlreadyExistsError
|
2019-10-22 20:01:09 +00:00
|
|
|
* @throws DoesNotExistException
|
2020-03-20 13:51:03 +00:00
|
|
|
* @throws HtmlParseError
|
2019-10-22 20:01:09 +00:00
|
|
|
* @throws MultipleObjectsReturnedException
|
|
|
|
* @throws UnauthorizedAccessError
|
2019-12-28 16:41:31 +00:00
|
|
|
* @throws UserLimitExceededError
|
2021-01-20 14:42:41 +00:00
|
|
|
*
|
|
|
|
* @psalm-return array{imported: list<array>, errors: array<array-key, mixed|string>}
|
2019-10-22 20:01:09 +00:00
|
|
|
*/
|
2020-03-20 13:51:03 +00:00
|
|
|
public function import($userId, string $content, int $rootFolderId = null): array {
|
2019-10-22 20:01:09 +00:00
|
|
|
$imported = [];
|
|
|
|
$errors = [];
|
2020-10-12 19:51:51 +00:00
|
|
|
|
2020-03-20 13:51:03 +00:00
|
|
|
if ($rootFolderId === null) {
|
|
|
|
$rootFolder = $this->folderMapper->findRootFolder($userId);
|
|
|
|
} else {
|
|
|
|
$rootFolder = $this->folderMapper->find($rootFolderId);
|
|
|
|
if ($rootFolder->getUserId() !== $userId) {
|
|
|
|
throw new UnauthorizedAccessError('Not allowed to access folder ' . $rootFolder->getId());
|
2019-10-22 20:01:09 +00:00
|
|
|
}
|
|
|
|
}
|
2020-10-12 19:51:51 +00:00
|
|
|
|
2019-10-22 20:01:09 +00:00
|
|
|
$this->bookmarksParser->parse($content, false);
|
2020-10-12 19:51:51 +00:00
|
|
|
|
|
|
|
// Disable invalidation, since we're going to add a bunch of new data to the tree at a single point
|
|
|
|
$this->hashManager->setInvalidationEnabled(false);
|
|
|
|
|
2019-10-22 20:01:09 +00:00
|
|
|
foreach ($this->bookmarksParser->currentFolder['children'] as $folder) {
|
2020-03-20 13:51:03 +00:00
|
|
|
$imported[] = $this->importFolder($userId, $folder, $rootFolder->getId(), $errors);
|
2019-10-22 20:01:09 +00:00
|
|
|
}
|
|
|
|
foreach ($this->bookmarksParser->currentFolder['bookmarks'] as $bookmark) {
|
|
|
|
try {
|
2020-03-20 13:51:03 +00:00
|
|
|
$bm = $this->importBookmark($userId, $rootFolder->getId(), $bookmark);
|
2019-10-22 20:01:09 +00:00
|
|
|
} catch (UrlParseError $e) {
|
2019-12-28 16:41:31 +00:00
|
|
|
$errors[] = 'Failed to parse URL: ' . $bookmark['href'];
|
2019-10-22 20:01:09 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
$imported[] = ['type' => 'bookmark', 'id' => $bm->getId(), 'title' => $bookmark['title'], 'url' => $bookmark['href']];
|
|
|
|
}
|
2020-10-12 19:51:51 +00:00
|
|
|
|
|
|
|
$this->hashManager->setInvalidationEnabled(true);
|
2020-10-12 20:30:36 +00:00
|
|
|
$this->hashManager->invalidateFolder($rootFolder->getId());
|
2020-10-12 19:51:51 +00:00
|
|
|
|
2019-10-22 20:01:09 +00:00
|
|
|
return ['imported' => $imported, 'errors' => $errors];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-01-20 14:42:41 +00:00
|
|
|
* @param string $userId
|
2019-10-22 20:01:09 +00:00
|
|
|
* @param array $folderParams
|
|
|
|
* @param int $parentId
|
|
|
|
* @param array $errors
|
2020-10-23 10:20:50 +00:00
|
|
|
* @param int|null $index
|
|
|
|
*
|
2021-01-20 14:42:41 +00:00
|
|
|
* @return (array[]|int|mixed|string)[]
|
2020-10-23 10:20:50 +00:00
|
|
|
*
|
2019-10-22 20:01:09 +00:00
|
|
|
* @throws DoesNotExistException
|
|
|
|
* @throws MultipleObjectsReturnedException
|
2019-11-25 17:20:38 +00:00
|
|
|
* @throws UnauthorizedAccessError
|
2019-12-28 16:41:31 +00:00
|
|
|
* @throws AlreadyExistsError
|
|
|
|
* @throws UserLimitExceededError
|
2020-10-08 12:46:14 +00:00
|
|
|
* @throws UnsupportedOperation
|
2021-01-20 14:42:41 +00:00
|
|
|
*
|
|
|
|
* @psalm-return array{type: string, id: int, title: mixed, children: list<array>}
|
2019-10-22 20:01:09 +00:00
|
|
|
*/
|
2021-01-20 14:42:41 +00:00
|
|
|
private function importFolder(string $userId, array $folderParams, int $parentId, &$errors = [], $index = null): array {
|
2019-10-22 20:01:09 +00:00
|
|
|
$folder = new Folder();
|
|
|
|
$folder->setUserId($userId);
|
|
|
|
$folder->setTitle($folderParams['title']);
|
|
|
|
$folder = $this->folderMapper->insert($folder);
|
2020-10-08 12:46:14 +00:00
|
|
|
$this->treeMapper->move(TreeMapper::TYPE_FOLDER, $folder->getId(), $parentId, $index);
|
2019-10-24 11:32:23 +00:00
|
|
|
$newFolder = ['type' => 'folder', 'id' => $folder->getId(), 'title' => $folderParams['title'], 'children' => []];
|
2020-10-08 12:46:14 +00:00
|
|
|
$index = 0;
|
2019-10-22 20:01:09 +00:00
|
|
|
foreach ($folderParams['bookmarks'] as $bookmark) {
|
|
|
|
try {
|
2020-10-08 12:46:14 +00:00
|
|
|
$bm = $this->importBookmark($userId, $folder->getId(), $bookmark, $index++);
|
2019-10-22 20:01:09 +00:00
|
|
|
} catch (UrlParseError $e) {
|
2019-12-28 16:41:31 +00:00
|
|
|
$errors[] = 'Failed to parse URL: ' . $bookmark['href'];
|
2019-10-22 20:01:09 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
$newFolder['children'][] = ['type' => 'bookmark', 'id' => $bm->getId(), 'title' => $bookmark['title'], 'url' => $bookmark['href']];
|
|
|
|
}
|
2019-10-24 11:32:23 +00:00
|
|
|
foreach ($folderParams['children'] as $childFolder) {
|
2020-10-08 12:46:14 +00:00
|
|
|
$newFolder['children'][] = $this->importFolder($userId, $childFolder, $folder->getId(), $errors, $index++);
|
2019-10-22 20:01:09 +00:00
|
|
|
}
|
|
|
|
return $newFolder;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-01-20 14:42:41 +00:00
|
|
|
* @param string $userId
|
2019-10-22 20:01:09 +00:00
|
|
|
* @param int $folderId
|
|
|
|
* @param array $bookmark
|
2021-01-20 16:38:11 +00:00
|
|
|
* @param null|int $index
|
2019-10-22 20:01:09 +00:00
|
|
|
* @return Bookmark|Entity
|
2022-06-08 13:45:06 +00:00
|
|
|
* @throws UrlParseError|AlreadyExistsError|UnsupportedOperation|UserLimitExceededError|MultipleObjectsReturnedException
|
2019-10-22 20:01:09 +00:00
|
|
|
*/
|
2021-01-20 14:42:41 +00:00
|
|
|
private function importBookmark(string $userId, int $folderId, array $bookmark, $index = null) {
|
2019-10-22 20:01:09 +00:00
|
|
|
$bm = new Bookmark();
|
|
|
|
$bm->setUserId($userId);
|
|
|
|
$bm->setUrl($bookmark['href']);
|
|
|
|
$bm->setTitle($bookmark['title']);
|
2020-03-20 13:51:03 +00:00
|
|
|
$bm->setDescription($bookmark['description']);
|
|
|
|
if (isset($bookmark['add_date'])) {
|
|
|
|
$bm->setAdded($bookmark['add_date']->getTimestamp());
|
|
|
|
}
|
2019-10-22 20:01:09 +00:00
|
|
|
|
|
|
|
// insert bookmark
|
|
|
|
$bm = $this->bookmarkMapper->insertOrUpdate($bm);
|
|
|
|
// add to folder
|
2020-10-08 12:46:14 +00:00
|
|
|
$this->treeMapper->addToFolders(TreeMapper::TYPE_BOOKMARK, $bm->getId(), [$folderId], $index);
|
2019-10-22 20:01:09 +00:00
|
|
|
// add tags
|
|
|
|
$this->tagMapper->addTo($bookmark['tags'], $bm->getId());
|
|
|
|
|
|
|
|
return $bm;
|
|
|
|
}
|
|
|
|
}
|