diff --git a/config.ini b/config.ini index 937a9120..29f7a93b 100644 --- a/config.ini +++ b/config.ini @@ -92,3 +92,5 @@ deleteComment.own=registered deleteComment.all=moderator listTags=anonymous +mergeTags=moderator +renameTags=moderator diff --git a/public_html/media/css/tag-list.css b/public_html/media/css/tag-list.css new file mode 100644 index 00000000..0ae17233 --- /dev/null +++ b/public_html/media/css/tag-list.css @@ -0,0 +1,36 @@ +.tags ul { + list-style-type: none; + margin: 0; + padding: 0; + column-width: 14em; + -moz-column-width: 14em; + -webkit-column-width: 14em; +} +.tags li { + margin: 0.2em 0.5em; + text-align: top; + width: 14em; + display: inline-block; +} + +.form-wrapper { + width: 50%; + display: inline-block; + text-align: center; +} +.small-screen .form-wrapper { + width: 100%; +} +form.aligned { + text-align: left; + margin: 2em auto; +} +form.aligned label.left { + width: 7em; +} +form.aligned input { + width: 24em; +} +form h1 { + text-align: center; +} diff --git a/src/Controllers/PostController.php b/src/Controllers/PostController.php index 0bd51dd2..8dbf8506 100644 --- a/src/Controllers/PostController.php +++ b/src/Controllers/PostController.php @@ -236,7 +236,7 @@ class PostController /* tags */ $suppliedTags = InputHelper::get('tags'); - $suppliedTags = Model_Post::validateTags($suppliedTags); + $suppliedTags = Model_Tag::validateTags($suppliedTags); $dbTags = Model_Tag::insertOrUpdate($suppliedTags); /* source */ @@ -301,7 +301,7 @@ class PostController if (InputHelper::get('tags-token') != $currentToken) throw new SimpleException('Someone else has changed the tags in the meantime'); - $suppliedTags = Model_Post::validateTags($suppliedTags); + $suppliedTags = Model_Tag::validateTags($suppliedTags); $dbTags = Model_Tag::insertOrUpdate($suppliedTags); $post->sharedTag = $dbTags; $edited = true; diff --git a/src/Controllers/TagController.php b/src/Controllers/TagController.php index d78a1f94..657a44d8 100644 --- a/src/Controllers/TagController.php +++ b/src/Controllers/TagController.php @@ -6,6 +6,7 @@ class TagController */ public function listAction() { + $this->context->stylesheets []= 'tag-list.css'; $this->context->subTitle = 'tags'; PrivilegesHelper::confirmWithException(Privilege::ListTags); @@ -16,7 +17,7 @@ class TagController $dbQuery->innerJoin('post_tag'); $dbQuery->on('tag.id = post_tag.tag_id'); $dbQuery->groupBy('tag.id'); - $dbQuery->orderBy('count desc, LOWER(tag.name) asc'); + $dbQuery->orderBy('LOWER(tag.name)')->asc(); $rows = $dbQuery->get(); $tags = []; @@ -30,4 +31,49 @@ class TagController $this->context->transport->tags = $tags; $this->context->transport->tagDistribution = $tagDistribution; } + + /** + * @route /tags/merge + */ + public function mergeAction() + { + PrivilegesHelper::confirmWithException(Privilege::MergeTags); + $sourceTag = Model_Tag::locate(InputHelper::get('source-tag')); + $targetTag = Model_Tag::locate(InputHelper::get('target-tag')); + + R::preload($sourceTag, 'post'); + + foreach ($sourceTag->sharedPost as $post) + { + foreach ($post->sharedTag as $key => $postTag) + if ($postTag->id == $sourceTag->id) + unset($post->sharedTag[$key]); + $post->sharedTag []= $targetTag; + R::store($post); + } + R::trash($sourceTag); + \Chibi\UrlHelper::forward(\Chibi\UrlHelper::route('tag', 'list')); + $this->view->context->success = true; + } + + /** + * @route /tags/rename + */ + public function renameAction() + { + PrivilegesHelper::confirmWithException(Privilege::MergeTags); + + $suppliedSourceTag = InputHelper::get('source-tag'); + $suppliedSourceTag = Model_Tag::validateTag($suppliedSourceTag); + + $suppliedTargetTag = InputHelper::get('target-tag'); + $suppliedTargetTag = Model_Tag::validateTag($suppliedTargetTag); + + $sourceTag = Model_Tag::locate($suppliedSourceTag); + $sourceTag->name = $suppliedTargetTag; + R::store($sourceTag); + + \Chibi\UrlHelper::forward(\Chibi\UrlHelper::route('tag', 'list')); + $this->context->transport->success = true; + } } diff --git a/src/Models/Model_Post.php b/src/Models/Model_Post.php index 6f5fbfac..7ee8ad42 100644 --- a/src/Models/Model_Post.php +++ b/src/Models/Model_Post.php @@ -38,38 +38,4 @@ class Model_Post extends RedBean_SimpleModel return $source; } - - - public static function validateTag($tag) - { - $tag = trim($tag); - - $minLength = 1; - $maxLength = 64; - if (strlen($tag) < $minLength) - throw new SimpleException('Tag must have at least ' . $minLength . ' characters'); - if (strlen($tag) > $maxLength) - throw new SimpleException('Tag must have at most ' . $maxLength . ' characters'); - - if (!preg_match('/^[a-zA-Z0-9_-]+$/i', $tag)) - throw new SimpleException('Invalid tag "' . $tag . '"'); - - return $tag; - } - - public static function validateTags($tags) - { - $tags = trim($tags); - $tags = preg_split('/[,;\s]+/', $tags); - $tags = array_filter($tags, function($x) { return $x != ''; }); - $tags = array_unique($tags); - - foreach ($tags as $key => $tag) - $tags[$key] = self::validateTag($tag); - - if (empty($tags)) - throw new SimpleException('No tags set'); - - return $tags; - } } diff --git a/src/Models/Model_Tag.php b/src/Models/Model_Tag.php index 0a1d0a4f..c3ed0cc0 100644 --- a/src/Models/Model_Tag.php +++ b/src/Models/Model_Tag.php @@ -1,6 +1,14 @@ $maxLength) + throw new SimpleException('Tag must have at most ' . $maxLength . ' characters'); + + if (!preg_match('/^[a-zA-Z0-9_-]+$/i', $tag)) + throw new SimpleException('Invalid tag "' . $tag . '"'); + + return $tag; + } + + public static function validateTags($tags) + { + $tags = trim($tags); + $tags = preg_split('/[,;\s]+/', $tags); + $tags = array_filter($tags, function($x) { return $x != ''; }); + $tags = array_unique($tags); + + foreach ($tags as $key => $tag) + $tags[$key] = self::validateTag($tag); + + if (empty($tags)) + throw new SimpleException('No tags set'); + + return $tags; + } } diff --git a/src/Models/Privilege.php b/src/Models/Privilege.php index 151e25d1..9ed0bc2d 100644 --- a/src/Models/Privilege.php +++ b/src/Models/Privilege.php @@ -30,4 +30,6 @@ class Privilege extends Enum const DeleteComment = 24; const ListTags = 21; + const MergeTags = 27; + const RenameTags = 27; } diff --git a/src/Views/tag-list.phtml b/src/Views/tag-list.phtml index 792f041c..ba997c68 100644 --- a/src/Views/tag-list.phtml +++ b/src/Views/tag-list.phtml @@ -1,9 +1,55 @@ - +
+ +
+ + +
+
+

merge tags

+
+ +
+
+ +
+ +
+
+ +
+ + +
+
+
+ + + +
+
+

rename tags

+
+ +
+
+ +
+ +
+
+ +
+ + +
+
+
+