From 21979ff11ceee0042642ac17147858a4155d54c5 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Wed, 20 Jan 2016 23:34:33 +0100 Subject: [PATCH] Add exclusion in tag search * Searching '-mytag' will now exlude all shaares with 'mytag' tag. * All tags starting with a '-' are renamed without it (through the Updater). * Unit tests. Minor code changes: * LinkDB->filter() can now take no parameters (get all link depending on logged status). * tagsStrToArray() is now static and filters blank tags. --- application/LinkDB.php | 2 +- application/LinkFilter.php | 30 ++++++++++++++++++++++-------- application/Updater.php | 15 +++++++++++++++ tests/LinkDBTest.php | 6 ++++-- tests/LinkFilterTest.php | 16 ++++++++++++++++ tests/Updater/UpdaterTest.php | 18 ++++++++++++++++++ tests/utils/ReferenceLinkDB.php | 2 +- 7 files changed, 77 insertions(+), 12 deletions(-) diff --git a/application/LinkDB.php b/application/LinkDB.php index a95b3f3..416aa0d 100644 --- a/application/LinkDB.php +++ b/application/LinkDB.php @@ -340,7 +340,7 @@ You use the community supported version of the original Shaarli project, by Seba * * @return array filtered links */ - public function filter($type, $request, $casesensitive = false, $privateonly = false) + public function filter($type = '', $request = '', $casesensitive = false, $privateonly = false) { $linkFilter = new LinkFilter($this->_links); $requestFilter = is_array($request) ? implode(' ', $request) : $request; diff --git a/application/LinkFilter.php b/application/LinkFilter.php index 096d3b0..ceb47d1 100644 --- a/application/LinkFilter.php +++ b/application/LinkFilter.php @@ -209,19 +209,33 @@ class LinkFilter */ public function filterTags($tags, $casesensitive = false, $privateonly = false) { - $searchtags = $this->tagsStrToArray($tags, $casesensitive); + $searchtags = self::tagsStrToArray($tags, $casesensitive); $filtered = array(); + if (empty($searchtags)) { + return $filtered; + } - foreach ($this->links as $l) { + foreach ($this->links as $link) { // ignore non private links when 'privatonly' is on. - if (! $l['private'] && $privateonly === true) { + if (! $link['private'] && $privateonly === true) { continue; } - $linktags = $this->tagsStrToArray($l['tags'], $casesensitive); + $linktags = self::tagsStrToArray($link['tags'], $casesensitive); - if (count(array_intersect($linktags, $searchtags)) == count($searchtags)) { - $filtered[$l['linkdate']] = $l; + $found = true; + for ($i = 0 ; $i < count($searchtags) && $found; $i++) { + // Exclusive search, quit if tag found. + // Or, tag not found in the link, quit. + if (($searchtags[$i][0] == '-' && in_array(substr($searchtags[$i], 1), $linktags)) + || ($searchtags[$i][0] != '-') && ! in_array($searchtags[$i], $linktags) + ) { + $found = false; + } + } + + if ($found) { + $filtered[$link['linkdate']] = $link; } } krsort($filtered); @@ -266,12 +280,12 @@ class LinkFilter * * @return array filtered tags string. */ - public function tagsStrToArray($tags, $casesensitive) + public static function tagsStrToArray($tags, $casesensitive) { // We use UTF-8 conversion to handle various graphemes (i.e. cyrillic, or greek) $tagsOut = $casesensitive ? $tags : mb_convert_case($tags, MB_CASE_LOWER, 'UTF-8'); $tagsOut = str_replace(',', ' ', $tagsOut); - return explode(' ', trim($tagsOut)); + return array_filter(explode(' ', trim($tagsOut)), 'strlen'); } } diff --git a/application/Updater.php b/application/Updater.php index 20ae0c4..773a1ff 100644 --- a/application/Updater.php +++ b/application/Updater.php @@ -131,6 +131,21 @@ class Updater return true; } + + /** + * Rename tags starting with a '-' to work with tag exclusion search. + */ + public function updateMethodRenameDashTags() + { + $linklist = $this->linkDB->filter(); + foreach ($linklist as $link) { + $link['tags'] = preg_replace('/(^| )\-/', '$1', $link['tags']); + $link['tags'] = implode(' ', array_unique(LinkFilter::tagsStrToArray($link['tags'], true))); + $this->linkDB[$link['linkdate']] = $link; + } + $this->linkDB->savedb($this->config['config']['PAGECACHE']); + return true; + } } /** diff --git a/tests/LinkDBTest.php b/tests/LinkDBTest.php index 3b1a205..06edea7 100644 --- a/tests/LinkDBTest.php +++ b/tests/LinkDBTest.php @@ -276,7 +276,8 @@ class LinkDBTest extends PHPUnit_Framework_TestCase 'media' => 1, 'software' => 1, 'stallman' => 1, - 'free' => 1 + 'free' => 1, + '-exclude' => 1, ), self::$publicLinkDB->allTags() ); @@ -295,7 +296,8 @@ class LinkDBTest extends PHPUnit_Framework_TestCase 'html' => 1, 'w3c' => 1, 'css' => 1, - 'Mercurial' => 1 + 'Mercurial' => 1, + '-exclude' => 1, ), self::$privateLinkDB->allTags() ); diff --git a/tests/LinkFilterTest.php b/tests/LinkFilterTest.php index 5fb2423..164af0d 100644 --- a/tests/LinkFilterTest.php +++ b/tests/LinkFilterTest.php @@ -254,4 +254,20 @@ class LinkFilterTest extends PHPUnit_Framework_TestCase count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'free software')) ); } + + /** + * Tag search with exclusion. + */ + public function testTagFilterWithExclusion() + { + $this->assertEquals( + 1, + count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, 'gnu -free')) + ); + + $this->assertEquals( + 5, + count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, '-free')) + ); + } } diff --git a/tests/Updater/UpdaterTest.php b/tests/Updater/UpdaterTest.php index 63ed5e0..84b8235 100644 --- a/tests/Updater/UpdaterTest.php +++ b/tests/Updater/UpdaterTest.php @@ -13,6 +13,11 @@ class UpdaterTest extends PHPUnit_Framework_TestCase */ private static $configFields; + /** + * @var string Path to test datastore. + */ + protected static $testDatastore = 'sandbox/datastore.php'; + /** * Executed before each test. */ @@ -31,6 +36,7 @@ class UpdaterTest extends PHPUnit_Framework_TestCase 'config' => array( 'CONFIG_FILE' => 'tests/Updater/config.php', 'DATADIR' => 'tests/Updater', + 'PAGECACHE' => 'sandbox/pagecache', 'config1' => 'config1data', 'config2' => 'config2data', ) @@ -224,4 +230,16 @@ class UpdaterTest extends PHPUnit_Framework_TestCase include self::$configFields['config']['CONFIG_FILE']; $this->assertEquals(self::$configFields['login'], $GLOBALS['login']); } + + public function testRenameDashTags() + { + $refDB = new ReferenceLinkDB(); + $refDB->write(self::$testDatastore); + $linkDB = new LinkDB(self::$testDatastore, true, false); + $this->assertEmpty($linkDB->filter(LinkFilter::$FILTER_TAG, 'exclude')); + $updater = new Updater(array(), self::$configFields, $linkDB, true); + $updater->updateMethodRenameDashTags(); + var_dump($linkDB->filter(LinkFilter::$FILTER_TAG, 'exclude')); + $this->assertNotEmpty($linkDB->filter(LinkFilter::$FILTER_TAG, 'exclude')); + } } diff --git a/tests/utils/ReferenceLinkDB.php b/tests/utils/ReferenceLinkDB.php index 011317e..2f188d2 100644 --- a/tests/utils/ReferenceLinkDB.php +++ b/tests/utils/ReferenceLinkDB.php @@ -19,7 +19,7 @@ class ReferenceLinkDB 'Richard Stallman and the Free Software Revolution', 0, '20150310_114633', - 'free gnu software stallman' + 'free gnu software stallman -exclude' ); $this->addLink(