diff --git a/core/client/app/controllers/post-settings-menu.js b/core/client/app/controllers/post-settings-menu.js index 25bd0fdcd2..6cbcc80fed 100644 --- a/core/client/app/controllers/post-settings-menu.js +++ b/core/client/app/controllers/post-settings-menu.js @@ -476,6 +476,8 @@ export default Ember.Controller.extend(SettingsMenuMixin, { availableTagNames = null, tagToAdd = null; + tagName = tagName.trim(); + // abort if tag is already selected if (currentTagNames.contains(tagName.toLowerCase())) { return; diff --git a/core/client/app/validators/tag-settings.js b/core/client/app/validators/tag-settings.js index a09b8b505a..4830790cb5 100644 --- a/core/client/app/validators/tag-settings.js +++ b/core/client/app/validators/tag-settings.js @@ -8,6 +8,9 @@ var TagSettingsValidator = BaseValidator.create({ if (validator.empty(name)) { model.get('errors').add('name', 'You must specify a name for the tag.'); this.invalidate(); + } else if (name.match(/^,/)) { + model.get('errors').add('name', 'Tag names can\'t start with commas.'); + this.invalidate(); } }, metaTitle: function (model) { diff --git a/core/server/data/schema.js b/core/server/data/schema.js index 4087622be6..68ee40dd17 100644 --- a/core/server/data/schema.js +++ b/core/server/data/schema.js @@ -100,7 +100,7 @@ var db = { tags: { id: {type: 'increments', nullable: false, primary: true}, uuid: {type: 'string', maxlength: 36, nullable: false, validations: {isUUID: true}}, - name: {type: 'string', maxlength: 150, nullable: false}, + name: {type: 'string', maxlength: 150, nullable: false, validations: {matches: /^([^,]|$)/}}, slug: {type: 'string', maxlength: 150, nullable: false, unique: true}, description: {type: 'string', maxlength: 200, nullable: true}, image: {type: 'text', maxlength: 2000, nullable: true}, diff --git a/core/test/functional/client/tags_test.js b/core/test/functional/client/tags_test.js index f50338102a..255316fbbf 100644 --- a/core/test/functional/client/tags_test.js +++ b/core/test/functional/client/tags_test.js @@ -17,7 +17,7 @@ CasperTest.begin('Tags screen is correct', 6, function suite(test) { }); }); -CasperTest.begin('Tag creation', 14, function suite(test) { +CasperTest.begin('Tag creation', 16, function suite(test) { casper.thenOpenAndWaitForPageLoad('settings.tags'); casper.thenClick('.view-actions .btn-green'); @@ -44,7 +44,7 @@ CasperTest.begin('Tag creation', 14, function suite(test) { test.assertSelectorHasText('.settings-tags .tags-count', '0'); }); - casper.then(function testNameValidation() { + casper.then(function testMissingNameValidation() { casper.fill('.tag-settings-pane form', { name: '' }); @@ -56,6 +56,18 @@ CasperTest.begin('Tag creation', 14, function suite(test) { }); }); + casper.then(function testNameStartsWithCommaValidation() { + casper.fill('.tag-settings-pane form', { + name: ',, commas' + }); + casper.waitForText('Tag names can\'t start with commas.', function onSuccess() { + test.assertExists('.form-group.error input[name="name"]'); + test.assert(true, 'Error displayed for tag name starting with comma'); + }, function doneWaiting() { + test.fail('Error not displayed for tag name starting with comma'); + }); + }); + casper.thenClick('.meta-data-button'); casper.waitForOpaque('.tag-meta-settings-pane', function onSuccess() { diff --git a/core/test/integration/api/api_tags_spec.js b/core/test/integration/api/api_tags_spec.js index d75dc32413..6aa8b5af7f 100644 --- a/core/test/integration/api/api_tags_spec.js +++ b/core/test/integration/api/api_tags_spec.js @@ -52,6 +52,20 @@ describe('Tags API', function () { done(); }).catch(done); }); + + it('rejects invalid names with ValidationError', function (done) { + var invalidTag = _.clone(newTag); + + invalidTag.name = ', starts with a comma'; + + TagAPI.add({tags: [invalidTag]}, testUtils.context.admin) + .then(function () { + done(new Error('Adding a tag with an invalid name is not rejected.')); + }).catch(function (errors) { + errors.should.have.enumerable(0).with.property('errorType', 'ValidationError'); + done(); + }).catch(done); + }); }); describe('Edit', function () { @@ -86,6 +100,18 @@ describe('Tags API', function () { done(); }).catch(done); }); + + it('rejects invalid names with ValidationError', function (done) { + var invalidTagName = ', starts with a comma'; + + TagAPI.edit({tags: [{name: invalidTagName}]}, _.extend({}, context.editor, {id: firstTag})) + .then(function () { + done(new Error('Adding a tag with an invalid name is not rejected.')); + }).catch(function (errors) { + errors.should.have.enumerable(0).with.property('errorType', 'ValidationError'); + done(); + }).catch(done); + }); }); describe('Destroy', function () {