Added tag name changing
This commit is contained in:
parent
74e6e008dc
commit
672185f959
13 changed files with 199 additions and 13 deletions
1
TODO
1
TODO
|
@ -26,7 +26,6 @@ everything related to tags:
|
||||||
- basic tags
|
- basic tags
|
||||||
- merging
|
- merging
|
||||||
- tag editing
|
- tag editing
|
||||||
- name
|
|
||||||
- category (from config.ini)
|
- category (from config.ini)
|
||||||
- description
|
- description
|
||||||
- related tags
|
- related tags
|
||||||
|
|
|
@ -57,6 +57,7 @@ changePostFlags = regularUser, powerUser, moderator, administrator
|
||||||
|
|
||||||
listTags = anonymous, regularUser, powerUser, moderator, administrator
|
listTags = anonymous, regularUser, powerUser, moderator, administrator
|
||||||
massTag = powerUser, moderator, administrator
|
massTag = powerUser, moderator, administrator
|
||||||
|
changeTagName = moderator, administrator
|
||||||
|
|
||||||
listComments = anonymous, regularUser, powerUser, moderator, administrator
|
listComments = anonymous, regularUser, powerUser, moderator, administrator
|
||||||
addComments = regularUser, powerUser, moderator, administrator
|
addComments = regularUser, powerUser, moderator, administrator
|
||||||
|
|
|
@ -17,3 +17,9 @@
|
||||||
#tag-view h3 {
|
#tag-view h3 {
|
||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#tag-view form {
|
||||||
|
text-align: left;
|
||||||
|
max-width: 20em;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
|
@ -43,6 +43,7 @@ App.Auth = function(_, jQuery, util, api, appState, promise) {
|
||||||
|
|
||||||
listTags: 'listTags',
|
listTags: 'listTags',
|
||||||
massTag: 'massTag',
|
massTag: 'massTag',
|
||||||
|
changeTagName: 'changeTagName',
|
||||||
|
|
||||||
viewHistory: 'viewHistory',
|
viewHistory: 'viewHistory',
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,19 +6,28 @@ App.Presenters.TagPresenter = function(
|
||||||
jQuery,
|
jQuery,
|
||||||
util,
|
util,
|
||||||
promise,
|
promise,
|
||||||
|
auth,
|
||||||
api,
|
api,
|
||||||
|
router,
|
||||||
keyboard,
|
keyboard,
|
||||||
topNavigationPresenter) {
|
topNavigationPresenter,
|
||||||
|
messagePresenter) {
|
||||||
|
|
||||||
var $el = jQuery('#content');
|
var $el = jQuery('#content');
|
||||||
|
var $messages = $el;
|
||||||
var templates = {};
|
var templates = {};
|
||||||
|
|
||||||
|
var tag;
|
||||||
var tagName;
|
var tagName;
|
||||||
|
|
||||||
|
var privileges = {};
|
||||||
|
|
||||||
function init(params, loaded) {
|
function init(params, loaded) {
|
||||||
topNavigationPresenter.select('tags');
|
topNavigationPresenter.select('tags');
|
||||||
topNavigationPresenter.changeTitle('Tags');
|
topNavigationPresenter.changeTitle('Tags');
|
||||||
|
|
||||||
|
privileges.canChangeName = auth.hasPrivilege(auth.privileges.changeTagName);
|
||||||
|
|
||||||
promise.wait(
|
promise.wait(
|
||||||
util.promiseTemplate('tag'),
|
util.promiseTemplate('tag'),
|
||||||
util.promiseTemplate('post-list-item'))
|
util.promiseTemplate('post-list-item'))
|
||||||
|
@ -33,22 +42,50 @@ App.Presenters.TagPresenter = function(
|
||||||
function reinit(params, loaded) {
|
function reinit(params, loaded) {
|
||||||
tagName = params.tagName;
|
tagName = params.tagName;
|
||||||
|
|
||||||
render();
|
messagePresenter.hideMessages($messages);
|
||||||
loaded();
|
|
||||||
|
|
||||||
promise.wait(api.get('posts', {query: tagName}))
|
promise.wait(
|
||||||
.then(function(response) {
|
api.get('tags/' + tagName),
|
||||||
var posts = response.json.data;
|
api.get('posts', {query: tagName}))
|
||||||
|
.then(function(tagResponse, postsResponse) {
|
||||||
|
tag = tagResponse.json;
|
||||||
|
var posts = postsResponse.json.data;
|
||||||
posts = posts.slice(0, 8);
|
posts = posts.slice(0, 8);
|
||||||
|
|
||||||
|
render();
|
||||||
|
loaded();
|
||||||
|
|
||||||
renderPosts(posts);
|
renderPosts(posts);
|
||||||
}).fail(function(response) {
|
}).fail(function(tagResponse, postsResponse) {
|
||||||
console.log(new Error(response));
|
messagePresenter.showError($messages, tagResponse.json.error || postsResponse.json.error);
|
||||||
|
loaded();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function render() {
|
function render() {
|
||||||
$el.html(templates.tag({tagName: tagName}));
|
$el.html(templates.tag({privileges: privileges, tag: tag, tagName: tagName}));
|
||||||
$el.find('.post-list').hide();
|
$el.find('.post-list').hide();
|
||||||
|
$el.find('form').submit(editFormSubmitted);
|
||||||
|
}
|
||||||
|
|
||||||
|
function editFormSubmitted(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var $form = $el.find('form');
|
||||||
|
var formData = {};
|
||||||
|
|
||||||
|
if (privileges.canChangeName) {
|
||||||
|
formData.name = $form.find('[name=name]').val();
|
||||||
|
}
|
||||||
|
|
||||||
|
promise.wait(api.put('/tags/' + tag.name, formData))
|
||||||
|
.then(function(response) {
|
||||||
|
tag = response.json;
|
||||||
|
render();
|
||||||
|
router.navigate('#/tag/' + tag.name);
|
||||||
|
}).fail(function(response) {
|
||||||
|
console.log(response);
|
||||||
|
window.alert('An error occurred');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderPosts(posts) {
|
function renderPosts(posts) {
|
||||||
|
@ -76,4 +113,4 @@ App.Presenters.TagPresenter = function(
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
App.DI.register('tagPresenter', ['_', 'jQuery', 'util', 'promise', 'api', 'keyboard', 'topNavigationPresenter'], App.Presenters.TagPresenter);
|
App.DI.register('tagPresenter', ['_', 'jQuery', 'util', 'promise', 'auth', 'api', 'router', 'keyboard', 'topNavigationPresenter', 'messagePresenter'], App.Presenters.TagPresenter);
|
||||||
|
|
|
@ -3,6 +3,26 @@
|
||||||
<h1><%= tagName %></h1>
|
<h1><%= tagName %></h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<% if (_.any(privileges)) { %>
|
||||||
|
<form class="edit">
|
||||||
|
<% if (privileges.canChangeName) { %>
|
||||||
|
<div class="form-row">
|
||||||
|
<label class="form-label" for="tag-name">Name:</label>
|
||||||
|
<div class="form-input">
|
||||||
|
<input maxlength="200" type="text" name="name" id="tag-name" placeholder="New tag name" value="<%= tag.name %>"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label class="form-label"></label>
|
||||||
|
<div class="form-input">
|
||||||
|
<button type="submit">Update</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
<div class="post-list">
|
<div class="post-list">
|
||||||
<h3>Example usages</h3>
|
<h3>Example usages</h3>
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Szurubooru\Controllers;
|
namespace Szurubooru\Controllers;
|
||||||
use Szurubooru\Controllers\ViewProxies\TagViewProxy;
|
use Szurubooru\Controllers\ViewProxies\TagViewProxy;
|
||||||
|
use Szurubooru\FormData\TagEditFormData;
|
||||||
use Szurubooru\Helpers\InputReader;
|
use Szurubooru\Helpers\InputReader;
|
||||||
use Szurubooru\Privilege;
|
use Szurubooru\Privilege;
|
||||||
use Szurubooru\Router;
|
use Szurubooru\Router;
|
||||||
|
@ -33,6 +34,16 @@ final class TagController extends AbstractController
|
||||||
public function registerRoutes(Router $router)
|
public function registerRoutes(Router $router)
|
||||||
{
|
{
|
||||||
$router->get('/api/tags', [$this, 'getTags']);
|
$router->get('/api/tags', [$this, 'getTags']);
|
||||||
|
$router->get('/api/tags/:tagName', [$this, 'getTag']);
|
||||||
|
$router->put('/api/tags/:tagName', [$this, 'updateTag']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTag($tagName)
|
||||||
|
{
|
||||||
|
$this->privilegeService->assertPrivilege(Privilege::LIST_TAGS);
|
||||||
|
|
||||||
|
$tag = $this->tagService->getByName($tagName);
|
||||||
|
return $this->tagViewProxy->fromEntity($tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTags()
|
public function getTags()
|
||||||
|
@ -49,4 +60,16 @@ final class TagController extends AbstractController
|
||||||
'pageSize' => $result->getPageSize(),
|
'pageSize' => $result->getPageSize(),
|
||||||
'totalRecords' => $result->getTotalRecords()];
|
'totalRecords' => $result->getTotalRecords()];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function updateTag($tagName)
|
||||||
|
{
|
||||||
|
$tag = $this->tagService->getByName($tagName);
|
||||||
|
$formData = new TagEditFormData($this->inputReader);
|
||||||
|
|
||||||
|
if ($formData->name !== null)
|
||||||
|
$this->privilegeService->assertPrivilege(Privilege::CHANGE_TAG_NAME);
|
||||||
|
|
||||||
|
$tag = $this->tagService->updateTag($tag, $formData);
|
||||||
|
return $this->tagViewProxy->fromEntity($tag);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -242,12 +242,15 @@ abstract class AbstractDao implements ICrudDao, IBatchDao
|
||||||
$query->where($sql, ...$bindings);
|
$query->where($sql, ...$bindings);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function arrayToEntities(array $arrayEntities)
|
protected function arrayToEntities(array $arrayEntities, $entityConverter = null)
|
||||||
{
|
{
|
||||||
|
if ($entityConverter === null)
|
||||||
|
$entityConverter = $this->entityConverter;
|
||||||
|
|
||||||
$entities = [];
|
$entities = [];
|
||||||
foreach ($arrayEntities as $arrayEntity)
|
foreach ($arrayEntities as $arrayEntity)
|
||||||
{
|
{
|
||||||
$entity = $this->entityConverter->toEntity($arrayEntity);
|
$entity = $entityConverter->toEntity($arrayEntity);
|
||||||
$entities[$entity->getId()] = $entity;
|
$entities[$entity->getId()] = $entity;
|
||||||
}
|
}
|
||||||
return $entities;
|
return $entities;
|
||||||
|
|
|
@ -52,6 +52,17 @@ class PostDao extends AbstractDao implements ICrudDao
|
||||||
return $this->findOneBy('name', $name);
|
return $this->findOneBy('name', $name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function findByTagName($tagName)
|
||||||
|
{
|
||||||
|
$query = $this->fpdo->from('posts')
|
||||||
|
->disableSmartJoin()
|
||||||
|
->innerJoin('postTags ON postTags.postId = posts.id')
|
||||||
|
->innerJoin('tags ON postTags.tagId = tags.id')
|
||||||
|
->where('tags.name', $tagName);
|
||||||
|
$arrayEntities = iterator_to_array($query);
|
||||||
|
return $this->arrayToEntities($arrayEntities);
|
||||||
|
}
|
||||||
|
|
||||||
public function findByContentChecksum($checksum)
|
public function findByContentChecksum($checksum)
|
||||||
{
|
{
|
||||||
return $this->findOneBy('contentChecksum', $checksum);
|
return $this->findOneBy('contentChecksum', $checksum);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Szurubooru\Dao;
|
namespace Szurubooru\Dao;
|
||||||
|
use Szurubooru\Dao\EntityConverters\PostEntityConverter;
|
||||||
use Szurubooru\Dao\EntityConverters\TagEntityConverter;
|
use Szurubooru\Dao\EntityConverters\TagEntityConverter;
|
||||||
use Szurubooru\DatabaseConnection;
|
use Szurubooru\DatabaseConnection;
|
||||||
|
|
||||||
|
@ -13,6 +14,11 @@ class TagDao extends AbstractDao implements ICrudDao
|
||||||
new TagEntityConverter());
|
new TagEntityConverter());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function findByName($tagName)
|
||||||
|
{
|
||||||
|
return $this->findOneBy('name', $tagName);
|
||||||
|
}
|
||||||
|
|
||||||
public function findByNames($tagNames)
|
public function findByNames($tagNames)
|
||||||
{
|
{
|
||||||
return $this->findBy('name', $tagNames);
|
return $this->findBy('name', $tagNames);
|
||||||
|
|
24
src/FormData/TagEditFormData.php
Normal file
24
src/FormData/TagEditFormData.php
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
namespace Szurubooru\FormData;
|
||||||
|
use Szurubooru\IValidatable;
|
||||||
|
use Szurubooru\Validator;
|
||||||
|
|
||||||
|
class TagEditFormData implements IValidatable
|
||||||
|
{
|
||||||
|
public $name;
|
||||||
|
|
||||||
|
public function __construct($inputReader = null)
|
||||||
|
{
|
||||||
|
if ($inputReader !== null)
|
||||||
|
{
|
||||||
|
$this->name = $inputReader->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validate(Validator $validator)
|
||||||
|
{
|
||||||
|
if ($this->name !== null)
|
||||||
|
$validator->validatePostTags([$this->name]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@ class Privilege
|
||||||
|
|
||||||
const LIST_TAGS = 'listTags';
|
const LIST_TAGS = 'listTags';
|
||||||
const MASS_TAG = 'massTag';
|
const MASS_TAG = 'massTag';
|
||||||
|
const CHANGE_TAG_NAME = 'changeTagName';
|
||||||
|
|
||||||
const LIST_COMMENTS = 'listComments';
|
const LIST_COMMENTS = 'listComments';
|
||||||
const ADD_COMMENTS = 'addComments';
|
const ADD_COMMENTS = 'addComments';
|
||||||
|
|
|
@ -1,31 +1,55 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Szurubooru\Services;
|
namespace Szurubooru\Services;
|
||||||
use Szurubooru\Dao\PublicFileDao;
|
use Szurubooru\Dao\PublicFileDao;
|
||||||
|
use Szurubooru\Dao\PostDao;
|
||||||
use Szurubooru\Dao\TagDao;
|
use Szurubooru\Dao\TagDao;
|
||||||
use Szurubooru\Dao\TransactionManager;
|
use Szurubooru\Dao\TransactionManager;
|
||||||
use Szurubooru\Entities\Tag;
|
use Szurubooru\Entities\Tag;
|
||||||
|
use Szurubooru\FormData\TagEditFormData;
|
||||||
use Szurubooru\SearchServices\Filters\TagFilter;
|
use Szurubooru\SearchServices\Filters\TagFilter;
|
||||||
use Szurubooru\Services\TimeService;
|
use Szurubooru\Services\TimeService;
|
||||||
|
use Szurubooru\Validator;
|
||||||
|
|
||||||
class TagService
|
class TagService
|
||||||
{
|
{
|
||||||
|
private $validator;
|
||||||
private $transactionManager;
|
private $transactionManager;
|
||||||
|
private $postDao;
|
||||||
private $tagDao;
|
private $tagDao;
|
||||||
private $fileDao;
|
private $fileDao;
|
||||||
private $timeService;
|
private $timeService;
|
||||||
|
private $historyService;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
Validator $validator,
|
||||||
TransactionManager $transactionManager,
|
TransactionManager $transactionManager,
|
||||||
|
PostDao $postDao,
|
||||||
TagDao $tagDao,
|
TagDao $tagDao,
|
||||||
PublicFileDao $fileDao,
|
PublicFileDao $fileDao,
|
||||||
|
HistoryService $historyService,
|
||||||
TimeService $timeService)
|
TimeService $timeService)
|
||||||
{
|
{
|
||||||
|
$this->validator = $validator;
|
||||||
$this->transactionManager = $transactionManager;
|
$this->transactionManager = $transactionManager;
|
||||||
|
$this->postDao = $postDao;
|
||||||
$this->tagDao = $tagDao;
|
$this->tagDao = $tagDao;
|
||||||
$this->fileDao = $fileDao;
|
$this->fileDao = $fileDao;
|
||||||
|
$this->historyService = $historyService;
|
||||||
$this->timeService = $timeService;
|
$this->timeService = $timeService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getByName($tagName)
|
||||||
|
{
|
||||||
|
$transactionFunc = function() use ($tagName)
|
||||||
|
{
|
||||||
|
$tag = $this->tagDao->findByName($tagName);
|
||||||
|
if (!$tag)
|
||||||
|
throw new \InvalidArgumentException('Tag with name "' . $tagName . '" was not found.');
|
||||||
|
return $tag;
|
||||||
|
};
|
||||||
|
return $this->transactionManager->rollback($transactionFunc);
|
||||||
|
}
|
||||||
|
|
||||||
public function getFiltered(TagFilter $filter)
|
public function getFiltered(TagFilter $filter)
|
||||||
{
|
{
|
||||||
$transactionFunc = function() use ($filter)
|
$transactionFunc = function() use ($filter)
|
||||||
|
@ -96,4 +120,34 @@ class TagService
|
||||||
};
|
};
|
||||||
return $this->transactionManager->commit($transactionFunc);
|
return $this->transactionManager->commit($transactionFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function updateTag(Tag $tag, TagEditFormData $formData)
|
||||||
|
{
|
||||||
|
$transactionFunc = function() use ($tag, $formData)
|
||||||
|
{
|
||||||
|
$this->validator->validate($formData);
|
||||||
|
|
||||||
|
if ($formData->name !== null)
|
||||||
|
$this->updateTagName($tag, $formData->name);
|
||||||
|
|
||||||
|
return $this->tagDao->save($tag);
|
||||||
|
};
|
||||||
|
$ret = $this->transactionManager->commit($transactionFunc);
|
||||||
|
|
||||||
|
$transactionFunc = function() use ($tag)
|
||||||
|
{
|
||||||
|
$posts = $this->postDao->findByTagName($tag->getName());
|
||||||
|
foreach ($posts as $post)
|
||||||
|
$this->historyService->saveSnapshot($this->historyService->getPostChangeSnapshot($post));
|
||||||
|
};
|
||||||
|
$this->transactionManager->commit($transactionFunc);
|
||||||
|
|
||||||
|
$this->exportJson();
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function updateTagName(Tag $tag, $newName)
|
||||||
|
{
|
||||||
|
$tag->setName($newName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue