Added tag listing
This commit is contained in:
parent
7579d962ba
commit
49124298a2
12 changed files with 230 additions and 7 deletions
3
TODO
3
TODO
|
@ -29,9 +29,6 @@ everything related to tags:
|
||||||
- automatic removal of unused tags in backend
|
- automatic removal of unused tags in backend
|
||||||
- tags.json refresh when editing post
|
- tags.json refresh when editing post
|
||||||
- basic tags
|
- basic tags
|
||||||
- listing
|
|
||||||
- sort alphabetically
|
|
||||||
- sort by time of addition
|
|
||||||
- mass tag
|
- mass tag
|
||||||
- merging
|
- merging
|
||||||
- tag editing
|
- tag editing
|
||||||
|
|
28
public_html/css/tag-list.css
Normal file
28
public_html/css/tag-list.css
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#tag-list {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tag-list ul {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ta-list ul.order {
|
||||||
|
margin: -0.5em -0.5em 0.5em -0.5em;
|
||||||
|
}
|
||||||
|
#tag-list ul.order li {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 0.5em;
|
||||||
|
}
|
||||||
|
#tag-list ul.order a {
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0.2em 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tag-list table {
|
||||||
|
min-width: 20em;
|
||||||
|
text-align: left;
|
||||||
|
margin: 1em auto;
|
||||||
|
}
|
|
@ -39,6 +39,7 @@
|
||||||
<link rel="stylesheet" type="text/css" href="/css/history.css"/>
|
<link rel="stylesheet" type="text/css" href="/css/history.css"/>
|
||||||
<link rel="stylesheet" type="text/css" href="/css/comments.css"/>
|
<link rel="stylesheet" type="text/css" href="/css/comments.css"/>
|
||||||
<link rel="stylesheet" type="text/css" href="/css/tags.css"/>
|
<link rel="stylesheet" type="text/css" href="/css/tags.css"/>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/css/tag-list.css"/>
|
||||||
<!-- /build -->
|
<!-- /build -->
|
||||||
|
|
||||||
<!-- build:remove -->
|
<!-- build:remove -->
|
||||||
|
|
|
@ -2,27 +2,94 @@ var App = App || {};
|
||||||
App.Presenters = App.Presenters || {};
|
App.Presenters = App.Presenters || {};
|
||||||
|
|
||||||
App.Presenters.TagListPresenter = function(
|
App.Presenters.TagListPresenter = function(
|
||||||
|
_,
|
||||||
jQuery,
|
jQuery,
|
||||||
|
util,
|
||||||
|
promise,
|
||||||
|
pagerPresenter,
|
||||||
topNavigationPresenter) {
|
topNavigationPresenter) {
|
||||||
|
|
||||||
var $el = jQuery('#content');
|
var $el = jQuery('#content');
|
||||||
|
var templates = {};
|
||||||
|
|
||||||
function init(args, loaded) {
|
function init(args, loaded) {
|
||||||
topNavigationPresenter.select('tags');
|
topNavigationPresenter.select('tags');
|
||||||
topNavigationPresenter.changeTitle('Tags');
|
topNavigationPresenter.changeTitle('Tags');
|
||||||
|
|
||||||
|
promise.wait(
|
||||||
|
util.promiseTemplate('tag-list'),
|
||||||
|
util.promiseTemplate('tag-list-item'))
|
||||||
|
.then(function(listTemplate, listItemTemplate) {
|
||||||
|
templates.list = listTemplate;
|
||||||
|
templates.listItem = listItemTemplate;
|
||||||
|
|
||||||
render();
|
render();
|
||||||
loaded();
|
loaded();
|
||||||
|
|
||||||
|
pagerPresenter.init({
|
||||||
|
baseUri: '#/tags',
|
||||||
|
backendUri: '/tags',
|
||||||
|
$target: $el.find('.pagination-target'),
|
||||||
|
updateCallback: function(data, clear) {
|
||||||
|
renderTags(data.entities, clear);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
reinit(args, function() {});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function reinit(args, loaded) {
|
||||||
|
loaded();
|
||||||
|
|
||||||
|
var searchArgs = util.parseComplexRouteArgs(args.searchArgs);
|
||||||
|
searchArgs.order = searchArgs.order || 'name,asc';
|
||||||
|
searchArgs.page = parseInt(searchArgs.page) || 1;
|
||||||
|
updateActiveOrder(searchArgs.order);
|
||||||
|
|
||||||
|
pagerPresenter.reinit({
|
||||||
|
page: searchArgs.page,
|
||||||
|
searchParams: {
|
||||||
|
order: searchArgs.order}});
|
||||||
|
}
|
||||||
|
|
||||||
|
function deinit() {
|
||||||
|
pagerPresenter.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
function render() {
|
function render() {
|
||||||
$el.html('Tag list placeholder');
|
$el.html(templates.list());
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateActiveOrder(activeOrder) {
|
||||||
|
$el.find('.order li a').removeClass('active');
|
||||||
|
$el.find('.order [href*="' + activeOrder + '"]').addClass('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderTags(tags, clear) {
|
||||||
|
var $target = $el.find('.tags');
|
||||||
|
|
||||||
|
if (clear) {
|
||||||
|
$target.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
_.each(tags, function(tag) {
|
||||||
|
var $item = jQuery(templates.listItem({
|
||||||
|
tag: tag,
|
||||||
|
formatRelativeTime: util.formatRelativeTime,
|
||||||
|
}));
|
||||||
|
$target.append($item);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
init: init,
|
init: init,
|
||||||
|
reinit: reinit,
|
||||||
|
deinit: deinit,
|
||||||
render: render,
|
render: render,
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
App.DI.register('tagListPresenter', ['jQuery', 'topNavigationPresenter'], App.Presenters.TagListPresenter);
|
App.DI.register('tagListPresenter', ['_', 'jQuery', 'util', 'promise', 'pagerPresenter', 'topNavigationPresenter'], App.Presenters.TagListPresenter);
|
||||||
|
|
|
@ -24,7 +24,7 @@ App.Presenters.UserPresenter = function(
|
||||||
|
|
||||||
function init(args, loaded) {
|
function init(args, loaded) {
|
||||||
promise.wait(util.promiseTemplate('user'))
|
promise.wait(util.promiseTemplate('user'))
|
||||||
.then(function(template, response) {
|
.then(function(template) {
|
||||||
$messages = $el.find('.messages');
|
$messages = $el.find('.messages');
|
||||||
templates.user = template;
|
templates.user = template;
|
||||||
reinit(args, loaded);
|
reinit(args, loaded);
|
||||||
|
|
5
public_html/templates/tag-list-item.tpl
Normal file
5
public_html/templates/tag-list-item.tpl
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<tr class="tag">
|
||||||
|
<td>
|
||||||
|
<%= tag.name %> (<%= tag.usages %>)
|
||||||
|
</td>
|
||||||
|
</tr>
|
18
public_html/templates/tag-list.tpl
Normal file
18
public_html/templates/tag-list.tpl
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<div id="tag-list">
|
||||||
|
<ul class="order">
|
||||||
|
<li>
|
||||||
|
<a class="big-button" href="#/tags/order=name,asc">Tags</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a class="big-button" href="#/tags/order=creation_time,desc">Recent</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a class="big-button" href="#/tags/order=usage_count,desc">Popular</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="pagination-target">
|
||||||
|
<table class="tags">
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
45
src/Controllers/TagController.php
Normal file
45
src/Controllers/TagController.php
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
<?php
|
||||||
|
namespace Szurubooru\Controllers;
|
||||||
|
|
||||||
|
final class TagController extends AbstractController
|
||||||
|
{
|
||||||
|
private $privilegeService;
|
||||||
|
private $tagService;
|
||||||
|
private $tagViewProxy;
|
||||||
|
private $tagSearchParser;
|
||||||
|
private $inputReader;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
\Szurubooru\Services\PrivilegeService $privilegeService,
|
||||||
|
\Szurubooru\Services\TagService $tagService,
|
||||||
|
\Szurubooru\Controllers\ViewProxies\TagViewProxy $tagViewProxy,
|
||||||
|
\Szurubooru\SearchServices\Parsers\TagSearchParser $tagSearchParser,
|
||||||
|
\Szurubooru\Helpers\InputReader $inputReader)
|
||||||
|
{
|
||||||
|
$this->privilegeService = $privilegeService;
|
||||||
|
$this->tagService = $tagService;
|
||||||
|
$this->tagViewProxy = $tagViewProxy;
|
||||||
|
$this->tagSearchParser = $tagSearchParser;
|
||||||
|
$this->inputReader = $inputReader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function registerRoutes(\Szurubooru\Router $router)
|
||||||
|
{
|
||||||
|
$router->get('/api/tags', [$this, 'getTags']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTags()
|
||||||
|
{
|
||||||
|
$this->privilegeService->assertPrivilege(\Szurubooru\Privilege::LIST_TAGS);
|
||||||
|
|
||||||
|
$filter = $this->tagSearchParser->createFilterFromInputReader($this->inputReader);
|
||||||
|
$filter->setPageSize(50);
|
||||||
|
|
||||||
|
$result = $this->tagService->getFiltered($filter);
|
||||||
|
$entities = $this->tagViewProxy->fromArray($result->getEntities());
|
||||||
|
return [
|
||||||
|
'data' => $entities,
|
||||||
|
'pageSize' => $result->getPageSize(),
|
||||||
|
'totalRecords' => $result->getTotalRecords()];
|
||||||
|
}
|
||||||
|
}
|
15
src/SearchServices/Filters/TagFilter.php
Normal file
15
src/SearchServices/Filters/TagFilter.php
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<?php
|
||||||
|
namespace Szurubooru\SearchServices\Filters;
|
||||||
|
|
||||||
|
class TagFilter extends BasicFilter implements IFilter
|
||||||
|
{
|
||||||
|
const ORDER_ID = 'id';
|
||||||
|
const ORDER_NAME = 'name';
|
||||||
|
const ORDER_CREATION_TIME = 'creationTime';
|
||||||
|
const ORDER_USAGE_COUNT = 'usages';
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->setOrder([self::ORDER_ID => self::ORDER_DESC]);
|
||||||
|
}
|
||||||
|
}
|
37
src/SearchServices/Parsers/TagSearchParser.php
Normal file
37
src/SearchServices/Parsers/TagSearchParser.php
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
<?php
|
||||||
|
namespace Szurubooru\SearchServices\Parsers;
|
||||||
|
|
||||||
|
class TagSearchParser extends AbstractSearchParser
|
||||||
|
{
|
||||||
|
protected function createFilter()
|
||||||
|
{
|
||||||
|
return new \Szurubooru\SearchServices\Filters\TagFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function decorateFilterFromToken($filter, $token)
|
||||||
|
{
|
||||||
|
throw new \Szurubooru\NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function decorateFilterFromNamedToken($filter, $namedToken)
|
||||||
|
{
|
||||||
|
throw new \Szurubooru\NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getOrderColumn($token)
|
||||||
|
{
|
||||||
|
if ($token === 'id')
|
||||||
|
return \Szurubooru\SearchServices\Filters\TagFilter::ORDER_ID;
|
||||||
|
|
||||||
|
elseif ($token === 'name')
|
||||||
|
return \Szurubooru\SearchServices\Filters\TagFilter::ORDER_NAME;
|
||||||
|
|
||||||
|
elseif ($token === 'creation_time')
|
||||||
|
return \Szurubooru\SearchServices\Filters\TagFilter::ORDER_CREATION_TIME;
|
||||||
|
|
||||||
|
elseif ($token === 'usage_count')
|
||||||
|
return \Szurubooru\SearchServices\Filters\TagFilter::ORDER_USAGE_COUNT;
|
||||||
|
|
||||||
|
throw new \Szurubooru\NotSupportedException();
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,6 +17,15 @@ class TagService
|
||||||
$this->timeService = $timeService;
|
$this->timeService = $timeService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getFiltered(\Szurubooru\SearchServices\Filters\TagFilter $filter)
|
||||||
|
{
|
||||||
|
$transactionFunc = function() use ($filter)
|
||||||
|
{
|
||||||
|
return $this->tagDao->findFiltered($filter);
|
||||||
|
};
|
||||||
|
return $this->transactionManager->rollback($transactionFunc);
|
||||||
|
}
|
||||||
|
|
||||||
public function createTags(array $tags)
|
public function createTags(array $tags)
|
||||||
{
|
{
|
||||||
$transactionFunc = function() use ($tags)
|
$transactionFunc = function() use ($tags)
|
||||||
|
|
|
@ -49,6 +49,7 @@ return [
|
||||||
$container->get(\Szurubooru\Controllers\FavoritesController::class),
|
$container->get(\Szurubooru\Controllers\FavoritesController::class),
|
||||||
$container->get(\Szurubooru\Controllers\ScoreController::class),
|
$container->get(\Szurubooru\Controllers\ScoreController::class),
|
||||||
$container->get(\Szurubooru\Controllers\CommentController::class),
|
$container->get(\Szurubooru\Controllers\CommentController::class),
|
||||||
|
$container->get(\Szurubooru\Controllers\TagController::class),
|
||||||
];
|
];
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
Loading…
Reference in a new issue