Added tag merging
This commit is contained in:
parent
be4b742d21
commit
2d529107db
11 changed files with 113 additions and 29 deletions
|
@ -74,6 +74,7 @@ changeTagImplications = moderator, administrator
|
|||
changeTagSuggestions = moderator, administrator
|
||||
banTags = moderator, administrator
|
||||
deleteTags = moderator, administrator
|
||||
mergeTags = moderator, administrator
|
||||
|
||||
listComments = regularUser, powerUser, moderator, administrator
|
||||
addComments = regularUser, powerUser, moderator, administrator
|
||||
|
|
|
@ -46,6 +46,7 @@ App.Auth = function(_, jQuery, util, api, appState, promise) {
|
|||
deleteOwnComments: 'deleteOwnComments',
|
||||
deleteAllComments: 'deleteAllComments',
|
||||
deleteTags: 'deleteTags',
|
||||
mergeTags: 'mergeTags',
|
||||
|
||||
listTags: 'listTags',
|
||||
massTag: 'massTag',
|
||||
|
|
|
@ -37,6 +37,7 @@ App.Presenters.TagPresenter = function(
|
|||
privileges.canBan = auth.hasPrivilege(auth.privileges.banTags);
|
||||
privileges.canViewHistory = auth.hasPrivilege(auth.privileges.viewHistory);
|
||||
privileges.canDelete = auth.hasPrivilege(auth.privileges.deleteTags);
|
||||
privileges.canMerge = auth.hasPrivilege(auth.privileges.mergeTags);
|
||||
|
||||
promise.wait(
|
||||
util.promiseTemplate('tag'),
|
||||
|
@ -92,6 +93,7 @@ App.Presenters.TagPresenter = function(
|
|||
$el.find('form').submit(function(e) { e.preventDefault(); });
|
||||
$el.find('form button[name=update]').click(updateButtonClicked);
|
||||
$el.find('form button[name=delete]').click(deleteButtonClicked);
|
||||
$el.find('form button[name=merge]').click(mergeButtonClicked);
|
||||
implicationsTagInput = App.Controls.TagInput($el.find('[name=implications]'));
|
||||
suggestionsTagInput = App.Controls.TagInput($el.find('[name=suggestions]'));
|
||||
}
|
||||
|
@ -141,6 +143,17 @@ App.Presenters.TagPresenter = function(
|
|||
});
|
||||
}
|
||||
|
||||
function mergeButtonClicked(e) {
|
||||
if (targetTag = window.prompt('What tag should this be merged to?')) {
|
||||
promise.wait(api.put('/tags/' + tag.name + '/merge', {targetTag: targetTag}))
|
||||
.then(function(response) {
|
||||
router.navigate('#/tags');
|
||||
}).fail(function(response) {
|
||||
window.alert(response.json && response.json.error || 'An error occured.');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function renderPosts(posts) {
|
||||
var $target = $el.find('.post-list ul');
|
||||
_.each(posts, function(post) {
|
||||
|
|
|
@ -74,6 +74,9 @@
|
|||
<% if (privileges.canDelete) { %>
|
||||
<button type="submit" name="delete">Delete</button>
|
||||
<% } %>
|
||||
<% if (privileges.canMerge) { %>
|
||||
<button type="submit" name="merge">Merge with…</button>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
|
|
|
@ -37,6 +37,7 @@ final class TagController extends AbstractController
|
|||
$router->get('/api/tags/:tagName', [$this, 'getTag']);
|
||||
$router->get('/api/tags/:tagName/siblings', [$this, 'getTagSiblings']);
|
||||
$router->put('/api/tags/:tagName', [$this, 'updateTag']);
|
||||
$router->put('/api/tags/:tagName/merge', [$this, 'mergeTag']);
|
||||
$router->delete('/api/tags/:tagName', [$this, 'deleteTag']);
|
||||
}
|
||||
|
||||
|
@ -105,6 +106,15 @@ final class TagController extends AbstractController
|
|||
return $this->tagService->deleteTag($tag);
|
||||
}
|
||||
|
||||
public function mergeTag($tagName)
|
||||
{
|
||||
$targetTagName = $this->inputReader->targetTag;
|
||||
$sourceTag = $this->tagService->getByName($tagName);
|
||||
$targetTag = $this->tagService->getByName($targetTagName);
|
||||
$this->privilegeService->assertPrivilege(Privilege::MERGE_TAGS);
|
||||
return $this->tagService->mergeTag($sourceTag, $targetTag);
|
||||
}
|
||||
|
||||
private function getFullFetchConfig()
|
||||
{
|
||||
return
|
||||
|
|
|
@ -47,6 +47,7 @@ class Privilege
|
|||
const CHANGE_TAG_SUGGESTIONS = 'changeTagSuggestions';
|
||||
const BAN_TAGS = 'banTags';
|
||||
const DELETE_TAGS = 'deleteTags';
|
||||
const MERGE_TAGS = 'mergeTags';
|
||||
|
||||
const LIST_COMMENTS = 'listComments';
|
||||
const ADD_COMMENTS = 'addComments';
|
||||
|
|
|
@ -186,6 +186,29 @@ class TagService
|
|||
$this->transactionManager->commit($transactionFunc);
|
||||
}
|
||||
|
||||
public function mergeTag(Tag $sourceTag, Tag $targetTag)
|
||||
{
|
||||
$transactionFunc = function() use ($sourceTag, $targetTag)
|
||||
{
|
||||
$posts = $this->postDao->findByTagName($sourceTag->getName());
|
||||
foreach ($posts as $post)
|
||||
{
|
||||
$newTags = $post->getTags();
|
||||
$newTags = array_filter($newTags, function(Tag $tag) use ($sourceTag) {
|
||||
return $tag->getId() !== $sourceTag->getId();
|
||||
});
|
||||
$newTags []= $targetTag;
|
||||
$post->setTags($newTags);
|
||||
$this->postDao->save($post);
|
||||
$this->postHistoryService->savePostChange($post);
|
||||
}
|
||||
|
||||
$this->tagHistoryService->saveTagDeletion($sourceTag);
|
||||
$this->tagDao->deleteById($sourceTag->getId());
|
||||
};
|
||||
$this->transactionManager->commit($transactionFunc);
|
||||
}
|
||||
|
||||
private function updateTagName(Tag $tag, $newName)
|
||||
{
|
||||
$otherTag = $this->tagDao->findByName($newName);
|
||||
|
|
|
@ -4,6 +4,7 @@ use Szurubooru\Config;
|
|||
use Szurubooru\Dao\PublicFileDao;
|
||||
use Szurubooru\DatabaseConnection;
|
||||
use Szurubooru\Entities\Post;
|
||||
use Szurubooru\Entities\Tag;
|
||||
use Szurubooru\Entities\User;
|
||||
use Szurubooru\Injector;
|
||||
use Szurubooru\Tests\AbstractTestCase;
|
||||
|
@ -35,6 +36,14 @@ abstract class AbstractDatabaseTestCase extends AbstractTestCase
|
|||
$this->databaseConnection->close();
|
||||
}
|
||||
|
||||
protected static function getTestTag($name = 'test')
|
||||
{
|
||||
$tag = new Tag();
|
||||
$tag->setName($name);
|
||||
$tag->setCreationTime(date('c'));
|
||||
return $tag;
|
||||
}
|
||||
|
||||
protected static function getTestPost()
|
||||
{
|
||||
$post = new Post();
|
||||
|
|
|
@ -13,9 +13,9 @@ final class TagDaoFilterTest extends AbstractDatabaseTestCase
|
|||
{
|
||||
public function testCategories()
|
||||
{
|
||||
$tag1 = $this->getTestTag('test 1');
|
||||
$tag2 = $this->getTestTag('test 2');
|
||||
$tag3 = $this->getTestTag('test 3');
|
||||
$tag1 = self::getTestTag('test 1');
|
||||
$tag2 = self::getTestTag('test 2');
|
||||
$tag3 = self::getTestTag('test 3');
|
||||
$tag2->setCategory('misc');
|
||||
$tag3->setCategory('other');
|
||||
$tagDao = $this->getTagDao();
|
||||
|
@ -36,9 +36,9 @@ final class TagDaoFilterTest extends AbstractDatabaseTestCase
|
|||
|
||||
public function testCompositeCategories()
|
||||
{
|
||||
$tag1 = $this->getTestTag('test 1');
|
||||
$tag2 = $this->getTestTag('test 2');
|
||||
$tag3 = $this->getTestTag('test 3');
|
||||
$tag1 = self::getTestTag('test 1');
|
||||
$tag2 = self::getTestTag('test 2');
|
||||
$tag3 = self::getTestTag('test 3');
|
||||
$tag2->setCategory('misc');
|
||||
$tag3->setCategory('other');
|
||||
$tagDao = $this->getTagDao();
|
||||
|
@ -61,12 +61,4 @@ final class TagDaoFilterTest extends AbstractDatabaseTestCase
|
|||
{
|
||||
return new TagDao($this->databaseConnection);
|
||||
}
|
||||
|
||||
private function getTestTag($name)
|
||||
{
|
||||
$tag = new Tag();
|
||||
$tag->setName($name);
|
||||
$tag->setCreationTime(date('c'));
|
||||
return $tag;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ final class TagDaoTest extends AbstractDatabaseTestCase
|
|||
|
||||
public function testSaving()
|
||||
{
|
||||
$tag = $this->getTestTag('test');
|
||||
$tag = self::getTestTag('test');
|
||||
$tag->setCreationTime(date('c', mktime(0, 0, 0, 10, 1, 2014)));
|
||||
$this->assertFalse($tag->isBanned());
|
||||
$tag->setBanned(true);
|
||||
|
@ -26,17 +26,17 @@ final class TagDaoTest extends AbstractDatabaseTestCase
|
|||
|
||||
public function testSavingRelations()
|
||||
{
|
||||
$tag1 = $this->getTestTag('test 1');
|
||||
$tag2 = $this->getTestTag('test 2');
|
||||
$tag3 = $this->getTestTag('test 3');
|
||||
$tag4 = $this->getTestTag('test 4');
|
||||
$tag1 = self::getTestTag('test 1');
|
||||
$tag2 = self::getTestTag('test 2');
|
||||
$tag3 = self::getTestTag('test 3');
|
||||
$tag4 = self::getTestTag('test 4');
|
||||
$tagDao = $this->getTagDao();
|
||||
$tagDao->save($tag1);
|
||||
$tagDao->save($tag2);
|
||||
$tagDao->save($tag3);
|
||||
$tagDao->save($tag4);
|
||||
|
||||
$tag = $this->getTestTag('test');
|
||||
$tag = self::getTestTag('test');
|
||||
$tag->setImpliedTags([$tag1, $tag3]);
|
||||
$tag->setSuggestedTags([$tag2, $tag4]);
|
||||
|
||||
|
@ -84,13 +84,4 @@ final class TagDaoTest extends AbstractDatabaseTestCase
|
|||
{
|
||||
return new TagDao($this->databaseConnection);
|
||||
}
|
||||
|
||||
private function getTestTag($name)
|
||||
{
|
||||
$tag = new Tag();
|
||||
$tag->setName($name);
|
||||
$tag->setCreationTime(date('c'));
|
||||
$tag->setCategory('default');
|
||||
return $tag;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
<?php
|
||||
namespace Szurubooru\Tests\Services;
|
||||
use Szurubooru\Dao\PostDao;
|
||||
use Szurubooru\Dao\PublicFileDao;
|
||||
use Szurubooru\Dao\TagDao;
|
||||
use Szurubooru\Entities\Post;
|
||||
use Szurubooru\Entities\Tag;
|
||||
use Szurubooru\Injector;
|
||||
use Szurubooru\Services\TagService;
|
||||
|
@ -116,6 +119,43 @@ final class TagServiceTest extends AbstractDatabaseTestCase
|
|||
$this->assertEquals('[{"name":"test","usages":0,"banned":false}]', $fileDao->load('tags.json'));
|
||||
}
|
||||
|
||||
public function testMerging()
|
||||
{
|
||||
$tag1 = self::getTestTag('test 1');
|
||||
$tag2 = self::getTestTag('test 2');
|
||||
$tag3 = self::getTestTag('test 3');
|
||||
|
||||
$tagDao = Injector::get(TagDao::class);
|
||||
$tagDao->save($tag1);
|
||||
$tagDao->save($tag2);
|
||||
$tagDao->save($tag3);
|
||||
|
||||
$post1 = self::getTestPost();
|
||||
$post2 = self::getTestPost();
|
||||
$post3 = self::getTestPost();
|
||||
$post1->setTags([$tag1]);
|
||||
$post2->setTags([$tag1, $tag3]);
|
||||
$post3->setTags([$tag2, $tag3]);
|
||||
|
||||
$postDao = Injector::get(PostDao::class);
|
||||
$postDao->save($post1);
|
||||
$postDao->save($post2);
|
||||
$postDao->save($post3);
|
||||
|
||||
$tagService = $this->getTagService();
|
||||
$tagService->mergeTag($tag1, $tag2);
|
||||
|
||||
$this->assertNull($tagDao->findByName($tag1->getName()));
|
||||
$this->assertNotNull($tagDao->findByName($tag2->getName()));
|
||||
|
||||
$post1 = $postDao->findById($post1->getId());
|
||||
$post2 = $postDao->findById($post2->getId());
|
||||
$post3 = $postDao->findById($post3->getId());
|
||||
$this->assertEntitiesEqual([$tag2], array_values($post1->getTags()));
|
||||
$this->assertEntitiesEqual([$tag2, $tag3], array_values($post2->getTags()));
|
||||
$this->assertEntitiesEqual([$tag2, $tag3], array_values($post3->getTags()));
|
||||
}
|
||||
|
||||
public function testExportMultiple()
|
||||
{
|
||||
$tag1 = new Tag();
|
||||
|
|
Loading…
Reference in a new issue