From f24896b237e40718fb6eaa2869592eb0855a47fd Mon Sep 17 00:00:00 2001 From: VirtualTam Date: Mon, 3 Dec 2018 01:10:39 +0100 Subject: namespacing: \Shaarli\Bookmark\LinkDB Signed-off-by: VirtualTam --- application/LinkDB.php | 591 ------------------- application/LinkFilter.php | 2 + application/LinkUtils.php | 2 + application/NetscapeBookmarkUtils.php | 1 + application/Updater.php | 2 + application/api/ApiMiddleware.php | 2 +- application/api/controllers/ApiController.php | 2 +- application/bookmark/LinkDB.php | 601 +++++++++++++++++++ application/feed/CachedPage.php | 1 + application/feed/FeedBuilder.php | 15 +- application/render/PageBuilder.php | 2 +- composer.json | 1 + index.php | 3 +- tests/LinkDBTest.php | 647 -------------------- tests/LinkFilterTest.php | 2 + tests/NetscapeBookmarkUtils/BookmarkExportTest.php | 2 + tests/NetscapeBookmarkUtils/BookmarkImportTest.php | 1 + tests/Updater/DummyUpdater.php | 2 + tests/Updater/UpdaterTest.php | 2 + tests/api/controllers/info/InfoTest.php | 2 +- tests/api/controllers/links/DeleteLinkTest.php | 6 +- tests/api/controllers/links/GetLinkIdTest.php | 4 +- tests/api/controllers/links/GetLinksTest.php | 6 +- tests/api/controllers/links/PostLinkTest.php | 6 +- tests/api/controllers/links/PutLinkTest.php | 6 +- tests/api/controllers/tags/DeleteTagTest.php | 8 +- tests/api/controllers/tags/GetTagNameTest.php | 2 +- tests/api/controllers/tags/GetTagsTest.php | 4 +- tests/api/controllers/tags/PutTagTest.php | 4 +- tests/bookmark/LinkDBTest.php | 653 +++++++++++++++++++++ tests/feed/FeedBuilderTest.php | 4 +- tests/http/UrlTest.php | 1 - tests/plugins/PluginIssoTest.php | 2 + tests/utils/ReferenceLinkDB.php | 3 + 34 files changed, 1315 insertions(+), 1277 deletions(-) delete mode 100644 application/LinkDB.php create mode 100644 application/bookmark/LinkDB.php delete mode 100644 tests/LinkDBTest.php create mode 100644 tests/bookmark/LinkDBTest.php diff --git a/application/LinkDB.php b/application/LinkDB.php deleted file mode 100644 index a5b42727..00000000 --- a/application/LinkDB.php +++ /dev/null @@ -1,591 +0,0 @@ -link offset) - private $urls; - - /** - * @var array List of all links IDS mapped with their array offset. - * Map: id->offset. - */ - protected $ids; - - // List of offset keys (for the Iterator interface implementation) - private $keys; - - // Position in the $this->keys array (for the Iterator interface) - private $position; - - // Is the user logged in? (used to filter private links) - private $loggedIn; - - // Hide public links - private $hidePublicLinks; - - // link redirector set in user settings. - private $redirector; - - /** - * Set this to `true` to urlencode link behind redirector link, `false` to leave it untouched. - * - * Example: - * anonym.to needs clean URL while dereferer.org needs urlencoded URL. - * - * @var boolean $redirectorEncode parameter: true or false - */ - private $redirectorEncode; - - /** - * Creates a new LinkDB - * - * Checks if the datastore exists; else, attempts to create a dummy one. - * - * @param string $datastore datastore file path. - * @param boolean $isLoggedIn is the user logged in? - * @param boolean $hidePublicLinks if true all links are private. - * @param string $redirector link redirector set in user settings. - * @param boolean $redirectorEncode Enable urlencode on redirected urls (default: true). - */ - public function __construct( - $datastore, - $isLoggedIn, - $hidePublicLinks, - $redirector = '', - $redirectorEncode = true - ) { - $this->datastore = $datastore; - $this->loggedIn = $isLoggedIn; - $this->hidePublicLinks = $hidePublicLinks; - $this->redirector = $redirector; - $this->redirectorEncode = $redirectorEncode === true; - $this->check(); - $this->read(); - } - - /** - * Countable - Counts elements of an object - */ - public function count() - { - return count($this->links); - } - - /** - * ArrayAccess - Assigns a value to the specified offset - */ - public function offsetSet($offset, $value) - { - // TODO: use exceptions instead of "die" - if (!$this->loggedIn) { - die(t('You are not authorized to add a link.')); - } - if (!isset($value['id']) || empty($value['url'])) { - die(t('Internal Error: A link should always have an id and URL.')); - } - if (($offset !== null && ! is_int($offset)) || ! is_int($value['id'])) { - die(t('You must specify an integer as a key.')); - } - if ($offset !== null && $offset !== $value['id']) { - die(t('Array offset and link ID must be equal.')); - } - - // If the link exists, we reuse the real offset, otherwise new entry - $existing = $this->getLinkOffset($offset); - if ($existing !== null) { - $offset = $existing; - } else { - $offset = count($this->links); - } - $this->links[$offset] = $value; - $this->urls[$value['url']] = $offset; - $this->ids[$value['id']] = $offset; - } - - /** - * ArrayAccess - Whether or not an offset exists - */ - public function offsetExists($offset) - { - return array_key_exists($this->getLinkOffset($offset), $this->links); - } - - /** - * ArrayAccess - Unsets an offset - */ - public function offsetUnset($offset) - { - if (!$this->loggedIn) { - // TODO: raise an exception - die('You are not authorized to delete a link.'); - } - $realOffset = $this->getLinkOffset($offset); - $url = $this->links[$realOffset]['url']; - unset($this->urls[$url]); - unset($this->ids[$realOffset]); - unset($this->links[$realOffset]); - } - - /** - * ArrayAccess - Returns the value at specified offset - */ - public function offsetGet($offset) - { - $realOffset = $this->getLinkOffset($offset); - return isset($this->links[$realOffset]) ? $this->links[$realOffset] : null; - } - - /** - * Iterator - Returns the current element - */ - public function current() - { - return $this[$this->keys[$this->position]]; - } - - /** - * Iterator - Returns the key of the current element - */ - public function key() - { - return $this->keys[$this->position]; - } - - /** - * Iterator - Moves forward to next element - */ - public function next() - { - ++$this->position; - } - - /** - * Iterator - Rewinds the Iterator to the first element - * - * Entries are sorted by date (latest first) - */ - public function rewind() - { - $this->keys = array_keys($this->ids); - $this->position = 0; - } - - /** - * Iterator - Checks if current position is valid - */ - public function valid() - { - return isset($this->keys[$this->position]); - } - - /** - * Checks if the DB directory and file exist - * - * If no DB file is found, creates a dummy DB. - */ - private function check() - { - if (file_exists($this->datastore)) { - return; - } - - // Create a dummy database for example - $this->links = array(); - $link = array( - 'id' => 1, - 'title'=> t('The personal, minimalist, super-fast, database free, bookmarking service'), - 'url'=>'https://shaarli.readthedocs.io', - 'description'=>t( - 'Welcome to Shaarli! This is your first public bookmark. ' - .'To edit or delete me, you must first login. - -To learn how to use Shaarli, consult the link "Documentation" at the bottom of this page. - -You use the community supported version of the original Shaarli project, by Sebastien Sauvage.' - ), - 'private'=>0, - 'created'=> new DateTime(), - 'tags'=>'opensource software' - ); - $link['shorturl'] = link_small_hash($link['created'], $link['id']); - $this->links[1] = $link; - - $link = array( - 'id' => 0, - 'title'=> t('My secret stuff... - Pastebin.com'), - 'url'=>'http://sebsauvage.net/paste/?8434b27936c09649#bR7XsXhoTiLcqCpQbmOpBi3rq2zzQUC5hBI7ZT1O3x8=', - 'description'=> t('Shhhh! I\'m a private link only YOU can see. You can delete me too.'), - 'private'=>1, - 'created'=> new DateTime('1 minute ago'), - 'tags'=>'secretstuff', - ); - $link['shorturl'] = link_small_hash($link['created'], $link['id']); - $this->links[0] = $link; - - // Write database to disk - $this->write(); - } - - /** - * Reads database from disk to memory - */ - private function read() - { - // Public links are hidden and user not logged in => nothing to show - if ($this->hidePublicLinks && !$this->loggedIn) { - $this->links = array(); - return; - } - - $this->urls = []; - $this->ids = []; - $this->links = FileUtils::readFlatDB($this->datastore, []); - - $toremove = array(); - foreach ($this->links as $key => &$link) { - if (! $this->loggedIn && $link['private'] != 0) { - // Transition for not upgraded databases. - unset($this->links[$key]); - continue; - } - - // Sanitize data fields. - sanitizeLink($link); - - // Remove private tags if the user is not logged in. - if (! $this->loggedIn) { - $link['tags'] = preg_replace('/(^|\s+)\.[^($|\s)]+\s*/', ' ', $link['tags']); - } - - // Do not use the redirector for internal links (Shaarli note URL starting with a '?'). - if (!empty($this->redirector) && !startsWith($link['url'], '?')) { - $link['real_url'] = $this->redirector; - if ($this->redirectorEncode) { - $link['real_url'] .= urlencode(unescape($link['url'])); - } else { - $link['real_url'] .= $link['url']; - } - } else { - $link['real_url'] = $link['url']; - } - - // To be able to load links before running the update, and prepare the update - if (! isset($link['created'])) { - $link['id'] = $link['linkdate']; - $link['created'] = DateTime::createFromFormat(self::LINK_DATE_FORMAT, $link['linkdate']); - if (! empty($link['updated'])) { - $link['updated'] = DateTime::createFromFormat(self::LINK_DATE_FORMAT, $link['updated']); - } - $link['shorturl'] = smallHash($link['linkdate']); - } - - $this->urls[$link['url']] = $key; - $this->ids[$link['id']] = $key; - } - } - - /** - * Saves the database from memory to disk - * - * @throws IOException the datastore is not writable - */ - private function write() - { - $this->reorder(); - FileUtils::writeFlatDB($this->datastore, $this->links); - } - - /** - * Saves the database from memory to disk - * - * @param string $pageCacheDir page cache directory - */ - public function save($pageCacheDir) - { - if (!$this->loggedIn) { - // TODO: raise an Exception instead - die('You are not authorized to change the database.'); - } - - $this->write(); - - invalidateCaches($pageCacheDir); - } - - /** - * Returns the link for a given URL, or False if it does not exist. - * - * @param string $url URL to search for - * - * @return mixed the existing link if it exists, else 'false' - */ - public function getLinkFromUrl($url) - { - if (isset($this->urls[$url])) { - return $this->links[$this->urls[$url]]; - } - return false; - } - - /** - * Returns the shaare corresponding to a smallHash. - * - * @param string $request QUERY_STRING server parameter. - * - * @return array $filtered array containing permalink data. - * - * @throws LinkNotFoundException if the smallhash is malformed or doesn't match any link. - */ - public function filterHash($request) - { - $request = substr($request, 0, 6); - $linkFilter = new LinkFilter($this->links); - return $linkFilter->filter(LinkFilter::$FILTER_HASH, $request); - } - - /** - * Returns the list of articles for a given day. - * - * @param string $request day to filter. Format: YYYYMMDD. - * - * @return array list of shaare found. - */ - public function filterDay($request) - { - $linkFilter = new LinkFilter($this->links); - return $linkFilter->filter(LinkFilter::$FILTER_DAY, $request); - } - - /** - * Filter links according to search parameters. - * - * @param array $filterRequest Search request content. Supported keys: - * - searchtags: list of tags - * - searchterm: term search - * @param bool $casesensitive Optional: Perform case sensitive filter - * @param string $visibility return only all/private/public links - * @param string $untaggedonly return only untagged links - * - * @return array filtered links, all links if no suitable filter was provided. - */ - public function filterSearch( - $filterRequest = array(), - $casesensitive = false, - $visibility = 'all', - $untaggedonly = false - ) { - // Filter link database according to parameters. - $searchtags = isset($filterRequest['searchtags']) ? escape($filterRequest['searchtags']) : ''; - $searchterm = isset($filterRequest['searchterm']) ? escape($filterRequest['searchterm']) : ''; - - // Search tags + fullsearch - blank string parameter will return all links. - $type = LinkFilter::$FILTER_TAG | LinkFilter::$FILTER_TEXT; // == "vuotext" - $request = [$searchtags, $searchterm]; - - $linkFilter = new LinkFilter($this); - return $linkFilter->filter($type, $request, $casesensitive, $visibility, $untaggedonly); - } - - /** - * Returns the list tags appearing in the links with the given tags - * - * @param array $filteringTags tags selecting the links to consider - * @param string $visibility process only all/private/public links - * - * @return array tag => linksCount - */ - public function linksCountPerTag($filteringTags = [], $visibility = 'all') - { - $links = $this->filterSearch(['searchtags' => $filteringTags], false, $visibility); - $tags = []; - $caseMapping = []; - foreach ($links as $link) { - foreach (preg_split('/\s+/', $link['tags'], 0, PREG_SPLIT_NO_EMPTY) as $tag) { - if (empty($tag)) { - continue; - } - // The first case found will be displayed. - if (!isset($caseMapping[strtolower($tag)])) { - $caseMapping[strtolower($tag)] = $tag; - $tags[$caseMapping[strtolower($tag)]] = 0; - } - $tags[$caseMapping[strtolower($tag)]]++; - } - } - - /* - * Formerly used arsort(), which doesn't define the sort behaviour for equal values. - * Also, this function doesn't produce the same result between PHP 5.6 and 7. - * - * So we now use array_multisort() to sort tags by DESC occurrences, - * then ASC alphabetically for equal values. - * - * @see https://github.com/shaarli/Shaarli/issues/1142 - */ - $keys = array_keys($tags); - $tmpTags = array_combine($keys, $keys); - array_multisort($tags, SORT_DESC, $tmpTags, SORT_ASC, $tags); - return $tags; - } - - /** - * Rename or delete a tag across all links. - * - * @param string $from Tag to rename - * @param string $to New tag. If none is provided, the from tag will be deleted - * - * @return array|bool List of altered links or false on error - */ - public function renameTag($from, $to) - { - if (empty($from)) { - return false; - } - $delete = empty($to); - // True for case-sensitive tag search. - $linksToAlter = $this->filterSearch(['searchtags' => $from], true); - foreach ($linksToAlter as $key => &$value) { - $tags = preg_split('/\s+/', trim($value['tags'])); - if (($pos = array_search($from, $tags)) !== false) { - if ($delete) { - unset($tags[$pos]); // Remove tag. - } else { - $tags[$pos] = trim($to); - } - $value['tags'] = trim(implode(' ', array_unique($tags))); - $this[$value['id']] = $value; - } - } - - return $linksToAlter; - } - - /** - * Returns the list of days containing articles (oldest first) - * Output: An array containing days (in format YYYYMMDD). - */ - public function days() - { - $linkDays = array(); - foreach ($this->links as $link) { - $linkDays[$link['created']->format('Ymd')] = 0; - } - $linkDays = array_keys($linkDays); - sort($linkDays); - - return $linkDays; - } - - /** - * Reorder links by creation date (newest first). - * - * Also update the urls and ids mapping arrays. - * - * @param string $order ASC|DESC - */ - public function reorder($order = 'DESC') - { - $order = $order === 'ASC' ? -1 : 1; - // Reorder array by dates. - usort($this->links, function ($a, $b) use ($order) { - if (isset($a['sticky']) && isset($b['sticky']) && $a['sticky'] !== $b['sticky']) { - return $a['sticky'] ? -1 : 1; - } - return $a['created'] < $b['created'] ? 1 * $order : -1 * $order; - }); - - $this->urls = []; - $this->ids = []; - foreach ($this->links as $key => $link) { - $this->urls[$link['url']] = $key; - $this->ids[$link['id']] = $key; - } - } - - /** - * Return the next key for link creation. - * E.g. If the last ID is 597, the next will be 598. - * - * @return int next ID. - */ - public function getNextId() - { - if (!empty($this->ids)) { - return max(array_keys($this->ids)) + 1; - } - return 0; - } - - /** - * Returns a link offset in links array from its unique ID. - * - * @param int $id Persistent ID of a link. - * - * @return int Real offset in local array, or null if doesn't exist. - */ - protected function getLinkOffset($id) - { - if (isset($this->ids[$id])) { - return $this->ids[$id]; - } - return null; - } -} diff --git a/application/LinkFilter.php b/application/LinkFilter.php index 8f147974..91c79905 100644 --- a/application/LinkFilter.php +++ b/application/LinkFilter.php @@ -1,5 +1,7 @@ get('resource.datastore'), true, $conf->get('privacy.hide_public_links'), diff --git a/application/api/controllers/ApiController.php b/application/api/controllers/ApiController.php index 47e0e178..cab97dc4 100644 --- a/application/api/controllers/ApiController.php +++ b/application/api/controllers/ApiController.php @@ -25,7 +25,7 @@ abstract class ApiController protected $conf; /** - * @var \LinkDB + * @var \Shaarli\Bookmark\LinkDB */ protected $linkDb; diff --git a/application/bookmark/LinkDB.php b/application/bookmark/LinkDB.php new file mode 100644 index 00000000..3b77422a --- /dev/null +++ b/application/bookmark/LinkDB.php @@ -0,0 +1,601 @@ +link offset) + private $urls; + + /** + * @var array List of all links IDS mapped with their array offset. + * Map: id->offset. + */ + protected $ids; + + // List of offset keys (for the Iterator interface implementation) + private $keys; + + // Position in the $this->keys array (for the Iterator interface) + private $position; + + // Is the user logged in? (used to filter private links) + private $loggedIn; + + // Hide public links + private $hidePublicLinks; + + // link redirector set in user settings. + private $redirector; + + /** + * Set this to `true` to urlencode link behind redirector link, `false` to leave it untouched. + * + * Example: + * anonym.to needs clean URL while dereferer.org needs urlencoded URL. + * + * @var boolean $redirectorEncode parameter: true or false + */ + private $redirectorEncode; + + /** + * Creates a new LinkDB + * + * Checks if the datastore exists; else, attempts to create a dummy one. + * + * @param string $datastore datastore file path. + * @param boolean $isLoggedIn is the user logged in? + * @param boolean $hidePublicLinks if true all links are private. + * @param string $redirector link redirector set in user settings. + * @param boolean $redirectorEncode Enable urlencode on redirected urls (default: true). + */ + public function __construct( + $datastore, + $isLoggedIn, + $hidePublicLinks, + $redirector = '', + $redirectorEncode = true + ) { + + $this->datastore = $datastore; + $this->loggedIn = $isLoggedIn; + $this->hidePublicLinks = $hidePublicLinks; + $this->redirector = $redirector; + $this->redirectorEncode = $redirectorEncode === true; + $this->check(); + $this->read(); + } + + /** + * Countable - Counts elements of an object + */ + public function count() + { + return count($this->links); + } + + /** + * ArrayAccess - Assigns a value to the specified offset + */ + public function offsetSet($offset, $value) + { + // TODO: use exceptions instead of "die" + if (!$this->loggedIn) { + die(t('You are not authorized to add a link.')); + } + if (!isset($value['id']) || empty($value['url'])) { + die(t('Internal Error: A link should always have an id and URL.')); + } + if (($offset !== null && !is_int($offset)) || !is_int($value['id'])) { + die(t('You must specify an integer as a key.')); + } + if ($offset !== null && $offset !== $value['id']) { + die(t('Array offset and link ID must be equal.')); + } + + // If the link exists, we reuse the real offset, otherwise new entry + $existing = $this->getLinkOffset($offset); + if ($existing !== null) { + $offset = $existing; + } else { + $offset = count($this->links); + } + $this->links[$offset] = $value; + $this->urls[$value['url']] = $offset; + $this->ids[$value['id']] = $offset; + } + + /** + * ArrayAccess - Whether or not an offset exists + */ + public function offsetExists($offset) + { + return array_key_exists($this->getLinkOffset($offset), $this->links); + } + + /** + * ArrayAccess - Unsets an offset + */ + public function offsetUnset($offset) + { + if (!$this->loggedIn) { + // TODO: raise an exception + die('You are not authorized to delete a link.'); + } + $realOffset = $this->getLinkOffset($offset); + $url = $this->links[$realOffset]['url']; + unset($this->urls[$url]); + unset($this->ids[$realOffset]); + unset($this->links[$realOffset]); + } + + /** + * ArrayAccess - Returns the value at specified offset + */ + public function offsetGet($offset) + { + $realOffset = $this->getLinkOffset($offset); + return isset($this->links[$realOffset]) ? $this->links[$realOffset] : null; + } + + /** + * Iterator - Returns the current element + */ + public function current() + { + return $this[$this->keys[$this->position]]; + } + + /** + * Iterator - Returns the key of the current element + */ + public function key() + { + return $this->keys[$this->position]; + } + + /** + * Iterator - Moves forward to next element + */ + public function next() + { + ++$this->position; + } + + /** + * Iterator - Rewinds the Iterator to the first element + * + * Entries are sorted by date (latest first) + */ + public function rewind() + { + $this->keys = array_keys($this->ids); + $this->position = 0; + } + + /** + * Iterator - Checks if current position is valid + */ + public function valid() + { + return isset($this->keys[$this->position]); + } + + /** + * Checks if the DB directory and file exist + * + * If no DB file is found, creates a dummy DB. + */ + private function check() + { + if (file_exists($this->datastore)) { + return; + } + + // Create a dummy database for example + $this->links = array(); + $link = array( + 'id' => 1, + 'title' => t('The personal, minimalist, super-fast, database free, bookmarking service'), + 'url' => 'https://shaarli.readthedocs.io', + 'description' => t( + 'Welcome to Shaarli! This is your first public bookmark. ' + . 'To edit or delete me, you must first login. + +To learn how to use Shaarli, consult the link "Documentation" at the bottom of this page. + +You use the community supported version of the original Shaarli project, by Sebastien Sauvage.' + ), + 'private' => 0, + 'created' => new DateTime(), + 'tags' => 'opensource software' + ); + $link['shorturl'] = link_small_hash($link['created'], $link['id']); + $this->links[1] = $link; + + $link = array( + 'id' => 0, + 'title' => t('My secret stuff... - Pastebin.com'), + 'url' => 'http://sebsauvage.net/paste/?8434b27936c09649#bR7XsXhoTiLcqCpQbmOpBi3rq2zzQUC5hBI7ZT1O3x8=', + 'description' => t('Shhhh! I\'m a private link only YOU can see. You can delete me too.'), + 'private' => 1, + 'created' => new DateTime('1 minute ago'), + 'tags' => 'secretstuff', + ); + $link['shorturl'] = link_small_hash($link['created'], $link['id']); + $this->links[0] = $link; + + // Write database to disk + $this->write(); + } + + /** + * Reads database from disk to memory + */ + private function read() + { + // Public links are hidden and user not logged in => nothing to show + if ($this->hidePublicLinks && !$this->loggedIn) { + $this->links = array(); + return; + } + + $this->urls = []; + $this->ids = []; + $this->links = FileUtils::readFlatDB($this->datastore, []); + + $toremove = array(); + foreach ($this->links as $key => &$link) { + if (!$this->loggedIn && $link['private'] != 0) { + // Transition for not upgraded databases. + unset($this->links[$key]); + continue; + } + + // Sanitize data fields. + sanitizeLink($link); + + // Remove private tags if the user is not logged in. + if (!$this->loggedIn) { + $link['tags'] = preg_replace('/(^|\s+)\.[^($|\s)]+\s*/', ' ', $link['tags']); + } + + // Do not use the redirector for internal links (Shaarli note URL starting with a '?'). + if (!empty($this->redirector) && !startsWith($link['url'], '?')) { + $link['real_url'] = $this->redirector; + if ($this->redirectorEncode) { + $link['real_url'] .= urlencode(unescape($link['url'])); + } else { + $link['real_url'] .= $link['url']; + } + } else { + $link['real_url'] = $link['url']; + } + + // To be able to load links before running the update, and prepare the update + if (!isset($link['created'])) { + $link['id'] = $link['linkdate']; + $link['created'] = DateTime::createFromFormat(self::LINK_DATE_FORMAT, $link['linkdate']); + if (!empty($link['updated'])) { + $link['updated'] = DateTime::createFromFormat(self::LINK_DATE_FORMAT, $link['updated']); + } + $link['shorturl'] = smallHash($link['linkdate']); + } + + $this->urls[$link['url']] = $key; + $this->ids[$link['id']] = $key; + } + } + + /** + * Saves the database from memory to disk + * + * @throws IOException the datastore is not writable + */ + private function write() + { + $this->reorder(); + FileUtils::writeFlatDB($this->datastore, $this->links); + } + + /** + * Saves the database from memory to disk + * + * @param string $pageCacheDir page cache directory + */ + public function save($pageCacheDir) + { + if (!$this->loggedIn) { + // TODO: raise an Exception instead + die('You are not authorized to change the database.'); + } + + $this->write(); + + invalidateCaches($pageCacheDir); + } + + /** + * Returns the link for a given URL, or False if it does not exist. + * + * @param string $url URL to search for + * + * @return mixed the existing link if it exists, else 'false' + */ + public function getLinkFromUrl($url) + { + if (isset($this->urls[$url])) { + return $this->links[$this->urls[$url]]; + } + return false; + } + + /** + * Returns the shaare corresponding to a smallHash. + * + * @param string $request QUERY_STRING server parameter. + * + * @return array $filtered array containing permalink data. + * + * @throws LinkNotFoundException if the smallhash is malformed or doesn't match any link. + */ + public function filterHash($request) + { + $request = substr($request, 0, 6); + $linkFilter = new LinkFilter($this->links); + return $linkFilter->filter(LinkFilter::$FILTER_HASH, $request); + } + + /** + * Returns the list of articles for a given day. + * + * @param string $request day to filter. Format: YYYYMMDD. + * + * @return array list of shaare found. + */ + public function filterDay($request) + { + $linkFilter = new LinkFilter($this->links); + return $linkFilter->filter(LinkFilter::$FILTER_DAY, $request); + } + + /** + * Filter links according to search parameters. + * + * @param array $filterRequest Search request content. Supported keys: + * - searchtags: list of tags + * - searchterm: term search + * @param bool $casesensitive Optional: Perform case sensitive filter + * @param string $visibility return only all/private/public links + * @param string $untaggedonly return only untagged links + * + * @return array filtered links, all links if no suitable filter was provided. + */ + public function filterSearch( + $filterRequest = array(), + $casesensitive = false, + $visibility = 'all', + $untaggedonly = false + ) { + + // Filter link database according to parameters. + $searchtags = isset($filterRequest['searchtags']) ? escape($filterRequest['searchtags']) : ''; + $searchterm = isset($filterRequest['searchterm']) ? escape($filterRequest['searchterm']) : ''; + + // Search tags + fullsearch - blank string parameter will return all links. + $type = LinkFilter::$FILTER_TAG | LinkFilter::$FILTER_TEXT; // == "vuotext" + $request = [$searchtags, $searchterm]; + + $linkFilter = new LinkFilter($this); + return $linkFilter->filter($type, $request, $casesensitive, $visibility, $untaggedonly); + } + + /** + * Returns the list tags appearing in the links with the given tags + * + * @param array $filteringTags tags selecting the links to consider + * @param string $visibility process only all/private/public links + * + * @return array tag => linksCount + */ + public function linksCountPerTag($filteringTags = [], $visibility = 'all') + { + $links = $this->filterSearch(['searchtags' => $filteringTags], false, $visibility); + $tags = []; + $caseMapping = []; + foreach ($links as $link) { + foreach (preg_split('/\s+/', $link['tags'], 0, PREG_SPLIT_NO_EMPTY) as $tag) { + if (empty($tag)) { + continue; + } + // The first case found will be displayed. + if (!isset($caseMapping[strtolower($tag)])) { + $caseMapping[strtolower($tag)] = $tag; + $tags[$caseMapping[strtolower($tag)]] = 0; + } + $tags[$caseMapping[strtolower($tag)]]++; + } + } + + /* + * Formerly used arsort(), which doesn't define the sort behaviour for equal values. + * Also, this function doesn't produce the same result between PHP 5.6 and 7. + * + * So we now use array_multisort() to sort tags by DESC occurrences, + * then ASC alphabetically for equal values. + * + * @see https://github.com/shaarli/Shaarli/issues/1142 + */ + $keys = array_keys($tags); + $tmpTags = array_combine($keys, $keys); + array_multisort($tags, SORT_DESC, $tmpTags, SORT_ASC, $tags); + return $tags; + } + + /** + * Rename or delete a tag across all links. + * + * @param string $from Tag to rename + * @param string $to New tag. If none is provided, the from tag will be deleted + * + * @return array|bool List of altered links or false on error + */ + public function renameTag($from, $to) + { + if (empty($from)) { + return false; + } + $delete = empty($to); + // True for case-sensitive tag search. + $linksToAlter = $this->filterSearch(['searchtags' => $from], true); + foreach ($linksToAlter as $key => &$value) { + $tags = preg_split('/\s+/', trim($value['tags'])); + if (($pos = array_search($from, $tags)) !== false) { + if ($delete) { + unset($tags[$pos]); // Remove tag. + } else { + $tags[$pos] = trim($to); + } + $value['tags'] = trim(implode(' ', array_unique($tags))); + $this[$value['id']] = $value; + } + } + + return $linksToAlter; + } + + /** + * Returns the list of days containing articles (oldest first) + * Output: An array containing days (in format YYYYMMDD). + */ + public function days() + { + $linkDays = array(); + foreach ($this->links as $link) { + $linkDays[$link['created']->format('Ymd')] = 0; + } + $linkDays = array_keys($linkDays); + sort($linkDays); + + return $linkDays; + } + + /** + * Reorder links by creation date (newest first). + * + * Also update the urls and ids mapping arrays. + * + * @param string $order ASC|DESC + */ + public function reorder($order = 'DESC') + { + $order = $order === 'ASC' ? -1 : 1; + // Reorder array by dates. + usort($this->links, function ($a, $b) use ($order) { + if (isset($a['sticky']) && isset($b['sticky']) && $a['sticky'] !== $b['sticky']) { + return $a['sticky'] ? -1 : 1; + } + return $a['created'] < $b['created'] ? 1 * $order : -1 * $order; + }); + + $this->urls = []; + $this->ids = []; + foreach ($this->links as $key => $link) { + $this->urls[$link['url']] = $key; + $this->ids[$link['id']] = $key; + } + } + + /** + * Return the next key for link creation. + * E.g. If the last ID is 597, the next will be 598. + * + * @return int next ID. + */ + public function getNextId() + { + if (!empty($this->ids)) { + return max(array_keys($this->ids)) + 1; + } + return 0; + } + + /** + * Returns a link offset in links array from its unique ID. + * + * @param int $id Persistent ID of a link. + * + * @return int Real offset in local array, or null if doesn't exist. + */ + protected function getLinkOffset($id) + { + if (isset($this->ids[$id])) { + return $this->ids[$id]; + } + return null; + } +} diff --git a/application/feed/CachedPage.php b/application/feed/CachedPage.php index 1c51ac73..d809bdd9 100644 --- a/application/feed/CachedPage.php +++ b/application/feed/CachedPage.php @@ -1,6 +1,7 @@ write(self::$testDatastore); - - self::$publicLinkDB = new LinkDB(self::$testDatastore, false, false); - self::$privateLinkDB = new LinkDB(self::$testDatastore, true, false); - } - - /** - * Resets test data for each test - */ - protected function setUp() - { - if (file_exists(self::$testDatastore)) { - unlink(self::$testDatastore); - } - } - - /** - * Allows to test LinkDB's private methods - * - * @see - * https://sebastian-bergmann.de/archives/881-Testing-Your-Privates.html - * http://stackoverflow.com/a/2798203 - */ - protected static function getMethod($name) - { - $class = new ReflectionClass('LinkDB'); - $method = $class->getMethod($name); - $method->setAccessible(true); - return $method; - } - - /** - * Instantiate LinkDB objects - logged in user - */ - public function testConstructLoggedIn() - { - new LinkDB(self::$testDatastore, true, false); - $this->assertFileExists(self::$testDatastore); - } - - /** - * Instantiate LinkDB objects - logged out or public instance - */ - public function testConstructLoggedOut() - { - new LinkDB(self::$testDatastore, false, false); - $this->assertFileExists(self::$testDatastore); - } - - /** - * Attempt to instantiate a LinkDB whereas the datastore is not writable - * - * @expectedException Shaarli\Exceptions\IOException - * @expectedExceptionMessageRegExp /Error accessing "null"/ - */ - public function testConstructDatastoreNotWriteable() - { - new LinkDB('null/store.db', false, false); - } - - /** - * The DB doesn't exist, ensure it is created with dummy content - */ - public function testCheckDBNew() - { - $linkDB = new LinkDB(self::$testDatastore, false, false); - unlink(self::$testDatastore); - $this->assertFileNotExists(self::$testDatastore); - - $checkDB = self::getMethod('check'); - $checkDB->invokeArgs($linkDB, array()); - $this->assertFileExists(self::$testDatastore); - - // ensure the correct data has been written - $this->assertGreaterThan(0, filesize(self::$testDatastore)); - } - - /** - * The DB exists, don't do anything - */ - public function testCheckDBLoad() - { - $linkDB = new LinkDB(self::$testDatastore, false, false); - $datastoreSize = filesize(self::$testDatastore); - $this->assertGreaterThan(0, $datastoreSize); - - $checkDB = self::getMethod('check'); - $checkDB->invokeArgs($linkDB, array()); - - // ensure the datastore is left unmodified - $this->assertEquals( - $datastoreSize, - filesize(self::$testDatastore) - ); - } - - /** - * Load an empty DB - */ - public function testReadEmptyDB() - { - file_put_contents(self::$testDatastore, ''); - $emptyDB = new LinkDB(self::$testDatastore, false, false); - $this->assertEquals(0, sizeof($emptyDB)); - $this->assertEquals(0, count($emptyDB)); - } - - /** - * Load public links from the DB - */ - public function testReadPublicDB() - { - $this->assertEquals( - self::$refDB->countPublicLinks(), - sizeof(self::$publicLinkDB) - ); - } - - /** - * Load public and private links from the DB - */ - public function testReadPrivateDB() - { - $this->assertEquals( - self::$refDB->countLinks(), - sizeof(self::$privateLinkDB) - ); - } - - /** - * Save the links to the DB - */ - public function testSave() - { - $testDB = new LinkDB(self::$testDatastore, true, false); - $dbSize = sizeof($testDB); - - $link = array( - 'id' => 42, - 'title'=>'an additional link', - 'url'=>'http://dum.my', - 'description'=>'One more', - 'private'=>0, - 'created'=> DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150518_190000'), - 'tags'=>'unit test' - ); - $testDB[$link['id']] = $link; - $testDB->save('tests'); - - $testDB = new LinkDB(self::$testDatastore, true, false); - $this->assertEquals($dbSize + 1, sizeof($testDB)); - } - - /** - * Count existing links - */ - public function testCount() - { - $this->assertEquals( - self::$refDB->countPublicLinks(), - self::$publicLinkDB->count() - ); - $this->assertEquals( - self::$refDB->countLinks(), - self::$privateLinkDB->count() - ); - } - - /** - * Count existing links - public links hidden - */ - public function testCountHiddenPublic() - { - $linkDB = new LinkDB(self::$testDatastore, false, true); - - $this->assertEquals( - 0, - $linkDB->count() - ); - $this->assertEquals( - 0, - $linkDB->count() - ); - } - - /** - * List the days for which links have been posted - */ - public function testDays() - { - $this->assertEquals( - array('20100309', '20100310', '20121206', '20121207', '20130614', '20150310'), - self::$publicLinkDB->days() - ); - - $this->assertEquals( - array('20100309', '20100310', '20121206', '20121207', '20130614', '20141125', '20150310'), - self::$privateLinkDB->days() - ); - } - - /** - * The URL corresponds to an existing entry in the DB - */ - public function testGetKnownLinkFromURL() - { - $link = self::$publicLinkDB->getLinkFromUrl('http://mediagoblin.org/'); - - $this->assertNotEquals(false, $link); - $this->assertContains( - 'A free software media publishing platform', - $link['description'] - ); - } - - /** - * The URL is not in the DB - */ - public function testGetUnknownLinkFromURL() - { - $this->assertEquals( - false, - self::$publicLinkDB->getLinkFromUrl('http://dev.null') - ); - } - - /** - * Lists all tags - */ - public function testAllTags() - { - $this->assertEquals( - array( - 'web' => 3, - 'cartoon' => 2, - 'gnu' => 2, - 'dev' => 1, - 'samba' => 1, - 'media' => 1, - 'software' => 1, - 'stallman' => 1, - 'free' => 1, - '-exclude' => 1, - 'hashtag' => 2, - // The DB contains a link with `sTuff` and another one with `stuff` tag. - // They need to be grouped with the first case found - order by date DESC: `sTuff`. - 'sTuff' => 2, - 'ut' => 1, - ), - self::$publicLinkDB->linksCountPerTag() - ); - - $this->assertEquals( - array( - 'web' => 4, - 'cartoon' => 3, - 'gnu' => 2, - 'dev' => 2, - 'samba' => 1, - 'media' => 1, - 'software' => 1, - 'stallman' => 1, - 'free' => 1, - 'html' => 1, - 'w3c' => 1, - 'css' => 1, - 'Mercurial' => 1, - 'sTuff' => 2, - '-exclude' => 1, - '.hidden' => 1, - 'hashtag' => 2, - 'tag1' => 1, - 'tag2' => 1, - 'tag3' => 1, - 'tag4' => 1, - 'ut' => 1, - ), - self::$privateLinkDB->linksCountPerTag() - ); - $this->assertEquals( - array( - 'web' => 4, - 'cartoon' => 2, - 'gnu' => 1, - 'dev' => 1, - 'samba' => 1, - 'media' => 1, - 'html' => 1, - 'w3c' => 1, - 'css' => 1, - 'Mercurial' => 1, - '.hidden' => 1, - 'hashtag' => 1, - ), - self::$privateLinkDB->linksCountPerTag(['web']) - ); - $this->assertEquals( - array( - 'web' => 1, - 'html' => 1, - 'w3c' => 1, - 'css' => 1, - 'Mercurial' => 1, - ), - self::$privateLinkDB->linksCountPerTag(['web'], 'private') - ); - } - - /** - * Test real_url without redirector. - */ - public function testLinkRealUrlWithoutRedirector() - { - $db = new LinkDB(self::$testDatastore, false, false); - foreach ($db as $link) { - $this->assertEquals($link['url'], $link['real_url']); - } - } - - /** - * Test real_url with redirector. - */ - public function testLinkRealUrlWithRedirector() - { - $redirector = 'http://redirector.to?'; - $db = new LinkDB(self::$testDatastore, false, false, $redirector); - foreach ($db as $link) { - $this->assertStringStartsWith($redirector, $link['real_url']); - $this->assertNotFalse(strpos($link['real_url'], urlencode('://'))); - } - - $db = new LinkDB(self::$testDatastore, false, false, $redirector, false); - foreach ($db as $link) { - $this->assertStringStartsWith($redirector, $link['real_url']); - $this->assertFalse(strpos($link['real_url'], urlencode('://'))); - } - } - - /** - * Test filter with string. - */ - public function testFilterString() - { - $tags = 'dev cartoon'; - $request = array('searchtags' => $tags); - $this->assertEquals( - 2, - count(self::$privateLinkDB->filterSearch($request, true, false)) - ); - } - - /** - * Test filter with string. - */ - public function testFilterArray() - { - $tags = array('dev', 'cartoon'); - $request = array('searchtags' => $tags); - $this->assertEquals( - 2, - count(self::$privateLinkDB->filterSearch($request, true, false)) - ); - } - - /** - * Test hidden tags feature: - * tags starting with a dot '.' are only visible when logged in. - */ - public function testHiddenTags() - { - $tags = '.hidden'; - $request = array('searchtags' => $tags); - $this->assertEquals( - 1, - count(self::$privateLinkDB->filterSearch($request, true, false)) - ); - - $this->assertEquals( - 0, - count(self::$publicLinkDB->filterSearch($request, true, false)) - ); - } - - /** - * Test filterHash() with a valid smallhash. - */ - public function testFilterHashValid() - { - $request = smallHash('20150310_114651'); - $this->assertEquals( - 1, - count(self::$publicLinkDB->filterHash($request)) - ); - $request = smallHash('20150310_114633' . 8); - $this->assertEquals( - 1, - count(self::$publicLinkDB->filterHash($request)) - ); - } - - /** - * Test filterHash() with an invalid smallhash. - * - * @expectedException LinkNotFoundException - */ - public function testFilterHashInValid1() - { - $request = 'blabla'; - self::$publicLinkDB->filterHash($request); - } - - /** - * Test filterHash() with an empty smallhash. - * - * @expectedException LinkNotFoundException - */ - public function testFilterHashInValid() - { - self::$publicLinkDB->filterHash(''); - } - - /** - * Test reorder with asc/desc parameter. - */ - public function testReorderLinksDesc() - { - self::$privateLinkDB->reorder('ASC'); - $stickyIds = [11, 10]; - $standardIds = [42, 4, 9, 1, 0, 7, 6, 8, 41]; - $linkIds = array_merge($stickyIds, $standardIds); - $cpt = 0; - foreach (self::$privateLinkDB as $key => $value) { - $this->assertEquals($linkIds[$cpt++], $key); - } - self::$privateLinkDB->reorder('DESC'); - $linkIds = array_merge(array_reverse($stickyIds), array_reverse($standardIds)); - $cpt = 0; - foreach (self::$privateLinkDB as $key => $value) { - $this->assertEquals($linkIds[$cpt++], $key); - } - } - - /** - * Test rename tag with a valid value present in multiple links - */ - public function testRenameTagMultiple() - { - self::$refDB->write(self::$testDatastore); - $linkDB = new LinkDB(self::$testDatastore, true, false); - - $res = $linkDB->renameTag('cartoon', 'Taz'); - $this->assertEquals(3, count($res)); - $this->assertContains(' Taz ', $linkDB[4]['tags']); - $this->assertContains(' Taz ', $linkDB[1]['tags']); - $this->assertContains(' Taz ', $linkDB[0]['tags']); - } - - /** - * Test rename tag with a valid value - */ - public function testRenameTagCaseSensitive() - { - self::$refDB->write(self::$testDatastore); - $linkDB = new LinkDB(self::$testDatastore, true, false, ''); - - $res = $linkDB->renameTag('sTuff', 'Taz'); - $this->assertEquals(1, count($res)); - $this->assertEquals('Taz', $linkDB[41]['tags']); - } - - /** - * Test rename tag with invalid values - */ - public function testRenameTagInvalid() - { - $linkDB = new LinkDB(self::$testDatastore, false, false); - - $this->assertFalse($linkDB->renameTag('', 'test')); - $this->assertFalse($linkDB->renameTag('', '')); - // tag non existent - $this->assertEquals([], $linkDB->renameTag('test', '')); - $this->assertEquals([], $linkDB->renameTag('test', 'retest')); - } - - /** - * Test delete tag with a valid value - */ - public function testDeleteTag() - { - self::$refDB->write(self::$testDatastore); - $linkDB = new LinkDB(self::$testDatastore, true, false); - - $res = $linkDB->renameTag('cartoon', null); - $this->assertEquals(3, count($res)); - $this->assertNotContains('cartoon', $linkDB[4]['tags']); - } - - /** - * Test linksCountPerTag all tags without filter. - * Equal occurrences should be sorted alphabetically. - */ - public function testCountLinkPerTagAllNoFilter() - { - $expected = [ - 'web' => 4, - 'cartoon' => 3, - 'dev' => 2, - 'gnu' => 2, - 'hashtag' => 2, - 'sTuff' => 2, - '-exclude' => 1, - '.hidden' => 1, - 'Mercurial' => 1, - 'css' => 1, - 'free' => 1, - 'html' => 1, - 'media' => 1, - 'samba' => 1, - 'software' => 1, - 'stallman' => 1, - 'tag1' => 1, - 'tag2' => 1, - 'tag3' => 1, - 'tag4' => 1, - 'ut' => 1, - 'w3c' => 1, - ]; - $tags = self::$privateLinkDB->linksCountPerTag(); - - $this->assertEquals($expected, $tags, var_export($tags, true)); - } - - /** - * Test linksCountPerTag all tags with filter. - * Equal occurrences should be sorted alphabetically. - */ - public function testCountLinkPerTagAllWithFilter() - { - $expected = [ - 'gnu' => 2, - 'hashtag' => 2, - '-exclude' => 1, - '.hidden' => 1, - 'free' => 1, - 'media' => 1, - 'software' => 1, - 'stallman' => 1, - 'stuff' => 1, - 'web' => 1, - ]; - $tags = self::$privateLinkDB->linksCountPerTag(['gnu']); - - $this->assertEquals($expected, $tags, var_export($tags, true)); - } - - /** - * Test linksCountPerTag public tags with filter. - * Equal occurrences should be sorted alphabetically. - */ - public function testCountLinkPerTagPublicWithFilter() - { - $expected = [ - 'gnu' => 2, - 'hashtag' => 2, - '-exclude' => 1, - '.hidden' => 1, - 'free' => 1, - 'media' => 1, - 'software' => 1, - 'stallman' => 1, - 'stuff' => 1, - 'web' => 1, - ]; - $tags = self::$privateLinkDB->linksCountPerTag(['gnu'], 'public'); - - $this->assertEquals($expected, $tags, var_export($tags, true)); - } - - /** - * Test linksCountPerTag public tags with filter. - * Equal occurrences should be sorted alphabetically. - */ - public function testCountLinkPerTagPrivateWithFilter() - { - $expected = [ - 'cartoon' => 1, - 'dev' => 1, - 'tag1' => 1, - 'tag2' => 1, - 'tag3' => 1, - 'tag4' => 1, - ]; - $tags = self::$privateLinkDB->linksCountPerTag(['dev'], 'private'); - - $this->assertEquals($expected, $tags, var_export($tags, true)); - } -} diff --git a/tests/LinkFilterTest.php b/tests/LinkFilterTest.php index eb54c359..db28b1c4 100644 --- a/tests/LinkFilterTest.php +++ b/tests/LinkFilterTest.php @@ -1,5 +1,7 @@ container = new Container(); $this->container['conf'] = $this->conf; - $this->container['db'] = new \LinkDB(self::$testDatastore, true, false); + $this->container['db'] = new \Shaarli\Bookmark\LinkDB(self::$testDatastore, true, false); $this->container['history'] = null; $this->controller = new Info($this->container); diff --git a/tests/api/controllers/links/DeleteLinkTest.php b/tests/api/controllers/links/DeleteLinkTest.php index 07371e7a..adca9a4e 100644 --- a/tests/api/controllers/links/DeleteLinkTest.php +++ b/tests/api/controllers/links/DeleteLinkTest.php @@ -32,7 +32,7 @@ class DeleteLinkTest extends \PHPUnit_Framework_TestCase protected $refDB = null; /** - * @var \LinkDB instance. + * @var \Shaarli\Bookmark\LinkDB instance. */ protected $linkDB; @@ -59,7 +59,7 @@ class DeleteLinkTest extends \PHPUnit_Framework_TestCase $this->conf = new ConfigManager('tests/utils/config/configJson'); $this->refDB = new \ReferenceLinkDB(); $this->refDB->write(self::$testDatastore); - $this->linkDB = new \LinkDB(self::$testDatastore, true, false); + $this->linkDB = new \Shaarli\Bookmark\LinkDB(self::$testDatastore, true, false); $refHistory = new \ReferenceHistory(); $refHistory->write(self::$testHistory); $this->history = new \Shaarli\History(self::$testHistory); @@ -96,7 +96,7 @@ class DeleteLinkTest extends \PHPUnit_Framework_TestCase $this->assertEquals(204, $response->getStatusCode()); $this->assertEmpty((string) $response->getBody()); - $this->linkDB = new \LinkDB(self::$testDatastore, true, false); + $this->linkDB = new \Shaarli\Bookmark\LinkDB(self::$testDatastore, true, false); $this->assertFalse(isset($this->linkDB[$id])); $historyEntry = $this->history->getHistory()[0]; diff --git a/tests/api/controllers/links/GetLinkIdTest.php b/tests/api/controllers/links/GetLinkIdTest.php index 57528d5a..bf58000b 100644 --- a/tests/api/controllers/links/GetLinkIdTest.php +++ b/tests/api/controllers/links/GetLinkIdTest.php @@ -61,7 +61,7 @@ class GetLinkIdTest extends \PHPUnit_Framework_TestCase $this->container = new Container(); $this->container['conf'] = $this->conf; - $this->container['db'] = new \LinkDB(self::$testDatastore, true, false); + $this->container['db'] = new \Shaarli\Bookmark\LinkDB(self::$testDatastore, true, false); $this->container['history'] = null; $this->controller = new Links($this->container); @@ -108,7 +108,7 @@ class GetLinkIdTest extends \PHPUnit_Framework_TestCase $this->assertEquals('sTuff', $data['tags'][0]); $this->assertEquals(false, $data['private']); $this->assertEquals( - \DateTime::createFromFormat(\LinkDB::LINK_DATE_FORMAT, '20150310_114651')->format(\DateTime::ATOM), + \DateTime::createFromFormat(\Shaarli\Bookmark\LinkDB::LINK_DATE_FORMAT, '20150310_114651')->format(\DateTime::ATOM), $data['created'] ); $this->assertEmpty($data['updated']); diff --git a/tests/api/controllers/links/GetLinksTest.php b/tests/api/controllers/links/GetLinksTest.php index 64f02774..1008d7b2 100644 --- a/tests/api/controllers/links/GetLinksTest.php +++ b/tests/api/controllers/links/GetLinksTest.php @@ -60,7 +60,7 @@ class GetLinksTest extends \PHPUnit_Framework_TestCase $this->container = new Container(); $this->container['conf'] = $this->conf; - $this->container['db'] = new \LinkDB(self::$testDatastore, true, false); + $this->container['db'] = new \Shaarli\Bookmark\LinkDB(self::$testDatastore, true, false); $this->container['history'] = null; $this->controller = new Links($this->container); @@ -114,7 +114,7 @@ class GetLinksTest extends \PHPUnit_Framework_TestCase $this->assertEquals('sTuff', $first['tags'][0]); $this->assertEquals(false, $first['private']); $this->assertEquals( - \DateTime::createFromFormat(\LinkDB::LINK_DATE_FORMAT, '20150310_114651')->format(\DateTime::ATOM), + \DateTime::createFromFormat(\Shaarli\Bookmark\LinkDB::LINK_DATE_FORMAT, '20150310_114651')->format(\DateTime::ATOM), $first['created'] ); $this->assertEmpty($first['updated']); @@ -125,7 +125,7 @@ class GetLinksTest extends \PHPUnit_Framework_TestCase // Update date $this->assertEquals( - \DateTime::createFromFormat(\LinkDB::LINK_DATE_FORMAT, '20160803_093033')->format(\DateTime::ATOM), + \DateTime::createFromFormat(\Shaarli\Bookmark\LinkDB::LINK_DATE_FORMAT, '20160803_093033')->format(\DateTime::ATOM), $link['updated'] ); } diff --git a/tests/api/controllers/links/PostLinkTest.php b/tests/api/controllers/links/PostLinkTest.php index a73f443c..ade07eeb 100644 --- a/tests/api/controllers/links/PostLinkTest.php +++ b/tests/api/controllers/links/PostLinkTest.php @@ -74,7 +74,7 @@ class PostLinkTest extends TestCase $this->container = new Container(); $this->container['conf'] = $this->conf; - $this->container['db'] = new \LinkDB(self::$testDatastore, true, false); + $this->container['db'] = new \Shaarli\Bookmark\LinkDB(self::$testDatastore, true, false); $this->container['history'] = new \Shaarli\History(self::$testHistory); $this->controller = new Links($this->container); @@ -210,11 +210,11 @@ class PostLinkTest extends TestCase $this->assertEquals(['gnu', 'media', 'web', '.hidden', 'hashtag'], $data['tags']); $this->assertEquals(false, $data['private']); $this->assertEquals( - \DateTime::createFromFormat(\LinkDB::LINK_DATE_FORMAT, '20130614_184135'), + \DateTime::createFromFormat(\Shaarli\Bookmark\LinkDB::LINK_DATE_FORMAT, '20130614_184135'), \DateTime::createFromFormat(\DateTime::ATOM, $data['created']) ); $this->assertEquals( - \DateTime::createFromFormat(\LinkDB::LINK_DATE_FORMAT, '20130615_184230'), + \DateTime::createFromFormat(\Shaarli\Bookmark\LinkDB::LINK_DATE_FORMAT, '20130615_184230'), \DateTime::createFromFormat(\DateTime::ATOM, $data['updated']) ); } diff --git a/tests/api/controllers/links/PutLinkTest.php b/tests/api/controllers/links/PutLinkTest.php index 3bb4d43f..eb6c7955 100644 --- a/tests/api/controllers/links/PutLinkTest.php +++ b/tests/api/controllers/links/PutLinkTest.php @@ -66,7 +66,7 @@ class PutLinkTest extends \PHPUnit_Framework_TestCase $this->container = new Container(); $this->container['conf'] = $this->conf; - $this->container['db'] = new \LinkDB(self::$testDatastore, true, false); + $this->container['db'] = new \Shaarli\Bookmark\LinkDB(self::$testDatastore, true, false); $this->container['history'] = new \Shaarli\History(self::$testHistory); $this->controller = new Links($this->container); @@ -198,11 +198,11 @@ class PutLinkTest extends \PHPUnit_Framework_TestCase $this->assertEquals(['gnu', 'media', 'web', '.hidden', 'hashtag'], $data['tags']); $this->assertEquals(false, $data['private']); $this->assertEquals( - \DateTime::createFromFormat(\LinkDB::LINK_DATE_FORMAT, '20130614_184135'), + \DateTime::createFromFormat(\Shaarli\Bookmark\LinkDB::LINK_DATE_FORMAT, '20130614_184135'), \DateTime::createFromFormat(\DateTime::ATOM, $data['created']) ); $this->assertEquals( - \DateTime::createFromFormat(\LinkDB::LINK_DATE_FORMAT, '20130615_184230'), + \DateTime::createFromFormat(\Shaarli\Bookmark\LinkDB::LINK_DATE_FORMAT, '20130615_184230'), \DateTime::createFromFormat(\DateTime::ATOM, $data['updated']) ); } diff --git a/tests/api/controllers/tags/DeleteTagTest.php b/tests/api/controllers/tags/DeleteTagTest.php index a1e419cd..02803ba2 100644 --- a/tests/api/controllers/tags/DeleteTagTest.php +++ b/tests/api/controllers/tags/DeleteTagTest.php @@ -32,7 +32,7 @@ class DeleteTagTest extends \PHPUnit_Framework_TestCase protected $refDB = null; /** - * @var \LinkDB instance. + * @var \Shaarli\Bookmark\LinkDB instance. */ protected $linkDB; @@ -59,7 +59,7 @@ class DeleteTagTest extends \PHPUnit_Framework_TestCase $this->conf = new ConfigManager('tests/utils/config/configJson'); $this->refDB = new \ReferenceLinkDB(); $this->refDB->write(self::$testDatastore); - $this->linkDB = new \LinkDB(self::$testDatastore, true, false); + $this->linkDB = new \Shaarli\Bookmark\LinkDB(self::$testDatastore, true, false); $refHistory = new \ReferenceHistory(); $refHistory->write(self::$testHistory); $this->history = new \Shaarli\History(self::$testHistory); @@ -97,7 +97,7 @@ class DeleteTagTest extends \PHPUnit_Framework_TestCase $this->assertEquals(204, $response->getStatusCode()); $this->assertEmpty((string) $response->getBody()); - $this->linkDB = new \LinkDB(self::$testDatastore, true, false); + $this->linkDB = new \Shaarli\Bookmark\LinkDB(self::$testDatastore, true, false); $tags = $this->linkDB->linksCountPerTag(); $this->assertFalse(isset($tags[$tagName])); @@ -131,7 +131,7 @@ class DeleteTagTest extends \PHPUnit_Framework_TestCase $this->assertEquals(204, $response->getStatusCode()); $this->assertEmpty((string) $response->getBody()); - $this->linkDB = new \LinkDB(self::$testDatastore, true, false); + $this->linkDB = new \Shaarli\Bookmark\LinkDB(self::$testDatastore, true, false); $tags = $this->linkDB->linksCountPerTag(); $this->assertFalse(isset($tags[$tagName])); $this->assertTrue($tags[strtolower($tagName)] > 0); diff --git a/tests/api/controllers/tags/GetTagNameTest.php b/tests/api/controllers/tags/GetTagNameTest.php index afac228e..8e0feccd 100644 --- a/tests/api/controllers/tags/GetTagNameTest.php +++ b/tests/api/controllers/tags/GetTagNameTest.php @@ -59,7 +59,7 @@ class GetTagNameTest extends \PHPUnit_Framework_TestCase $this->container = new Container(); $this->container['conf'] = $this->conf; - $this->container['db'] = new \LinkDB(self::$testDatastore, true, false); + $this->container['db'] = new \Shaarli\Bookmark\LinkDB(self::$testDatastore, true, false); $this->container['history'] = null; $this->controller = new Tags($this->container); diff --git a/tests/api/controllers/tags/GetTagsTest.php b/tests/api/controllers/tags/GetTagsTest.php index 3fab31b0..f071bfa8 100644 --- a/tests/api/controllers/tags/GetTagsTest.php +++ b/tests/api/controllers/tags/GetTagsTest.php @@ -38,7 +38,7 @@ class GetTagsTest extends \PHPUnit_Framework_TestCase protected $container; /** - * @var \LinkDB instance. + * @var \Shaarli\Bookmark\LinkDB instance. */ protected $linkDB; @@ -63,7 +63,7 @@ class GetTagsTest extends \PHPUnit_Framework_TestCase $this->container = new Container(); $this->container['conf'] = $this->conf; - $this->linkDB = new \LinkDB(self::$testDatastore, true, false); + $this->linkDB = new \Shaarli\Bookmark\LinkDB(self::$testDatastore, true, false); $this->container['db'] = $this->linkDB; $this->container['history'] = null; diff --git a/tests/api/controllers/tags/PutTagTest.php b/tests/api/controllers/tags/PutTagTest.php index c45fa722..d8c0fec8 100644 --- a/tests/api/controllers/tags/PutTagTest.php +++ b/tests/api/controllers/tags/PutTagTest.php @@ -43,7 +43,7 @@ class PutTagTest extends \PHPUnit_Framework_TestCase protected $container; /** - * @var \LinkDB instance. + * @var \Shaarli\Bookmark\LinkDB instance. */ protected $linkDB; @@ -72,7 +72,7 @@ class PutTagTest extends \PHPUnit_Framework_TestCase $this->container = new Container(); $this->container['conf'] = $this->conf; - $this->linkDB = new \LinkDB(self::$testDatastore, true, false); + $this->linkDB = new \Shaarli\Bookmark\LinkDB(self::$testDatastore, true, false); $this->container['db'] = $this->linkDB; $this->container['history'] = $this->history; diff --git a/tests/bookmark/LinkDBTest.php b/tests/bookmark/LinkDBTest.php new file mode 100644 index 00000000..f18a3155 --- /dev/null +++ b/tests/bookmark/LinkDBTest.php @@ -0,0 +1,653 @@ +write(self::$testDatastore); + + self::$publicLinkDB = new LinkDB(self::$testDatastore, false, false); + self::$privateLinkDB = new LinkDB(self::$testDatastore, true, false); + } + + /** + * Resets test data for each test + */ + protected function setUp() + { + if (file_exists(self::$testDatastore)) { + unlink(self::$testDatastore); + } + } + + /** + * Allows to test LinkDB's private methods + * + * @see + * https://sebastian-bergmann.de/archives/881-Testing-Your-Privates.html + * http://stackoverflow.com/a/2798203 + */ + protected static function getMethod($name) + { + $class = new ReflectionClass('Shaarli\Bookmark\LinkDB'); + $method = $class->getMethod($name); + $method->setAccessible(true); + return $method; + } + + /** + * Instantiate LinkDB objects - logged in user + */ + public function testConstructLoggedIn() + { + new LinkDB(self::$testDatastore, true, false); + $this->assertFileExists(self::$testDatastore); + } + + /** + * Instantiate LinkDB objects - logged out or public instance + */ + public function testConstructLoggedOut() + { + new LinkDB(self::$testDatastore, false, false); + $this->assertFileExists(self::$testDatastore); + } + + /** + * Attempt to instantiate a LinkDB whereas the datastore is not writable + * + * @expectedException Shaarli\Exceptions\IOException + * @expectedExceptionMessageRegExp /Error accessing "null"/ + */ + public function testConstructDatastoreNotWriteable() + { + new LinkDB('null/store.db', false, false); + } + + /** + * The DB doesn't exist, ensure it is created with dummy content + */ + public function testCheckDBNew() + { + $linkDB = new LinkDB(self::$testDatastore, false, false); + unlink(self::$testDatastore); + $this->assertFileNotExists(self::$testDatastore); + + $checkDB = self::getMethod('check'); + $checkDB->invokeArgs($linkDB, array()); + $this->assertFileExists(self::$testDatastore); + + // ensure the correct data has been written + $this->assertGreaterThan(0, filesize(self::$testDatastore)); + } + + /** + * The DB exists, don't do anything + */ + public function testCheckDBLoad() + { + $linkDB = new LinkDB(self::$testDatastore, false, false); + $datastoreSize = filesize(self::$testDatastore); + $this->assertGreaterThan(0, $datastoreSize); + + $checkDB = self::getMethod('check'); + $checkDB->invokeArgs($linkDB, array()); + + // ensure the datastore is left unmodified + $this->assertEquals( + $datastoreSize, + filesize(self::$testDatastore) + ); + } + + /** + * Load an empty DB + */ + public function testReadEmptyDB() + { + file_put_contents(self::$testDatastore, ''); + $emptyDB = new LinkDB(self::$testDatastore, false, false); + $this->assertEquals(0, sizeof($emptyDB)); + $this->assertEquals(0, count($emptyDB)); + } + + /** + * Load public links from the DB + */ + public function testReadPublicDB() + { + $this->assertEquals( + self::$refDB->countPublicLinks(), + sizeof(self::$publicLinkDB) + ); + } + + /** + * Load public and private links from the DB + */ + public function testReadPrivateDB() + { + $this->assertEquals( + self::$refDB->countLinks(), + sizeof(self::$privateLinkDB) + ); + } + + /** + * Save the links to the DB + */ + public function testSave() + { + $testDB = new LinkDB(self::$testDatastore, true, false); + $dbSize = sizeof($testDB); + + $link = array( + 'id' => 42, + 'title' => 'an additional link', + 'url' => 'http://dum.my', + 'description' => 'One more', + 'private' => 0, + 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150518_190000'), + 'tags' => 'unit test' + ); + $testDB[$link['id']] = $link; + $testDB->save('tests'); + + $testDB = new LinkDB(self::$testDatastore, true, false); + $this->assertEquals($dbSize + 1, sizeof($testDB)); + } + + /** + * Count existing links + */ + public function testCount() + { + $this->assertEquals( + self::$refDB->countPublicLinks(), + self::$publicLinkDB->count() + ); + $this->assertEquals( + self::$refDB->countLinks(), + self::$privateLinkDB->count() + ); + } + + /** + * Count existing links - public links hidden + */ + public function testCountHiddenPublic() + { + $linkDB = new LinkDB(self::$testDatastore, false, true); + + $this->assertEquals( + 0, + $linkDB->count() + ); + $this->assertEquals( + 0, + $linkDB->count() + ); + } + + /** + * List the days for which links have been posted + */ + public function testDays() + { + $this->assertEquals( + array('20100309', '20100310', '20121206', '20121207', '20130614', '20150310'), + self::$publicLinkDB->days() + ); + + $this->assertEquals( + array('20100309', '20100310', '20121206', '20121207', '20130614', '20141125', '20150310'), + self::$privateLinkDB->days() + ); + } + + /** + * The URL corresponds to an existing entry in the DB + */ + public function testGetKnownLinkFromURL() + { + $link = self::$publicLinkDB->getLinkFromUrl('http://mediagoblin.org/'); + + $this->assertNotEquals(false, $link); + $this->assertContains( + 'A free software media publishing platform', + $link['description'] + ); + } + + /** + * The URL is not in the DB + */ + public function testGetUnknownLinkFromURL() + { + $this->assertEquals( + false, + self::$publicLinkDB->getLinkFromUrl('http://dev.null') + ); + } + + /** + * Lists all tags + */ + public function testAllTags() + { + $this->assertEquals( + array( + 'web' => 3, + 'cartoon' => 2, + 'gnu' => 2, + 'dev' => 1, + 'samba' => 1, + 'media' => 1, + 'software' => 1, + 'stallman' => 1, + 'free' => 1, + '-exclude' => 1, + 'hashtag' => 2, + // The DB contains a link with `sTuff` and another one with `stuff` tag. + // They need to be grouped with the first case found - order by date DESC: `sTuff`. + 'sTuff' => 2, + 'ut' => 1, + ), + self::$publicLinkDB->linksCountPerTag() + ); + + $this->assertEquals( + array( + 'web' => 4, + 'cartoon' => 3, + 'gnu' => 2, + 'dev' => 2, + 'samba' => 1, + 'media' => 1, + 'software' => 1, + 'stallman' => 1, + 'free' => 1, + 'html' => 1, + 'w3c' => 1, + 'css' => 1, + 'Mercurial' => 1, + 'sTuff' => 2, + '-exclude' => 1, + '.hidden' => 1, + 'hashtag' => 2, + 'tag1' => 1, + 'tag2' => 1, + 'tag3' => 1, + 'tag4' => 1, + 'ut' => 1, + ), + self::$privateLinkDB->linksCountPerTag() + ); + $this->assertEquals( + array( + 'web' => 4, + 'cartoon' => 2, + 'gnu' => 1, + 'dev' => 1, + 'samba' => 1, + 'media' => 1, + 'html' => 1, + 'w3c' => 1, + 'css' => 1, + 'Mercurial' => 1, + '.hidden' => 1, + 'hashtag' => 1, + ), + self::$privateLinkDB->linksCountPerTag(['web']) + ); + $this->assertEquals( + array( + 'web' => 1, + 'html' => 1, + 'w3c' => 1, + 'css' => 1, + 'Mercurial' => 1, + ), + self::$privateLinkDB->linksCountPerTag(['web'], 'private') + ); + } + + /** + * Test real_url without redirector. + */ + public function testLinkRealUrlWithoutRedirector() + { + $db = new LinkDB(self::$testDatastore, false, false); + foreach ($db as $link) { + $this->assertEquals($link['url'], $link['real_url']); + } + } + + /** + * Test real_url with redirector. + */ + public function testLinkRealUrlWithRedirector() + { + $redirector = 'http://redirector.to?'; + $db = new LinkDB(self::$testDatastore, false, false, $redirector); + foreach ($db as $link) { + $this->assertStringStartsWith($redirector, $link['real_url']); + $this->assertNotFalse(strpos($link['real_url'], urlencode('://'))); + } + + $db = new LinkDB(self::$testDatastore, false, false, $redirector, false); + foreach ($db as $link) { + $this->assertStringStartsWith($redirector, $link['real_url']); + $this->assertFalse(strpos($link['real_url'], urlencode('://'))); + } + } + + /** + * Test filter with string. + */ + public function testFilterString() + { + $tags = 'dev cartoon'; + $request = array('searchtags' => $tags); + $this->assertEquals( + 2, + count(self::$privateLinkDB->filterSearch($request, true, false)) + ); + } + + /** + * Test filter with string. + */ + public function testFilterArray() + { + $tags = array('dev', 'cartoon'); + $request = array('searchtags' => $tags); + $this->assertEquals( + 2, + count(self::$privateLinkDB->filterSearch($request, true, false)) + ); + } + + /** + * Test hidden tags feature: + * tags starting with a dot '.' are only visible when logged in. + */ + public function testHiddenTags() + { + $tags = '.hidden'; + $request = array('searchtags' => $tags); + $this->assertEquals( + 1, + count(self::$privateLinkDB->filterSearch($request, true, false)) + ); + + $this->assertEquals( + 0, + count(self::$publicLinkDB->filterSearch($request, true, false)) + ); + } + + /** + * Test filterHash() with a valid smallhash. + */ + public function testFilterHashValid() + { + $request = smallHash('20150310_114651'); + $this->assertEquals( + 1, + count(self::$publicLinkDB->filterHash($request)) + ); + $request = smallHash('20150310_114633' . 8); + $this->assertEquals( + 1, + count(self::$publicLinkDB->filterHash($request)) + ); + } + + /** + * Test filterHash() with an invalid smallhash. + * + * @expectedException LinkNotFoundException + */ + public function testFilterHashInValid1() + { + $request = 'blabla'; + self::$publicLinkDB->filterHash($request); + } + + /** + * Test filterHash() with an empty smallhash. + * + * @expectedException LinkNotFoundException + */ + public function testFilterHashInValid() + { + self::$publicLinkDB->filterHash(''); + } + + /** + * Test reorder with asc/desc parameter. + */ + public function testReorderLinksDesc() + { + self::$privateLinkDB->reorder('ASC'); + $stickyIds = [11, 10]; + $standardIds = [42, 4, 9, 1, 0, 7, 6, 8, 41]; + $linkIds = array_merge($stickyIds, $standardIds); + $cpt = 0; + foreach (self::$privateLinkDB as $key => $value) { + $this->assertEquals($linkIds[$cpt++], $key); + } + self::$privateLinkDB->reorder('DESC'); + $linkIds = array_merge(array_reverse($stickyIds), array_reverse($standardIds)); + $cpt = 0; + foreach (self::$privateLinkDB as $key => $value) { + $this->assertEquals($linkIds[$cpt++], $key); + } + } + + /** + * Test rename tag with a valid value present in multiple links + */ + public function testRenameTagMultiple() + { + self::$refDB->write(self::$testDatastore); + $linkDB = new LinkDB(self::$testDatastore, true, false); + + $res = $linkDB->renameTag('cartoon', 'Taz'); + $this->assertEquals(3, count($res)); + $this->assertContains(' Taz ', $linkDB[4]['tags']); + $this->assertContains(' Taz ', $linkDB[1]['tags']); + $this->assertContains(' Taz ', $linkDB[0]['tags']); + } + + /** + * Test rename tag with a valid value + */ + public function testRenameTagCaseSensitive() + { + self::$refDB->write(self::$testDatastore); + $linkDB = new LinkDB(self::$testDatastore, true, false, ''); + + $res = $linkDB->renameTag('sTuff', 'Taz'); + $this->assertEquals(1, count($res)); + $this->assertEquals('Taz', $linkDB[41]['tags']); + } + + /** + * Test rename tag with invalid values + */ + public function testRenameTagInvalid() + { + $linkDB = new LinkDB(self::$testDatastore, false, false); + + $this->assertFalse($linkDB->renameTag('', 'test')); + $this->assertFalse($linkDB->renameTag('', '')); + // tag non existent + $this->assertEquals([], $linkDB->renameTag('test', '')); + $this->assertEquals([], $linkDB->renameTag('test', 'retest')); + } + + /** + * Test delete tag with a valid value + */ + public function testDeleteTag() + { + self::$refDB->write(self::$testDatastore); + $linkDB = new LinkDB(self::$testDatastore, true, false); + + $res = $linkDB->renameTag('cartoon', null); + $this->assertEquals(3, count($res)); + $this->assertNotContains('cartoon', $linkDB[4]['tags']); + } + + /** + * Test linksCountPerTag all tags without filter. + * Equal occurrences should be sorted alphabetically. + */ + public function testCountLinkPerTagAllNoFilter() + { + $expected = [ + 'web' => 4, + 'cartoon' => 3, + 'dev' => 2, + 'gnu' => 2, + 'hashtag' => 2, + 'sTuff' => 2, + '-exclude' => 1, + '.hidden' => 1, + 'Mercurial' => 1, + 'css' => 1, + 'free' => 1, + 'html' => 1, + 'media' => 1, + 'samba' => 1, + 'software' => 1, + 'stallman' => 1, + 'tag1' => 1, + 'tag2' => 1, + 'tag3' => 1, + 'tag4' => 1, + 'ut' => 1, + 'w3c' => 1, + ]; + $tags = self::$privateLinkDB->linksCountPerTag(); + + $this->assertEquals($expected, $tags, var_export($tags, true)); + } + + /** + * Test linksCountPerTag all tags with filter. + * Equal occurrences should be sorted alphabetically. + */ + public function testCountLinkPerTagAllWithFilter() + { + $expected = [ + 'gnu' => 2, + 'hashtag' => 2, + '-exclude' => 1, + '.hidden' => 1, + 'free' => 1, + 'media' => 1, + 'software' => 1, + 'stallman' => 1, + 'stuff' => 1, + 'web' => 1, + ]; + $tags = self::$privateLinkDB->linksCountPerTag(['gnu']); + + $this->assertEquals($expected, $tags, var_export($tags, true)); + } + + /** + * Test linksCountPerTag public tags with filter. + * Equal occurrences should be sorted alphabetically. + */ + public function testCountLinkPerTagPublicWithFilter() + { + $expected = [ + 'gnu' => 2, + 'hashtag' => 2, + '-exclude' => 1, + '.hidden' => 1, + 'free' => 1, + 'media' => 1, + 'software' => 1, + 'stallman' => 1, + 'stuff' => 1, + 'web' => 1, + ]; + $tags = self::$privateLinkDB->linksCountPerTag(['gnu'], 'public'); + + $this->assertEquals($expected, $tags, var_export($tags, true)); + } + + /** + * Test linksCountPerTag public tags with filter. + * Equal occurrences should be sorted alphabetically. + */ + public function testCountLinkPerTagPrivateWithFilter() + { + $expected = [ + 'cartoon' => 1, + 'dev' => 1, + 'tag1' => 1, + 'tag2' => 1, + 'tag3' => 1, + 'tag4' => 1, + ]; + $tags = self::$privateLinkDB->linksCountPerTag(['dev'], 'private'); + + $this->assertEquals($expected, $tags, var_export($tags, true)); + } +} diff --git a/tests/feed/FeedBuilderTest.php b/tests/feed/FeedBuilderTest.php index 1fdbc60e..88d1c3ed 100644 --- a/tests/feed/FeedBuilderTest.php +++ b/tests/feed/FeedBuilderTest.php @@ -3,11 +3,9 @@ namespace Shaarli\Feed; use DateTime; -use LinkDB; +use Shaarli\Bookmark\LinkDB; use ReferenceLinkDB; -require_once 'application/LinkDB.php'; - /** * FeedBuilderTest class. * diff --git a/tests/http/UrlTest.php b/tests/http/UrlTest.php index 342b78a4..ae92f73a 100644 --- a/tests/http/UrlTest.php +++ b/tests/http/UrlTest.php @@ -5,7 +5,6 @@ namespace Shaarli\Http; - /** * Unitary tests for URL utilities */ diff --git a/tests/plugins/PluginIssoTest.php b/tests/plugins/PluginIssoTest.php index 2c9efbcd..f5fa1daa 100644 --- a/tests/plugins/PluginIssoTest.php +++ b/tests/plugins/PluginIssoTest.php @@ -1,4 +1,6 @@