Merge pull request #424 from ArthurHoaro/search
Link filter refactoring
This commit is contained in:
commit
88c15abb2a
8 changed files with 683 additions and 419 deletions
|
@ -62,6 +62,11 @@ class LinkDB implements Iterator, Countable, ArrayAccess
|
|||
// link redirector set in user settings.
|
||||
private $_redirector;
|
||||
|
||||
/**
|
||||
* @var LinkFilter instance.
|
||||
*/
|
||||
private $linkFilter;
|
||||
|
||||
/**
|
||||
* Creates a new LinkDB
|
||||
*
|
||||
|
@ -80,6 +85,7 @@ function __construct($datastore, $isLoggedIn, $hidePublicLinks, $redirector = ''
|
|||
$this->_redirector = $redirector;
|
||||
$this->_checkDB();
|
||||
$this->_readDB();
|
||||
$this->linkFilter = new LinkFilter($this->_links);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -334,114 +340,18 @@ public function getLinkFromUrl($url)
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the list of links corresponding to a full-text search
|
||||
* Filter links.
|
||||
*
|
||||
* Searches:
|
||||
* - in the URLs, title and description;
|
||||
* - are case-insensitive.
|
||||
* @param string $type Type of filter.
|
||||
* @param mixed $request Search request, string or array.
|
||||
* @param bool $casesensitive Optional: Perform case sensitive filter
|
||||
* @param bool $privateonly Optional: Returns private links only if true.
|
||||
*
|
||||
* Example:
|
||||
* print_r($mydb->filterFulltext('hollandais'));
|
||||
*
|
||||
* mb_convert_case($val, MB_CASE_LOWER, 'UTF-8')
|
||||
* - allows to perform searches on Unicode text
|
||||
* - see https://github.com/shaarli/Shaarli/issues/75 for examples
|
||||
* @return array filtered links
|
||||
*/
|
||||
public function filterFulltext($searchterms)
|
||||
{
|
||||
// FIXME: explode(' ',$searchterms) and perform a AND search.
|
||||
// FIXME: accept double-quotes to search for a string "as is"?
|
||||
$filtered = array();
|
||||
$search = mb_convert_case($searchterms, MB_CASE_LOWER, 'UTF-8');
|
||||
$keys = array('title', 'description', 'url', 'tags');
|
||||
|
||||
foreach ($this->_links as $link) {
|
||||
$found = false;
|
||||
|
||||
foreach ($keys as $key) {
|
||||
if (strpos(mb_convert_case($link[$key], MB_CASE_LOWER, 'UTF-8'),
|
||||
$search) !== false) {
|
||||
$found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($found) {
|
||||
$filtered[$link['linkdate']] = $link;
|
||||
}
|
||||
}
|
||||
krsort($filtered);
|
||||
return $filtered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of links associated with a given list of tags
|
||||
*
|
||||
* You can specify one or more tags, separated by space or a comma, e.g.
|
||||
* print_r($mydb->filterTags('linux programming'));
|
||||
*/
|
||||
public function filterTags($tags, $casesensitive=false)
|
||||
{
|
||||
// Same as above, we use UTF-8 conversion to handle various graphemes (i.e. cyrillic, or greek)
|
||||
// FIXME: is $casesensitive ever true?
|
||||
$t = str_replace(
|
||||
',', ' ',
|
||||
($casesensitive ? $tags : mb_convert_case($tags, MB_CASE_LOWER, 'UTF-8'))
|
||||
);
|
||||
|
||||
$searchtags = explode(' ', $t);
|
||||
$filtered = array();
|
||||
|
||||
foreach ($this->_links as $l) {
|
||||
$linktags = explode(
|
||||
' ',
|
||||
($casesensitive ? $l['tags']:mb_convert_case($l['tags'], MB_CASE_LOWER, 'UTF-8'))
|
||||
);
|
||||
|
||||
if (count(array_intersect($linktags, $searchtags)) == count($searchtags)) {
|
||||
$filtered[$l['linkdate']] = $l;
|
||||
}
|
||||
}
|
||||
krsort($filtered);
|
||||
return $filtered;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the list of articles for a given day, chronologically sorted
|
||||
*
|
||||
* Day must be in the form 'YYYYMMDD' (e.g. '20120125'), e.g.
|
||||
* print_r($mydb->filterDay('20120125'));
|
||||
*/
|
||||
public function filterDay($day)
|
||||
{
|
||||
if (! checkDateFormat('Ymd', $day)) {
|
||||
throw new Exception('Invalid date format');
|
||||
}
|
||||
|
||||
$filtered = array();
|
||||
foreach ($this->_links as $l) {
|
||||
if (startsWith($l['linkdate'], $day)) {
|
||||
$filtered[$l['linkdate']] = $l;
|
||||
}
|
||||
}
|
||||
ksort($filtered);
|
||||
return $filtered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the article corresponding to a smallHash
|
||||
*/
|
||||
public function filterSmallHash($smallHash)
|
||||
{
|
||||
$filtered = array();
|
||||
foreach ($this->_links as $l) {
|
||||
if ($smallHash == smallHash($l['linkdate'])) {
|
||||
// Yes, this is ugly and slow
|
||||
$filtered[$l['linkdate']] = $l;
|
||||
return $filtered;
|
||||
}
|
||||
}
|
||||
return $filtered;
|
||||
public function filter($type, $request, $casesensitive = false, $privateonly = false) {
|
||||
$requestFilter = is_array($request) ? implode(' ', $request) : $request;
|
||||
return $this->linkFilter->filter($type, trim($requestFilter), $casesensitive, $privateonly);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
259
application/LinkFilter.php
Normal file
259
application/LinkFilter.php
Normal file
|
@ -0,0 +1,259 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Class LinkFilter.
|
||||
*
|
||||
* Perform search and filter operation on link data list.
|
||||
*/
|
||||
class LinkFilter
|
||||
{
|
||||
/**
|
||||
* @var string permalinks.
|
||||
*/
|
||||
public static $FILTER_HASH = 'permalink';
|
||||
|
||||
/**
|
||||
* @var string text search.
|
||||
*/
|
||||
public static $FILTER_TEXT = 'fulltext';
|
||||
|
||||
/**
|
||||
* @var string tag filter.
|
||||
*/
|
||||
public static $FILTER_TAG = 'tags';
|
||||
|
||||
/**
|
||||
* @var string filter by day.
|
||||
*/
|
||||
public static $FILTER_DAY = 'FILTER_DAY';
|
||||
|
||||
/**
|
||||
* @var array all available links.
|
||||
*/
|
||||
private $links;
|
||||
|
||||
/**
|
||||
* @param array $links initialization.
|
||||
*/
|
||||
public function __construct($links)
|
||||
{
|
||||
$this->links = $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter links according to parameters.
|
||||
*
|
||||
* @param string $type Type of filter (eg. tags, permalink, etc.).
|
||||
* @param string $request Filter content.
|
||||
* @param bool $casesensitive Optional: Perform case sensitive filter if true.
|
||||
* @param bool $privateonly Optional: Only returns private links if true.
|
||||
*
|
||||
* @return array filtered link list.
|
||||
*/
|
||||
public function filter($type, $request, $casesensitive = false, $privateonly = false)
|
||||
{
|
||||
switch($type) {
|
||||
case self::$FILTER_HASH:
|
||||
return $this->filterSmallHash($request);
|
||||
break;
|
||||
case self::$FILTER_TEXT:
|
||||
return $this->filterFulltext($request, $privateonly);
|
||||
break;
|
||||
case self::$FILTER_TAG:
|
||||
return $this->filterTags($request, $casesensitive, $privateonly);
|
||||
break;
|
||||
case self::$FILTER_DAY:
|
||||
return $this->filterDay($request);
|
||||
break;
|
||||
default:
|
||||
return $this->noFilter($privateonly);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unknown filter, but handle private only.
|
||||
*
|
||||
* @param bool $privateonly returns private link only if true.
|
||||
*
|
||||
* @return array filtered links.
|
||||
*/
|
||||
private function noFilter($privateonly = false)
|
||||
{
|
||||
if (! $privateonly) {
|
||||
krsort($this->links);
|
||||
return $this->links;
|
||||
}
|
||||
|
||||
$out = array();
|
||||
foreach ($this->links as $value) {
|
||||
if ($value['private']) {
|
||||
$out[$value['linkdate']] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
krsort($out);
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the shaare corresponding to a smallHash.
|
||||
*
|
||||
* @param string $smallHash permalink hash.
|
||||
*
|
||||
* @return array $filtered array containing permalink data.
|
||||
*/
|
||||
private function filterSmallHash($smallHash)
|
||||
{
|
||||
$filtered = array();
|
||||
foreach ($this->links as $l) {
|
||||
if ($smallHash == smallHash($l['linkdate'])) {
|
||||
// Yes, this is ugly and slow
|
||||
$filtered[$l['linkdate']] = $l;
|
||||
return $filtered;
|
||||
}
|
||||
}
|
||||
return $filtered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of links corresponding to a full-text search
|
||||
*
|
||||
* Searches:
|
||||
* - in the URLs, title and description;
|
||||
* - are case-insensitive.
|
||||
*
|
||||
* Example:
|
||||
* print_r($mydb->filterFulltext('hollandais'));
|
||||
*
|
||||
* mb_convert_case($val, MB_CASE_LOWER, 'UTF-8')
|
||||
* - allows to perform searches on Unicode text
|
||||
* - see https://github.com/shaarli/Shaarli/issues/75 for examples
|
||||
*
|
||||
* @param string $searchterms search query.
|
||||
* @param bool $privateonly return only private links if true.
|
||||
*
|
||||
* @return array search results.
|
||||
*/
|
||||
private function filterFulltext($searchterms, $privateonly = false)
|
||||
{
|
||||
// FIXME: explode(' ',$searchterms) and perform a AND search.
|
||||
// FIXME: accept double-quotes to search for a string "as is"?
|
||||
$filtered = array();
|
||||
$search = mb_convert_case($searchterms, MB_CASE_LOWER, 'UTF-8');
|
||||
$explodedSearch = explode(' ', trim($search));
|
||||
$keys = array('title', 'description', 'url', 'tags');
|
||||
|
||||
// Iterate over every stored link.
|
||||
foreach ($this->links as $link) {
|
||||
$found = false;
|
||||
|
||||
// ignore non private links when 'privatonly' is on.
|
||||
if (! $link['private'] && $privateonly === true) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Iterate over searchable link fields.
|
||||
foreach ($keys as $key) {
|
||||
// Search full expression.
|
||||
if (strpos(
|
||||
mb_convert_case($link[$key], MB_CASE_LOWER, 'UTF-8'),
|
||||
$search
|
||||
) !== false) {
|
||||
$found = true;
|
||||
}
|
||||
|
||||
if ($found) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($found) {
|
||||
$filtered[$link['linkdate']] = $link;
|
||||
}
|
||||
}
|
||||
|
||||
krsort($filtered);
|
||||
return $filtered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of links associated with a given list of tags
|
||||
*
|
||||
* You can specify one or more tags, separated by space or a comma, e.g.
|
||||
* print_r($mydb->filterTags('linux programming'));
|
||||
*
|
||||
* @param string $tags list of tags separated by commas or blank spaces.
|
||||
* @param bool $casesensitive ignore case if false.
|
||||
* @param bool $privateonly returns private links only.
|
||||
*
|
||||
* @return array filtered links.
|
||||
*/
|
||||
public function filterTags($tags, $casesensitive = false, $privateonly = false)
|
||||
{
|
||||
$searchtags = $this->tagsStrToArray($tags, $casesensitive);
|
||||
$filtered = array();
|
||||
|
||||
foreach ($this->links as $l) {
|
||||
// ignore non private links when 'privatonly' is on.
|
||||
if (! $l['private'] && $privateonly === true) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$linktags = $this->tagsStrToArray($l['tags'], $casesensitive);
|
||||
|
||||
if (count(array_intersect($linktags, $searchtags)) == count($searchtags)) {
|
||||
$filtered[$l['linkdate']] = $l;
|
||||
}
|
||||
}
|
||||
krsort($filtered);
|
||||
return $filtered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of articles for a given day, chronologically sorted
|
||||
*
|
||||
* Day must be in the form 'YYYYMMDD' (e.g. '20120125'), e.g.
|
||||
* print_r($mydb->filterDay('20120125'));
|
||||
*
|
||||
* @param string $day day to filter.
|
||||
*
|
||||
* @return array all link matching given day.
|
||||
*
|
||||
* @throws Exception if date format is invalid.
|
||||
*/
|
||||
public function filterDay($day)
|
||||
{
|
||||
if (! checkDateFormat('Ymd', $day)) {
|
||||
throw new Exception('Invalid date format');
|
||||
}
|
||||
|
||||
$filtered = array();
|
||||
foreach ($this->links as $l) {
|
||||
if (startsWith($l['linkdate'], $day)) {
|
||||
$filtered[$l['linkdate']] = $l;
|
||||
}
|
||||
}
|
||||
ksort($filtered);
|
||||
return $filtered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a list of tags (str) to an array. Also
|
||||
* - handle case sensitivity.
|
||||
* - accepts spaces commas as separator.
|
||||
* - remove private tags for loggedout users.
|
||||
*
|
||||
* @param string $tags string containing a list of tags.
|
||||
* @param bool $casesensitive will convert everything to lowercase if false.
|
||||
*
|
||||
* @return array filtered tags string.
|
||||
*/
|
||||
public 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));
|
||||
}
|
||||
}
|
|
@ -72,10 +72,12 @@ function sanitizeLink(&$link)
|
|||
|
||||
/**
|
||||
* Checks if a string represents a valid date
|
||||
|
||||
* @param string $format The expected DateTime format of the string
|
||||
* @param string $string A string-formatted date
|
||||
*
|
||||
* @return bool whether the string is a valid date
|
||||
*
|
||||
* @param string a string-formatted date
|
||||
* @param format the expected DateTime format of the string
|
||||
* @return whether the string is a valid date
|
||||
* @see http://php.net/manual/en/class.datetime.php
|
||||
* @see http://php.net/manual/en/datetime.createfromformat.php
|
||||
*/
|
||||
|
|
210
index.php
210
index.php
|
@ -151,6 +151,7 @@
|
|||
require_once 'application/FileUtils.php';
|
||||
require_once 'application/HttpUtils.php';
|
||||
require_once 'application/LinkDB.php';
|
||||
require_once 'application/LinkFilter.php';
|
||||
require_once 'application/TimeZone.php';
|
||||
require_once 'application/Url.php';
|
||||
require_once 'application/Utils.php';
|
||||
|
@ -730,18 +731,23 @@ function showRSS()
|
|||
// Read links from database (and filter private links if user it not logged in).
|
||||
|
||||
// Optionally filter the results:
|
||||
$linksToDisplay=array();
|
||||
if (!empty($_GET['searchterm'])) $linksToDisplay = $LINKSDB->filterFulltext($_GET['searchterm']);
|
||||
else if (!empty($_GET['searchtags'])) $linksToDisplay = $LINKSDB->filterTags(trim($_GET['searchtags']));
|
||||
else $linksToDisplay = $LINKSDB;
|
||||
|
||||
$nblinksToDisplay = 50; // Number of links to display.
|
||||
if (!empty($_GET['nb'])) // In URL, you can specificy the number of links. Example: nb=200 or nb=all for all links.
|
||||
{
|
||||
$nblinksToDisplay = $_GET['nb']=='all' ? count($linksToDisplay) : max($_GET['nb']+0,1) ;
|
||||
if (!empty($_GET['searchterm'])) {
|
||||
$linksToDisplay = $LINKSDB->filter(LinkFilter::$FILTER_TEXT, $_GET['searchterm']);
|
||||
}
|
||||
elseif (!empty($_GET['searchtags'])) {
|
||||
$linksToDisplay = $LINKSDB->filter(LinkFilter::$FILTER_TAG, trim($_GET['searchtags']));
|
||||
}
|
||||
else {
|
||||
$linksToDisplay = $LINKSDB;
|
||||
}
|
||||
|
||||
$pageaddr=escape(index_url($_SERVER));
|
||||
$nblinksToDisplay = 50; // Number of links to display.
|
||||
// In URL, you can specificy the number of links. Example: nb=200 or nb=all for all links.
|
||||
if (!empty($_GET['nb'])) {
|
||||
$nblinksToDisplay = $_GET['nb'] == 'all' ? count($linksToDisplay) : max(intval($_GET['nb']), 1);
|
||||
}
|
||||
|
||||
$pageaddr = escape(index_url($_SERVER));
|
||||
echo '<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">';
|
||||
echo '<channel><title>'.$GLOBALS['title'].'</title><link>'.$pageaddr.'</link>';
|
||||
echo '<description>Shared links</description><language>en-en</language><copyright>'.$pageaddr.'</copyright>'."\n\n";
|
||||
|
@ -821,15 +827,20 @@ function showATOM()
|
|||
);
|
||||
|
||||
// Optionally filter the results:
|
||||
$linksToDisplay=array();
|
||||
if (!empty($_GET['searchterm'])) $linksToDisplay = $LINKSDB->filterFulltext($_GET['searchterm']);
|
||||
else if (!empty($_GET['searchtags'])) $linksToDisplay = $LINKSDB->filterTags(trim($_GET['searchtags']));
|
||||
else $linksToDisplay = $LINKSDB;
|
||||
if (!empty($_GET['searchterm'])) {
|
||||
$linksToDisplay = $LINKSDB->filter(LinkFilter::$FILTER_TEXT, $_GET['searchterm']);
|
||||
}
|
||||
else if (!empty($_GET['searchtags'])) {
|
||||
$linksToDisplay = $LINKSDB->filter(LinkFilter::$FILTER_TAG, trim($_GET['searchtags']));
|
||||
}
|
||||
else {
|
||||
$linksToDisplay = $LINKSDB;
|
||||
}
|
||||
|
||||
$nblinksToDisplay = 50; // Number of links to display.
|
||||
if (!empty($_GET['nb'])) // In URL, you can specificy the number of links. Example: nb=200 or nb=all for all links.
|
||||
{
|
||||
$nblinksToDisplay = $_GET['nb']=='all' ? count($linksToDisplay) : max($_GET['nb']+0,1) ;
|
||||
// In URL, you can specificy the number of links. Example: nb=200 or nb=all for all links.
|
||||
if (!empty($_GET['nb'])) {
|
||||
$nblinksToDisplay = $_GET['nb']=='all' ? count($linksToDisplay) : max(intval($_GET['nb']), 1);
|
||||
}
|
||||
|
||||
$pageaddr=escape(index_url($_SERVER));
|
||||
|
@ -1024,7 +1035,7 @@ function showDaily($pageBuilder)
|
|||
}
|
||||
|
||||
try {
|
||||
$linksToDisplay = $LINKSDB->filterDay($day);
|
||||
$linksToDisplay = $LINKSDB->filter(LinkFilter::$FILTER_DAY, $day);
|
||||
} catch (Exception $exc) {
|
||||
error_log($exc);
|
||||
$linksToDisplay = array();
|
||||
|
@ -1149,13 +1160,17 @@ function renderPage()
|
|||
if ($targetPage == Router::$PAGE_PICWALL)
|
||||
{
|
||||
// Optionally filter the results:
|
||||
$links=array();
|
||||
if (!empty($_GET['searchterm'])) $links = $LINKSDB->filterFulltext($_GET['searchterm']);
|
||||
elseif (!empty($_GET['searchtags'])) $links = $LINKSDB->filterTags(trim($_GET['searchtags']));
|
||||
else $links = $LINKSDB;
|
||||
if (!empty($_GET['searchterm'])) {
|
||||
$links = $LINKSDB->filter(LinkFilter::$FILTER_TEXT, $_GET['searchterm']);
|
||||
}
|
||||
elseif (! empty($_GET['searchtags'])) {
|
||||
$links = $LINKSDB->filter(LinkFilter::$FILTER_TAG, trim($_GET['searchtags']));
|
||||
}
|
||||
else {
|
||||
$links = $LINKSDB;
|
||||
}
|
||||
|
||||
$body='';
|
||||
$linksToDisplay=array();
|
||||
$linksToDisplay = array();
|
||||
|
||||
// Get only links which have a thumbnail.
|
||||
foreach($links as $link)
|
||||
|
@ -1282,13 +1297,15 @@ function renderPage()
|
|||
}
|
||||
|
||||
if (isset($params['searchtags'])) {
|
||||
$tags = explode(' ',$params['searchtags']);
|
||||
$tags=array_diff($tags, array($_GET['removetag'])); // Remove value from array $tags.
|
||||
if (count($tags)==0) {
|
||||
unset($params['searchtags']);
|
||||
} else {
|
||||
$tags = explode(' ', $params['searchtags']);
|
||||
// Remove value from array $tags.
|
||||
$tags = array_diff($tags, array($_GET['removetag']));
|
||||
$params['searchtags'] = implode(' ',$tags);
|
||||
|
||||
if (empty($params['searchtags'])) {
|
||||
unset($params['searchtags']);
|
||||
}
|
||||
|
||||
unset($params['page']); // We also remove page (keeping the same page has no sense, since the results are different)
|
||||
}
|
||||
header('Location: ?'.http_build_query($params));
|
||||
|
@ -1468,7 +1485,8 @@ function renderPage()
|
|||
// Delete a tag:
|
||||
if (isset($_POST['deletetag']) && !empty($_POST['fromtag'])) {
|
||||
$needle=trim($_POST['fromtag']);
|
||||
$linksToAlter = $LINKSDB->filterTags($needle,true); // True for case-sensitive tag search.
|
||||
// True for case-sensitive tag search.
|
||||
$linksToAlter = $LINKSDB->filter(LinkFilter::$FILTER_TAG, $needle, true);
|
||||
foreach($linksToAlter as $key=>$value)
|
||||
{
|
||||
$tags = explode(' ',trim($value['tags']));
|
||||
|
@ -1484,7 +1502,8 @@ function renderPage()
|
|||
// Rename a tag:
|
||||
if (isset($_POST['renametag']) && !empty($_POST['fromtag']) && !empty($_POST['totag'])) {
|
||||
$needle=trim($_POST['fromtag']);
|
||||
$linksToAlter = $LINKSDB->filterTags($needle,true); // true for case-sensitive tag search.
|
||||
// True for case-sensitive tag search.
|
||||
$linksToAlter = $LINKSDB->filter(LinkFilter::$FILTER_TAG, $needle, true);
|
||||
foreach($linksToAlter as $key=>$value)
|
||||
{
|
||||
$tags = explode(' ',trim($value['tags']));
|
||||
|
@ -1865,81 +1884,78 @@ function importFile()
|
|||
function buildLinkList($PAGE,$LINKSDB)
|
||||
{
|
||||
// ---- Filter link database according to parameters
|
||||
$linksToDisplay=array();
|
||||
$search_type='';
|
||||
$search_crits='';
|
||||
if (isset($_GET['searchterm'])) // Fulltext search
|
||||
{
|
||||
$linksToDisplay = $LINKSDB->filterFulltext(trim($_GET['searchterm']));
|
||||
$search_crits=escape(trim($_GET['searchterm']));
|
||||
$search_type='fulltext';
|
||||
$search_type = '';
|
||||
$search_crits = '';
|
||||
$privateonly = !empty($_SESSION['privateonly']) ? true : false;
|
||||
|
||||
// Fulltext search
|
||||
if (isset($_GET['searchterm'])) {
|
||||
$search_crits = escape(trim($_GET['searchterm']));
|
||||
$search_type = LinkFilter::$FILTER_TEXT;
|
||||
$linksToDisplay = $LINKSDB->filter($search_type, $search_crits, false, $privateonly);
|
||||
}
|
||||
elseif (isset($_GET['searchtags'])) // Search by tag
|
||||
{
|
||||
$linksToDisplay = $LINKSDB->filterTags(trim($_GET['searchtags']));
|
||||
$search_crits=explode(' ',escape(trim($_GET['searchtags'])));
|
||||
$search_type='tags';
|
||||
// Search by tag
|
||||
elseif (isset($_GET['searchtags'])) {
|
||||
$search_crits = explode(' ', escape(trim($_GET['searchtags'])));
|
||||
$search_type = LinkFilter::$FILTER_TAG;
|
||||
$linksToDisplay = $LINKSDB->filter($search_type, $search_crits, false, $privateonly);
|
||||
}
|
||||
elseif (isset($_SERVER['QUERY_STRING']) && preg_match('/[a-zA-Z0-9-_@]{6}(&.+?)?/',$_SERVER['QUERY_STRING'])) // Detect smallHashes in URL
|
||||
{
|
||||
$linksToDisplay = $LINKSDB->filterSmallHash(substr(trim($_SERVER["QUERY_STRING"], '/'),0,6));
|
||||
if (count($linksToDisplay)==0)
|
||||
{
|
||||
header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found");
|
||||
echo '<h1>404 Not found.</h1>Oh crap. The link you are trying to reach does not exist or has been deleted.';
|
||||
// Detect smallHashes in URL.
|
||||
elseif (isset($_SERVER['QUERY_STRING'])
|
||||
&& preg_match('/[a-zA-Z0-9-_@]{6}(&.+?)?/', $_SERVER['QUERY_STRING'])) {
|
||||
$search_type = LinkFilter::$FILTER_HASH;
|
||||
$search_crits = substr(trim($_SERVER["QUERY_STRING"], '/'), 0, 6);
|
||||
$linksToDisplay = $LINKSDB->filter($search_type, $search_crits);
|
||||
|
||||
if (count($linksToDisplay) == 0) {
|
||||
header($_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found');
|
||||
echo '<h1>404 Not found.</h1>Oh crap.
|
||||
The link you are trying to reach does not exist or has been deleted.';
|
||||
echo '<br>Would you mind <a href="?">clicking here</a>?';
|
||||
exit;
|
||||
}
|
||||
$search_type='permalink';
|
||||
}
|
||||
else
|
||||
$linksToDisplay = $LINKSDB; // Otherwise, display without filtering.
|
||||
|
||||
|
||||
// Option: Show only private links
|
||||
if (!empty($_SESSION['privateonly']))
|
||||
{
|
||||
$tmp = array();
|
||||
foreach($linksToDisplay as $linkdate=>$link)
|
||||
{
|
||||
if ($link['private']!=0) $tmp[$linkdate]=$link;
|
||||
}
|
||||
$linksToDisplay=$tmp;
|
||||
// Otherwise, display without filtering.
|
||||
else {
|
||||
$linksToDisplay = $LINKSDB->filter('', '', false, $privateonly);
|
||||
}
|
||||
|
||||
// ---- Handle paging.
|
||||
/* Can someone explain to me why you get the following error when using array_keys() on an object which implements the interface ArrayAccess???
|
||||
"Warning: array_keys() expects parameter 1 to be array, object given in ... "
|
||||
If my class implements ArrayAccess, why won't array_keys() accept it ? ( $keys=array_keys($linksToDisplay); )
|
||||
*/
|
||||
$keys=array(); foreach($linksToDisplay as $key=>$value) { $keys[]=$key; } // Stupid and ugly. Thanks PHP.
|
||||
$keys = array();
|
||||
foreach ($linksToDisplay as $key => $value) {
|
||||
$keys[] = $key;
|
||||
}
|
||||
|
||||
// If there is only a single link, we change on-the-fly the title of the page.
|
||||
if (count($linksToDisplay)==1) $GLOBALS['pagetitle'] = $linksToDisplay[$keys[0]]['title'].' - '.$GLOBALS['title'];
|
||||
if (count($linksToDisplay) == 1) {
|
||||
$GLOBALS['pagetitle'] = $linksToDisplay[$keys[0]]['title'].' - '.$GLOBALS['title'];
|
||||
}
|
||||
|
||||
// Select articles according to paging.
|
||||
$pagecount = ceil(count($keys)/$_SESSION['LINKS_PER_PAGE']);
|
||||
$pagecount = ($pagecount==0 ? 1 : $pagecount);
|
||||
$page=( empty($_GET['page']) ? 1 : intval($_GET['page']));
|
||||
$page = ( $page<1 ? 1 : $page );
|
||||
$page = ( $page>$pagecount ? $pagecount : $page );
|
||||
$i = ($page-1)*$_SESSION['LINKS_PER_PAGE']; // Start index.
|
||||
$end = $i+$_SESSION['LINKS_PER_PAGE'];
|
||||
$linkDisp=array(); // Links to display
|
||||
$pagecount = ceil(count($keys) / $_SESSION['LINKS_PER_PAGE']);
|
||||
$pagecount = $pagecount == 0 ? 1 : $pagecount;
|
||||
$page= empty($_GET['page']) ? 1 : intval($_GET['page']);
|
||||
$page = $page < 1 ? 1 : $page;
|
||||
$page = $page > $pagecount ? $pagecount : $page;
|
||||
// Start index.
|
||||
$i = ($page-1) * $_SESSION['LINKS_PER_PAGE'];
|
||||
$end = $i + $_SESSION['LINKS_PER_PAGE'];
|
||||
$linkDisp = array();
|
||||
while ($i<$end && $i<count($keys))
|
||||
{
|
||||
$link = $linksToDisplay[$keys[$i]];
|
||||
$link['description'] = format_description($link['description'], $GLOBALS['redirector']);
|
||||
$classLi = $i%2!=0 ? '' : 'publicLinkHightLight';
|
||||
$link['class'] = ($link['private']==0 ? $classLi : 'private');
|
||||
$link['timestamp']=linkdate2timestamp($link['linkdate']);
|
||||
$taglist = explode(' ',$link['tags']);
|
||||
$classLi = ($i % 2) != 0 ? '' : 'publicLinkHightLight';
|
||||
$link['class'] = $link['private'] == 0 ? $classLi : 'private';
|
||||
$link['timestamp'] = linkdate2timestamp($link['linkdate']);
|
||||
$taglist = explode(' ', $link['tags']);
|
||||
uasort($taglist, 'strcasecmp');
|
||||
$link['taglist']=$taglist;
|
||||
$link['taglist'] = $taglist;
|
||||
$link['shorturl'] = smallHash($link['linkdate']);
|
||||
if ($link["url"][0] === '?' && // Check for both signs of a note: starting with ? and 7 chars long. I doubt that you'll post any links that look like this.
|
||||
strlen($link["url"]) === 7) {
|
||||
$link["url"] = index_url($_SERVER) . $link["url"];
|
||||
// Check for both signs of a note: starting with ? and 7 chars long.
|
||||
if ($link['url'][0] === '?' &&
|
||||
strlen($link['url']) === 7) {
|
||||
$link['url'] = index_url($_SERVER) . $link['url'];
|
||||
}
|
||||
|
||||
$linkDisp[$keys[$i]] = $link;
|
||||
|
@ -1947,13 +1963,21 @@ function buildLinkList($PAGE,$LINKSDB)
|
|||
}
|
||||
|
||||
// Compute paging navigation
|
||||
$searchterm= ( empty($_GET['searchterm']) ? '' : '&searchterm='.$_GET['searchterm'] );
|
||||
$searchtags= ( empty($_GET['searchtags']) ? '' : '&searchtags='.$_GET['searchtags'] );
|
||||
$paging='';
|
||||
$previous_page_url=''; if ($i!=count($keys)) $previous_page_url='?page='.($page+1).$searchterm.$searchtags;
|
||||
$next_page_url='';if ($page>1) $next_page_url='?page='.($page-1).$searchterm.$searchtags;
|
||||
$searchterm = empty($_GET['searchterm']) ? '' : '&searchterm=' . $_GET['searchterm'];
|
||||
$searchtags = empty($_GET['searchtags']) ? '' : '&searchtags=' . $_GET['searchtags'];
|
||||
$previous_page_url = '';
|
||||
if ($i != count($keys)) {
|
||||
$previous_page_url = '?page=' . ($page+1) . $searchterm . $searchtags;
|
||||
}
|
||||
$next_page_url='';
|
||||
if ($page>1) {
|
||||
$next_page_url = '?page=' . ($page-1) . $searchterm . $searchtags;
|
||||
}
|
||||
|
||||
$token = ''; if (isLoggedIn()) $token=getToken();
|
||||
$token = '';
|
||||
if (isLoggedIn()) {
|
||||
$token = getToken();
|
||||
}
|
||||
|
||||
// Fill all template fields.
|
||||
$data = array(
|
||||
|
|
|
@ -301,217 +301,6 @@ public function testAllTags()
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter links using a tag
|
||||
*/
|
||||
public function testFilterOneTag()
|
||||
{
|
||||
$this->assertEquals(
|
||||
3,
|
||||
sizeof(self::$publicLinkDB->filterTags('web', false))
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
4,
|
||||
sizeof(self::$privateLinkDB->filterTags('web', false))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter links using a tag - case-sensitive
|
||||
*/
|
||||
public function testFilterCaseSensitiveTag()
|
||||
{
|
||||
$this->assertEquals(
|
||||
0,
|
||||
sizeof(self::$privateLinkDB->filterTags('mercurial', true))
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
1,
|
||||
sizeof(self::$privateLinkDB->filterTags('Mercurial', true))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter links using a tag combination
|
||||
*/
|
||||
public function testFilterMultipleTags()
|
||||
{
|
||||
$this->assertEquals(
|
||||
1,
|
||||
sizeof(self::$publicLinkDB->filterTags('dev cartoon', false))
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
2,
|
||||
sizeof(self::$privateLinkDB->filterTags('dev cartoon', false))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter links using a non-existent tag
|
||||
*/
|
||||
public function testFilterUnknownTag()
|
||||
{
|
||||
$this->assertEquals(
|
||||
0,
|
||||
sizeof(self::$publicLinkDB->filterTags('null', false))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return links for a given day
|
||||
*/
|
||||
public function testFilterDay()
|
||||
{
|
||||
$this->assertEquals(
|
||||
2,
|
||||
sizeof(self::$publicLinkDB->filterDay('20121206'))
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
3,
|
||||
sizeof(self::$privateLinkDB->filterDay('20121206'))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 404 - day not found
|
||||
*/
|
||||
public function testFilterUnknownDay()
|
||||
{
|
||||
$this->assertEquals(
|
||||
0,
|
||||
sizeof(self::$publicLinkDB->filterDay('19700101'))
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
0,
|
||||
sizeof(self::$privateLinkDB->filterDay('19700101'))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use an invalid date format
|
||||
* @expectedException Exception
|
||||
* @expectedExceptionMessageRegExp /Invalid date format/
|
||||
*/
|
||||
public function testFilterInvalidDayWithChars()
|
||||
{
|
||||
self::$privateLinkDB->filterDay('Rainy day, dream away');
|
||||
}
|
||||
|
||||
/**
|
||||
* Use an invalid date format
|
||||
* @expectedException Exception
|
||||
* @expectedExceptionMessageRegExp /Invalid date format/
|
||||
*/
|
||||
public function testFilterInvalidDayDigits()
|
||||
{
|
||||
self::$privateLinkDB->filterDay('20');
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a link entry with its hash
|
||||
*/
|
||||
public function testFilterSmallHash()
|
||||
{
|
||||
$links = self::$privateLinkDB->filterSmallHash('IuWvgA');
|
||||
|
||||
$this->assertEquals(
|
||||
1,
|
||||
sizeof($links)
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
'MediaGoblin',
|
||||
$links['20130614_184135']['title']
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* No link for this hash
|
||||
*/
|
||||
public function testFilterUnknownSmallHash()
|
||||
{
|
||||
$this->assertEquals(
|
||||
0,
|
||||
sizeof(self::$privateLinkDB->filterSmallHash('Iblaah'))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Full-text search - result from a link's URL
|
||||
*/
|
||||
public function testFilterFullTextURL()
|
||||
{
|
||||
$this->assertEquals(
|
||||
2,
|
||||
sizeof(self::$publicLinkDB->filterFullText('ars.userfriendly.org'))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Full-text search - result from a link's title only
|
||||
*/
|
||||
public function testFilterFullTextTitle()
|
||||
{
|
||||
// use miscellaneous cases
|
||||
$this->assertEquals(
|
||||
2,
|
||||
sizeof(self::$publicLinkDB->filterFullText('userfriendly -'))
|
||||
);
|
||||
$this->assertEquals(
|
||||
2,
|
||||
sizeof(self::$publicLinkDB->filterFullText('UserFriendly -'))
|
||||
);
|
||||
$this->assertEquals(
|
||||
2,
|
||||
sizeof(self::$publicLinkDB->filterFullText('uSeRFrIendlY -'))
|
||||
);
|
||||
|
||||
// use miscellaneous case and offset
|
||||
$this->assertEquals(
|
||||
2,
|
||||
sizeof(self::$publicLinkDB->filterFullText('RFrIendL'))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Full-text search - result from the link's description only
|
||||
*/
|
||||
public function testFilterFullTextDescription()
|
||||
{
|
||||
$this->assertEquals(
|
||||
1,
|
||||
sizeof(self::$publicLinkDB->filterFullText('media publishing'))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Full-text search - result from the link's tags only
|
||||
*/
|
||||
public function testFilterFullTextTags()
|
||||
{
|
||||
$this->assertEquals(
|
||||
2,
|
||||
sizeof(self::$publicLinkDB->filterFullText('gnu'))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Full-text search - result set from mixed sources
|
||||
*/
|
||||
public function testFilterFullTextMixed()
|
||||
{
|
||||
$this->assertEquals(
|
||||
2,
|
||||
sizeof(self::$publicLinkDB->filterFullText('free software'))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test real_url without redirector.
|
||||
*/
|
||||
|
@ -534,4 +323,28 @@ public function testLinkRealUrlWithRedirector()
|
|||
$this->assertStringStartsWith($redirector, $link['real_url']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test filter with string.
|
||||
*/
|
||||
public function testFilterString()
|
||||
{
|
||||
$tags = 'dev cartoon';
|
||||
$this->assertEquals(
|
||||
2,
|
||||
count(self::$privateLinkDB->filter(LinkFilter::$FILTER_TAG, $tags, true, false))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test filter with string.
|
||||
*/
|
||||
public function testFilterArray()
|
||||
{
|
||||
$tags = array('dev', 'cartoon');
|
||||
$this->assertEquals(
|
||||
2,
|
||||
count(self::$privateLinkDB->filter(LinkFilter::$FILTER_TAG, $tags, true, false))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
242
tests/LinkFilterTest.php
Normal file
242
tests/LinkFilterTest.php
Normal file
|
@ -0,0 +1,242 @@
|
|||
<?php
|
||||
|
||||
require_once 'application/LinkFilter.php';
|
||||
|
||||
/**
|
||||
* Class LinkFilterTest.
|
||||
*/
|
||||
class LinkFilterTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var LinkFilter instance.
|
||||
*/
|
||||
protected static $linkFilter;
|
||||
|
||||
/**
|
||||
* Instanciate linkFilter with ReferenceLinkDB data.
|
||||
*/
|
||||
public static function setUpBeforeClass()
|
||||
{
|
||||
$refDB = new ReferenceLinkDB();
|
||||
self::$linkFilter = new LinkFilter($refDB->getLinks());
|
||||
}
|
||||
|
||||
/**
|
||||
* Blank filter.
|
||||
*/
|
||||
public function testFilter()
|
||||
{
|
||||
$this->assertEquals(
|
||||
6,
|
||||
count(self::$linkFilter->filter('', ''))
|
||||
);
|
||||
|
||||
// Private only.
|
||||
$this->assertEquals(
|
||||
2,
|
||||
count(self::$linkFilter->filter('', '', false, true))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter links using a tag
|
||||
*/
|
||||
public function testFilterOneTag()
|
||||
{
|
||||
$this->assertEquals(
|
||||
4,
|
||||
count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, 'web', false))
|
||||
);
|
||||
|
||||
// Private only.
|
||||
$this->assertEquals(
|
||||
1,
|
||||
count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, 'web', false, true))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter links using a tag - case-sensitive
|
||||
*/
|
||||
public function testFilterCaseSensitiveTag()
|
||||
{
|
||||
$this->assertEquals(
|
||||
0,
|
||||
count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, 'mercurial', true))
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
1,
|
||||
count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, 'Mercurial', true))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter links using a tag combination
|
||||
*/
|
||||
public function testFilterMultipleTags()
|
||||
{
|
||||
$this->assertEquals(
|
||||
2,
|
||||
count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, 'dev cartoon', false))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter links using a non-existent tag
|
||||
*/
|
||||
public function testFilterUnknownTag()
|
||||
{
|
||||
$this->assertEquals(
|
||||
0,
|
||||
count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, 'null', false))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return links for a given day
|
||||
*/
|
||||
public function testFilterDay()
|
||||
{
|
||||
$this->assertEquals(
|
||||
3,
|
||||
count(self::$linkFilter->filter(LinkFilter::$FILTER_DAY, '20121206'))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 404 - day not found
|
||||
*/
|
||||
public function testFilterUnknownDay()
|
||||
{
|
||||
$this->assertEquals(
|
||||
0,
|
||||
count(self::$linkFilter->filter(LinkFilter::$FILTER_DAY, '19700101'))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use an invalid date format
|
||||
* @expectedException Exception
|
||||
* @expectedExceptionMessageRegExp /Invalid date format/
|
||||
*/
|
||||
public function testFilterInvalidDayWithChars()
|
||||
{
|
||||
self::$linkFilter->filter(LinkFilter::$FILTER_DAY, 'Rainy day, dream away');
|
||||
}
|
||||
|
||||
/**
|
||||
* Use an invalid date format
|
||||
* @expectedException Exception
|
||||
* @expectedExceptionMessageRegExp /Invalid date format/
|
||||
*/
|
||||
public function testFilterInvalidDayDigits()
|
||||
{
|
||||
self::$linkFilter->filter(LinkFilter::$FILTER_DAY, '20');
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a link entry with its hash
|
||||
*/
|
||||
public function testFilterSmallHash()
|
||||
{
|
||||
$links = self::$linkFilter->filter(LinkFilter::$FILTER_HASH, 'IuWvgA');
|
||||
|
||||
$this->assertEquals(
|
||||
1,
|
||||
count($links)
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
'MediaGoblin',
|
||||
$links['20130614_184135']['title']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* No link for this hash
|
||||
*/
|
||||
public function testFilterUnknownSmallHash()
|
||||
{
|
||||
$this->assertEquals(
|
||||
0,
|
||||
count(self::$linkFilter->filter(LinkFilter::$FILTER_HASH, 'Iblaah'))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Full-text search - result from a link's URL
|
||||
*/
|
||||
public function testFilterFullTextURL()
|
||||
{
|
||||
$this->assertEquals(
|
||||
2,
|
||||
count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'ars.userfriendly.org'))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Full-text search - result from a link's title only
|
||||
*/
|
||||
public function testFilterFullTextTitle()
|
||||
{
|
||||
// use miscellaneous cases
|
||||
$this->assertEquals(
|
||||
2,
|
||||
count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'userfriendly -'))
|
||||
);
|
||||
$this->assertEquals(
|
||||
2,
|
||||
count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'UserFriendly -'))
|
||||
);
|
||||
$this->assertEquals(
|
||||
2,
|
||||
count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'uSeRFrIendlY -'))
|
||||
);
|
||||
|
||||
// use miscellaneous case and offset
|
||||
$this->assertEquals(
|
||||
2,
|
||||
count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'RFrIendL'))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Full-text search - result from the link's description only
|
||||
*/
|
||||
public function testFilterFullTextDescription()
|
||||
{
|
||||
$this->assertEquals(
|
||||
1,
|
||||
count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'media publishing'))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Full-text search - result from the link's tags only
|
||||
*/
|
||||
public function testFilterFullTextTags()
|
||||
{
|
||||
$this->assertEquals(
|
||||
2,
|
||||
count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'gnu'))
|
||||
);
|
||||
|
||||
// Private only.
|
||||
$this->assertEquals(
|
||||
1,
|
||||
count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'web', false, true))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Full-text search - result set from mixed sources
|
||||
*/
|
||||
public function testFilterFullTextMixed()
|
||||
{
|
||||
$this->assertEquals(
|
||||
2,
|
||||
count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'free software'))
|
||||
);
|
||||
}
|
||||
}
|
|
@ -124,4 +124,9 @@ public function countPrivateLinks()
|
|||
{
|
||||
return $this->_privateCount;
|
||||
}
|
||||
|
||||
public function getLinks()
|
||||
{
|
||||
return $this->_links;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,15 +7,24 @@
|
|||
<body>
|
||||
<div id="pageheader">
|
||||
{include="page.header"}
|
||||
|
||||
<div id="headerform" class="search">
|
||||
<form method="GET" class="searchform" name="searchform">
|
||||
<input type="text" tabindex="1" id="searchform_value" name="searchterm" placeholder="Search text" value="">
|
||||
<input type="text" tabindex="1" id="searchform_value" name="searchterm" placeholder="Search text"
|
||||
{if="!empty($search_crits) && $search_type=='fulltext'"}
|
||||
value="{$search_crits}"
|
||||
{/if}
|
||||
>
|
||||
<input type="submit" value="Search" class="bigbutton">
|
||||
</form>
|
||||
<form method="GET" class="tagfilter" name="tagfilter">
|
||||
<input type="text" tabindex="2" name="searchtags" id="tagfilter_value" placeholder="Filter by tag" value=""
|
||||
<input type="text" tabindex="2" name="searchtags" id="tagfilter_value" placeholder="Filter by tag"
|
||||
{if="!empty($search_crits) && $search_type=='tags'"}
|
||||
value="{function="implode(' ', $search_crits)"}"
|
||||
{/if}
|
||||
autocomplete="off" class="awesomplete" data-multiple data-minChars="1"
|
||||
data-list="{loop="$tags"}{$key}, {/loop}">
|
||||
>
|
||||
<input type="submit" value="Search" class="bigbutton">
|
||||
</form>
|
||||
{loop="$plugins_header.fields_toolbar"}
|
||||
|
@ -44,7 +53,7 @@
|
|||
<div id="searchcriteria">{$result_count} results for tags <i>
|
||||
{loop="search_crits"}
|
||||
<span class="linktag" title="Remove tag">
|
||||
<a href="?removetag={$value}">{$value} <span class="remove">x</span></a>
|
||||
<a href="?removetag={function="urlencode($value)"}">{$value} <span class="remove">x</span></a>
|
||||
</span>
|
||||
{/loop}</i></div>
|
||||
{/if}
|
||||
|
|
Loading…
Reference in a new issue