mirror of
https://github.com/discourse/discourse.git
synced 2025-03-14 10:33:43 +00:00
DEV: replace UserFieldsValidation mixin with helper class (#31670)
This PR refactors the UserFieldsValidation mixin into a helper class. ### Key Changes * moved this.userFields to a tracked collection of TrackedUserFields stored on the helper class itself * introduced `validationVisible` property to explicitly control when to display the validation result, we expect the helper to not display validation until form submission in the `SignupController` and `CreateAccount` components. * added backward compatibility to the plugin API `addCustomUserFieldValidationCallback` to ensure ex-Core instances of the mixin continues working till we remove them
This commit is contained in:
@ -36,16 +36,14 @@ import discourseComputed, { bind } from "discourse/lib/decorators";
|
||||
import NameValidationHelper from "discourse/lib/name-validation-helper";
|
||||
import PasswordValidationHelper from "discourse/lib/password-validation-helper";
|
||||
import { userPath } from "discourse/lib/url";
|
||||
import UserFieldsValidationHelper from "discourse/lib/user-fields-validation-helper";
|
||||
import UsernameValidationHelper from "discourse/lib/username-validation-helper";
|
||||
import { emailValid } from "discourse/lib/utilities";
|
||||
import UserFieldsValidation from "discourse/mixins/user-fields-validation";
|
||||
import { findAll } from "discourse/models/login-method";
|
||||
import User from "discourse/models/user";
|
||||
import { i18n } from "discourse-i18n";
|
||||
|
||||
export default class CreateAccount extends Component.extend(
|
||||
UserFieldsValidation
|
||||
) {
|
||||
export default class CreateAccount extends Component {
|
||||
@service site;
|
||||
@service siteSettings;
|
||||
@service login;
|
||||
@ -62,12 +60,16 @@ export default class CreateAccount extends Component.extend(
|
||||
formSubmitted = false;
|
||||
rejectedEmails = A();
|
||||
prefilledUsername = null;
|
||||
userFields = null;
|
||||
maskPassword = true;
|
||||
emailValidationVisible = false;
|
||||
nameValidationHelper = new NameValidationHelper(this);
|
||||
usernameValidationHelper = new UsernameValidationHelper(this);
|
||||
passwordValidationHelper = new PasswordValidationHelper(this);
|
||||
userFieldsValidationHelper = new UserFieldsValidationHelper({
|
||||
getUserFields: () => this.site.get("user_fields"),
|
||||
getAccountPassword: () => this.accountPassword,
|
||||
showValidationOnInit: false,
|
||||
});
|
||||
|
||||
@setting("enable_local_logins") canCreateLocal;
|
||||
@setting("require_invite_code") requireInviteCode;
|
||||
@ -88,6 +90,16 @@ export default class CreateAccount extends Component.extend(
|
||||
}
|
||||
}
|
||||
|
||||
@dependentKeyCompat
|
||||
get userFields() {
|
||||
return this.userFieldsValidationHelper.userFields;
|
||||
}
|
||||
|
||||
@dependentKeyCompat
|
||||
get userFieldsValidation() {
|
||||
return this.userFieldsValidationHelper.userFieldsValidation;
|
||||
}
|
||||
|
||||
@action
|
||||
setAccountUsername(event) {
|
||||
this.accountUsername = event.target.value;
|
||||
@ -436,9 +448,7 @@ export default class CreateAccount extends Component.extend(
|
||||
// Add the userFields to the data
|
||||
if (!isEmpty(this.userFields)) {
|
||||
attrs.userFields = {};
|
||||
this.userFields.forEach(
|
||||
(f) => (attrs.userFields[f.get("field.id")] = f.get("value"))
|
||||
);
|
||||
this.userFields.forEach((f) => (attrs.userFields[f.field.id] = f.value));
|
||||
}
|
||||
|
||||
this.set("formSubmitted", true);
|
||||
@ -529,6 +539,7 @@ export default class CreateAccount extends Component.extend(
|
||||
createAccount() {
|
||||
this.set("flash", "");
|
||||
this.nameValidationHelper.forceValidationReason = true;
|
||||
this.userFieldsValidationHelper.validationVisible = true;
|
||||
this.set("emailValidationVisible", true);
|
||||
|
||||
const validation = [
|
||||
@ -555,6 +566,7 @@ export default class CreateAccount extends Component.extend(
|
||||
return;
|
||||
}
|
||||
|
||||
this.userFieldsValidationHelper.validationVisible = false;
|
||||
this.nameValidationHelper.forceValidationReason = false;
|
||||
this.performAccountCreation();
|
||||
}
|
||||
|
@ -11,15 +11,13 @@ import getUrl from "discourse/lib/get-url";
|
||||
import NameValidationHelper from "discourse/lib/name-validation-helper";
|
||||
import PasswordValidationHelper from "discourse/lib/password-validation-helper";
|
||||
import DiscourseURL from "discourse/lib/url";
|
||||
import UserFieldsValidationHelper from "discourse/lib/user-fields-validation-helper";
|
||||
import UsernameValidationHelper from "discourse/lib/username-validation-helper";
|
||||
import { emailValid } from "discourse/lib/utilities";
|
||||
import UserFieldsValidation from "discourse/mixins/user-fields-validation";
|
||||
import { findAll as findLoginMethods } from "discourse/models/login-method";
|
||||
import { i18n } from "discourse-i18n";
|
||||
|
||||
export default class InvitesShowController extends Controller.extend(
|
||||
UserFieldsValidation
|
||||
) {
|
||||
export default class InvitesShowController extends Controller {
|
||||
@tracked accountPassword;
|
||||
@tracked accountUsername;
|
||||
@tracked isDeveloper;
|
||||
@ -27,6 +25,10 @@ export default class InvitesShowController extends Controller.extend(
|
||||
nameValidationHelper = new NameValidationHelper(this);
|
||||
usernameValidationHelper = new UsernameValidationHelper(this);
|
||||
passwordValidationHelper = new PasswordValidationHelper(this);
|
||||
userFieldsValidationHelper = new UserFieldsValidationHelper({
|
||||
getUserFields: () => this.site.get("user_fields"),
|
||||
getAccountPassword: () => this.accountPassword,
|
||||
});
|
||||
successMessage = null;
|
||||
@readOnly("model.is_invite_link") isInviteLink;
|
||||
@readOnly("model.invited_by") invitedBy;
|
||||
@ -41,11 +43,19 @@ export default class InvitesShowController extends Controller.extend(
|
||||
@alias("model.different_external_email") differentExternalEmail;
|
||||
@not("externalAuthsOnly") passwordRequired;
|
||||
errorMessage = null;
|
||||
userFields = null;
|
||||
authOptions = null;
|
||||
rejectedEmails = [];
|
||||
maskPassword = true;
|
||||
|
||||
get userFields() {
|
||||
return this.userFieldsValidationHelper.userFields;
|
||||
}
|
||||
|
||||
@dependentKeyCompat
|
||||
get userFieldsValidation() {
|
||||
return this.userFieldsValidationHelper.userFieldsValidation;
|
||||
}
|
||||
|
||||
@action
|
||||
setAccountUsername(event) {
|
||||
this.accountUsername = event.target.value;
|
||||
@ -320,11 +330,10 @@ export default class InvitesShowController extends Controller.extend(
|
||||
|
||||
@action
|
||||
submit() {
|
||||
const userFields = this.userFields;
|
||||
let userCustomFields = {};
|
||||
if (!isEmpty(userFields)) {
|
||||
userFields.forEach(function (f) {
|
||||
userCustomFields[f.get("field.id")] = f.get("value");
|
||||
if (!isEmpty(this.userFields)) {
|
||||
this.userFields.forEach(function (f) {
|
||||
userCustomFields[f.field.id] = f.value;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -16,16 +16,14 @@ import discourseComputed, { bind } from "discourse/lib/decorators";
|
||||
import NameValidationHelper from "discourse/lib/name-validation-helper";
|
||||
import PasswordValidationHelper from "discourse/lib/password-validation-helper";
|
||||
import { userPath } from "discourse/lib/url";
|
||||
import UserFieldsValidationHelper from "discourse/lib/user-fields-validation-helper";
|
||||
import UsernameValidationHelper from "discourse/lib/username-validation-helper";
|
||||
import { emailValid } from "discourse/lib/utilities";
|
||||
import UserFieldsValidation from "discourse/mixins/user-fields-validation";
|
||||
import { findAll } from "discourse/models/login-method";
|
||||
import User from "discourse/models/user";
|
||||
import { i18n } from "discourse-i18n";
|
||||
|
||||
export default class SignupPageController extends Controller.extend(
|
||||
UserFieldsValidation
|
||||
) {
|
||||
export default class SignupPageController extends Controller {
|
||||
@service site;
|
||||
@service siteSettings;
|
||||
@service login;
|
||||
@ -42,12 +40,16 @@ export default class SignupPageController extends Controller.extend(
|
||||
formSubmitted = false;
|
||||
rejectedEmails = A();
|
||||
prefilledUsername = null;
|
||||
userFields = null;
|
||||
maskPassword = true;
|
||||
emailValidationVisible = false;
|
||||
nameValidationHelper = new NameValidationHelper(this);
|
||||
usernameValidationHelper = new UsernameValidationHelper(this);
|
||||
passwordValidationHelper = new PasswordValidationHelper(this);
|
||||
userFieldsValidationHelper = new UserFieldsValidationHelper({
|
||||
getUserFields: () => this.site.get("user_fields"),
|
||||
getAccountPassword: () => this.accountPassword,
|
||||
showValidationOnInit: false,
|
||||
});
|
||||
|
||||
@notEmpty("authOptions") hasAuthOptions;
|
||||
@setting("enable_local_logins") canCreateLocal;
|
||||
@ -63,6 +65,16 @@ export default class SignupPageController extends Controller.extend(
|
||||
this.fetchConfirmationValue();
|
||||
}
|
||||
|
||||
@dependentKeyCompat
|
||||
get userFields() {
|
||||
return this.userFieldsValidationHelper.userFields;
|
||||
}
|
||||
|
||||
@dependentKeyCompat
|
||||
get userFieldsValidation() {
|
||||
return this.userFieldsValidationHelper.userFieldsValidation;
|
||||
}
|
||||
|
||||
@dependentKeyCompat
|
||||
get usernameValidation() {
|
||||
return this.usernameValidationHelper.usernameValidation;
|
||||
@ -421,9 +433,7 @@ export default class SignupPageController extends Controller.extend(
|
||||
// Add the userFields to the data
|
||||
if (!isEmpty(this.userFields)) {
|
||||
attrs.userFields = {};
|
||||
this.userFields.forEach(
|
||||
(f) => (attrs.userFields[f.get("field.id")] = f.get("value"))
|
||||
);
|
||||
this.userFields.forEach((f) => (attrs.userFields[f.field.id] = f.value));
|
||||
}
|
||||
|
||||
this.set("formSubmitted", true);
|
||||
@ -514,6 +524,7 @@ export default class SignupPageController extends Controller.extend(
|
||||
createAccount() {
|
||||
this.set("flash", "");
|
||||
this.nameValidationHelper.forceValidationReason = true;
|
||||
this.userFieldsValidationHelper.validationVisible = true;
|
||||
this.set("emailValidationVisible", true);
|
||||
|
||||
const validation = [
|
||||
@ -540,6 +551,7 @@ export default class SignupPageController extends Controller.extend(
|
||||
return;
|
||||
}
|
||||
|
||||
this.userFieldsValidationHelper.validationVisible = false;
|
||||
this.nameValidationHelper.forceValidationReason = false;
|
||||
this.performAccountCreation();
|
||||
}
|
||||
|
@ -117,9 +117,10 @@ import {
|
||||
_registerTransformer,
|
||||
transformerTypes,
|
||||
} from "discourse/lib/transformer";
|
||||
import { addCustomUserFieldValidationCallback } from "discourse/lib/user-fields-validation-helper";
|
||||
import { registerUserMenuTab } from "discourse/lib/user-menu/tab";
|
||||
import { replaceFormatter } from "discourse/lib/utilities";
|
||||
import { addCustomUserFieldValidationCallback } from "discourse/mixins/user-fields-validation";
|
||||
import { addCustomUserFieldValidationCallback as addCustomUserFieldValidationCallbackDeprecated } from "discourse/mixins/user-fields-validation";
|
||||
import Composer, {
|
||||
registerCustomizationCallback,
|
||||
} from "discourse/models/composer";
|
||||
@ -1572,6 +1573,7 @@ class PluginApi {
|
||||
**/
|
||||
|
||||
addCustomUserFieldValidationCallback(callback) {
|
||||
addCustomUserFieldValidationCallbackDeprecated(callback);
|
||||
addCustomUserFieldValidationCallback(callback);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,114 @@
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import { TrackedArray } from "@ember-compat/tracked-built-ins";
|
||||
import { i18n } from "discourse-i18n";
|
||||
|
||||
const addCustomUserFieldValidationCallbacks = [];
|
||||
|
||||
export function addCustomUserFieldValidationCallback(callback) {
|
||||
addCustomUserFieldValidationCallbacks.push(callback);
|
||||
}
|
||||
|
||||
function failedResult(attrs) {
|
||||
return {
|
||||
failed: true,
|
||||
ok: false,
|
||||
...attrs,
|
||||
};
|
||||
}
|
||||
|
||||
function validResult(attrs) {
|
||||
return { ok: true, ...attrs };
|
||||
}
|
||||
|
||||
class TrackedUserField {
|
||||
@tracked value = null;
|
||||
field;
|
||||
getValidationVisible;
|
||||
getAccountPassword;
|
||||
|
||||
constructor({ field, getValidationVisible, getAccountPassword }) {
|
||||
this.field = field;
|
||||
this.getValidationVisible = getValidationVisible;
|
||||
this.getAccountPassword = getAccountPassword;
|
||||
}
|
||||
|
||||
get validation() {
|
||||
if (!this.getValidationVisible()) {
|
||||
return validResult();
|
||||
}
|
||||
|
||||
let validation = validResult();
|
||||
if (this.field.required && (!this.value || isEmpty(this.value))) {
|
||||
const reasonKey =
|
||||
this.field.field_type === "confirm"
|
||||
? "user_fields.required_checkbox"
|
||||
: "user_fields.required";
|
||||
validation = failedResult({
|
||||
reason: i18n(reasonKey, {
|
||||
name: this.field.name,
|
||||
}),
|
||||
element: this.field.element,
|
||||
});
|
||||
} else if (
|
||||
this.getAccountPassword() &&
|
||||
this.field.field_type === "text" &&
|
||||
this.value &&
|
||||
this.value.toLowerCase().includes(this.getAccountPassword().toLowerCase())
|
||||
) {
|
||||
validation = failedResult({
|
||||
reason: i18n("user_fields.same_as_password"),
|
||||
element: this.field.element,
|
||||
});
|
||||
}
|
||||
|
||||
addCustomUserFieldValidationCallbacks.forEach((callback) => {
|
||||
const customUserFieldValidationObject = callback(this);
|
||||
if (customUserFieldValidationObject) {
|
||||
validation = customUserFieldValidationObject;
|
||||
}
|
||||
});
|
||||
|
||||
return validation;
|
||||
}
|
||||
}
|
||||
|
||||
export default class UserFieldsValidationHelper {
|
||||
@tracked userFields = new TrackedArray();
|
||||
@tracked validationVisible = true;
|
||||
|
||||
constructor({
|
||||
getUserFields,
|
||||
getAccountPassword,
|
||||
showValidationOnInit = true,
|
||||
}) {
|
||||
this.getUserFields = getUserFields;
|
||||
this.getAccountPassword = getAccountPassword;
|
||||
this.validationVisible = showValidationOnInit;
|
||||
this.initializeUserFields();
|
||||
}
|
||||
|
||||
initializeUserFields() {
|
||||
let userFields = this.getUserFields();
|
||||
if (userFields) {
|
||||
const getValidationVisible = () => this.validationVisible;
|
||||
this.userFields = new TrackedArray(
|
||||
userFields.sortBy("position").map((f) => {
|
||||
return new TrackedUserField({
|
||||
field: f,
|
||||
getValidationVisible,
|
||||
getAccountPassword: this.getAccountPassword,
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
get userFieldsValidation() {
|
||||
if (!this.userFields) {
|
||||
return validResult();
|
||||
}
|
||||
const invalidUserField = this.userFields.find((f) => f.validation.failed);
|
||||
return invalidUserField ? invalidUserField.validation : validResult();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user