aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorArthur <arthur@hoa.ro>2016-02-15 21:12:39 +0100
committerArthur <arthur@hoa.ro>2016-02-15 21:12:39 +0100
commit1e7331126d81a5759ab91c221f7e0f164aeebfb5 (patch)
treea5d084066e1e49fae01ae72f102b3eab2fb6d8ac
parent6e607ca613b47e17f7516e94adfee930d4f3e1e8 (diff)
parentce354bf1a61ce2478529ad558b24cdf9678c398a (diff)
downloadShaarli-1e7331126d81a5759ab91c221f7e0f164aeebfb5.tar.gz
Shaarli-1e7331126d81a5759ab91c221f7e0f164aeebfb5.tar.zst
Shaarli-1e7331126d81a5759ab91c221f7e0f164aeebfb5.zip
Merge pull request #446 from ArthurHoaro/search-tag-exclude
Add exclusion in tag search
-rw-r--r--application/LinkDB.php2
-rw-r--r--application/LinkFilter.php30
-rw-r--r--application/Updater.php15
-rw-r--r--index.php2
-rw-r--r--tests/LinkDBTest.php6
-rw-r--r--tests/LinkFilterTest.php16
-rw-r--r--tests/Updater/UpdaterTest.php18
-rw-r--r--tests/utils/ReferenceLinkDB.php2
8 files changed, 79 insertions, 12 deletions
diff --git a/application/LinkDB.php b/application/LinkDB.php
index a95b3f36..416aa0d3 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
340 * 340 *
341 * @return array filtered links 341 * @return array filtered links
342 */ 342 */
343 public function filter($type, $request, $casesensitive = false, $privateonly = false) 343 public function filter($type = '', $request = '', $casesensitive = false, $privateonly = false)
344 { 344 {
345 $linkFilter = new LinkFilter($this->_links); 345 $linkFilter = new LinkFilter($this->_links);
346 $requestFilter = is_array($request) ? implode(' ', $request) : $request; 346 $requestFilter = is_array($request) ? implode(' ', $request) : $request;
diff --git a/application/LinkFilter.php b/application/LinkFilter.php
index 096d3b04..ceb47d16 100644
--- a/application/LinkFilter.php
+++ b/application/LinkFilter.php
@@ -209,19 +209,33 @@ class LinkFilter
209 */ 209 */
210 public function filterTags($tags, $casesensitive = false, $privateonly = false) 210 public function filterTags($tags, $casesensitive = false, $privateonly = false)
211 { 211 {
212 $searchtags = $this->tagsStrToArray($tags, $casesensitive); 212 $searchtags = self::tagsStrToArray($tags, $casesensitive);
213 $filtered = array(); 213 $filtered = array();
214 if (empty($searchtags)) {
215 return $filtered;
216 }
214 217
215 foreach ($this->links as $l) { 218 foreach ($this->links as $link) {
216 // ignore non private links when 'privatonly' is on. 219 // ignore non private links when 'privatonly' is on.
217 if (! $l['private'] && $privateonly === true) { 220 if (! $link['private'] && $privateonly === true) {
218 continue; 221 continue;
219 } 222 }
220 223
221 $linktags = $this->tagsStrToArray($l['tags'], $casesensitive); 224 $linktags = self::tagsStrToArray($link['tags'], $casesensitive);
222 225
223 if (count(array_intersect($linktags, $searchtags)) == count($searchtags)) { 226 $found = true;
224 $filtered[$l['linkdate']] = $l; 227 for ($i = 0 ; $i < count($searchtags) && $found; $i++) {
228 // Exclusive search, quit if tag found.
229 // Or, tag not found in the link, quit.
230 if (($searchtags[$i][0] == '-' && in_array(substr($searchtags[$i], 1), $linktags))
231 || ($searchtags[$i][0] != '-') && ! in_array($searchtags[$i], $linktags)
232 ) {
233 $found = false;
234 }
235 }
236
237 if ($found) {
238 $filtered[$link['linkdate']] = $link;
225 } 239 }
226 } 240 }
227 krsort($filtered); 241 krsort($filtered);
@@ -266,12 +280,12 @@ class LinkFilter
266 * 280 *
267 * @return array filtered tags string. 281 * @return array filtered tags string.
268 */ 282 */
269 public function tagsStrToArray($tags, $casesensitive) 283 public static function tagsStrToArray($tags, $casesensitive)
270 { 284 {
271 // We use UTF-8 conversion to handle various graphemes (i.e. cyrillic, or greek) 285 // We use UTF-8 conversion to handle various graphemes (i.e. cyrillic, or greek)
272 $tagsOut = $casesensitive ? $tags : mb_convert_case($tags, MB_CASE_LOWER, 'UTF-8'); 286 $tagsOut = $casesensitive ? $tags : mb_convert_case($tags, MB_CASE_LOWER, 'UTF-8');
273 $tagsOut = str_replace(',', ' ', $tagsOut); 287 $tagsOut = str_replace(',', ' ', $tagsOut);
274 288
275 return explode(' ', trim($tagsOut)); 289 return array_filter(explode(' ', trim($tagsOut)), 'strlen');
276 } 290 }
277} 291}
diff --git a/application/Updater.php b/application/Updater.php
index 20ae0c4d..773a1ffa 100644
--- a/application/Updater.php
+++ b/application/Updater.php
@@ -131,6 +131,21 @@ class Updater
131 131
132 return true; 132 return true;
133 } 133 }
134
135 /**
136 * Rename tags starting with a '-' to work with tag exclusion search.
137 */
138 public function updateMethodRenameDashTags()
139 {
140 $linklist = $this->linkDB->filter();
141 foreach ($linklist as $link) {
142 $link['tags'] = preg_replace('/(^| )\-/', '$1', $link['tags']);
143 $link['tags'] = implode(' ', array_unique(LinkFilter::tagsStrToArray($link['tags'], true)));
144 $this->linkDB[$link['linkdate']] = $link;
145 }
146 $this->linkDB->savedb($this->config['config']['PAGECACHE']);
147 return true;
148 }
134} 149}
135 150
136/** 151/**
diff --git a/index.php b/index.php
index 4382bd80..d9fe5bc2 100644
--- a/index.php
+++ b/index.php
@@ -1558,6 +1558,8 @@ function renderPage()
1558 } 1558 }
1559 // Remove multiple spaces. 1559 // Remove multiple spaces.
1560 $tags = trim(preg_replace('/\s\s+/', ' ', $_POST['lf_tags'])); 1560 $tags = trim(preg_replace('/\s\s+/', ' ', $_POST['lf_tags']));
1561 // Remove first '-' char in tags.
1562 $tags = preg_replace('/(^| )\-/', '$1', $tags);
1561 // Remove duplicates. 1563 // Remove duplicates.
1562 $tags = implode(' ', array_unique(explode(' ', $tags))); 1564 $tags = implode(' ', array_unique(explode(' ', $tags)));
1563 $linkdate = $_POST['lf_linkdate']; 1565 $linkdate = $_POST['lf_linkdate'];
diff --git a/tests/LinkDBTest.php b/tests/LinkDBTest.php
index 3b1a2057..06edea79 100644
--- a/tests/LinkDBTest.php
+++ b/tests/LinkDBTest.php
@@ -276,7 +276,8 @@ class LinkDBTest extends PHPUnit_Framework_TestCase
276 'media' => 1, 276 'media' => 1,
277 'software' => 1, 277 'software' => 1,
278 'stallman' => 1, 278 'stallman' => 1,
279 'free' => 1 279 'free' => 1,
280 '-exclude' => 1,
280 ), 281 ),
281 self::$publicLinkDB->allTags() 282 self::$publicLinkDB->allTags()
282 ); 283 );
@@ -295,7 +296,8 @@ class LinkDBTest extends PHPUnit_Framework_TestCase
295 'html' => 1, 296 'html' => 1,
296 'w3c' => 1, 297 'w3c' => 1,
297 'css' => 1, 298 'css' => 1,
298 'Mercurial' => 1 299 'Mercurial' => 1,
300 '-exclude' => 1,
299 ), 301 ),
300 self::$privateLinkDB->allTags() 302 self::$privateLinkDB->allTags()
301 ); 303 );
diff --git a/tests/LinkFilterTest.php b/tests/LinkFilterTest.php
index 5fb2423f..164af0d4 100644
--- a/tests/LinkFilterTest.php
+++ b/tests/LinkFilterTest.php
@@ -254,4 +254,20 @@ class LinkFilterTest extends PHPUnit_Framework_TestCase
254 count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'free software')) 254 count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'free software'))
255 ); 255 );
256 } 256 }
257
258 /**
259 * Tag search with exclusion.
260 */
261 public function testTagFilterWithExclusion()
262 {
263 $this->assertEquals(
264 1,
265 count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, 'gnu -free'))
266 );
267
268 $this->assertEquals(
269 5,
270 count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, '-free'))
271 );
272 }
257} 273}
diff --git a/tests/Updater/UpdaterTest.php b/tests/Updater/UpdaterTest.php
index 63ed5e03..84b82350 100644
--- a/tests/Updater/UpdaterTest.php
+++ b/tests/Updater/UpdaterTest.php
@@ -14,6 +14,11 @@ class UpdaterTest extends PHPUnit_Framework_TestCase
14 private static $configFields; 14 private static $configFields;
15 15
16 /** 16 /**
17 * @var string Path to test datastore.
18 */
19 protected static $testDatastore = 'sandbox/datastore.php';
20
21 /**
17 * Executed before each test. 22 * Executed before each test.
18 */ 23 */
19 public function setUp() 24 public function setUp()
@@ -31,6 +36,7 @@ class UpdaterTest extends PHPUnit_Framework_TestCase
31 'config' => array( 36 'config' => array(
32 'CONFIG_FILE' => 'tests/Updater/config.php', 37 'CONFIG_FILE' => 'tests/Updater/config.php',
33 'DATADIR' => 'tests/Updater', 38 'DATADIR' => 'tests/Updater',
39 'PAGECACHE' => 'sandbox/pagecache',
34 'config1' => 'config1data', 40 'config1' => 'config1data',
35 'config2' => 'config2data', 41 'config2' => 'config2data',
36 ) 42 )
@@ -224,4 +230,16 @@ class UpdaterTest extends PHPUnit_Framework_TestCase
224 include self::$configFields['config']['CONFIG_FILE']; 230 include self::$configFields['config']['CONFIG_FILE'];
225 $this->assertEquals(self::$configFields['login'], $GLOBALS['login']); 231 $this->assertEquals(self::$configFields['login'], $GLOBALS['login']);
226 } 232 }
233
234 public function testRenameDashTags()
235 {
236 $refDB = new ReferenceLinkDB();
237 $refDB->write(self::$testDatastore);
238 $linkDB = new LinkDB(self::$testDatastore, true, false);
239 $this->assertEmpty($linkDB->filter(LinkFilter::$FILTER_TAG, 'exclude'));
240 $updater = new Updater(array(), self::$configFields, $linkDB, true);
241 $updater->updateMethodRenameDashTags();
242 var_dump($linkDB->filter(LinkFilter::$FILTER_TAG, 'exclude'));
243 $this->assertNotEmpty($linkDB->filter(LinkFilter::$FILTER_TAG, 'exclude'));
244 }
227} 245}
diff --git a/tests/utils/ReferenceLinkDB.php b/tests/utils/ReferenceLinkDB.php
index 011317ef..2f188d29 100644
--- a/tests/utils/ReferenceLinkDB.php
+++ b/tests/utils/ReferenceLinkDB.php
@@ -19,7 +19,7 @@ class ReferenceLinkDB
19 'Richard Stallman and the Free Software Revolution', 19 'Richard Stallman and the Free Software Revolution',
20 0, 20 0,
21 '20150310_114633', 21 '20150310_114633',
22 'free gnu software stallman' 22 'free gnu software stallman -exclude'
23 ); 23 );
24 24
25 $this->addLink( 25 $this->addLink(