* introduce class LinkFilter to handle link filter operation (and lighten LinkDB).
* handle 'private only' in filtering.
* update template to prefill search fields with current search terms.
* coding style.
* unit test (mostly move from LinkDB to LinkFilter).
PS: preparation for #358 #315 and 'AND' search.
// link redirector set in user settings.
private $_redirector;
+ /**
+ * @var LinkFilter instance.
+ */
+ private $linkFilter;
+
/**
* Creates a new LinkDB
*
$this->_redirector = $redirector;
$this->_checkDB();
$this->_readDB();
+ $this->linkFilter = new LinkFilter($this->_links);
}
/**
}
/**
- * Returns the list of links corresponding to a full-text search
+ * Filter links.
*
- * 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
- */
- 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
+ * @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.
*
- * You can specify one or more tags, separated by space or a comma, e.g.
- * print_r($mydb->filterTags('linux programming'));
+ * @return array filtered links
*/
- 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, $requestFilter, $casesensitive, $privateonly);
}
/**
--- /dev/null
+<?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));
+ }
+}
/**
* 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
+ * @see http://php.net/manual/en/class.datetime.php
+ * @see http://php.net/manual/en/datetime.createfromformat.php
*/
function checkDateFormat($format, $string)
{
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';
// 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;
+ 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;
+ }
$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));
+ $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";
);
// 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));
}
try {
- $linksToDisplay = $LINKSDB->filterDay($day);
+ $linksToDisplay = $LINKSDB->filter(LinkFilter::$FILTER_DAY, $day);
} catch (Exception $exc) {
error_log($exc);
$linksToDisplay = array();
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)
}
if (isset($params['searchtags'])) {
- $tags = explode(' ',$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']);
if (!empty($_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']));
if (!empty($_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']));
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';
- }
- elseif (isset($_GET['searchtags'])) // Search by tag
- {
- $linksToDisplay = $LINKSDB->filterTags(trim($_GET['searchtags']));
- $search_crits=explode(' ',escape(trim($_GET['searchtags'])));
- $search_type='tags';
- }
- 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.';
+ $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);
+ }
+ // 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);
+ }
+ // 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;
}
// 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(
}
/**
- * 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
+ * Test real_url without redirector.
*/
- public function testFilterFullTextTitle()
+ public function testLinkRealUrlWithoutRedirector()
{
- // 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'))
- );
+ $db = new LinkDB(self::$testDatastore, false, false);
+ foreach($db as $link) {
+ $this->assertEquals($link['url'], $link['real_url']);
+ }
}
/**
- * Full-text search - result from the link's description only
+ * Test real_url with redirector.
*/
- public function testFilterFullTextDescription()
+ public function testLinkRealUrlWithRedirector()
{
- $this->assertEquals(
- 1,
- sizeof(self::$publicLinkDB->filterFullText('media publishing'))
- );
+ $redirector = 'http://redirector.to?';
+ $db = new LinkDB(self::$testDatastore, false, false, $redirector);
+ foreach($db as $link) {
+ $this->assertStringStartsWith($redirector, $link['real_url']);
+ }
}
/**
- * Full-text search - result from the link's tags only
+ * Test filter with string.
*/
- public function testFilterFullTextTags()
+ public function testFilterString()
{
+ $tags = 'dev cartoon';
$this->assertEquals(
2,
- sizeof(self::$publicLinkDB->filterFullText('gnu'))
+ count(self::$privateLinkDB->filter(LinkFilter::$FILTER_TAG, $tags, true, false))
);
}
/**
- * Full-text search - result set from mixed sources
+ * Test filter with string.
*/
- public function testFilterFullTextMixed()
+ public function testFilterArray()
{
+ $tags = array('dev', 'cartoon');
$this->assertEquals(
2,
- sizeof(self::$publicLinkDB->filterFullText('free software'))
+ count(self::$privateLinkDB->filter(LinkFilter::$FILTER_TAG, $tags, true, false))
);
}
-
- /**
- * Test real_url without redirector.
- */
- public function testLinkRealUrlWithoutRedirector()
- {
- $db = new LinkDB(self::$testDatastore, false, false);
- foreach($db as $link) {
- $this->assertEquals($link['url'], $link['real_url']);
- }
- }
-
- /**
- * Test real_url with redirector.
- */
- public function testLinkRealUrlWithRedirector()
- {
- $redirector = 'http://redirector.to?';
- $db = new LinkDB(self::$testDatastore, false, false, $redirector);
- foreach($db as $link) {
- $this->assertStringStartsWith($redirector, $link['real_url']);
- }
- }
}
--- /dev/null
+<?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'))
+ );
+ }
+}
{
return $this->_privateCount;
}
+
+ public function getLinks()
+ {
+ return $this->_links;
+ }
}
<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=""
- autocomplete="off" class="awesomplete" data-multiple data-minChars="1"
- data-list="{loop="$tags"}{$key}, {/loop}">
+ <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"}