Skip to content

FEATURE: Add value validation regex #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { Input } from "@ember/component";
import { fn } from "@ember/helper";
import { on } from "@ember/modifier";
import { inject as service } from "@ember/service";
import withEventValue from "discourse/helpers/with-event-value";
import { withPluginApi } from "discourse/lib/plugin-api";
import i18n from "discourse-common/helpers/i18n";
import AdminFormRow from "admin/components/admin-form-row";
Expand All @@ -18,9 +21,12 @@ export default class CustomUserFields extends Component {
constructor() {
super(...arguments);
withPluginApi("1.21.0", (api) => {
["has_custom_validation", "show_values", "target_user_field_ids"].forEach(
(property) => api.includeUserFieldPropertyOnSave(property)
);
[
"has_custom_validation",
"show_values",
"target_user_field_ids",
"value_validation_regex",
].forEach((property) => api.includeUserFieldPropertyOnSave(property));
});
}

Expand All @@ -37,6 +43,28 @@ export default class CustomUserFields extends Component {
</AdminFormRow>

{{#if @outletArgs.buffered.has_custom_validation}}
<AdminFormRow
@label="discourse_authentication_validations.value_validation_regex.label"
>
<input
{{on
"input"
(withEventValue
(fn (mut @outletArgs.buffered.value_validation_regex))
)
}}
value={{@outletArgs.buffered.value_validation_regex}}
type="text"
class="value-validation-regex-input"
/>
<br />
<span>
{{i18n
"discourse_authentication_validations.value_validation_regex.description"
}}
</span>
</AdminFormRow>

<AdminFormRow
@label="discourse_authentication_validations.show_values.label"
>
Expand All @@ -59,6 +87,7 @@ export default class CustomUserFields extends Component {
@content={{this.userFieldsMinusCurrent}}
@valueProperty="id"
@value={{@outletArgs.buffered.target_user_field_ids}}
class="target-user-field-ids-input"
/>
<br />
<span>
Expand Down
35 changes: 35 additions & 0 deletions assets/javascripts/discourse/initializers/user-field-validation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import EmberObject from "@ember/object";
import { withPluginApi } from "discourse/lib/plugin-api";
import I18n from "discourse-i18n";

export default {
name: "user-field-validation",
initialize() {
withPluginApi("1.33.0", (api) => {
api.addCustomUserFieldValidationCallback((userField) => {
if (
userField.field.has_custom_validation &&
userField.field.value_validation_regex &&
userField.value
) {
if (
!new RegExp(userField.field.value_validation_regex).test(
userField.value
)
) {
return EmberObject.create({
failed: true,
reason: I18n.t(
"discourse_authentication_validations.value_validation_error_message",
{
user_field_name: userField.field.name,
}
),
element: userField.field.element,
});
}
}
});
});
},
};
4 changes: 4 additions & 0 deletions assets/stylesheets/common/admin/common.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.show-values-input,
.target-user-field-ids-input {
margin: 0.4em 0;
}
Empty file.
5 changes: 5 additions & 0 deletions config/locales/client.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,8 @@ en:
target_user_field_ids:
label: "Target User Fields"
description: "The User Field(s) that will be made visible"
value_validation_regex:
label: "Value Validation Regex"
description: "The regular expression that the value must match (only applicable to text fields)"
value_validation_error_message: |
Please enter a valid %{user_field_name}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

class AddValueValidationRegexToUserField < ActiveRecord::Migration[7.0]
def change
add_column :user_fields, :value_validation_regex, :string, default: "", null: true
end
end
10 changes: 9 additions & 1 deletion plugin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

enabled_site_setting :discourse_authentication_validations_enabled

register_asset "stylesheets/common/admin/common.scss"

module ::DiscourseAuthenticationValidations
PLUGIN_NAME = "discourse-authentication-validations"
end
Expand All @@ -20,9 +22,15 @@ module ::DiscourseAuthenticationValidations
add_to_serializer(:user_field, :has_custom_validation) { object.has_custom_validation }
add_to_serializer(:user_field, :show_values) { object.show_values }
add_to_serializer(:user_field, :target_user_field_ids) { object.target_user_field_ids }
add_to_serializer(:user_field, :value_validation_regex) { object.value_validation_regex }

register_modifier(:admin_user_fields_columns) do |columns|
columns.push(:has_custom_validation, :show_values, :target_user_field_ids)
columns.push(
:has_custom_validation,
:show_values,
:target_user_field_ids,
:value_validation_regex,
)
columns
end
end
112 changes: 112 additions & 0 deletions spec/system/value_validation_regex_user_field_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# frozen_string_literal: true

RSpec.describe "Discourse Authentication Validation - Value Validation Regex - User Field",
type: :system do
let(:signup_modal) { PageObjects::Modals::Signup.new }
fab!(:user_field_with_value_validation_regex) do
Fabricate(
:user_field,
name: "phone number",
field_type: "text",
editable: true,
required: true,
has_custom_validation: true,
show_values: [],
target_user_field_ids: [],
value_validation_regex: "^\\d{2} \\d{4} \\d{4}$", # 00 0000 0000
)
end

fab!(:user_field_without_value_validation_regex) do
Fabricate(
:user_field,
name: "unvalidated phone number",
field_type: "text",
editable: true,
required: true,
has_custom_validation: true,
show_values: [],
target_user_field_ids: [],
value_validation_regex: nil,
)
end

before { SiteSetting.discourse_authentication_validations_enabled = true }

context "when a user field has a value_validation_regex" do
it "displays a validation error message when the target user field input is incorrect" do
signup_modal.open
signup_modal.fill_custom_field("phone-number", "not a phone number")
signup_modal.click_create_account

expect(signup_modal).to have_text(
I18n.t(
"js.discourse_authentication_validations.value_validation_error_message",
{ user_field_name: user_field_with_value_validation_regex.name },
),
)
end

it "displays the default error message for no value" do
signup_modal.open
signup_modal.click_create_account

expect(signup_modal).not_to have_text(
I18n.t(
"js.discourse_authentication_validations.value_validation_error_message",
{ user_field_name: user_field_with_value_validation_regex.name },
),
)
expect(signup_modal).to have_text(
I18n.t("js.user_fields.required", { name: user_field_with_value_validation_regex.name }),
)
end

it "clears the validation error message when the target user field input is correct" do
signup_modal.open
signup_modal.fill_custom_field("phone-number", "not a phone number")
signup_modal.click_create_account

signup_modal.fill_custom_field("phone-number", "11 2222 3333")
signup_modal.click_create_account

expect(signup_modal).not_to have_text(
I18n.t(
"js.discourse_authentication_validations.value_validation_error_message",
{ user_field_name: user_field_with_value_validation_regex.name },
),
)
end
end

context "when a user field does not have a value_validation_regex" do
it "does not display a validation error message" do
signup_modal.open
signup_modal.fill_custom_field("unvalidated-phone-number", "not a phone number")
signup_modal.click_create_account

expect(signup_modal).not_to have_text(
I18n.t(
"js.discourse_authentication_validations.value_validation_error_message",
{ user_field_name: user_field_without_value_validation_regex.name },
),
)
end
end

context "when a user field has a value_validation_regex and has_custom_validation is false" do
it "does not display a validation error message" do
user_field_with_value_validation_regex.update!(has_custom_validation: false)
signup_modal.open
signup_modal.fill_custom_field("phone-number", "not a phone number")
signup_modal.click_create_account

expect(signup_modal).not_to have_text(
I18n.t(
"js.discourse_authentication_validations.value_validation_error_message",
{ user_field_name: user_field_with_value_validation_regex.name },
),
)
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const userFields = {
};

acceptance(
"Discourse Authentication Validations - Custom User Fields",
"Discourse Authentication Validations - Admin Page - Custom User Fields",
function (needs) {
needs.user();
needs.settings({
Expand Down