Enforce min/max tag count settings

This commit is contained in:
Toby Zerner 2015-08-04 13:04:22 +09:30
parent 92e9b5b414
commit a042f04fa3
8 changed files with 127 additions and 18 deletions

View File

@ -3,6 +3,7 @@ import DiscussionPage from 'flarum/components/DiscussionPage';
import Button from 'flarum/components/Button';
import highlight from 'flarum/helpers/highlight';
import classList from 'flarum/utils/classList';
import extractText from 'flarum/utils/extractText';
import tagLabel from 'tags/helpers/tagLabel';
import tagIcon from 'tags/helpers/tagIcon';
@ -24,6 +25,19 @@ export default class TagDiscussionModal extends Modal {
} else if (this.props.discussion) {
this.props.discussion.tags().map(this.addTag.bind(this));
}
this.minPrimary = app.forum.attribute('minPrimaryTags');
this.maxPrimary = app.forum.attribute('maxPrimaryTags');
this.minSecondary = app.forum.attribute('minSecondaryTags');
this.maxSecondary = app.forum.attribute('maxSecondaryTags');
}
primaryCount() {
return this.selected.filter(tag => tag.isPrimary()).length;
}
secondaryCount() {
return this.selected.filter(tag => !tag.isPrimary()).length;
}
/**
@ -75,17 +89,46 @@ export default class TagDiscussionModal extends Modal {
: app.trans('tags.tag_new_discussion_title');
}
getInstruction(primaryCount, secondaryCount) {
if (primaryCount < this.minPrimary) {
return app.trans('tags.choose_primary_tags', {count: this.minPrimary - primaryCount});
} else if (secondaryCount < this.minSecondary) {
return app.trans('tags.choose_secondary_tags', {count: this.minSecondary - secondaryCount});
}
return '';
}
content() {
let tags = this.tags;
const filter = this.filter().toLowerCase();
const primaryCount = this.primaryCount();
const secondaryCount = this.secondaryCount();
// Filter out all child tags whose parents have not been selected. This
// makes it impossible to select a child if its parent hasn't been selected.
tags = tags.filter(tag => {
const parent = tag.parent();
return parent === false || this.selected.indexOf(parent) !== -1;
});
// If the number of selected primary/secondary tags is at the maximum, then
// we'll filter out all other tags of that type.
if (primaryCount >= app.forum.attribute('maxPrimaryTags')) {
tags = tags.filter(tag => !tag.isPrimary() || this.selected.indexOf(tag) !== -1);
}
if (secondaryCount >= app.forum.attribute('maxSecondaryTags')) {
tags = tags.filter(tag => tag.isPrimary() || this.selected.indexOf(tag) !== -1);
}
// If the user has entered text in the filter input, then filter by tags
// whose name matches what they've entered.
if (filter) {
tags = tags.filter(tag => tag.name().substr(0, filter.length).toLowerCase() === filter);
}
if (tags.indexOf(this.index) === -1) {
this.index = tags[0];
}
if (tags.indexOf(this.index) === -1) this.index = tags[0];
return [
<div className="Modal-body">
@ -103,7 +146,7 @@ export default class TagDiscussionModal extends Modal {
)}
</span>
<input className="FormControl"
placeholder={!this.selected.length ? app.trans('tags.discussion_tags_placeholder') : ''}
placeholder={extractText(this.getInstruction(primaryCount, secondaryCount))}
value={this.filter()}
oninput={m.withAttr('value', this.filter)}
onkeydown={this.onkeydown.bind(this)}
@ -115,7 +158,7 @@ export default class TagDiscussionModal extends Modal {
{Button.component({
type: 'submit',
className: 'Button Button--primary',
disabled: !this.selected.length,
disabled: primaryCount < this.minPrimary || secondaryCount < this.minSecondary,
icon: 'check',
children: app.trans('tags.confirm')
})}
@ -125,12 +168,9 @@ export default class TagDiscussionModal extends Modal {
<div className="Modal-footer">
<ul className="TagDiscussionModal-list SelectTagList">
{tags.map(tag => {
if (!filter && tag.parent() && this.selected.indexOf(tag.parent()) === -1) {
return '';
}
return (
{tags
.filter(tag => filter || !tag.parent() || this.selected.indexOf(tag.parent()) !== -1)
.map(tag => (
<li data-index={tag.id()}
className={classList({
pinned: tag.position() !== null,
@ -154,8 +194,7 @@ export default class TagDiscussionModal extends Modal {
</span>
) : ''}
</li>
);
})}
))}
</ul>
</div>
];

View File

@ -1,5 +1,6 @@
import Model from 'flarum/Model';
import mixin from 'flarum/utils/mixin';
import computed from 'flarum/utils/computed';
export default class Tag extends mixin(Model, {
name: Model.attribute('name'),
@ -21,5 +22,7 @@ export default class Tag extends mixin(Model, {
lastDiscussion: Model.hasOne('lastDiscussion'),
isRestricted: Model.attribute('isRestricted'),
canStartDiscussion: Model.attribute('canStartDiscussion')
canStartDiscussion: Model.attribute('canStartDiscussion'),
isPrimary: computed('position', 'parent', (position, parent) => position !== null && parent === false)
}) {}

View File

@ -58,8 +58,12 @@
cursor: not-allowed;
}
.TagsInput-selected {
.TagLabel {
.TagsInput-tag {
margin-right: 5px;
&:last-child {
margin-right: 10px;
}
}
}

View File

@ -7,7 +7,12 @@ tags:
tag_new_discussion_title: Choose Tags for Your Discussion
edit_discussion_tags_title: "Edit Tags for {title}"
edit_discussion_tags_link: Edit Tags
discussion_tags_placeholder: Choose one or more topics
choose_primary_tags:
one: Choose a primary tag
other: "Choose {count} primary tags"
choose_secondary_tags:
one: Choose 1 more tag
other: "Choose {count} more tags"
confirm: Confirm
more: More...
tag_cloud_title: Tags

View File

@ -70,6 +70,15 @@ class AddApiAttributes
if ($event->serializer instanceof DiscussionSerializer) {
$event->attributes['canTag'] = $event->model->can($event->actor, 'tag');
}
if ($event->serializer instanceof ForumSerializer) {
$settings = app('Flarum\Core\Settings\SettingsRepository');
$event->attributes['minPrimaryTags'] = $settings->get('tags.min_primary_tags');
$event->attributes['maxPrimaryTags'] = $settings->get('tags.max_primary_tags');
$event->attributes['minSecondaryTags'] = $settings->get('tags.min_secondary_tags');
$event->attributes['maxSecondaryTags'] = $settings->get('tags.max_secondary_tags');
}
}
public function addRoutes(RegisterApiRoutes $event)

View File

@ -35,7 +35,8 @@ class AddClientAssets
'tags.tag_new_discussion_title',
'tags.edit_discussion_tags_title',
'tags.edit_discussion_tags_link',
'tags.discussion_tags_placeholder',
'tags.choose_primary_tags',
'tags.choose_secondary_tags',
'tags.confirm',
'tags.more',
'tags.tag_cloud_title',

View File

@ -5,9 +5,18 @@ use Flarum\Tags\Events\DiscussionWasTagged;
use Flarum\Events\DiscussionWillBeSaved;
use Flarum\Core\Discussions\Discussion;
use Flarum\Core\Exceptions\PermissionDeniedException;
use Flarum\Core\Settings\SettingsRepository;
use Flarum\Tags\TagCountException;
class PersistData
{
protected $settings;
public function __construct(SettingsRepository $settings)
{
$this->settings = $settings;
}
public function subscribe($events)
{
$events->listen(DiscussionWillBeSaved::class, [$this, 'whenDiscussionWillBeSaved']);
@ -25,13 +34,25 @@ class PersistData
$newTagIds[] = (int) $link['id'];
}
$newTags = Tag::whereIn('id', $newTagIds);
$newTags = Tag::whereIn('id', $newTagIds)->get();
$primaryCount = 0;
$secondaryCount = 0;
foreach ($newTags as $tag) {
if (! $tag->can($actor, 'startDiscussion')) {
throw new PermissionDeniedException;
}
if ($tag->position !== null && $tag->parent_id === null) {
$primaryCount++;
} else {
$secondaryCount++;
}
}
$this->validatePrimaryTagCount($primaryCount);
$this->validateSecondaryTagCount($secondaryCount);
$oldTags = [];
if ($discussion->exists) {
@ -50,4 +71,24 @@ class PersistData
});
}
}
protected function validatePrimaryTagCount($count)
{
$min = $this->settings->get('tags.min_primary_tags');
$max = $this->settings->get('tags.max_primary_tags');
if ($count < $min || $count > $max) {
throw new TagCountException(['tags' => sprintf('Discussion must have between %d and %d primary tags.', $min, $max)]);
}
}
protected function validateSecondaryTagCount($count)
{
$min = $this->settings->get('tags.min_secondary_tags');
$max = $this->settings->get('tags.max_secondary_tags');
if ($count < $min || $count > $max) {
throw new TagCountException(['tags' => sprintf('Discussion must have between %d and %d secondary tags.', $min, $max)]);
}
}
}

View File

@ -0,0 +1,7 @@
<?php namespace Flarum\Tags;
use Flarum\Core\Exceptions\ValidationException;
class TagCountException extends ValidationException
{
}