Added tag searching
This commit is contained in:
parent
bf58207950
commit
b7f077df9b
6 changed files with 154 additions and 63 deletions
|
@ -1,12 +1,25 @@
|
|||
#tag-list {
|
||||
min-width: 15em;
|
||||
max-width: 40em;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#tag-list form {
|
||||
float: left;
|
||||
white-space: nowrap;
|
||||
}
|
||||
#tag-list ul {
|
||||
float: right;
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
#tag-list .search:after {
|
||||
display: block;
|
||||
content: '';
|
||||
clear: both;
|
||||
}
|
||||
|
||||
#ta-list ul.order {
|
||||
margin: -0.5em -0.5em 0.5em -0.5em;
|
||||
|
@ -21,13 +34,13 @@
|
|||
}
|
||||
|
||||
#tag-list table {
|
||||
min-width: 20em;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
margin: 1em auto;
|
||||
}
|
||||
|
||||
#tag-list th,
|
||||
#tag-list td {
|
||||
#tag-list th:not(:last-child),
|
||||
#tag-list td:not(:last-child) {
|
||||
padding-right: 1.5em;
|
||||
}
|
||||
|
||||
|
@ -43,3 +56,10 @@
|
|||
#tag-list .usages {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media all and (max-width: 40em) {
|
||||
#tag-list .implications,
|
||||
#tag-list .suggestions {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,11 +11,16 @@ App.Presenters.TagListPresenter = function(
|
|||
topNavigationPresenter) {
|
||||
|
||||
var $el = jQuery('#content');
|
||||
var $searchInput;
|
||||
var templates = {};
|
||||
|
||||
function init(params, loaded) {
|
||||
var params;
|
||||
|
||||
function init(_params, loaded) {
|
||||
topNavigationPresenter.select('tags');
|
||||
topNavigationPresenter.changeTitle('Tags');
|
||||
params = _params;
|
||||
params.query = params.query || {};
|
||||
|
||||
promise.wait(
|
||||
util.promiseTemplate('tag-list'),
|
||||
|
@ -44,7 +49,8 @@ App.Presenters.TagListPresenter = function(
|
|||
});
|
||||
}
|
||||
|
||||
function reinit(params, loaded) {
|
||||
function reinit(_params, loaded) {
|
||||
params = _params;
|
||||
params.query = params.query || {};
|
||||
params.query.order = params.query.order || 'name,asc';
|
||||
updateActiveOrder(params.query.order);
|
||||
|
@ -55,7 +61,12 @@ App.Presenters.TagListPresenter = function(
|
|||
$el.find('table a').eq(0).focus();
|
||||
});
|
||||
|
||||
keyboard.keyup('q', function() {
|
||||
$searchInput.eq(0).focus().select();
|
||||
});
|
||||
|
||||
loaded();
|
||||
softRender();
|
||||
}
|
||||
|
||||
function deinit() {
|
||||
|
@ -64,6 +75,33 @@ App.Presenters.TagListPresenter = function(
|
|||
|
||||
function render() {
|
||||
$el.html(templates.list());
|
||||
$searchInput = $el.find('input[name=query]');
|
||||
$searchInput.keydown(searchInputKeyPressed);
|
||||
$el.find('form').submit(searchFormSubmitted);
|
||||
softRender();
|
||||
}
|
||||
|
||||
function softRender() {
|
||||
$searchInput.val(params.query.query);
|
||||
}
|
||||
|
||||
|
||||
function searchInputKeyPressed(e) {
|
||||
if (e.which !== KEY_RETURN) {
|
||||
return;
|
||||
}
|
||||
updateSearch();
|
||||
}
|
||||
|
||||
function searchFormSubmitted(e) {
|
||||
e.preventDefault();
|
||||
updateSearch();
|
||||
}
|
||||
|
||||
function updateSearch() {
|
||||
$searchInput.blur();
|
||||
params.query.query = $searchInput.val().trim();
|
||||
pagerPresenter.setQuery(params.query);
|
||||
}
|
||||
|
||||
function updateActiveOrder(activeOrder) {
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
<div id="tag-list">
|
||||
<div class="search">
|
||||
<form>
|
||||
<input type="text" name="query" placeholder="Search tags..."/>
|
||||
<button type="submit" name="search">Search</button>
|
||||
</form>
|
||||
|
||||
<ul class="order">
|
||||
<li>
|
||||
<a class="big-button" href="#/tags/order=name,asc">Tags</a>
|
||||
|
@ -10,6 +16,7 @@
|
|||
<a class="big-button" href="#/tags/order=usage_count,desc">Popular</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="pagination-target">
|
||||
<table class="tags">
|
||||
|
|
|
@ -5,6 +5,8 @@ use Szurubooru\Dao\EntityConverters\TagEntityConverter;
|
|||
use Szurubooru\DatabaseConnection;
|
||||
use Szurubooru\Entities\Entity;
|
||||
use Szurubooru\Entities\Tag;
|
||||
use Szurubooru\SearchServices\Filters\TagFilter;
|
||||
use Szurubooru\SearchServices\Requirements\Requirement;
|
||||
|
||||
class TagDao extends AbstractDao implements ICrudDao
|
||||
{
|
||||
|
@ -67,6 +69,52 @@ class TagDao extends AbstractDao implements ICrudDao
|
|||
$this->deleteBy('usages', 0);
|
||||
}
|
||||
|
||||
public function export()
|
||||
{
|
||||
$exported = [];
|
||||
foreach ($this->fpdo->from('tags') as $arrayEntity)
|
||||
{
|
||||
$exported[$arrayEntity['id']] = [
|
||||
'name' => $arrayEntity['name'],
|
||||
'usages' => intval($arrayEntity['usages']),
|
||||
'banned' => boolval($arrayEntity['banned'])
|
||||
];
|
||||
}
|
||||
|
||||
//upgrades on old databases
|
||||
try
|
||||
{
|
||||
$relations = iterator_to_array($this->fpdo->from('tagRelations'));
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
$relations = [];
|
||||
}
|
||||
|
||||
foreach ($relations as $arrayEntity)
|
||||
{
|
||||
$key1 = $arrayEntity['tag1id'];
|
||||
$key2 = $arrayEntity['tag2id'];
|
||||
$type = intval($arrayEntity['type']);
|
||||
if ($type === self::TAG_RELATION_IMPLICATION)
|
||||
$target = 'implications';
|
||||
elseif ($type === self::TAG_RELATION_SUGGESTION)
|
||||
$target = 'suggestions';
|
||||
else
|
||||
continue;
|
||||
|
||||
if (!isset($exported[$key1]) or !isset($exported[$key2]))
|
||||
continue;
|
||||
|
||||
if (!isset($exported[$key1][$target]))
|
||||
$exported[$key1][$target] = [];
|
||||
|
||||
$exported[$key1][$target][] = $exported[$key2]['name'];
|
||||
}
|
||||
|
||||
return array_values($exported);
|
||||
}
|
||||
|
||||
protected function afterLoad(Entity $tag)
|
||||
{
|
||||
$tag->setLazyLoader(
|
||||
|
@ -90,6 +138,22 @@ class TagDao extends AbstractDao implements ICrudDao
|
|||
$this->syncSuggestedTags($tag);
|
||||
}
|
||||
|
||||
protected function decorateQueryFromRequirement($query, Requirement $requirement)
|
||||
{
|
||||
if ($requirement->getType() === TagFilter::REQUIREMENT_PARTIAL_TAG_NAME)
|
||||
{
|
||||
$sql = 'INSTR(LOWER(tags.name), LOWER(?)) > 0';
|
||||
|
||||
if ($requirement->isNegated())
|
||||
$sql = 'NOT ' . $sql;
|
||||
|
||||
$query->where($sql, $requirement->getValue()->getValue());
|
||||
return;
|
||||
}
|
||||
|
||||
parent::decorateQueryFromRequirement($query, $requirement);
|
||||
}
|
||||
|
||||
private function findImpliedTags(Tag $tag)
|
||||
{
|
||||
return $this->findRelatedTagsByType($tag, self::TAG_RELATION_IMPLICATION);
|
||||
|
@ -138,52 +202,6 @@ class TagDao extends AbstractDao implements ICrudDao
|
|||
}
|
||||
}
|
||||
|
||||
public function export()
|
||||
{
|
||||
$exported = [];
|
||||
foreach ($this->fpdo->from('tags') as $arrayEntity)
|
||||
{
|
||||
$exported[$arrayEntity['id']] = [
|
||||
'name' => $arrayEntity['name'],
|
||||
'usages' => intval($arrayEntity['usages']),
|
||||
'banned' => boolval($arrayEntity['banned'])
|
||||
];
|
||||
}
|
||||
|
||||
//upgrades on old databases
|
||||
try
|
||||
{
|
||||
$relations = iterator_to_array($this->fpdo->from('tagRelations'));
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
$relations = [];
|
||||
}
|
||||
|
||||
foreach ($relations as $arrayEntity)
|
||||
{
|
||||
$key1 = $arrayEntity['tag1id'];
|
||||
$key2 = $arrayEntity['tag2id'];
|
||||
$type = intval($arrayEntity['type']);
|
||||
if ($type === self::TAG_RELATION_IMPLICATION)
|
||||
$target = 'implications';
|
||||
elseif ($type === self::TAG_RELATION_SUGGESTION)
|
||||
$target = 'suggestions';
|
||||
else
|
||||
continue;
|
||||
|
||||
if (!isset($exported[$key1]) or !isset($exported[$key2]))
|
||||
continue;
|
||||
|
||||
if (!isset($exported[$key1][$target]))
|
||||
$exported[$key1][$target] = [];
|
||||
|
||||
$exported[$key1][$target][] = $exported[$key2]['name'];
|
||||
}
|
||||
|
||||
return array_values($exported);
|
||||
}
|
||||
|
||||
private function findRelatedTagsByType(Tag $tag, $type)
|
||||
{
|
||||
$tagId = $tag->getId();
|
||||
|
|
|
@ -8,6 +8,8 @@ class TagFilter extends BasicFilter implements IFilter
|
|||
const ORDER_CREATION_TIME = 'creationTime';
|
||||
const ORDER_USAGE_COUNT = 'usages';
|
||||
|
||||
const REQUIREMENT_PARTIAL_TAG_NAME = 'partialTagName';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->setOrder([self::ORDER_ID => self::ORDER_DESC]);
|
||||
|
|
|
@ -3,6 +3,8 @@ namespace Szurubooru\SearchServices\Parsers;
|
|||
use Szurubooru\NotSupportedException;
|
||||
use Szurubooru\SearchServices\Filters\IFilter;
|
||||
use Szurubooru\SearchServices\Filters\TagFilter;
|
||||
use Szurubooru\SearchServices\Requirements\Requirement;
|
||||
use Szurubooru\SearchServices\Requirements\RequirementSingleValue;
|
||||
use Szurubooru\SearchServices\Tokens\NamedSearchToken;
|
||||
use Szurubooru\SearchServices\Tokens\SearchToken;
|
||||
|
||||
|
@ -15,7 +17,11 @@ class TagSearchParser extends AbstractSearchParser
|
|||
|
||||
protected function decorateFilterFromToken(IFilter $filter, SearchToken $token)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
$requirement = new Requirement();
|
||||
$requirement->setType(TagFilter::REQUIREMENT_PARTIAL_TAG_NAME);
|
||||
$requirement->setValue(new RequirementSingleValue($token->getValue()));
|
||||
$requirement->setNegated($token->isNegated());
|
||||
$filter->addRequirement($requirement);
|
||||
}
|
||||
|
||||
protected function decorateFilterFromNamedToken(IFilter $filter, NamedSearchToken $namedToken)
|
||||
|
|
Loading…
Reference in a new issue