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
This commit is contained in:
ArthurHoaro 2016-03-21 21:40:49 +01:00
parent ee88a4bcc2
commit 528a6f8a23
8 changed files with 158 additions and 106 deletions

View File

@ -103,23 +103,7 @@ class FeedBuilder
public function buildData()
{
// Optionally filter the results:
$searchtags = !empty($this->userInput['searchtags']) ? escape($this->userInput['searchtags']) : '';
$searchterm = !empty($this->userInput['searchterm']) ? escape($this->userInput['searchterm']) : '';
if (! empty($searchtags) && ! empty($searchterm)) {
$linksToDisplay = $this->linkDB->filter(
LinkFilter::$FILTER_TAG | LinkFilter::$FILTER_TEXT,
array($searchtags, $searchterm)
);
}
elseif ($searchtags) {
$linksToDisplay = $this->linkDB->filter(LinkFilter::$FILTER_TAG, $searchtags);
}
elseif ($searchterm) {
$linksToDisplay = $this->linkDB->filter(LinkFilter::$FILTER_TEXT, $searchterm);
}
else {
$linksToDisplay = $this->linkDB;
}
$linksToDisplay = $this->linkDB->filterSearch($this->userInput);
$nblinksToDisplay = $this->getNbLinks(count($linksToDisplay));

View File

@ -341,17 +341,71 @@ You use the community supported version of the original Shaarli project, by Seba
}
/**
* Filter links.
* Returns the shaare corresponding to a smallHash.
*
* @param string $type Type of filter.
* @param mixed $request Search request, string or array.
* @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 bool $privateonly Optional: Returns private links only if true.
*
* @return array filtered links
* @return array filtered links, all links if no suitable filter was provided.
*/
public function filter($type = '', $request = '', $casesensitive = false, $privateonly = false)
public function filterSearch($filterRequest = array(), $casesensitive = false, $privateonly = false)
{
// Filter link database according to parameters.
$searchtags = !empty($filterRequest['searchtags']) ? escape($filterRequest['searchtags']) : '';
$searchterm = !empty($filterRequest['searchterm']) ? escape($filterRequest['searchterm']) : '';
// Search tags + fullsearch.
if (empty($type) && ! empty($searchtags) && ! empty($searchterm)) {
$type = LinkFilter::$FILTER_TAG | LinkFilter::$FILTER_TEXT;
$request = array($searchtags, $searchterm);
}
// Search by tags.
elseif (! empty($searchtags)) {
$type = LinkFilter::$FILTER_TAG;
$request = $searchtags;
}
// Fulltext search.
elseif (! empty($searchterm)) {
$type = LinkFilter::$FILTER_TEXT;
$request = $searchterm;
}
// Otherwise, display without filtering.
else {
$type = '';
$request = '';
}
$linkFilter = new LinkFilter($this->_links);
return $linkFilter->filter($type, $request, $casesensitive, $privateonly);
}

View File

@ -44,7 +44,7 @@ class LinkFilter
* Filter links according to parameters.
*
* @param string $type Type of filter (eg. tags, permalink, etc.).
* @param string $request Filter content.
* @param mixed $request Filter content.
* @param bool $casesensitive Optional: Perform case sensitive filter if true.
* @param bool $privateonly Optional: Only returns private links if true.
*
@ -110,6 +110,8 @@ class LinkFilter
* @param string $smallHash permalink hash.
*
* @return array $filtered array containing permalink data.
*
* @throws LinkNotFoundException if the smallhash doesn't match any link.
*/
private function filterSmallHash($smallHash)
{
@ -121,6 +123,11 @@ class LinkFilter
return $filtered;
}
}
if (empty($filtered)) {
throw new LinkNotFoundException();
}
return $filtered;
}
@ -318,3 +325,8 @@ class LinkFilter
return array_filter(explode(' ', trim($tagsOut)), 'strlen');
}
}
class LinkNotFoundException extends Exception
{
protected $message = 'The link you are trying to reach does not exist or has been deleted.';
}

View File

@ -137,7 +137,7 @@ class Updater
*/
public function updateMethodRenameDashTags()
{
$linklist = $this->linkDB->filter();
$linklist = $this->linkDB->filterSearch();
foreach ($linklist as $link) {
$link['tags'] = preg_replace('/(^| )\-/', '$1', $link['tags']);
$link['tags'] = implode(' ', array_unique(LinkFilter::tagsStrToArray($link['tags'], true)));

View File

@ -816,7 +816,7 @@ function showDaily($pageBuilder)
}
try {
$linksToDisplay = $LINKSDB->filter(LinkFilter::$FILTER_DAY, $day);
$linksToDisplay = $LINKSDB->filterDay($day);
} catch (Exception $exc) {
error_log($exc);
$linksToDisplay = array();
@ -962,24 +962,7 @@ function renderPage()
if ($targetPage == Router::$PAGE_PICWALL)
{
// Optionally filter the results:
$searchtags = !empty($_GET['searchtags']) ? escape($_GET['searchtags']) : '';
$searchterm = !empty($_GET['searchterm']) ? escape($_GET['searchterm']) : '';
if (! empty($searchtags) && ! empty($searchterm)) {
$links = $LINKSDB->filter(
LinkFilter::$FILTER_TAG | LinkFilter::$FILTER_TEXT,
array($searchtags, $searchterm)
);
}
elseif ($searchtags) {
$links = $LINKSDB->filter(LinkFilter::$FILTER_TAG, $searchtags);
}
elseif ($searchterm) {
$links = $LINKSDB->filter(LinkFilter::$FILTER_TEXT, $searchterm);
}
else {
$links = $LINKSDB;
}
$links = $LINKSDB->filterSearch($_GET);
$linksToDisplay = array();
// Get only links which have a thumbnail.
@ -1071,7 +1054,7 @@ function renderPage()
startsWith($query,'do='. $targetPage) && !isLoggedIn()
);
$cached = $cache->cachedVersion();
if (!empty($cached)) {
if (false && !empty($cached)) {
echo $cached;
exit;
}
@ -1352,9 +1335,9 @@ function renderPage()
// Delete a tag:
if (isset($_POST['deletetag']) && !empty($_POST['fromtag'])) {
$needle=trim($_POST['fromtag']);
$needle = trim($_POST['fromtag']);
// True for case-sensitive tag search.
$linksToAlter = $LINKSDB->filter(LinkFilter::$FILTER_TAG, $needle, true);
$linksToAlter = $LINKSDB->filterSearch(array('searchtags' => $needle), true);
foreach($linksToAlter as $key=>$value)
{
$tags = explode(' ',trim($value['tags']));
@ -1369,9 +1352,9 @@ function renderPage()
// Rename a tag:
if (isset($_POST['renametag']) && !empty($_POST['fromtag']) && !empty($_POST['totag'])) {
$needle=trim($_POST['fromtag']);
$needle = trim($_POST['fromtag']);
// True for case-sensitive tag search.
$linksToAlter = $LINKSDB->filter(LinkFilter::$FILTER_TAG, $needle, true);
$linksToAlter = $LINKSDB->filterSearch(array('searchtags' => $needle), true);
foreach($linksToAlter as $key=>$value)
{
$tags = explode(' ',trim($value['tags']));
@ -1807,60 +1790,32 @@ function importFile()
}
}
// -----------------------------------------------------------------------------------------------
// Template for the list of links (<div id="linklist">)
// This function fills all the necessary fields in the $PAGE for the template 'linklist.html'
/**
* Template for the list of links (<div id="linklist">)
* This function fills all the necessary fields in the $PAGE for the template 'linklist.html'
*
* @param pageBuilder $PAGE pageBuilder instance.
* @param LinkDB $LINKSDB LinkDB instance.
*/
function buildLinkList($PAGE,$LINKSDB)
{
// Filter link database according to parameters.
// Used in templates
$searchtags = !empty($_GET['searchtags']) ? escape($_GET['searchtags']) : '';
$searchterm = !empty($_GET['searchterm']) ? escape(trim($_GET['searchterm'])) : '';
$privateonly = !empty($_SESSION['privateonly']) ? true : false;
$searchterm = !empty($_GET['searchterm']) ? escape($_GET['searchterm']) : '';
// Search tags + fullsearch.
if (! empty($searchtags) && ! empty($searchterm)) {
$linksToDisplay = $LINKSDB->filter(
LinkFilter::$FILTER_TAG | LinkFilter::$FILTER_TEXT,
array($searchtags, $searchterm),
false,
$privateonly
);
}
// Search by tags.
elseif (! empty($searchtags)) {
$linksToDisplay = $LINKSDB->filter(
LinkFilter::$FILTER_TAG,
$searchtags,
false,
$privateonly
);
}
// Fulltext search.
elseif (! empty($searchterm)) {
$linksToDisplay = $LINKSDB->filter(
LinkFilter::$FILTER_TEXT,
$searchterm,
false,
$privateonly
);
}
// Detect smallHashes in URL.
elseif (! empty($_SERVER['QUERY_STRING'])
&& preg_match('/[a-zA-Z0-9-_@]{6}(&.+?)?/', $_SERVER['QUERY_STRING'])
) {
$linksToDisplay = $LINKSDB->filter(
LinkFilter::$FILTER_HASH,
substr(trim($_SERVER["QUERY_STRING"], '/'), 0, 6)
);
if (count($linksToDisplay) == 0) {
$PAGE->render404('The link you are trying to reach does not exist or has been deleted.');
// Smallhash filter
if (! empty($_SERVER['QUERY_STRING'])
&& preg_match('/^[a-zA-Z0-9-_@]{6}($|&|#)/', $_SERVER['QUERY_STRING'])) {
try {
$linksToDisplay = $LINKSDB->filterHash($_SERVER['QUERY_STRING']);
} catch (LinkNotFoundException $e) {
$PAGE->render404($e->getMessage());
exit;
}
}
// Otherwise, display without filtering.
else {
$linksToDisplay = $LINKSDB->filter('', '', false, $privateonly);
} else {
// Filter links according search parameters.
$privateonly = !empty($_SESSION['privateonly']);
$linksToDisplay = $LINKSDB->filterSearch($_GET, false, $privateonly);
}
// ---- Handle paging.

View File

@ -17,8 +17,20 @@ class LinkDBTest extends PHPUnit_Framework_TestCase
{
// datastore to test write operations
protected static $testDatastore = 'sandbox/datastore.php';
/**
* @var ReferenceLinkDB instance.
*/
protected static $refDB = null;
/**
* @var LinkDB public LinkDB instance.
*/
protected static $publicLinkDB = null;
/**
* @var LinkDB private LinkDB instance.
*/
protected static $privateLinkDB = null;
/**
@ -335,9 +347,10 @@ class LinkDBTest extends PHPUnit_Framework_TestCase
public function testFilterString()
{
$tags = 'dev cartoon';
$request = array('searchtags' => $tags);
$this->assertEquals(
2,
count(self::$privateLinkDB->filter(LinkFilter::$FILTER_TAG, $tags, true, false))
count(self::$privateLinkDB->filterSearch($request, true, false))
);
}
@ -347,9 +360,10 @@ class LinkDBTest extends PHPUnit_Framework_TestCase
public function testFilterArray()
{
$tags = array('dev', 'cartoon');
$request = array('searchtags' => $tags);
$this->assertEquals(
2,
count(self::$privateLinkDB->filter(LinkFilter::$FILTER_TAG, $tags, true, false))
count(self::$privateLinkDB->filterSearch($request, true, false))
);
}
@ -360,14 +374,48 @@ class LinkDBTest extends PHPUnit_Framework_TestCase
public function testHiddenTags()
{
$tags = '.hidden';
$request = array('searchtags' => $tags);
$this->assertEquals(
1,
count(self::$privateLinkDB->filter(LinkFilter::$FILTER_TAG, $tags, true, false))
count(self::$privateLinkDB->filterSearch($request, true, false))
);
$this->assertEquals(
0,
count(self::$publicLinkDB->filter(LinkFilter::$FILTER_TAG, $tags, true, false))
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))
);
}
/**
* 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('');
}
}

View File

@ -165,13 +165,12 @@ class LinkFilterTest extends PHPUnit_Framework_TestCase
/**
* No link for this hash
*
* @expectedException LinkNotFoundException
*/
public function testFilterUnknownSmallHash()
{
$this->assertEquals(
0,
count(self::$linkFilter->filter(LinkFilter::$FILTER_HASH, 'Iblaah'))
);
self::$linkFilter->filter(LinkFilter::$FILTER_HASH, 'Iblaah');
}
/**

View File

@ -236,9 +236,9 @@ class UpdaterTest extends PHPUnit_Framework_TestCase
$refDB = new ReferenceLinkDB();
$refDB->write(self::$testDatastore);
$linkDB = new LinkDB(self::$testDatastore, true, false);
$this->assertEmpty($linkDB->filter(LinkFilter::$FILTER_TAG, 'exclude'));
$this->assertEmpty($linkDB->filterSearch(array('searchtags' => 'exclude')));
$updater = new Updater(array(), self::$configFields, $linkDB, true);
$updater->updateMethodRenameDashTags();
$this->assertNotEmpty($linkDB->filter(LinkFilter::$FILTER_TAG, 'exclude'));
$this->assertNotEmpty($linkDB->filterSearch(array('searchtags' => 'exclude')));
}
}