aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorArthurHoaro <arthur@hoa.ro>2016-03-21 21:40:49 +0100
committerArthurHoaro <arthur@hoa.ro>2016-03-25 19:17:59 +0100
commit528a6f8a232c060faf024008e4f8a09b4aa8dabc (patch)
tree86cac78b7f4d3998bedd923da83145c37ec88ff4
parentee88a4bcc29da721cf43b750663aebeac4969517 (diff)
downloadShaarli-528a6f8a232c060faf024008e4f8a09b4aa8dabc.tar.gz
Shaarli-528a6f8a232c060faf024008e4f8a09b4aa8dabc.tar.zst
Shaarli-528a6f8a232c060faf024008e4f8a09b4aa8dabc.zip
Refactor filter in LinkDB
* search type now carried by LinkDB in order to factorize code between different search sources. * LinkDB->filter split in 3 method: filterSearch, filterHash, filterDay (we know what type of filter is needed). * filterHash now throw a LinkNotFoundException if it doesn't exist: internal implementation choice, still displays a 404. * Smallhash regex has been rewritten. * Unit tests update
-rw-r--r--application/FeedBuilder.php18
-rw-r--r--application/LinkDB.php64
-rw-r--r--application/LinkFilter.php14
-rw-r--r--application/Updater.php2
-rw-r--r--index.php99
-rw-r--r--tests/LinkDBTest.php56
-rw-r--r--tests/LinkFilterTest.php7
-rw-r--r--tests/Updater/UpdaterTest.php4
8 files changed, 158 insertions, 106 deletions
diff --git a/application/FeedBuilder.php b/application/FeedBuilder.php
index 50e09831..ddefe6ce 100644
--- a/application/FeedBuilder.php
+++ b/application/FeedBuilder.php
@@ -103,23 +103,7 @@ class FeedBuilder
103 public function buildData() 103 public function buildData()
104 { 104 {
105 // Optionally filter the results: 105 // Optionally filter the results:
106 $searchtags = !empty($this->userInput['searchtags']) ? escape($this->userInput['searchtags']) : ''; 106 $linksToDisplay = $this->linkDB->filterSearch($this->userInput);
107 $searchterm = !empty($this->userInput['searchterm']) ? escape($this->userInput['searchterm']) : '';
108 if (! empty($searchtags) && ! empty($searchterm)) {
109 $linksToDisplay = $this->linkDB->filter(
110 LinkFilter::$FILTER_TAG | LinkFilter::$FILTER_TEXT,
111 array($searchtags, $searchterm)
112 );
113 }
114 elseif ($searchtags) {
115 $linksToDisplay = $this->linkDB->filter(LinkFilter::$FILTER_TAG, $searchtags);
116 }
117 elseif ($searchterm) {
118 $linksToDisplay = $this->linkDB->filter(LinkFilter::$FILTER_TEXT, $searchterm);
119 }
120 else {
121 $linksToDisplay = $this->linkDB;
122 }
123 107
124 $nblinksToDisplay = $this->getNbLinks(count($linksToDisplay)); 108 $nblinksToDisplay = $this->getNbLinks(count($linksToDisplay));
125 109
diff --git a/application/LinkDB.php b/application/LinkDB.php
index 1b505620..a62341fc 100644
--- a/application/LinkDB.php
+++ b/application/LinkDB.php
@@ -341,17 +341,71 @@ You use the community supported version of the original Shaarli project, by Seba
341 } 341 }
342 342
343 /** 343 /**
344 * Filter links. 344 * Returns the shaare corresponding to a smallHash.
345 * 345 *
346 * @param string $type Type of filter. 346 * @param string $request QUERY_STRING server parameter.
347 * @param mixed $request Search request, string or array. 347 *
348 * @return array $filtered array containing permalink data.
349 *
350 * @throws LinkNotFoundException if the smallhash is malformed or doesn't match any link.
351 */
352 public function filterHash($request)
353 {
354 $request = substr($request, 0, 6);
355 $linkFilter = new LinkFilter($this->_links);
356 return $linkFilter->filter(LinkFilter::$FILTER_HASH, $request);
357 }
358
359 /**
360 * Returns the list of articles for a given day.
361 *
362 * @param string $request day to filter. Format: YYYYMMDD.
363 *
364 * @return array list of shaare found.
365 */
366 public function filterDay($request) {
367 $linkFilter = new LinkFilter($this->_links);
368 return $linkFilter->filter(LinkFilter::$FILTER_DAY, $request);
369 }
370
371 /**
372 * Filter links according to search parameters.
373 *
374 * @param array $filterRequest Search request content. Supported keys:
375 * - searchtags: list of tags
376 * - searchterm: term search
348 * @param bool $casesensitive Optional: Perform case sensitive filter 377 * @param bool $casesensitive Optional: Perform case sensitive filter
349 * @param bool $privateonly Optional: Returns private links only if true. 378 * @param bool $privateonly Optional: Returns private links only if true.
350 * 379 *
351 * @return array filtered links 380 * @return array filtered links, all links if no suitable filter was provided.
352 */ 381 */
353 public function filter($type = '', $request = '', $casesensitive = false, $privateonly = false) 382 public function filterSearch($filterRequest = array(), $casesensitive = false, $privateonly = false)
354 { 383 {
384 // Filter link database according to parameters.
385 $searchtags = !empty($filterRequest['searchtags']) ? escape($filterRequest['searchtags']) : '';
386 $searchterm = !empty($filterRequest['searchterm']) ? escape($filterRequest['searchterm']) : '';
387
388 // Search tags + fullsearch.
389 if (empty($type) && ! empty($searchtags) && ! empty($searchterm)) {
390 $type = LinkFilter::$FILTER_TAG | LinkFilter::$FILTER_TEXT;
391 $request = array($searchtags, $searchterm);
392 }
393 // Search by tags.
394 elseif (! empty($searchtags)) {
395 $type = LinkFilter::$FILTER_TAG;
396 $request = $searchtags;
397 }
398 // Fulltext search.
399 elseif (! empty($searchterm)) {
400 $type = LinkFilter::$FILTER_TEXT;
401 $request = $searchterm;
402 }
403 // Otherwise, display without filtering.
404 else {
405 $type = '';
406 $request = '';
407 }
408
355 $linkFilter = new LinkFilter($this->_links); 409 $linkFilter = new LinkFilter($this->_links);
356 return $linkFilter->filter($type, $request, $casesensitive, $privateonly); 410 return $linkFilter->filter($type, $request, $casesensitive, $privateonly);
357 } 411 }
diff --git a/application/LinkFilter.php b/application/LinkFilter.php
index 3fd803cb..5e0d8015 100644
--- a/application/LinkFilter.php
+++ b/application/LinkFilter.php
@@ -44,7 +44,7 @@ class LinkFilter
44 * Filter links according to parameters. 44 * Filter links according to parameters.
45 * 45 *
46 * @param string $type Type of filter (eg. tags, permalink, etc.). 46 * @param string $type Type of filter (eg. tags, permalink, etc.).
47 * @param string $request Filter content. 47 * @param mixed $request Filter content.
48 * @param bool $casesensitive Optional: Perform case sensitive filter if true. 48 * @param bool $casesensitive Optional: Perform case sensitive filter if true.
49 * @param bool $privateonly Optional: Only returns private links if true. 49 * @param bool $privateonly Optional: Only returns private links if true.
50 * 50 *
@@ -110,6 +110,8 @@ class LinkFilter
110 * @param string $smallHash permalink hash. 110 * @param string $smallHash permalink hash.
111 * 111 *
112 * @return array $filtered array containing permalink data. 112 * @return array $filtered array containing permalink data.
113 *
114 * @throws LinkNotFoundException if the smallhash doesn't match any link.
113 */ 115 */
114 private function filterSmallHash($smallHash) 116 private function filterSmallHash($smallHash)
115 { 117 {
@@ -121,6 +123,11 @@ class LinkFilter
121 return $filtered; 123 return $filtered;
122 } 124 }
123 } 125 }
126
127 if (empty($filtered)) {
128 throw new LinkNotFoundException();
129 }
130
124 return $filtered; 131 return $filtered;
125 } 132 }
126 133
@@ -318,3 +325,8 @@ class LinkFilter
318 return array_filter(explode(' ', trim($tagsOut)), 'strlen'); 325 return array_filter(explode(' ', trim($tagsOut)), 'strlen');
319 } 326 }
320} 327}
328
329class LinkNotFoundException extends Exception
330{
331 protected $message = 'The link you are trying to reach does not exist or has been deleted.';
332}
diff --git a/application/Updater.php b/application/Updater.php
index 773a1ffa..58c13c07 100644
--- a/application/Updater.php
+++ b/application/Updater.php
@@ -137,7 +137,7 @@ class Updater
137 */ 137 */
138 public function updateMethodRenameDashTags() 138 public function updateMethodRenameDashTags()
139 { 139 {
140 $linklist = $this->linkDB->filter(); 140 $linklist = $this->linkDB->filterSearch();
141 foreach ($linklist as $link) { 141 foreach ($linklist as $link) {
142 $link['tags'] = preg_replace('/(^| )\-/', '$1', $link['tags']); 142 $link['tags'] = preg_replace('/(^| )\-/', '$1', $link['tags']);
143 $link['tags'] = implode(' ', array_unique(LinkFilter::tagsStrToArray($link['tags'], true))); 143 $link['tags'] = implode(' ', array_unique(LinkFilter::tagsStrToArray($link['tags'], true)));
diff --git a/index.php b/index.php
index 6e14ff3f..79c66648 100644
--- a/index.php
+++ b/index.php
@@ -816,7 +816,7 @@ function showDaily($pageBuilder)
816 } 816 }
817 817
818 try { 818 try {
819 $linksToDisplay = $LINKSDB->filter(LinkFilter::$FILTER_DAY, $day); 819 $linksToDisplay = $LINKSDB->filterDay($day);
820 } catch (Exception $exc) { 820 } catch (Exception $exc) {
821 error_log($exc); 821 error_log($exc);
822 $linksToDisplay = array(); 822 $linksToDisplay = array();
@@ -962,24 +962,7 @@ function renderPage()
962 if ($targetPage == Router::$PAGE_PICWALL) 962 if ($targetPage == Router::$PAGE_PICWALL)
963 { 963 {
964 // Optionally filter the results: 964 // Optionally filter the results:
965 $searchtags = !empty($_GET['searchtags']) ? escape($_GET['searchtags']) : ''; 965 $links = $LINKSDB->filterSearch($_GET);
966 $searchterm = !empty($_GET['searchterm']) ? escape($_GET['searchterm']) : '';
967 if (! empty($searchtags) && ! empty($searchterm)) {
968 $links = $LINKSDB->filter(
969 LinkFilter::$FILTER_TAG | LinkFilter::$FILTER_TEXT,
970 array($searchtags, $searchterm)
971 );
972 }
973 elseif ($searchtags) {
974 $links = $LINKSDB->filter(LinkFilter::$FILTER_TAG, $searchtags);
975 }
976 elseif ($searchterm) {
977 $links = $LINKSDB->filter(LinkFilter::$FILTER_TEXT, $searchterm);
978 }
979 else {
980 $links = $LINKSDB;
981 }
982
983 $linksToDisplay = array(); 966 $linksToDisplay = array();
984 967
985 // Get only links which have a thumbnail. 968 // Get only links which have a thumbnail.
@@ -1071,7 +1054,7 @@ function renderPage()
1071 startsWith($query,'do='. $targetPage) && !isLoggedIn() 1054 startsWith($query,'do='. $targetPage) && !isLoggedIn()
1072 ); 1055 );
1073 $cached = $cache->cachedVersion(); 1056 $cached = $cache->cachedVersion();
1074 if (!empty($cached)) { 1057 if (false && !empty($cached)) {
1075 echo $cached; 1058 echo $cached;
1076 exit; 1059 exit;
1077 } 1060 }
@@ -1352,9 +1335,9 @@ function renderPage()
1352 1335
1353 // Delete a tag: 1336 // Delete a tag:
1354 if (isset($_POST['deletetag']) && !empty($_POST['fromtag'])) { 1337 if (isset($_POST['deletetag']) && !empty($_POST['fromtag'])) {
1355 $needle=trim($_POST['fromtag']); 1338 $needle = trim($_POST['fromtag']);
1356 // True for case-sensitive tag search. 1339 // True for case-sensitive tag search.
1357 $linksToAlter = $LINKSDB->filter(LinkFilter::$FILTER_TAG, $needle, true); 1340 $linksToAlter = $LINKSDB->filterSearch(array('searchtags' => $needle), true);
1358 foreach($linksToAlter as $key=>$value) 1341 foreach($linksToAlter as $key=>$value)
1359 { 1342 {
1360 $tags = explode(' ',trim($value['tags'])); 1343 $tags = explode(' ',trim($value['tags']));
@@ -1369,9 +1352,9 @@ function renderPage()
1369 1352
1370 // Rename a tag: 1353 // Rename a tag:
1371 if (isset($_POST['renametag']) && !empty($_POST['fromtag']) && !empty($_POST['totag'])) { 1354 if (isset($_POST['renametag']) && !empty($_POST['fromtag']) && !empty($_POST['totag'])) {
1372 $needle=trim($_POST['fromtag']); 1355 $needle = trim($_POST['fromtag']);
1373 // True for case-sensitive tag search. 1356 // True for case-sensitive tag search.
1374 $linksToAlter = $LINKSDB->filter(LinkFilter::$FILTER_TAG, $needle, true); 1357 $linksToAlter = $LINKSDB->filterSearch(array('searchtags' => $needle), true);
1375 foreach($linksToAlter as $key=>$value) 1358 foreach($linksToAlter as $key=>$value)
1376 { 1359 {
1377 $tags = explode(' ',trim($value['tags'])); 1360 $tags = explode(' ',trim($value['tags']));
@@ -1807,60 +1790,32 @@ function importFile()
1807 } 1790 }
1808} 1791}
1809 1792
1810// ----------------------------------------------------------------------------------------------- 1793/**
1811// Template for the list of links (<div id="linklist">) 1794 * Template for the list of links (<div id="linklist">)
1812// This function fills all the necessary fields in the $PAGE for the template 'linklist.html' 1795 * This function fills all the necessary fields in the $PAGE for the template 'linklist.html'
1796 *
1797 * @param pageBuilder $PAGE pageBuilder instance.
1798 * @param LinkDB $LINKSDB LinkDB instance.
1799 */
1813function buildLinkList($PAGE,$LINKSDB) 1800function buildLinkList($PAGE,$LINKSDB)
1814{ 1801{
1815 // Filter link database according to parameters. 1802 // Used in templates
1816 $searchtags = !empty($_GET['searchtags']) ? escape($_GET['searchtags']) : ''; 1803 $searchtags = !empty($_GET['searchtags']) ? escape($_GET['searchtags']) : '';
1817 $searchterm = !empty($_GET['searchterm']) ? escape(trim($_GET['searchterm'])) : ''; 1804 $searchterm = !empty($_GET['searchterm']) ? escape($_GET['searchterm']) : '';
1818 $privateonly = !empty($_SESSION['privateonly']) ? true : false;
1819
1820 // Search tags + fullsearch.
1821 if (! empty($searchtags) && ! empty($searchterm)) {
1822 $linksToDisplay = $LINKSDB->filter(
1823 LinkFilter::$FILTER_TAG | LinkFilter::$FILTER_TEXT,
1824 array($searchtags, $searchterm),
1825 false,
1826 $privateonly
1827 );
1828 }
1829 // Search by tags.
1830 elseif (! empty($searchtags)) {
1831 $linksToDisplay = $LINKSDB->filter(
1832 LinkFilter::$FILTER_TAG,
1833 $searchtags,
1834 false,
1835 $privateonly
1836 );
1837 }
1838 // Fulltext search.
1839 elseif (! empty($searchterm)) {
1840 $linksToDisplay = $LINKSDB->filter(
1841 LinkFilter::$FILTER_TEXT,
1842 $searchterm,
1843 false,
1844 $privateonly
1845 );
1846 }
1847 // Detect smallHashes in URL.
1848 elseif (! empty($_SERVER['QUERY_STRING'])
1849 && preg_match('/[a-zA-Z0-9-_@]{6}(&.+?)?/', $_SERVER['QUERY_STRING'])
1850 ) {
1851 $linksToDisplay = $LINKSDB->filter(
1852 LinkFilter::$FILTER_HASH,
1853 substr(trim($_SERVER["QUERY_STRING"], '/'), 0, 6)
1854 );
1855 1805
1856 if (count($linksToDisplay) == 0) { 1806 // Smallhash filter
1857 $PAGE->render404('The link you are trying to reach does not exist or has been deleted.'); 1807 if (! empty($_SERVER['QUERY_STRING'])
1808 && preg_match('/^[a-zA-Z0-9-_@]{6}($|&|#)/', $_SERVER['QUERY_STRING'])) {
1809 try {
1810 $linksToDisplay = $LINKSDB->filterHash($_SERVER['QUERY_STRING']);
1811 } catch (LinkNotFoundException $e) {
1812 $PAGE->render404($e->getMessage());
1858 exit; 1813 exit;
1859 } 1814 }
1860 } 1815 } else {
1861 // Otherwise, display without filtering. 1816 // Filter links according search parameters.
1862 else { 1817 $privateonly = !empty($_SESSION['privateonly']);
1863 $linksToDisplay = $LINKSDB->filter('', '', false, $privateonly); 1818 $linksToDisplay = $LINKSDB->filterSearch($_GET, false, $privateonly);
1864 } 1819 }
1865 1820
1866 // ---- Handle paging. 1821 // ---- Handle paging.
diff --git a/tests/LinkDBTest.php b/tests/LinkDBTest.php
index b6a273b3..52d31400 100644
--- a/tests/LinkDBTest.php
+++ b/tests/LinkDBTest.php
@@ -17,8 +17,20 @@ class LinkDBTest extends PHPUnit_Framework_TestCase
17{ 17{
18 // datastore to test write operations 18 // datastore to test write operations
19 protected static $testDatastore = 'sandbox/datastore.php'; 19 protected static $testDatastore = 'sandbox/datastore.php';
20
21 /**
22 * @var ReferenceLinkDB instance.
23 */
20 protected static $refDB = null; 24 protected static $refDB = null;
25
26 /**
27 * @var LinkDB public LinkDB instance.
28 */
21 protected static $publicLinkDB = null; 29 protected static $publicLinkDB = null;
30
31 /**
32 * @var LinkDB private LinkDB instance.
33 */
22 protected static $privateLinkDB = null; 34 protected static $privateLinkDB = null;
23 35
24 /** 36 /**
@@ -335,9 +347,10 @@ class LinkDBTest extends PHPUnit_Framework_TestCase
335 public function testFilterString() 347 public function testFilterString()
336 { 348 {
337 $tags = 'dev cartoon'; 349 $tags = 'dev cartoon';
350 $request = array('searchtags' => $tags);
338 $this->assertEquals( 351 $this->assertEquals(
339 2, 352 2,
340 count(self::$privateLinkDB->filter(LinkFilter::$FILTER_TAG, $tags, true, false)) 353 count(self::$privateLinkDB->filterSearch($request, true, false))
341 ); 354 );
342 } 355 }
343 356
@@ -347,9 +360,10 @@ class LinkDBTest extends PHPUnit_Framework_TestCase
347 public function testFilterArray() 360 public function testFilterArray()
348 { 361 {
349 $tags = array('dev', 'cartoon'); 362 $tags = array('dev', 'cartoon');
363 $request = array('searchtags' => $tags);
350 $this->assertEquals( 364 $this->assertEquals(
351 2, 365 2,
352 count(self::$privateLinkDB->filter(LinkFilter::$FILTER_TAG, $tags, true, false)) 366 count(self::$privateLinkDB->filterSearch($request, true, false))
353 ); 367 );
354 } 368 }
355 369
@@ -360,14 +374,48 @@ class LinkDBTest extends PHPUnit_Framework_TestCase
360 public function testHiddenTags() 374 public function testHiddenTags()
361 { 375 {
362 $tags = '.hidden'; 376 $tags = '.hidden';
377 $request = array('searchtags' => $tags);
363 $this->assertEquals( 378 $this->assertEquals(
364 1, 379 1,
365 count(self::$privateLinkDB->filter(LinkFilter::$FILTER_TAG, $tags, true, false)) 380 count(self::$privateLinkDB->filterSearch($request, true, false))
366 ); 381 );
367 382
368 $this->assertEquals( 383 $this->assertEquals(
369 0, 384 0,
370 count(self::$publicLinkDB->filter(LinkFilter::$FILTER_TAG, $tags, true, false)) 385 count(self::$publicLinkDB->filterSearch($request, true, false))
371 ); 386 );
372 } 387 }
388
389 /**
390 * Test filterHash() with a valid smallhash.
391 */
392 public function testFilterHashValid()
393 {
394 $request = smallHash('20150310_114651');
395 $this->assertEquals(
396 1,
397 count(self::$publicLinkDB->filterHash($request))
398 );
399 }
400
401 /**
402 * Test filterHash() with an invalid smallhash.
403 *
404 * @expectedException LinkNotFoundException
405 */
406 public function testFilterHashInValid1()
407 {
408 $request = 'blabla';
409 self::$publicLinkDB->filterHash($request);
410 }
411
412 /**
413 * Test filterHash() with an empty smallhash.
414 *
415 * @expectedException LinkNotFoundException
416 */
417 public function testFilterHashInValid()
418 {
419 self::$publicLinkDB->filterHash('');
420 }
373} 421}
diff --git a/tests/LinkFilterTest.php b/tests/LinkFilterTest.php
index f991a9c9..1620bb78 100644
--- a/tests/LinkFilterTest.php
+++ b/tests/LinkFilterTest.php
@@ -165,13 +165,12 @@ class LinkFilterTest extends PHPUnit_Framework_TestCase
165 165
166 /** 166 /**
167 * No link for this hash 167 * No link for this hash
168 *
169 * @expectedException LinkNotFoundException
168 */ 170 */
169 public function testFilterUnknownSmallHash() 171 public function testFilterUnknownSmallHash()
170 { 172 {
171 $this->assertEquals( 173 self::$linkFilter->filter(LinkFilter::$FILTER_HASH, 'Iblaah');
172 0,
173 count(self::$linkFilter->filter(LinkFilter::$FILTER_HASH, 'Iblaah'))
174 );
175 } 174 }
176 175
177 /** 176 /**
diff --git a/tests/Updater/UpdaterTest.php b/tests/Updater/UpdaterTest.php
index d865066b..a29d9067 100644
--- a/tests/Updater/UpdaterTest.php
+++ b/tests/Updater/UpdaterTest.php
@@ -236,9 +236,9 @@ class UpdaterTest extends PHPUnit_Framework_TestCase
236 $refDB = new ReferenceLinkDB(); 236 $refDB = new ReferenceLinkDB();
237 $refDB->write(self::$testDatastore); 237 $refDB->write(self::$testDatastore);
238 $linkDB = new LinkDB(self::$testDatastore, true, false); 238 $linkDB = new LinkDB(self::$testDatastore, true, false);
239 $this->assertEmpty($linkDB->filter(LinkFilter::$FILTER_TAG, 'exclude')); 239 $this->assertEmpty($linkDB->filterSearch(array('searchtags' => 'exclude')));
240 $updater = new Updater(array(), self::$configFields, $linkDB, true); 240 $updater = new Updater(array(), self::$configFields, $linkDB, true);
241 $updater->updateMethodRenameDashTags(); 241 $updater->updateMethodRenameDashTags();
242 $this->assertNotEmpty($linkDB->filter(LinkFilter::$FILTER_TAG, 'exclude')); 242 $this->assertNotEmpty($linkDB->filterSearch(array('searchtags' => 'exclude')));
243 } 243 }
244} 244}