Empty tag search will look for not tagged links

Fixes #784

From now, searching for tags with an empty value will return only not tagged links,
with the search bar showing `x results [not tagged]`.

Note that using the api, the searchtags request parameter must be set to `false` to get the same result.

  - [ ] Update API doc
This commit is contained in:
ArthurHoaro 2017-04-01 12:17:37 +02:00
parent b64d83cd2b
commit 7d86f40bdb
12 changed files with 115 additions and 36 deletions

View file

@ -97,6 +97,11 @@ public function __construct($linkDB, $feedType, $serverInfo, $userInput, $isLogg
*/ */
public function buildData() public function buildData()
{ {
// Search for untagged links
if (isset($this->userInput['searchtags']) && empty($this->userInput['searchtags'])) {
$this->userInput['searchtags'] = false;
}
// Optionally filter the results: // Optionally filter the results:
$linksToDisplay = $this->linkDB->filterSearch($this->userInput); $linksToDisplay = $this->linkDB->filterSearch($this->userInput);

View file

@ -450,29 +450,12 @@ public function filterDay($request) {
public function filterSearch($filterRequest = array(), $casesensitive = false, $visibility = 'all') public function filterSearch($filterRequest = array(), $casesensitive = false, $visibility = 'all')
{ {
// Filter link database according to parameters. // Filter link database according to parameters.
$searchtags = !empty($filterRequest['searchtags']) ? escape($filterRequest['searchtags']) : ''; $searchtags = isset($filterRequest['searchtags']) ? escape($filterRequest['searchtags']) : '';
$searchterm = !empty($filterRequest['searchterm']) ? escape($filterRequest['searchterm']) : ''; $searchterm = isset($filterRequest['searchterm']) ? escape($filterRequest['searchterm']) : '';
// Search tags + fullsearch. // Search tags + fullsearch - blank string parameter will return all links.
if (! empty($searchtags) && ! empty($searchterm)) { $type = LinkFilter::$FILTER_TAG | LinkFilter::$FILTER_TEXT;
$type = LinkFilter::$FILTER_TAG | LinkFilter::$FILTER_TEXT; $request = [$searchtags, $searchterm];
$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); $linkFilter = new LinkFilter($this);
return $linkFilter->filter($type, $request, $casesensitive, $visibility); return $linkFilter->filter($type, $request, $casesensitive, $visibility);

View file

@ -253,6 +253,9 @@ public function filterTags($tags, $casesensitive = false, $visibility = 'all')
{ {
// Implode if array for clean up. // Implode if array for clean up.
$tags = is_array($tags) ? trim(implode(' ', $tags)) : $tags; $tags = is_array($tags) ? trim(implode(' ', $tags)) : $tags;
if ($tags === false) {
return $this->filterUntagged($visibility);
}
if (empty($tags)) { if (empty($tags)) {
return $this->noFilter($visibility); return $this->noFilter($visibility);
} }
@ -295,6 +298,33 @@ public function filterTags($tags, $casesensitive = false, $visibility = 'all')
return $filtered; return $filtered;
} }
/**
* Return only links without any tag.
*
* @param string $visibility return only all/private/public links.
*
* @return array filtered links.
*/
public function filterUntagged($visibility)
{
$filtered = [];
foreach ($this->links as $key => $link) {
if ($visibility !== 'all') {
if (! $link['private'] && $visibility === 'private') {
continue;
} else if ($link['private'] && $visibility === 'public') {
continue;
}
}
if (empty(trim($link['tags']))) {
$filtered[$key] = $link;
}
}
return $filtered;
}
/** /**
* Returns the list of articles for a given day, chronologically sorted * Returns the list of articles for a given day, chronologically sorted
* *

View file

@ -91,6 +91,10 @@ function endsWith($haystack, $needle, $case = true)
*/ */
function escape($input) function escape($input)
{ {
if (is_bool($input)) {
return $input;
}
if (is_array($input)) { if (is_array($input)) {
$out = array(); $out = array();
foreach($input as $key => $value) { foreach($input as $key => $value) {

View file

@ -1609,7 +1609,15 @@ function($a, $b) { return $a['order'] - $b['order']; }
function buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager) function buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager)
{ {
// Used in templates // Used in templates
$searchtags = !empty($_GET['searchtags']) ? escape(normalize_spaces($_GET['searchtags'])) : ''; if (isset($_GET['searchtags'])) {
if (! empty($_GET['searchtags'])) {
$searchtags = escape(normalize_spaces($_GET['searchtags']));
} else {
$searchtags = false;
}
} else {
$searchtags = '';
}
$searchterm = !empty($_GET['searchterm']) ? escape(normalize_spaces($_GET['searchterm'])) : ''; $searchterm = !empty($_GET['searchterm']) ? escape(normalize_spaces($_GET['searchterm'])) : '';
// Smallhash filter // Smallhash filter
@ -1624,7 +1632,11 @@ function buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager)
} else { } else {
// Filter links according search parameters. // Filter links according search parameters.
$visibility = ! empty($_SESSION['privateonly']) ? 'private' : 'all'; $visibility = ! empty($_SESSION['privateonly']) ? 'private' : 'all';
$linksToDisplay = $LINKSDB->filterSearch($_GET, false, $visibility); $request = [
'searchtags' => $searchtags,
'searchterm' => $searchterm,
];
$linksToDisplay = $LINKSDB->filterSearch($request, false, $visibility);
} }
// ---- Handle paging. // ---- Handle paging.
@ -1671,7 +1683,7 @@ function buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager)
} }
// Compute paging navigation // Compute paging navigation
$searchtagsUrl = empty($searchtags) ? '' : '&searchtags=' . urlencode($searchtags); $searchtagsUrl = $searchtags === '' ? '' : '&searchtags=' . urlencode($searchtags);
$searchtermUrl = empty($searchterm) ? '' : '&searchterm=' . urlencode($searchterm); $searchtermUrl = empty($searchterm) ? '' : '&searchterm=' . urlencode($searchterm);
$previous_page_url = ''; $previous_page_url = '';
if ($i != count($keys)) { if ($i != count($keys)) {

View file

@ -448,7 +448,7 @@ public function testFilterHashInValid()
public function testReorderLinksDesc() public function testReorderLinksDesc()
{ {
self::$privateLinkDB->reorder('ASC'); self::$privateLinkDB->reorder('ASC');
$linkIds = array(42, 4, 1, 0, 7, 6, 8, 41); $linkIds = array(42, 4, 9, 1, 0, 7, 6, 8, 41);
$cpt = 0; $cpt = 0;
foreach (self::$privateLinkDB as $key => $value) { foreach (self::$privateLinkDB as $key => $value) {
$this->assertEquals($linkIds[$cpt++], $key); $this->assertEquals($linkIds[$cpt++], $key);

View file

@ -63,6 +63,12 @@ public function testFilter()
count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, '')) count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, ''))
); );
// Untagged only
$this->assertEquals(
self::$refDB->countUntaggedLinks(),
count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, false))
);
$this->assertEquals( $this->assertEquals(
ReferenceLinkDB::$NB_LINKS_TOTAL, ReferenceLinkDB::$NB_LINKS_TOTAL,
count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, '')) count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, ''))
@ -146,7 +152,7 @@ public function testFilterUnknownTag()
public function testFilterDay() public function testFilterDay()
{ {
$this->assertEquals( $this->assertEquals(
3, 4,
count(self::$linkFilter->filter(LinkFilter::$FILTER_DAY, '20121206')) count(self::$linkFilter->filter(LinkFilter::$FILTER_DAY, '20121206'))
); );
} }
@ -339,7 +345,7 @@ public function testExcludeSearch()
); );
$this->assertEquals( $this->assertEquals(
7, ReferenceLinkDB::$NB_LINKS_TOTAL - 1,
count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, '-revolution')) count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, '-revolution'))
); );
} }
@ -399,7 +405,7 @@ public function testTagFilterWithExclusion()
); );
$this->assertEquals( $this->assertEquals(
7, ReferenceLinkDB::$NB_LINKS_TOTAL - 1,
count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, '-free')) count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, '-free'))
); );
} }
@ -425,6 +431,13 @@ public function testFilterCrossedSearch()
array('', $terms) array('', $terms)
)) ))
); );
$this->assertEquals(
1,
count(self::$linkFilter->filter(
LinkFilter::$FILTER_TAG | LinkFilter::$FILTER_TEXT,
array(false, 'PSR-2')
))
);
$this->assertEquals( $this->assertEquals(
1, 1,
count(self::$linkFilter->filter( count(self::$linkFilter->filter(

View file

@ -94,7 +94,7 @@ public function testGetLinks()
$this->assertEquals($this->refDB->countLinks(), count($data)); $this->assertEquals($this->refDB->countLinks(), count($data));
// Check order // Check order
$order = [41, 8, 6, 7, 0, 1, 4, 42]; $order = [41, 8, 6, 7, 0, 1, 9, 4, 42];
$cpt = 0; $cpt = 0;
foreach ($data as $link) { foreach ($data as $link) {
$this->assertEquals(self::NB_FIELDS_LINK, count($link)); $this->assertEquals(self::NB_FIELDS_LINK, count($link));
@ -163,7 +163,7 @@ public function testGetLinksLimitAll()
$data = json_decode((string) $response->getBody(), true); $data = json_decode((string) $response->getBody(), true);
$this->assertEquals($this->refDB->countLinks(), count($data)); $this->assertEquals($this->refDB->countLinks(), count($data));
// Check order // Check order
$order = [41, 8, 6, 7, 0, 1, 4, 42]; $order = [41, 8, 6, 7, 0, 1, 9, 4, 42];
$cpt = 0; $cpt = 0;
foreach ($data as $link) { foreach ($data as $link) {
$this->assertEquals(self::NB_FIELDS_LINK, count($link)); $this->assertEquals(self::NB_FIELDS_LINK, count($link));

View file

@ -80,7 +80,7 @@ public function testGetInfo()
$this->assertEquals(200, $response->getStatusCode()); $this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true); $data = json_decode((string) $response->getBody(), true);
$this->assertEquals(8, $data['global_counter']); $this->assertEquals(\ReferenceLinkDB::$NB_LINKS_TOTAL, $data['global_counter']);
$this->assertEquals(2, $data['private_counter']); $this->assertEquals(2, $data['private_counter']);
$this->assertEquals('Shaarli', $data['settings']['title']); $this->assertEquals('Shaarli', $data['settings']['title']);
$this->assertEquals('?', $data['settings']['header_link']); $this->assertEquals('?', $data['settings']['header_link']);
@ -103,7 +103,7 @@ public function testGetInfo()
$this->assertEquals(200, $response->getStatusCode()); $this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true); $data = json_decode((string) $response->getBody(), true);
$this->assertEquals(8, $data['global_counter']); $this->assertEquals(\ReferenceLinkDB::$NB_LINKS_TOTAL, $data['global_counter']);
$this->assertEquals(2, $data['private_counter']); $this->assertEquals(2, $data['private_counter']);
$this->assertEquals($title, $data['settings']['title']); $this->assertEquals($title, $data['settings']['title']);
$this->assertEquals($headerLink, $data['settings']['header_link']); $this->assertEquals($headerLink, $data['settings']['header_link']);

View file

@ -4,7 +4,7 @@
*/ */
class ReferenceLinkDB class ReferenceLinkDB
{ {
public static $NB_LINKS_TOTAL = 8; public static $NB_LINKS_TOTAL = 9;
private $_links = array(); private $_links = array();
private $_publicCount = 0; private $_publicCount = 0;
@ -37,6 +37,16 @@ public function __construct()
'ut' 'ut'
); );
$this->addLink(
9,
'PSR-2: Coding Style Guide',
'http://www.php-fig.org/psr/psr-2/',
'This guide extends and expands on PSR-1, the basic coding standard.',
0,
DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_152312'),
''
);
$this->addLink( $this->addLink(
8, 8,
'Free as in Freedom 2.0 @website', 'Free as in Freedom 2.0 @website',
@ -161,6 +171,20 @@ public function countPrivateLinks()
return $this->_privateCount; return $this->_privateCount;
} }
/**
* Returns the number of links without tag
*/
public function countUntaggedLinks()
{
$cpt = 0;
foreach ($this->_links as $link) {
if (empty($link['tags'])) {
++$cpt;
}
}
return $cpt;
}
public function getLinks() public function getLinks()
{ {
return $this->_links; return $this->_links;

View file

@ -89,7 +89,7 @@
<div id="searchcriteria">{'Nothing found.'|t}</div> <div id="searchcriteria">{'Nothing found.'|t}</div>
</div> </div>
</div> </div>
{elseif="!empty($search_term) or !empty($search_tags) or !empty($visibility)"} {elseif="!empty($search_term) or $search_tags !== '' or !empty($visibility)"}
<div class="pure-g pure-alert pure-alert-success search-result"> <div class="pure-g pure-alert pure-alert-success search-result">
<div class="pure-u-2-24"></div> <div class="pure-u-2-24"></div>
<div class="pure-u-20-24"> <div class="pure-u-20-24">
@ -105,6 +105,10 @@
<a href="?removetag={function="urlencode($value)"}">{$value}<span class="remove"><i class="fa fa-times"></i></span></a> <a href="?removetag={function="urlencode($value)"}">{$value}<span class="remove"><i class="fa fa-times"></i></span></a>
</span> </span>
{/loop} {/loop}
{elseif="$search_tags === false"}
<span class="label label-tag" title="{'Remove tag'|t}">
<a href="?">{'untagged'|t}<span class="remove"><i class="fa fa-times"></i></span></a>
</span>
{/if} {/if}
{if="!empty($visibility)"} {if="!empty($visibility)"}
{'with status'|t} {'with status'|t}

View file

@ -55,7 +55,7 @@
{if="count($links)==0"} {if="count($links)==0"}
<div id="searchcriteria">Nothing found.</div> <div id="searchcriteria">Nothing found.</div>
{elseif="!empty($search_term) or !empty($search_tags)"} {elseif="!empty($search_term) or $search_tags !== ''"}
<div id="searchcriteria"> <div id="searchcriteria">
{$result_count} results {$result_count} results
{if="!empty($search_term)"} {if="!empty($search_term)"}
@ -69,6 +69,10 @@
<a href="?removetag={function="urlencode($value)"}">{$value} <span class="remove">x</span></a> <a href="?removetag={function="urlencode($value)"}">{$value} <span class="remove">x</span></a>
</span> </span>
{/loop} {/loop}
{elseif="$search_tags === false"}
<span class="linktag" title="Remove tag">
<a href="?">untagged <span class="remove">x</span></a>
</span>
{/if} {/if}
</div> </div>
{/if} {/if}