From 983c708eceff0528db4fc5e5c694378ad29d42de Mon Sep 17 00:00:00 2001 From: Kevin Ansfield Date: Mon, 25 Apr 2016 10:54:36 +0100 Subject: [PATCH] Don't share errors and hasValidated references between validator instances no issue - ensure that each validator instance gets it's own `errors` and `hasValidated` objects - updates some uses of `ValidationEngine` that were relying on side-effects of the unintended reference sharing - fixes issue with add subscriber modal displaying an error state after opening if it previously had errors when closing --- ghost/admin/app/controllers/settings/general.js | 4 ---- ghost/admin/app/controllers/team/user.js | 5 +---- ghost/admin/app/mixins/validation-engine.js | 10 ++++++++-- ghost/admin/app/templates/settings/general.hbs | 6 +++--- ghost/admin/app/templates/team/user.hbs | 10 +++++----- ghost/admin/tests/acceptance/team-test.js | 6 +++--- 6 files changed, 20 insertions(+), 21 deletions(-) diff --git a/ghost/admin/app/controllers/settings/general.js b/ghost/admin/app/controllers/settings/general.js index 1332bd4b76..da7a4653c4 100644 --- a/ghost/admin/app/controllers/settings/general.js +++ b/ghost/admin/app/controllers/settings/general.js @@ -92,10 +92,6 @@ export default Controller.extend(SettingsSaveMixin, { }, actions: { - validate(property) { - this.get('model').validate({property}); - }, - checkPostsPerPage() { let postsPerPage = this.get('model.postsPerPage'); diff --git a/ghost/admin/app/controllers/team/user.js b/ghost/admin/app/controllers/team/user.js index e917b32fc6..e275d2d5cf 100644 --- a/ghost/admin/app/controllers/team/user.js +++ b/ghost/admin/app/controllers/team/user.js @@ -1,7 +1,6 @@ import Ember from 'ember'; import isNumber from 'ghost/utils/isNumber'; import boundOneWay from 'ghost/utils/bound-one-way'; -import ValidationEngine from 'ghost/mixins/validation-engine'; const { Controller, @@ -12,9 +11,7 @@ const { } = Ember; const {alias, and, not, or, readOnly} = computed; -export default Controller.extend(ValidationEngine, { - // ValidationEngine settings - validationType: 'user', +export default Controller.extend({ submitting: false, lastPromise: null, showDeleteUserModal: false, diff --git a/ghost/admin/app/mixins/validation-engine.js b/ghost/admin/app/mixins/validation-engine.js index 22c769f72d..8f4490e54b 100644 --- a/ghost/admin/app/mixins/validation-engine.js +++ b/ghost/admin/app/mixins/validation-engine.js @@ -49,11 +49,17 @@ export default Mixin.create({ // This adds the Errors object to the validation engine, and shouldn't affect // ember-data models because they essentially use the same thing - errors: Errors.create(), + errors: null, // Store whether a property has been validated yet, so that we know whether or not // to show error / success validation for a field - hasValidated: emberA(), + hasValidated: null, + + init() { + this._super(...arguments); + this.set('errors', Errors.create()); + this.set('hasValidated', emberA()); + }, /** * Passes the model to the validator specified by validationType. diff --git a/ghost/admin/app/templates/settings/general.hbs b/ghost/admin/app/templates/settings/general.hbs index 1fb0cf1e2f..1cc9e290c0 100644 --- a/ghost/admin/app/templates/settings/general.hbs +++ b/ghost/admin/app/templates/settings/general.hbs @@ -12,14 +12,14 @@ {{#gh-form-group errors=model.errors hasValidated=model.hasValidated property="title"}} - {{gh-input id="blog-title" class="gh-input" name="general[title]" type="text" value=model.title focusOut=(action "validate" "title")}} + {{gh-input id="blog-title" class="gh-input" name="general[title]" type="text" value=model.title focusOut=(action "validate" "title" target=model)}} {{gh-error-message errors=model.errors property="title"}}

The name of your blog

{{/gh-form-group}} {{#gh-form-group errors=model.errors hasValidated=model.hasValidated property="description" class="description-container"}} - {{gh-textarea id="blog-description" class="gh-input" name="general[description]" value=model.description focusOut=(action "validate" "description")}} + {{gh-textarea id="blog-description" class="gh-input" name="general[description]" value=model.description focusOut=(action "validate" "description" target=model)}} {{gh-error-message errors=model.errors property="description"}}

Describe what your blog is about @@ -108,7 +108,7 @@ {{#if model.isPrivate}} {{#gh-form-group errors=model.errors hasValidated=model.hasValidated property="password"}} - {{gh-input name="general[password]" type="text" value=model.password focusOut=(action "validate" "password")}} + {{gh-input name="general[password]" type="text" value=model.password focusOut=(action "validate" "password" target=model)}} {{gh-error-message errors=model.errors property="password"}}

This password will be needed to access your blog. All search engine optimization and social features are now disabled. This password is stored in plaintext.

{{/gh-form-group}} diff --git a/ghost/admin/app/templates/team/user.hbs b/ghost/admin/app/templates/team/user.hbs index 0711204ef8..e14a3184b3 100644 --- a/ghost/admin/app/templates/team/user.hbs +++ b/ghost/admin/app/templates/team/user.hbs @@ -79,7 +79,7 @@ {{#gh-form-group errors=user.errors hasValidated=user.hasValidated property="name" class="first-form-group"}} - {{input value=user.name id="user-name" class="gh-input user-name" placeholder="Full Name" autocorrect="off" focusOut=(action "validate" "name")}} + {{input value=user.name id="user-name" class="gh-input user-name" placeholder="Full Name" autocorrect="off" focusOut=(action "validate" "name" target=user)}} {{#if user.errors.name}} {{gh-error-message errors=user.errors property="name"}} {{else}} @@ -102,7 +102,7 @@ {{!-- Administrators only see text of Owner's email address but not input --}} {{#unless isAdminUserOnOwnerProfile}} - {{input type="email" value=user.email id="user-email" name="email" class="gh-input" placeholder="Email Address" autocapitalize="off" autocorrect="off" autocomplete="off" focusOut=(action "validate" "email")}} + {{input type="email" value=user.email id="user-email" name="email" class="gh-input" placeholder="Email Address" autocapitalize="off" autocorrect="off" autocomplete="off" focusOut=(action "validate" "email" target=user)}} {{gh-error-message errors=user.errors property="email"}} {{else}} {{user.email}} @@ -128,21 +128,21 @@ {{#gh-form-group errors=user.errors hasValidated=user.hasValidated property="location"}} - {{input type="text" value=user.location id="user-location" class="gh-input" focusOut=(action "validate" "location")}} + {{input type="text" value=user.location id="user-location" class="gh-input" focusOut=(action "validate" "location" target=user)}} {{gh-error-message errors=user.errors property="location"}}

Where in the world do you live?

{{/gh-form-group}} {{#gh-form-group errors=user.errors hasValidated=user.hasValidated property="website"}} - {{input type="url" value=user.website id="user-website" class="gh-input" autocapitalize="off" autocorrect="off" autocomplete="off" focusOut=(action "validate" "website")}} + {{input type="url" value=user.website id="user-website" class="gh-input" autocapitalize="off" autocorrect="off" autocomplete="off" focusOut=(action "validate" "website" target=user)}} {{gh-error-message errors=user.errors property="website"}}

Have a website or blog other than this one? Link it!

{{/gh-form-group}} {{#gh-form-group errors=user.errors hasValidated=user.hasValidated property="bio" class="bio-container"}} - {{textarea id="user-bio" class="gh-input" value=user.bio focusOut=(action "validate" "bio")}} + {{textarea id="user-bio" class="gh-input" value=user.bio focusOut=(action "validate" "bio" target=user)}} {{gh-error-message errors=user.errors property="bio"}}

Write about you, in 200 characters or less. diff --git a/ghost/admin/tests/acceptance/team-test.js b/ghost/admin/tests/acceptance/team-test.js index f161d0c7d0..bf2e3029bd 100644 --- a/ghost/admin/tests/acceptance/team-test.js +++ b/ghost/admin/tests/acceptance/team-test.js @@ -256,7 +256,7 @@ describe('Acceptance: Team', function () { triggerEvent('.user-details-top .first-form-group input.user-name', 'blur'); andThen(() => { - expect(find('.user-details-top .first-form-group').hasClass('error'), 'input is in error state').to.be.true; + expect(find('.user-details-top .first-form-group').hasClass('error'), 'username input is in error state with blank input').to.be.true; }); // test too long user name @@ -264,7 +264,7 @@ describe('Acceptance: Team', function () { triggerEvent('.user-details-top .first-form-group input.user-name', 'blur'); andThen(() => { - expect(find('.user-details-top .first-form-group').hasClass('error'), 'input is in error state').to.be.true; + expect(find('.user-details-top .first-form-group').hasClass('error'), 'username input is in error state with too long input').to.be.true; }); // reset name field @@ -292,7 +292,7 @@ describe('Acceptance: Team', function () { triggerEvent('.user-details-bottom input[name="email"]', 'blur'); andThen(() => { - expect(find('.user-details-bottom .form-group:nth-of-type(2)').hasClass('error'), 'email input should be in error state').to.be.true; + expect(find('.user-details-bottom .form-group:nth-of-type(2)').hasClass('error'), 'email input should be in error state with invalid email').to.be.true; }); fillIn('.user-details-bottom input[name="email"]', 'test@example.com');