Added tag name changing

This commit is contained in:
Marcin Kurczewski 2014-10-10 09:53:05 +02:00
parent 74e6e008dc
commit 672185f959
13 changed files with 199 additions and 13 deletions

1
TODO
View file

@ -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

View file

@ -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

View file

@ -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;
}

View file

@ -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',
}; };

View file

@ -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);

View file

@ -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>

View file

@ -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);
}
} }

View file

@ -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;

View file

@ -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);

View file

@ -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);

View 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]);
}
}

View file

@ -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';

View file

@ -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);
}
} }