FEATURE: Support for a `required` setting on user fields.

This commit is contained in:
Robin Ward 2014-10-08 14:38:18 -04:00
parent 9f2be41710
commit f9a8f6d6ce
16 changed files with 110 additions and 48 deletions

View File

@ -9,11 +9,23 @@ export default Ember.ObjectController.extend(BufferedContent, {
return UserField.fieldTypeById(this.get('field_type')).get('name');
}.property('field_type'),
flags: function() {
var ret = [];
if (this.get('editable')) {
ret.push(I18n.t('admin.user_fields.editable.enabled'));
}
if (this.get('required')) {
ret.push(I18n.t('admin.user_fields.required.enabled'));
}
return ret.join(', ');
}.property('editable', 'required'),
actions: {
save: function() {
var self = this;
var attrs = this.get('buffered').getProperties('name', 'description', 'field_type', 'editable');
var attrs = this.get('buffered').getProperties('name', 'description', 'field_type', 'editable', 'required');
this.get('model').save(attrs).then(function(res) {
self.set('model.id', res.user_field.id);

View File

@ -11,12 +11,10 @@
{{input value=f.buffered.name class="user-field-name" placeholder=userFieldsName}}
</div>
<div class='form-element'>
{{combo-box content=fieldTypes valueAttribute="id" value=f.buffered.field_type}}
{{input value=f.buffered.description class="user-field-desc" placeholder=userFieldsDescription}}
</div>
<div class='form-element'>
<label>
{{input type="checkbox" checked=f.buffered.editable}} {{i18n admin.user_fields.editable.title}}
</label>
{{combo-box content=fieldTypes valueAttribute="id" value=f.buffered.field_type}}
</div>
<div class='form-element controls'>
<button {{action "save"}}class='btn btn-primary'>{{fa-icon 'check'}} {{i18n admin.user_fields.save}}</button>
@ -25,29 +23,29 @@
</div>
<div class="row">
<div class='form-element'>
{{input value=f.buffered.description class="user-field-desc" placeholder=userFieldsDescription}}
<label>
{{input type="checkbox" checked=f.buffered.editable}} {{i18n admin.user_fields.editable.title}}
</label>
</div>
<div class='form-element'>
<label>
{{input type="checkbox" checked=f.buffered.required}} {{i18n admin.user_fields.required.title}}
</label>
</div>
</div>
{{else}}
<div class="row">
<div class='form-display'><strong>{{f.name}}</strong></div>
<div class='form-display'>{{f.fieldName}}</div>
<div class='form-display'>
{{#if f.editable}}
{{i18n admin.user_fields.editable.enabled}}
{{else}}
{{i18n admin.user_fields.editable.disabled}}
{{/if}}
<div class='form-display'><strong>{{f.name}}</strong></div>
<div class='form-display'>{{{f.description}}}</div>
<div class='form-display'>{{f.fieldName}}</div>
<div class='form-display'>
</div>
<div class='form-element controls'>
<button {{action "edit"}}class='btn btn-default'>{{fa-icon 'pencil'}} {{i18n admin.user_fields.edit}}</button>
<button {{action "destroy"}}class='btn btn-danger'>{{fa-icon 'trash-o'}} {{i18n admin.user_fields.delete}}</button>
</div>
</div>
<div class='form-element controls'>
<button {{action "edit"}}class='btn btn-default'>{{fa-icon 'pencil'}} {{i18n admin.user_fields.edit}}</button>
<button {{action "destroy"}}class='btn btn-danger'>{{fa-icon 'trash-o'}} {{i18n admin.user_fields.delete}}</button>
</div>
</div>
<div class="row">
{{f.description}}
</div>
<div class="row">{{f.flags}}</div>
{{/if}}
<div class='clearfix'></div>
{{/each}}

View File

@ -51,6 +51,7 @@ export default DiscourseController.extend(ModalFunctionality, {
// Validate required fields
var userFields = this.get('userFields');
if (userFields) { userFields = userFields.filterProperty('field.required'); }
if (!Ember.empty(userFields)) {
var anyEmpty = userFields.any(function(uf) {
var val = uf.get('value');

View File

@ -1,5 +1,6 @@
<label>{{{field.name}}}</label>
<div class='controls'>
{{input value=value}}
{{#if field.required}}<span class='required'>*</span>{{/if}}
<p>{{{field.description}}}</p>
</div>

View File

@ -28,6 +28,7 @@
}
.controls {
margin-left: 92px;
margin-bottom: 15px;
label {
width: auto;
text-align: left;

View File

@ -15,6 +15,15 @@
}
}
.user-field {
.required {
text-align: top;
color: $danger;
font-weight: bold;
font-size: 1.3em;
}
}
.end-of-stream {
border: 3px solid $primary;
width: 100%;
@ -22,7 +31,7 @@
.user-navigation {
.map {
.map {
height: 50px;
}
.avatar {

View File

@ -63,6 +63,7 @@ a#forgot-password-link {clear: left; float: left; }
label {
width: 85px;
}
input[type=text] {
margin-top: 0;
width: 184px;

View File

@ -1,7 +1,11 @@
class Admin::UserFieldsController < Admin::AdminController
def self.columns
[:name, :field_type, :editable, :description, :required]
end
def create
field = UserField.new(params.require(:user_field).permit(:name, :field_type, :editable, :description))
field = UserField.new(params.require(:user_field).permit(*Admin::UserFieldsController.columns))
json_result(field, serializer: UserFieldSerializer) do
field.save
end
@ -15,10 +19,10 @@ class Admin::UserFieldsController < Admin::AdminController
field_params = params.require(:user_field)
field = UserField.where(id: params.require(:id)).first
field.name = field_params[:name]
field.field_type = field_params[:field_type]
field.description = field_params[:description]
field.editable = field_params[:editable] == "true"
Admin::UserFieldsController.columns.each do |col|
field.send("#{col}=", field_params[col] || false)
end
json_result(field, serializer: UserFieldSerializer) do
field.save

View File

@ -49,11 +49,12 @@ class UsersController < ApplicationController
if params[:user_fields].present?
params[:custom_fields] ||= {}
UserField.where(editable: true).pluck(:id).each do |fid|
val = params[:user_fields][fid.to_s]
UserField.where(editable: true).each do |f|
val = params[:user_fields][f.id.to_s]
val = nil if val === "false"
return render_json_error(I18n.t("login.missing_user_field")) if val.blank?
params[:custom_fields]["user_field_#{fid}"] = val
return render_json_error(I18n.t("login.missing_user_field")) if val.blank? && f.required?
params[:custom_fields]["user_field_#{f.id}"] = val
end
end
@ -188,16 +189,19 @@ class UsersController < ApplicationController
user = User.new(user_params)
# Handle custom fields
user_field_ids = UserField.pluck(:id)
if user_field_ids.present?
if params[:user_fields].blank?
user_fields = UserField.all
if user_fields.present?
if params[:user_fields].blank? && UserField.where(required: true).exists?
return fail_with("login.missing_user_field")
else
fields = user.custom_fields
user_field_ids.each do |fid|
field_val = params[:user_fields][fid.to_s]
return fail_with("login.missing_user_field") if field_val.blank?
fields["user_field_#{fid}"] = field_val
user_fields.each do |f|
field_val = params[:user_fields][f.id.to_s]
if field_val.blank?
return fail_with("login.missing_user_field") if f.required?
else
fields["user_field_#{f.id}"] = field_val
end
end
user.custom_fields = fields
end

View File

@ -1,3 +1,8 @@
class UserFieldSerializer < ApplicationSerializer
attributes :id, :name, :description, :field_type, :editable
attributes :id,
:name,
:description,
:field_type,
:editable,
:required
end

View File

@ -2019,6 +2019,10 @@ en:
delete: "Delete"
cancel: "Cancel"
delete_confirm: "Are you sure you want to delete that user field?"
required:
title: "Required at signup?"
enabled: "required"
disabled: "not required"
editable:
title: "Editable after signup?"
enabled: "editable"

View File

@ -0,0 +1,5 @@
class AddRequiredSignupToUserFields < ActiveRecord::Migration
def change
add_column :user_fields, :required, :boolean, default: true, null: false
end
end

View File

@ -32,7 +32,7 @@ describe Admin::UserFieldsController do
context '.destroy' do
let!(:user_field) { Fabricate(:user_field) }
it "returns a list of user fields" do
it "deletes the user field" do
-> {
xhr :delete, :destroy, id: user_field.id
response.should be_success
@ -43,7 +43,7 @@ describe Admin::UserFieldsController do
context '.update' do
let!(:user_field) { Fabricate(:user_field) }
it "returns a list of user fields" do
it "updates the user field" do
xhr :put, :update, id: user_field.id, user_field: {name: 'fraggle', field_type: 'confirm', description: 'muppet'}
response.should be_success
user_field.reload

View File

@ -559,6 +559,7 @@ describe UsersController do
context "with custom fields" do
let!(:user_field) { Fabricate(:user_field) }
let!(:another_field) { Fabricate(:user_field) }
let!(:optional_field) { Fabricate(:user_field, required: false) }
context "without a value for the fields" do
let(:create_params) { {name: @user.name, password: 'watwatwat', username: @user.username, email: @user.email} }
@ -577,7 +578,7 @@ describe UsersController do
}
} }
it "should succeed" do
it "should succeed without the optional field" do
xhr :post, :create, create_params
response.should be_success
inserted = User.where(email: @user.email).first
@ -585,6 +586,19 @@ describe UsersController do
inserted.custom_fields.should be_present
inserted.custom_fields["user_field_#{user_field.id}"].should == 'value1'
inserted.custom_fields["user_field_#{another_field.id}"].should == 'value2'
inserted.custom_fields["user_field_#{optional_field.id}"].should be_blank
end
it "should succeed with the optional field" do
create_params[:user_fields][optional_field.id.to_s] = 'value3'
xhr :post, :create, create_params.merge(create_params)
response.should be_success
inserted = User.where(email: @user.email).first
inserted.should be_present
inserted.custom_fields.should be_present
inserted.custom_fields["user_field_#{user_field.id}"].should == 'value1'
inserted.custom_fields["user_field_#{another_field.id}"].should == 'value2'
inserted.custom_fields["user_field_#{optional_field.id}"].should == 'value3'
end
end
@ -904,6 +918,7 @@ describe UsersController do
context "with user fields" do
context "an editable field" do
let!(:user_field) { Fabricate(:user_field) }
let!(:optional_field) { Fabricate(:user_field, required: false ) }
it "should update the user field" do
put :update, username: user.username, name: 'Jim Tom', user_fields: { user_field.id.to_s => 'happy' }

View File

@ -3,4 +3,5 @@ Fabricator(:user_field) do
description "user field description"
field_type 'text'
editable true
required true
end

View File

@ -2,8 +2,9 @@ import { integration } from "helpers/qunit-helpers";
integration("Create Account - User Fields", {
site: {
user_fields: [{"id":34,"name":"I've read the terms of service","field_type":"confirm"},
{"id":35,"name":"What is your pet's name?","field_type":"text"}]
user_fields: [{"id":34,"name":"I've read the terms of service","field_type":"confirm","required":true},
{"id":35,"name":"What is your pet's name?","field_type":"text","required":true},
{"id":36,"name":"What's your dad like?","field_type":"text","required":false}]
}
});
@ -27,7 +28,7 @@ test("create account with user fields", function() {
ok(exists('.modal-footer .btn-primary:disabled'), 'create account is still disabled due to lack of user fields');
});
fillIn(".user-field input[type=text]", "Barky");
fillIn(".user-field input[type=text]:first", "Barky");
andThen(function() {
ok(exists('.modal-footer .btn-primary:disabled'), 'create account is disabled because field is not checked');
@ -35,7 +36,7 @@ test("create account with user fields", function() {
click(".user-field input[type=checkbox]");
andThen(function() {
not(exists('.modal-footer .btn-primary:disabled'), 'create account is disabled because field is not checked');
not(exists('.modal-footer .btn-primary:disabled'), 'create account is enabled because field is not checked');
});
click(".user-field input[type=checkbox]");