X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=application%2FLinkFilter.php;h=8f147974e9d5cf232f3cf3f509b3401fcf5e6b84;hb=f211e417bf637b8a83988175c29ee072c69f7642;hp=9551952886c0bee94c122983c5b3ce8c8d6332a2;hpb=b4ff0afb24db6e4cb3543bbd71f01bbb0716b144;p=github%2Fshaarli%2FShaarli.git diff --git a/application/LinkFilter.php b/application/LinkFilter.php index 95519528..8f147974 100644 --- a/application/LinkFilter.php +++ b/application/LinkFilter.php @@ -62,7 +62,7 @@ class LinkFilter $visibility = 'all'; } - switch($type) { + switch ($type) { case self::$FILTER_HASH: return $this->filterSmallHash($request); case self::$FILTER_TAG | self::$FILTER_TEXT: // == "vuotext" @@ -117,7 +117,7 @@ class LinkFilter foreach ($this->links as $key => $value) { if ($value['private'] && $visibility === 'private') { $out[$key] = $value; - } else if (! $value['private'] && $visibility === 'public') { + } elseif (! $value['private'] && $visibility === 'public') { $out[$key] = $value; } } @@ -205,12 +205,11 @@ class LinkFilter // Iterate over every stored link. foreach ($this->links as $id => $link) { - // ignore non private links when 'privatonly' is on. if ($visibility !== 'all') { if (! $link['private'] && $visibility === 'private') { continue; - } else if ($link['private'] && $visibility === 'public') { + } elseif ($link['private'] && $visibility === 'public') { continue; } } @@ -249,6 +248,51 @@ class LinkFilter return $filtered; } + /** + * generate a regex fragment out of a tag + * @param string $tag to to generate regexs from. may start with '-' to negate, contain '*' as wildcard + * @return string generated regex fragment + */ + private static function tag2regex($tag) + { + $len = strlen($tag); + if (!$len || $tag === "-" || $tag === "*") { + // nothing to search, return empty regex + return ''; + } + if ($tag[0] === "-") { + // query is negated + $i = 1; // use offset to start after '-' character + $regex = '(?!'; // create negative lookahead + } else { + $i = 0; // start at first character + $regex = '(?='; // use positive lookahead + } + $regex .= '.*(?:^| )'; // before tag may only be a space or the beginning + // iterate over string, separating it into placeholder and content + for (; $i < $len; $i++) { + if ($tag[$i] === '*') { + // placeholder found + $regex .= '[^ ]*?'; + } else { + // regular characters + $offset = strpos($tag, '*', $i); + if ($offset === false) { + // no placeholder found, set offset to end of string + $offset = $len; + } + // subtract one, as we want to get before the placeholder or end of string + $offset -= 1; + // we got a tag name that we want to search for. escape any regex characters to prevent conflicts. + $regex .= preg_quote(substr($tag, $i, $offset - $i + 1), '/'); + // move $i on + $i = $offset; + } + } + $regex .= '(?:$| ))'; // after the tag may only be a space or the end + return $regex; + } + /** * Returns the list of links associated with a given list of tags * @@ -263,46 +307,60 @@ class LinkFilter */ public function filterTags($tags, $casesensitive = false, $visibility = 'all') { - // Implode if array for clean up. - $tags = is_array($tags) ? trim(implode(' ', $tags)) : $tags; - if (empty($tags)) { + // get single tags (we may get passed an array, even though the docs say different) + $inputTags = $tags; + if (!is_array($tags)) { + // we got an input string, split tags + $inputTags = preg_split('/(?:\s+)|,/', $inputTags, -1, PREG_SPLIT_NO_EMPTY); + } + + if (!count($inputTags)) { + // no input tags return $this->noFilter($visibility); } - $searchtags = self::tagsStrToArray($tags, $casesensitive); - $filtered = array(); - if (empty($searchtags)) { - return $filtered; + // build regex from all tags + $re = '/^' . implode(array_map("self::tag2regex", $inputTags)) . '.*$/'; + if (!$casesensitive) { + // make regex case insensitive + $re .= 'i'; } + // create resulting array + $filtered = array(); + + // iterate over each link foreach ($this->links as $key => $link) { - // ignore non private links when 'privatonly' is on. + // check level of visibility + // ignore non private links when 'privateonly' is on. if ($visibility !== 'all') { if (! $link['private'] && $visibility === 'private') { continue; - } else if ($link['private'] && $visibility === 'public') { + } elseif ($link['private'] && $visibility === 'public') { continue; } } - - $linktags = self::tagsStrToArray($link['tags'], $casesensitive); - - $found = true; - for ($i = 0 ; $i < count($searchtags) && $found; $i++) { - // Exclusive search, quit if tag found. - // Or, tag not found in the link, quit. - if (($searchtags[$i][0] == '-' - && $this->searchTagAndHashTag(substr($searchtags[$i], 1), $linktags, $link['description'])) - || ($searchtags[$i][0] != '-') - && ! $this->searchTagAndHashTag($searchtags[$i], $linktags, $link['description']) - ) { - $found = false; + $search = $link['tags']; // build search string, start with tags of current link + if (strlen(trim($link['description'])) && strpos($link['description'], '#') !== false) { + // description given and at least one possible tag found + $descTags = array(); + // find all tags in the form of #tag in the description + preg_match_all( + '/(? 0) { - return true; - } - - return false; - } - /** * Convert a list of tags (str) to an array. Also * - handle case sensitivity. @@ -407,5 +443,11 @@ class LinkFilter class LinkNotFoundException extends Exception { - protected $message = 'The link you are trying to reach does not exist or has been deleted.'; + /** + * LinkNotFoundException constructor. + */ + public function __construct() + { + $this->message = t('The link you are trying to reach does not exist or has been deleted.'); + } }