From b1eb5d1d31e3ea256501c08a3ed9aa7183b27466 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Thu, 14 Apr 2016 17:59:37 +0200 Subject: Fixes #497: ignore case difference between tags While retrieving all tags, case differences will be ignored. This affects: * tag cloud * tag autocompletion --- application/LinkDB.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'application/LinkDB.php') diff --git a/application/LinkDB.php b/application/LinkDB.php index a62341fc..4c1a45b5 100644 --- a/application/LinkDB.php +++ b/application/LinkDB.php @@ -417,11 +417,18 @@ You use the community supported version of the original Shaarli project, by Seba public function allTags() { $tags = array(); + $caseMapping = array(); foreach ($this->_links as $link) { foreach (explode(' ', $link['tags']) as $tag) { - if (!empty($tag)) { - $tags[$tag] = (empty($tags[$tag]) ? 1 : $tags[$tag] + 1); + 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)]]++; } } // Sort tags by usage (most used tag first) -- cgit v1.2.3 From 9ccca40189652e529732683abcdf54fcf775c9ec Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Tue, 10 May 2016 23:18:04 +0200 Subject: Hashtag system * Hashtag are auto-linked with a filter search * Supports unicode * Compatible with markdown (excluded in code blocks) --- application/LinkDB.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'application/LinkDB.php') diff --git a/application/LinkDB.php b/application/LinkDB.php index b1072e07..929a6b0f 100644 --- a/application/LinkDB.php +++ b/application/LinkDB.php @@ -409,7 +409,7 @@ You use the community supported version of the original Shaarli project, by Seba $searchterm = !empty($filterRequest['searchterm']) ? escape($filterRequest['searchterm']) : ''; // Search tags + fullsearch. - if (empty($type) && ! empty($searchtags) && ! empty($searchterm)) { + if (! empty($searchtags) && ! empty($searchterm)) { $type = LinkFilter::$FILTER_TAG | LinkFilter::$FILTER_TEXT; $request = array($searchtags, $searchterm); } -- cgit v1.2.3 From 4b35853d6869e65b473fec065361e9ea7dbcf94d Mon Sep 17 00:00:00 2001 From: Chris Kuethe Date: Thu, 19 May 2016 11:33:45 -0700 Subject: Better whitespace handling in tags. Fixes #571 --- application/LinkDB.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'application/LinkDB.php') diff --git a/application/LinkDB.php b/application/LinkDB.php index 929a6b0f..e9d216ea 100644 --- a/application/LinkDB.php +++ b/application/LinkDB.php @@ -291,7 +291,7 @@ You use the community supported version of the original Shaarli project, by Seba // Remove private tags if the user is not logged in. if (! $this->_loggedIn) { - $link['tags'] = preg_replace('/(^| )\.[^($| )]+/', '', $link['tags']); + $link['tags'] = preg_replace('/(^|\s*)\.[^($| )]+\s*$/', '', $link['tags']); } // Do not use the redirector for internal links (Shaarli note URL starting with a '?'). @@ -442,7 +442,7 @@ You use the community supported version of the original Shaarli project, by Seba $tags = array(); $caseMapping = array(); foreach ($this->_links as $link) { - foreach (explode(' ', $link['tags']) as $tag) { + foreach (preg_split('/\s+/', $link['tags'], 0, PREG_SPLIT_NO_EMPTY) as $tag) { if (empty($tag)) { continue; } -- cgit v1.2.3 From 9866b40814a120f1a019f9f2c8ef66f44e1ddfcb Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Tue, 2 Aug 2016 10:34:21 +0200 Subject: Better whitespace handling in tags Correct PR #573 to work properly with hidden tags, and add ReferenceLinkDB UT. Fixes #571 - Closes #573 --- application/LinkDB.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'application/LinkDB.php') diff --git a/application/LinkDB.php b/application/LinkDB.php index e9d216ea..d80434bf 100644 --- a/application/LinkDB.php +++ b/application/LinkDB.php @@ -291,7 +291,7 @@ You use the community supported version of the original Shaarli project, by Seba // Remove private tags if the user is not logged in. if (! $this->_loggedIn) { - $link['tags'] = preg_replace('/(^|\s*)\.[^($| )]+\s*$/', '', $link['tags']); + $link['tags'] = preg_replace('/(^|\s+)\.[^($|\s)]+\s*/', ' ', $link['tags']); } // Do not use the redirector for internal links (Shaarli note URL starting with a '?'). -- cgit v1.2.3 From 9646b7da22c4c6f3419bfe51431720dd622374d8 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Wed, 3 Aug 2016 09:44:04 +0200 Subject: Save the update date in LinkDB and pass it to linklist templates It can be used as a timestamp by templates under the key 'updated_timestamp'. --- application/LinkDB.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'application/LinkDB.php') diff --git a/application/LinkDB.php b/application/LinkDB.php index d80434bf..de9e73b0 100644 --- a/application/LinkDB.php +++ b/application/LinkDB.php @@ -12,8 +12,9 @@ * * Available keys: * - description: description of the entry - * - linkdate: date of the creation of this entry, in the form YYYYMMDD_HHMMSS + * - linkdate: creation date of this entry, format: YYYYMMDD_HHMMSS * (e.g.'20110914_192317') + * - updated: last modification date of this entry, format: YYYYMMDD_HHMMSS * - private: Is this link private? 0=no, other value=yes * - tags: tags attached to this entry (separated by spaces) * - title Title of the link -- cgit v1.2.3 From 628b97cbdf276785eb9ff4f7a124e81e67d2f76c Mon Sep 17 00:00:00 2001 From: VirtualTam Date: Thu, 20 Oct 2016 21:10:56 +0200 Subject: LinkDB: do not prefix privates with an underscore Relates to #95 Signed-off-by: VirtualTam --- application/LinkDB.php | 134 ++++++++++++++++++++++++------------------------- 1 file changed, 67 insertions(+), 67 deletions(-) (limited to 'application/LinkDB.php') diff --git a/application/LinkDB.php b/application/LinkDB.php index de9e73b0..2d42c514 100644 --- a/application/LinkDB.php +++ b/application/LinkDB.php @@ -31,7 +31,7 @@ class LinkDB implements Iterator, Countable, ArrayAccess { // Links are stored as a PHP serialized string - private $_datastore; + private $datastore; // Link date storage format const LINK_DATE_FORMAT = 'Ymd_His'; @@ -45,26 +45,26 @@ class LinkDB implements Iterator, Countable, ArrayAccess // List of links (associative array) // - key: link date (e.g. "20110823_124546"), // - value: associative array (keys: title, description...) - private $_links; + private $links; // List of all recorded URLs (key=url, value=linkdate) // for fast reserve search (url-->linkdate) - private $_urls; + private $urls; // List of linkdate keys (for the Iterator interface implementation) - private $_keys; + private $keys; - // Position in the $this->_keys array (for the Iterator interface) - private $_position; + // Position in the $this->keys array (for the Iterator interface) + private $position; // Is the user logged in? (used to filter private links) - private $_loggedIn; + private $loggedIn; // Hide public links - private $_hidePublicLinks; + private $hidePublicLinks; // link redirector set in user settings. - private $_redirector; + private $redirector; /** * Set this to `true` to urlencode link behind redirector link, `false` to leave it untouched. @@ -95,13 +95,13 @@ class LinkDB implements Iterator, Countable, ArrayAccess $redirectorEncode = true ) { - $this->_datastore = $datastore; - $this->_loggedIn = $isLoggedIn; - $this->_hidePublicLinks = $hidePublicLinks; - $this->_redirector = $redirector; + $this->datastore = $datastore; + $this->loggedIn = $isLoggedIn; + $this->hidePublicLinks = $hidePublicLinks; + $this->redirector = $redirector; $this->redirectorEncode = $redirectorEncode === true; - $this->_checkDB(); - $this->_readDB(); + $this->checkDB(); + $this->readDB(); } /** @@ -109,7 +109,7 @@ class LinkDB implements Iterator, Countable, ArrayAccess */ public function count() { - return count($this->_links); + return count($this->links); } /** @@ -118,7 +118,7 @@ class LinkDB implements Iterator, Countable, ArrayAccess public function offsetSet($offset, $value) { // TODO: use exceptions instead of "die" - if (!$this->_loggedIn) { + if (!$this->loggedIn) { die('You are not authorized to add a link.'); } if (empty($value['linkdate']) || empty($value['url'])) { @@ -127,8 +127,8 @@ class LinkDB implements Iterator, Countable, ArrayAccess if (empty($offset)) { die('You must specify a key.'); } - $this->_links[$offset] = $value; - $this->_urls[$value['url']]=$offset; + $this->links[$offset] = $value; + $this->urls[$value['url']]=$offset; } /** @@ -136,7 +136,7 @@ class LinkDB implements Iterator, Countable, ArrayAccess */ public function offsetExists($offset) { - return array_key_exists($offset, $this->_links); + return array_key_exists($offset, $this->links); } /** @@ -144,13 +144,13 @@ class LinkDB implements Iterator, Countable, ArrayAccess */ public function offsetUnset($offset) { - if (!$this->_loggedIn) { + if (!$this->loggedIn) { // TODO: raise an exception die('You are not authorized to delete a link.'); } - $url = $this->_links[$offset]['url']; - unset($this->_urls[$url]); - unset($this->_links[$offset]); + $url = $this->links[$offset]['url']; + unset($this->urls[$url]); + unset($this->links[$offset]); } /** @@ -158,7 +158,7 @@ class LinkDB implements Iterator, Countable, ArrayAccess */ public function offsetGet($offset) { - return isset($this->_links[$offset]) ? $this->_links[$offset] : null; + return isset($this->links[$offset]) ? $this->links[$offset] : null; } /** @@ -166,7 +166,7 @@ class LinkDB implements Iterator, Countable, ArrayAccess */ function current() { - return $this->_links[$this->_keys[$this->_position]]; + return $this->links[$this->keys[$this->position]]; } /** @@ -174,7 +174,7 @@ class LinkDB implements Iterator, Countable, ArrayAccess */ function key() { - return $this->_keys[$this->_position]; + return $this->keys[$this->position]; } /** @@ -182,7 +182,7 @@ class LinkDB implements Iterator, Countable, ArrayAccess */ function next() { - ++$this->_position; + ++$this->position; } /** @@ -192,9 +192,9 @@ class LinkDB implements Iterator, Countable, ArrayAccess */ function rewind() { - $this->_keys = array_keys($this->_links); - rsort($this->_keys); - $this->_position = 0; + $this->keys = array_keys($this->links); + rsort($this->keys); + $this->position = 0; } /** @@ -202,7 +202,7 @@ class LinkDB implements Iterator, Countable, ArrayAccess */ function valid() { - return isset($this->_keys[$this->_position]); + return isset($this->keys[$this->position]); } /** @@ -210,14 +210,14 @@ class LinkDB implements Iterator, Countable, ArrayAccess * * If no DB file is found, creates a dummy DB. */ - private function _checkDB() + private function checkDB() { - if (file_exists($this->_datastore)) { + if (file_exists($this->datastore)) { return; } // Create a dummy database for example - $this->_links = array(); + $this->links = array(); $link = array( 'title'=>' Shaarli: the personal, minimalist, super-fast, no-database delicious clone', 'url'=>'https://github.com/shaarli/Shaarli/wiki', @@ -230,7 +230,7 @@ You use the community supported version of the original Shaarli project, by Seba 'linkdate'=> date('Ymd_His'), 'tags'=>'opensource software' ); - $this->_links[$link['linkdate']] = $link; + $this->links[$link['linkdate']] = $link; $link = array( 'title'=>'My secret stuff... - Pastebin.com', @@ -240,7 +240,7 @@ You use the community supported version of the original Shaarli project, by Seba 'linkdate'=> date('Ymd_His', strtotime('-1 minute')), 'tags'=>'secretstuff' ); - $this->_links[$link['linkdate']] = $link; + $this->links[$link['linkdate']] = $link; // Write database to disk $this->writeDB(); @@ -249,55 +249,55 @@ You use the community supported version of the original Shaarli project, by Seba /** * Reads database from disk to memory */ - private function _readDB() + private function readDB() { // Public links are hidden and user not logged in => nothing to show - if ($this->_hidePublicLinks && !$this->_loggedIn) { - $this->_links = array(); + if ($this->hidePublicLinks && !$this->loggedIn) { + $this->links = array(); return; } // Read data // Note that gzinflate is faster than gzuncompress. // See: http://www.php.net/manual/en/function.gzdeflate.php#96439 - $this->_links = array(); + $this->links = array(); - if (file_exists($this->_datastore)) { - $this->_links = unserialize(gzinflate(base64_decode( - substr(file_get_contents($this->_datastore), + if (file_exists($this->datastore)) { + $this->links = unserialize(gzinflate(base64_decode( + substr(file_get_contents($this->datastore), strlen(self::$phpPrefix), -strlen(self::$phpSuffix))))); } // If user is not logged in, filter private links. - if (!$this->_loggedIn) { + if (!$this->loggedIn) { $toremove = array(); - foreach ($this->_links as $link) { + foreach ($this->links as $link) { if ($link['private'] != 0) { $toremove[] = $link['linkdate']; } } foreach ($toremove as $linkdate) { - unset($this->_links[$linkdate]); + unset($this->links[$linkdate]); } } - $this->_urls = array(); - foreach ($this->_links as &$link) { + $this->urls = array(); + foreach ($this->links as &$link) { // Keep the list of the mapping URLs-->linkdate up-to-date. - $this->_urls[$link['url']] = $link['linkdate']; + $this->urls[$link['url']] = $link['linkdate']; // Sanitize data fields. sanitizeLink($link); // Remove private tags if the user is not logged in. - if (! $this->_loggedIn) { + 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 (!empty($this->redirector) && !startsWith($link['url'], '?')) { + $link['real_url'] = $this->redirector; if ($this->redirectorEncode) { $link['real_url'] .= urlencode(unescape($link['url'])); } else { @@ -317,17 +317,17 @@ You use the community supported version of the original Shaarli project, by Seba */ private function writeDB() { - if (is_file($this->_datastore) && !is_writeable($this->_datastore)) { + if (is_file($this->datastore) && !is_writeable($this->datastore)) { // The datastore exists but is not writeable - throw new IOException($this->_datastore); - } else if (!is_file($this->_datastore) && !is_writeable(dirname($this->_datastore))) { + throw new IOException($this->datastore); + } else if (!is_file($this->datastore) && !is_writeable(dirname($this->datastore))) { // The datastore does not exist and its parent directory is not writeable - throw new IOException(dirname($this->_datastore)); + throw new IOException(dirname($this->datastore)); } file_put_contents( - $this->_datastore, - self::$phpPrefix.base64_encode(gzdeflate(serialize($this->_links))).self::$phpSuffix + $this->datastore, + self::$phpPrefix.base64_encode(gzdeflate(serialize($this->links))).self::$phpSuffix ); } @@ -339,7 +339,7 @@ You use the community supported version of the original Shaarli project, by Seba */ public function savedb($pageCacheDir) { - if (!$this->_loggedIn) { + if (!$this->loggedIn) { // TODO: raise an Exception instead die('You are not authorized to change the database.'); } @@ -358,8 +358,8 @@ You use the community supported version of the original Shaarli project, by Seba */ public function getLinkFromUrl($url) { - if (isset($this->_urls[$url])) { - return $this->_links[$this->_urls[$url]]; + if (isset($this->urls[$url])) { + return $this->links[$this->urls[$url]]; } return false; } @@ -376,7 +376,7 @@ You use the community supported version of the original Shaarli project, by Seba public function filterHash($request) { $request = substr($request, 0, 6); - $linkFilter = new LinkFilter($this->_links); + $linkFilter = new LinkFilter($this->links); return $linkFilter->filter(LinkFilter::$FILTER_HASH, $request); } @@ -388,7 +388,7 @@ You use the community supported version of the original Shaarli project, by Seba * @return array list of shaare found. */ public function filterDay($request) { - $linkFilter = new LinkFilter($this->_links); + $linkFilter = new LinkFilter($this->links); return $linkFilter->filter(LinkFilter::$FILTER_DAY, $request); } @@ -430,7 +430,7 @@ You use the community supported version of the original Shaarli project, by Seba $request = ''; } - $linkFilter = new LinkFilter($this->_links); + $linkFilter = new LinkFilter($this->links); return $linkFilter->filter($type, $request, $casesensitive, $privateonly); } @@ -442,7 +442,7 @@ You use the community supported version of the original Shaarli project, by Seba { $tags = array(); $caseMapping = array(); - foreach ($this->_links as $link) { + foreach ($this->links as $link) { foreach (preg_split('/\s+/', $link['tags'], 0, PREG_SPLIT_NO_EMPTY) as $tag) { if (empty($tag)) { continue; @@ -467,7 +467,7 @@ You use the community supported version of the original Shaarli project, by Seba public function days() { $linkDays = array(); - foreach (array_keys($this->_links) as $day) { + foreach (array_keys($this->links) as $day) { $linkDays[substr($day, 0, 8)] = 0; } $linkDays = array_keys($linkDays); -- cgit v1.2.3 From f21abf329234ae4d5a1d56c5a9dd0bc11f80bac8 Mon Sep 17 00:00:00 2001 From: VirtualTam Date: Thu, 20 Oct 2016 21:19:51 +0200 Subject: LinkDB: update datastore method names Relates to https://github.com/shaarli/Shaarli/issues/95 Signed-off-by: VirtualTam --- application/LinkDB.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'application/LinkDB.php') diff --git a/application/LinkDB.php b/application/LinkDB.php index 2d42c514..f5f209f6 100644 --- a/application/LinkDB.php +++ b/application/LinkDB.php @@ -100,8 +100,8 @@ class LinkDB implements Iterator, Countable, ArrayAccess $this->hidePublicLinks = $hidePublicLinks; $this->redirector = $redirector; $this->redirectorEncode = $redirectorEncode === true; - $this->checkDB(); - $this->readDB(); + $this->check(); + $this->read(); } /** @@ -210,7 +210,7 @@ class LinkDB implements Iterator, Countable, ArrayAccess * * If no DB file is found, creates a dummy DB. */ - private function checkDB() + private function check() { if (file_exists($this->datastore)) { return; @@ -243,13 +243,13 @@ You use the community supported version of the original Shaarli project, by Seba $this->links[$link['linkdate']] = $link; // Write database to disk - $this->writeDB(); + $this->write(); } /** * Reads database from disk to memory */ - private function readDB() + private function read() { // Public links are hidden and user not logged in => nothing to show @@ -315,7 +315,7 @@ You use the community supported version of the original Shaarli project, by Seba * * @throws IOException the datastore is not writable */ - private function writeDB() + private function write() { if (is_file($this->datastore) && !is_writeable($this->datastore)) { // The datastore exists but is not writeable @@ -337,14 +337,14 @@ You use the community supported version of the original Shaarli project, by Seba * * @param string $pageCacheDir page cache directory */ - public function savedb($pageCacheDir) + public function save($pageCacheDir) { if (!$this->loggedIn) { // TODO: raise an Exception instead die('You are not authorized to change the database.'); } - $this->writeDB(); + $this->write(); invalidateCaches($pageCacheDir); } -- cgit v1.2.3 From 735ed4a94e1da5874195ac47c00612043a193edf Mon Sep 17 00:00:00 2001 From: VirtualTam Date: Thu, 20 Oct 2016 21:24:39 +0200 Subject: LinkDB: explicit method visibility Relates to https://github.com/shaarli/Shaarli/issues/95 Signed-off-by: VirtualTam --- application/LinkDB.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'application/LinkDB.php') diff --git a/application/LinkDB.php b/application/LinkDB.php index f5f209f6..c8b162b6 100644 --- a/application/LinkDB.php +++ b/application/LinkDB.php @@ -87,7 +87,7 @@ class LinkDB implements Iterator, Countable, ArrayAccess * @param string $redirector link redirector set in user settings. * @param boolean $redirectorEncode Enable urlencode on redirected urls (default: true). */ - function __construct( + public function __construct( $datastore, $isLoggedIn, $hidePublicLinks, @@ -164,7 +164,7 @@ class LinkDB implements Iterator, Countable, ArrayAccess /** * Iterator - Returns the current element */ - function current() + public function current() { return $this->links[$this->keys[$this->position]]; } @@ -172,7 +172,7 @@ class LinkDB implements Iterator, Countable, ArrayAccess /** * Iterator - Returns the key of the current element */ - function key() + public function key() { return $this->keys[$this->position]; } @@ -180,7 +180,7 @@ class LinkDB implements Iterator, Countable, ArrayAccess /** * Iterator - Moves forward to next element */ - function next() + public function next() { ++$this->position; } @@ -190,7 +190,7 @@ class LinkDB implements Iterator, Countable, ArrayAccess * * Entries are sorted by date (latest first) */ - function rewind() + public function rewind() { $this->keys = array_keys($this->links); rsort($this->keys); @@ -200,7 +200,7 @@ class LinkDB implements Iterator, Countable, ArrayAccess /** * Iterator - Checks if current position is valid */ - function valid() + public function valid() { return isset($this->keys[$this->position]); } -- cgit v1.2.3 From 29d108820f05615d5c36608fde86f64f0750531a Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Mon, 28 Nov 2016 15:30:17 +0100 Subject: Link ID refactoring Links now use an incremental unique numeric identifier. This ID is persistent and must never change. ArrayAccess is used to match the link ID with the array keys (see the comment in LinkDB for more details) Key 'created' added, with creation date as a DateTime object. 'updated' is now also a DateTime. --- application/LinkDB.php | 178 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 135 insertions(+), 43 deletions(-) (limited to 'application/LinkDB.php') diff --git a/application/LinkDB.php b/application/LinkDB.php index c8b162b6..e429ab4f 100644 --- a/application/LinkDB.php +++ b/application/LinkDB.php @@ -6,15 +6,15 @@ * * Example: * $myLinks = new LinkDB(); - * echo $myLinks['20110826_161819']['title']; + * echo $myLinks[350]['title']; * foreach ($myLinks as $link) * echo $link['title'].' at url '.$link['url'].'; description:'.$link['description']; * * Available keys: + * - id: primary key, incremental integer identifier (persistent) * - description: description of the entry - * - linkdate: creation date of this entry, format: YYYYMMDD_HHMMSS - * (e.g.'20110914_192317') - * - updated: last modification date of this entry, format: YYYYMMDD_HHMMSS + * - created: creation date of this entry, DateTime object. + * - updated: last modification date of this entry, DateTime object. * - private: Is this link private? 0=no, other value=yes * - tags: tags attached to this entry (separated by spaces) * - title Title of the link @@ -27,6 +27,19 @@ * - ArrayAccess: behaves like an associative array; * - Countable: there is a count() method; * - Iterator: usable in foreach () loops. + * + * ID mechanism: + * ArrayAccess is implemented in a way that will allow to access a link + * with the unique identifier ID directly with $link[ID]. + * Note that it's not the real key of the link array attribute. + * This mechanism is in place to have persistent link IDs, + * even though the internal array is reordered by date. + * Example: + * - DB: link #1 (2010-01-01) link #2 (2016-01-01) + * - Order: #2 #1 + * - Import links containing: link #3 (2013-01-01) + * - New DB: link #1 (2010-01-01) link #2 (2016-01-01) link #3 (2013-01-01) + * - Real order: #2 #3 #1 */ class LinkDB implements Iterator, Countable, ArrayAccess { @@ -47,11 +60,17 @@ class LinkDB implements Iterator, Countable, ArrayAccess // - value: associative array (keys: title, description...) private $links; - // List of all recorded URLs (key=url, value=linkdate) - // for fast reserve search (url-->linkdate) + // List of all recorded URLs (key=url, value=link offset) + // for fast reserve search (url-->link offset) private $urls; - // List of linkdate keys (for the Iterator interface implementation) + /** + * @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) @@ -121,14 +140,26 @@ class LinkDB implements Iterator, Countable, ArrayAccess if (!$this->loggedIn) { die('You are not authorized to add a link.'); } - if (empty($value['linkdate']) || empty($value['url'])) { - die('Internal Error: A link should always have a linkdate and URL.'); + if (!isset($value['id']) || empty($value['url'])) { + die('Internal Error: A link should always have an id and URL.'); } - if (empty($offset)) { - die('You must specify a key.'); + if ((! empty($offset) && ! is_int($offset)) || ! is_int($value['id'])) { + die('You must specify an integer as a key.'); + } + if (! empty($offset) && $offset !== $value['id']) { + die('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->urls[$value['url']] = $offset; + $this->ids[$value['id']] = $offset; } /** @@ -136,7 +167,7 @@ class LinkDB implements Iterator, Countable, ArrayAccess */ public function offsetExists($offset) { - return array_key_exists($offset, $this->links); + return array_key_exists($this->getLinkOffset($offset), $this->links); } /** @@ -148,9 +179,11 @@ class LinkDB implements Iterator, Countable, ArrayAccess // TODO: raise an exception die('You are not authorized to delete a link.'); } - $url = $this->links[$offset]['url']; + $realOffset = $this->getLinkOffset($offset); + $url = $this->links[$realOffset]['url']; unset($this->urls[$url]); - unset($this->links[$offset]); + unset($this->ids[$realOffset]); + unset($this->links[$realOffset]); } /** @@ -158,7 +191,8 @@ class LinkDB implements Iterator, Countable, ArrayAccess */ public function offsetGet($offset) { - return isset($this->links[$offset]) ? $this->links[$offset] : null; + $realOffset = $this->getLinkOffset($offset); + return isset($this->links[$realOffset]) ? $this->links[$realOffset] : null; } /** @@ -166,7 +200,7 @@ class LinkDB implements Iterator, Countable, ArrayAccess */ public function current() { - return $this->links[$this->keys[$this->position]]; + return $this[$this->keys[$this->position]]; } /** @@ -192,8 +226,7 @@ class LinkDB implements Iterator, Countable, ArrayAccess */ public function rewind() { - $this->keys = array_keys($this->links); - rsort($this->keys); + $this->keys = array_keys($this->ids); $this->position = 0; } @@ -219,6 +252,7 @@ class LinkDB implements Iterator, Countable, ArrayAccess // Create a dummy database for example $this->links = array(); $link = array( + 'id' => 1, 'title'=>' Shaarli: the personal, minimalist, super-fast, no-database delicious clone', 'url'=>'https://github.com/shaarli/Shaarli/wiki', 'description'=>'Welcome to Shaarli! This is your first public bookmark. To edit or delete me, you must first login. @@ -227,20 +261,21 @@ To learn how to use Shaarli, consult the link "Help/documentation" at the bottom You use the community supported version of the original Shaarli project, by Sebastien Sauvage.', 'private'=>0, - 'linkdate'=> date('Ymd_His'), + 'created'=> new DateTime(), 'tags'=>'opensource software' ); - $this->links[$link['linkdate']] = $link; + $this->links[1] = $link; $link = array( + 'id' => 0, 'title'=>'My secret stuff... - Pastebin.com', 'url'=>'http://sebsauvage.net/paste/?8434b27936c09649#bR7XsXhoTiLcqCpQbmOpBi3rq2zzQUC5hBI7ZT1O3x8=', 'description'=>'Shhhh! I\'m a private link only YOU can see. You can delete me too.', 'private'=>1, - 'linkdate'=> date('Ymd_His', strtotime('-1 minute')), + 'created'=> new DateTime('1 minute ago'), 'tags'=>'secretstuff' ); - $this->links[$link['linkdate']] = $link; + $this->links[0] = $link; // Write database to disk $this->write(); @@ -251,7 +286,6 @@ You use the community supported version of the original Shaarli project, by Seba */ private function read() { - // Public links are hidden and user not logged in => nothing to show if ($this->hidePublicLinks && !$this->loggedIn) { $this->links = array(); @@ -269,23 +303,13 @@ You use the community supported version of the original Shaarli project, by Seba strlen(self::$phpPrefix), -strlen(self::$phpSuffix))))); } - // If user is not logged in, filter private links. - if (!$this->loggedIn) { - $toremove = array(); - foreach ($this->links as $link) { - if ($link['private'] != 0) { - $toremove[] = $link['linkdate']; - } - } - foreach ($toremove as $linkdate) { - unset($this->links[$linkdate]); + $toremove = array(); + foreach ($this->links as $key => &$link) { + if (! $this->loggedIn && $link['private'] != 0) { + // Transition for not upgraded databases. + $toremove[] = $key; + continue; } - } - - $this->urls = array(); - foreach ($this->links as &$link) { - // Keep the list of the mapping URLs-->linkdate up-to-date. - $this->urls[$link['url']] = $link['linkdate']; // Sanitize data fields. sanitizeLink($link); @@ -307,7 +331,23 @@ You use the community supported version of the original Shaarli project, by Seba 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('Ymd_His', $link['linkdate']); + if (! empty($link['updated'])) { + $link['updated'] = DateTime::createFromFormat('Ymd_His', $link['updated']); + } + } + } + + // If user is not logged in, filter private links. + foreach ($toremove as $offset) { + unset($this->links[$offset]); } + + $this->reorder(); } /** @@ -430,7 +470,7 @@ You use the community supported version of the original Shaarli project, by Seba $request = ''; } - $linkFilter = new LinkFilter($this->links); + $linkFilter = new LinkFilter($this); return $linkFilter->filter($type, $request, $casesensitive, $privateonly); } @@ -467,12 +507,64 @@ You use the community supported version of the original Shaarli project, by Seba public function days() { $linkDays = array(); - foreach (array_keys($this->links) as $day) { - $linkDays[substr($day, 0, 8)] = 0; + 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) { + return $a['created'] < $b['created'] ? 1 * $order : -1 * $order; + }); + + $this->urls = array(); + $this->ids = array(); + 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 exists. + */ + protected function getLinkOffset($id) + { + if (isset($this->ids[$id])) { + return $this->ids[$id]; + } + return null; + } } -- cgit v1.2.3 From d592daea8343bb4dfecff5d97e93699581ccc58c Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Mon, 28 Nov 2016 18:24:15 +0100 Subject: Add a persistent 'shorturl' key to all links All existing link will keep their permalinks. New links will have smallhash generated with date+id. The purpose of this is to avoid collision between links due to their creation date. --- application/LinkDB.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'application/LinkDB.php') diff --git a/application/LinkDB.php b/application/LinkDB.php index e429ab4f..1e13286a 100644 --- a/application/LinkDB.php +++ b/application/LinkDB.php @@ -22,6 +22,7 @@ * Can be absolute or relative. * Relative URLs are permalinks (e.g.'?m-ukcw') * - real_url Absolute processed URL. + * - shorturl Permalink smallhash * * Implements 3 interfaces: * - ArrayAccess: behaves like an associative array; @@ -264,6 +265,7 @@ You use the community supported version of the original Shaarli project, by Seba 'created'=> new DateTime(), 'tags'=>'opensource software' ); + $link['shorturl'] = link_small_hash($link['created'], $link['id']); $this->links[1] = $link; $link = array( @@ -273,8 +275,9 @@ You use the community supported version of the original Shaarli project, by Seba 'description'=>'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' + 'tags'=>'secretstuff', ); + $link['shorturl'] = link_small_hash($link['created'], $link['id']); $this->links[0] = $link; // Write database to disk @@ -335,10 +338,11 @@ You use the community supported version of the original Shaarli project, by Seba // 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('Ymd_His', $link['linkdate']); + $link['created'] = DateTime::createFromFormat(self::LINK_DATE_FORMAT, $link['linkdate']); if (! empty($link['updated'])) { - $link['updated'] = DateTime::createFromFormat('Ymd_His', $link['updated']); + $link['updated'] = DateTime::createFromFormat(self::LINK_DATE_FORMAT, $link['updated']); } + $link['shorturl'] = smallHash($link['linkdate']); } } @@ -558,7 +562,7 @@ You use the community supported version of the original Shaarli project, by Seba * * @param int $id Persistent ID of a link. * - * @return int Real offset in local array, or null if doesn't exists. + * @return int Real offset in local array, or null if doesn't exist. */ protected function getLinkOffset($id) { -- cgit v1.2.3