{{/if}}
diff --git a/app/assets/javascripts/discourse/templates/user/preferences.hbs b/app/assets/javascripts/discourse/templates/user/preferences.hbs
index b5a1c83f45f..5f40d4c54ef 100644
--- a/app/assets/javascripts/discourse/templates/user/preferences.hbs
+++ b/app/assets/javascripts/discourse/templates/user/preferences.hbs
@@ -178,6 +178,11 @@
{{#unless editHistoryVisible}}
{{preference-checkbox labelKey="user.edit_history_public" checked=edit_history_public}}
{{/unless}}
+
+ {{#each userFields}}
+ {{user-field field=field value=value}}
+ {{/each}}
+
{{plugin-outlet "user_custom_preferences"}}
diff --git a/app/assets/javascripts/discourse/views/create-account.js.es6 b/app/assets/javascripts/discourse/views/create-account.js.es6
index f3a269f8fb3..8de0a1864d2 100644
--- a/app/assets/javascripts/discourse/views/create-account.js.es6
+++ b/app/assets/javascripts/discourse/views/create-account.js.es6
@@ -1,5 +1,5 @@
export default Discourse.ModalBodyView.extend({
- templateName: 'modal/create_account',
+ templateName: 'modal/create-account',
title: I18n.t('create_account.title'),
classNames: ['create-account'],
diff --git a/app/assets/stylesheets/common/admin/admin_base.scss b/app/assets/stylesheets/common/admin/admin_base.scss
index c068dd08654..ba9aac6d8f6 100644
--- a/app/assets/stylesheets/common/admin/admin_base.scss
+++ b/app/assets/stylesheets/common/admin/admin_base.scss
@@ -1331,21 +1331,14 @@ tr.not-activated {
border-bottom: 1px solid scale-color-diff();
.form-display {
- width: 35%;
+ width: 25%;
display: inline-block;
float: left;
}
.form-element {
float: left;
- width: 35%;
- margin-right: 10px;
- label {
- margin-right: 10px;
- }
- input, div.combobox {
- margin-left: 10px;
- }
+ width: 25%;
}
.controls {
diff --git a/app/assets/stylesheets/common/base/login.scss b/app/assets/stylesheets/common/base/login.scss
index 8e2065d00ef..04c71d75c95 100644
--- a/app/assets/stylesheets/common/base/login.scss
+++ b/app/assets/stylesheets/common/base/login.scss
@@ -10,4 +10,29 @@
.discourse-touch .caps-lock-warning {
display: none;
-}
\ No newline at end of file
+}
+
+
+.user-fields {
+
+ h3 {
+ line-height: 1.5em;
+ color: scale-color($primary, $lightness: 20%);
+ border-bottom: 1px solid scale-color($primary, $lightness: 50%);
+ margin-bottom: 20px;
+ }
+
+ .user-field {
+ label: {
+ display: block;
+ }
+ input[type=text] {
+ width: 80%;
+ display: block;
+ }
+ input[type=checkbox] {
+ margin-right: 5px;
+ }
+ margin-bottom: 20px;
+ }
+}
diff --git a/app/assets/stylesheets/common/base/user.scss b/app/assets/stylesheets/common/base/user.scss
index e2b9a56e3e6..d447e4d8723 100644
--- a/app/assets/stylesheets/common/base/user.scss
+++ b/app/assets/stylesheets/common/base/user.scss
@@ -63,3 +63,4 @@
margin: 5px 10px 5px 0;
}
}
+
diff --git a/app/assets/stylesheets/desktop/user.scss b/app/assets/stylesheets/desktop/user.scss
index d7250476fd4..cf0ab75c321 100644
--- a/app/assets/stylesheets/desktop/user.scss
+++ b/app/assets/stylesheets/desktop/user.scss
@@ -426,4 +426,13 @@
.suspensions {
background-color: #c22020;
}
+
+ .user-field {
+ margin-left: 160px;
+ margin-top: 10px;
+ input[type=text] {
+ width: 540px;
+ display: block;
+ }
+ }
}
diff --git a/app/controllers/admin/user_fields_controller.rb b/app/controllers/admin/user_fields_controller.rb
index a10147d0be7..99a95c46a1e 100644
--- a/app/controllers/admin/user_fields_controller.rb
+++ b/app/controllers/admin/user_fields_controller.rb
@@ -1,7 +1,7 @@
class Admin::UserFieldsController < Admin::AdminController
def create
- field = UserField.create!(params.require(:user_field).permit(:name, :field_type))
+ field = UserField.create!(params.require(:user_field).permit(:name, :field_type, :editable))
render_serialized(field, UserFieldSerializer)
end
@@ -15,7 +15,8 @@ class Admin::UserFieldsController < Admin::AdminController
field = UserField.where(id: params.require(:id)).first
field.name = field_params[:name]
field.field_type = field_params[:field_type]
- field.save
+ field.editable = field_params[:editable] == "true"
+ field.save!
render_serialized(field, UserFieldSerializer)
end
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index ea1298e377c..806315ca8f2 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -46,6 +46,16 @@ class UsersController < ApplicationController
def update
user = fetch_user_from_params
guardian.ensure_can_edit!(user)
+
+ if params[:user_fields].present?
+ params[:custom_fields] ||= {}
+ UserField.where(editable: true).pluck(:id).each do |fid|
+ val = params[:user_fields][fid.to_s]
+ return render_json_error(I18n.t("login.missing_user_field")) if val.blank?
+ params[:custom_fields]["user_field_#{fid}"] = val
+ end
+ end
+
json_result(user, serializer: UserSerializer, additional_errors: [:user_profile]) do |u|
updater = UserUpdater.new(current_user, user)
updater.update(params)
@@ -162,18 +172,34 @@ class UsersController < ApplicationController
end
def create
+ params.permit(:user_fields)
+
unless SiteSetting.allow_new_registrations
- render json: { success: false, message: I18n.t("login.new_registrations_disabled") }
- return
+ return fail_with("login.new_registrations_disabled")
end
if params[:password] && params[:password].length > User.max_password_length
- render json: { success: false, message: I18n.t("login.password_too_long") }
- return
+ return fail_with("login.password_too_long")
end
user = User.new(user_params)
+ # Handle custom fields
+ user_field_ids = UserField.pluck(:id)
+ if user_field_ids.present?
+ if params[:user_fields].blank?
+ 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
+ end
+ user.custom_fields = fields
+ end
+ end
+
authentication = UserAuthenticator.new(user, session)
if !authentication.has_authenticator? && !SiteSetting.enable_local_logins
@@ -194,6 +220,7 @@ class UsersController < ApplicationController
authentication.finish
activation.finish
+
render json: {
success: true,
active: user.active?,
@@ -550,4 +577,9 @@ class UsersController < ApplicationController
:active
).merge(ip_address: request.ip, registration_ip_address: request.ip)
end
+
+ def fail_with(key)
+ render json: { success: false, message: I18n.t(key) }
+ end
+
end
diff --git a/app/models/site.rb b/app/models/site.rb
index 6ae248eca62..84c69b807a6 100644
--- a/app/models/site.rb
+++ b/app/models/site.rb
@@ -33,6 +33,10 @@ class Site
@groups ||= Group.order(:name).map { |g| {:id => g.id, :name => g.name}}
end
+ def user_fields
+ UserField.all
+ end
+
def categories
@categories ||= begin
categories = Category
diff --git a/app/models/user.rb b/app/models/user.rb
index fd2bef99cc4..8ddbbd3344e 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -657,6 +657,18 @@ class User < ActiveRecord::Base
result.empty? ? I18n.t("user.no_accounts_associated") : result.join(", ")
end
+ def user_fields
+ return @user_fields if @user_fields
+ user_field_ids = UserField.pluck(:id)
+ if user_field_ids.present?
+ @user_fields = {}
+ user_field_ids.each do |fid|
+ @user_fields[fid.to_s] = custom_fields["user_field_#{fid}"]
+ end
+ end
+ @user_fields
+ end
+
protected
def badge_grant
diff --git a/app/models/user_field_serializer.rb b/app/models/user_field_serializer.rb
index 316f3511765..e3df3422658 100644
--- a/app/models/user_field_serializer.rb
+++ b/app/models/user_field_serializer.rb
@@ -1,3 +1,3 @@
class UserFieldSerializer < ApplicationSerializer
- attributes :id, :name, :field_type
+ attributes :id, :name, :field_type, :editable
end
diff --git a/app/serializers/admin_detailed_user_serializer.rb b/app/serializers/admin_detailed_user_serializer.rb
index c762e1da187..3646ccd210e 100644
--- a/app/serializers/admin_detailed_user_serializer.rb
+++ b/app/serializers/admin_detailed_user_serializer.rb
@@ -18,7 +18,8 @@ class AdminDetailedUserSerializer < AdminUserSerializer
:suspend_reason,
:primary_group_id,
:badge_count,
- :warnings_received_count
+ :warnings_received_count,
+ :user_fields
has_one :approved_by, serializer: BasicUserSerializer, embed: :objects
has_one :api_key, serializer: ApiKeySerializer, embed: :objects
@@ -74,4 +75,12 @@ class AdminDetailedUserSerializer < AdminUserSerializer
object.has_trust_level?(TrustLevel[2])
end
+ def user_fields
+ object.user_fields
+ end
+
+ def include_user_fields?
+ object.user_fields.present?
+ end
+
end
diff --git a/app/serializers/site_serializer.rb b/app/serializers/site_serializer.rb
index ca0355ddfc0..7d0dde56f09 100644
--- a/app/serializers/site_serializer.rb
+++ b/app/serializers/site_serializer.rb
@@ -16,6 +16,7 @@ class SiteSerializer < ApplicationSerializer
has_many :topic_flag_types, serializer: TopicFlagTypeSerializer, embed: :objects
has_many :trust_levels, embed: :objects
has_many :archetypes, embed: :objects, serializer: ArchetypeSerializer
+ has_many :user_fields, embed: :objects, serialzer: UserFieldSerializer
def default_archetype
diff --git a/app/serializers/user_serializer.rb b/app/serializers/user_serializer.rb
index f6f4f8f8057..38ae31e6b74 100644
--- a/app/serializers/user_serializer.rb
+++ b/app/serializers/user_serializer.rb
@@ -46,7 +46,8 @@ class UserSerializer < BasicUserSerializer
:notification_count,
:has_title_badges,
:edit_history_public,
- :custom_fields
+ :custom_fields,
+ :user_fields
has_one :invited_by, embed: :object, serializer: BasicUserSerializer
has_many :custom_groups, embed: :object, serializer: BasicGroupSerializer
@@ -253,6 +254,14 @@ class UserSerializer < BasicUserSerializer
can_edit && !SiteSetting.edit_history_visible_to_public
end
+ def user_fields
+ object.user_fields
+ end
+
+ def include_user_fields?
+ user_fields.present?
+ end
+
def custom_fields
fields = nil
diff --git a/app/services/user_updater.rb b/app/services/user_updater.rb
index 0203637e8dd..4afc58047e8 100644
--- a/app/services/user_updater.rb
+++ b/app/services/user_updater.rb
@@ -68,7 +68,7 @@ class UserUpdater
fields = attributes[:custom_fields]
if fields.present?
- user.custom_fields = fields
+ user.custom_fields = user.custom_fields.merge(fields)
end
User.transaction do
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 082a11851e1..f37374ae054 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -579,6 +579,7 @@ en:
create_account:
title: "Create New Account"
failed: "Something went wrong, perhaps this email is already registered, try the forgot password link"
+ required_information: "Required Information"
forgot_password:
title: "Forgot Password"
@@ -2006,6 +2007,10 @@ en:
delete: "Delete"
cancel: "Cancel"
delete_confirm: "Are you sure you want to delete that user field?"
+ editable:
+ title: "Editable after signup?"
+ enabled: "editable"
+ disabled: "not editable"
field_types:
text: 'Text Field'
diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml
index 4d4ec306cf3..ea4402a96c1 100644
--- a/config/locales/server.en.yml
+++ b/config/locales/server.en.yml
@@ -1103,6 +1103,7 @@ en:
omniauth_error_unknown: "Something went wrong processing your log in, please try again."
new_registrations_disabled: "New account registrations are not allowed at this time."
password_too_long: "Passwords are limited to 200 characters."
+ missing_user_field: "You have not completed all the user fields"
user:
no_accounts_associated: "No accounts associated"
diff --git a/db/migrate/20140929181930_add_editable_to_user_fields.rb b/db/migrate/20140929181930_add_editable_to_user_fields.rb
new file mode 100644
index 00000000000..bfb4ee47462
--- /dev/null
+++ b/db/migrate/20140929181930_add_editable_to_user_fields.rb
@@ -0,0 +1,5 @@
+class AddEditableToUserFields < ActiveRecord::Migration
+ def change
+ add_column :user_fields, :editable, :boolean, default: false, null: false
+ end
+end
diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb
index 4af59dcc5fb..6ba16251564 100644
--- a/spec/controllers/users_controller_spec.rb
+++ b/spec/controllers/users_controller_spec.rb
@@ -531,7 +531,6 @@ describe UsersController do
end
context 'when an Exception is raised' do
-
[ ActiveRecord::StatementInvalid,
RestClient::Forbidden ].each do |exception|
before { User.any_instance.stubs(:save).raises(exception) }
@@ -545,6 +544,40 @@ describe UsersController do
end
end
+ context "with custom fields" do
+ let!(:user_field) { Fabricate(:user_field) }
+ let!(:another_field) { Fabricate(:user_field) }
+
+ context "without a value for the fields" do
+ let(:create_params) { {name: @user.name, password: 'watwatwat', username: @user.username, email: @user.email} }
+ include_examples 'failed signup'
+ end
+
+ context "with values for the fields" do
+ let(:create_params) { {
+ name: @user.name,
+ password: 'watwatwat',
+ username: @user.username,
+ email: @user.email,
+ user_fields: {
+ user_field.id.to_s => 'value1',
+ another_field.id.to_s => 'value2',
+ }
+ } }
+
+ it "should succeed" do
+ xhr :post, :create, 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'
+ end
+
+ end
+ end
+
end
context '.username' do
@@ -844,12 +877,10 @@ describe UsersController do
context 'with authenticated user' do
context 'with permission to update' do
+ let!(:user) { log_in(:user) }
+
it 'allows the update' do
- user = Fabricate(:user, name: 'Billy Bob')
- log_in_user(user)
-
put :update, username: user.username, name: 'Jim Tom', custom_fields: {test: :it}
-
expect(response).to be_success
user.reload
@@ -858,14 +889,42 @@ describe UsersController do
expect(user.custom_fields['test']).to eq 'it'
end
- it 'returns user JSON' do
- user = log_in
+ context "with user fields" do
+ context "an editable field" do
+ let!(:user_field) { Fabricate(:user_field) }
+ it "should update the user field" do
+ put :update, username: user.username, name: 'Jim Tom', user_fields: { user_field.id.to_s => 'happy' }
+ expect(response).to be_success
+ expect(user.user_fields[user_field.id.to_s]).to eq 'happy'
+ end
+
+ it "cannot be updated to blank" do
+ put :update, username: user.username, name: 'Jim Tom', user_fields: { user_field.id.to_s => '' }
+ response.should_not be_success
+ user.user_fields[user_field.id.to_s].should_not == 'happy'
+ end
+ end
+
+ context "uneditable field" do
+ let!(:user_field) { Fabricate(:user_field, editable: false) }
+
+ it "does not update the user field" do
+ put :update, username: user.username, name: 'Jim Tom', user_fields: { user_field.id.to_s => 'happy' }
+ expect(response).to be_success
+ expect(user.user_fields[user_field.id.to_s]).to be_blank
+ end
+ end
+
+ end
+
+ it 'returns user JSON' do
put :update, username: user.username
json = JSON.parse(response.body)
expect(json['user']['id']).to eq user.id
end
+
end
context 'without permission to update' do
diff --git a/spec/fabricators/user_field.rb b/spec/fabricators/user_field.rb
index 3257d636f8e..16031101319 100644
--- a/spec/fabricators/user_field.rb
+++ b/spec/fabricators/user_field.rb
@@ -1,4 +1,5 @@
Fabricator(:user_field) do
name { sequence(:name) {|i| "field_#{i}" } }
field_type 'text'
+ editable true
end
diff --git a/test/javascripts/fixtures/site_fixtures.js.es6 b/test/javascripts/fixtures/site_fixtures.js.es6
new file mode 100644
index 00000000000..712b9a9df20
--- /dev/null
+++ b/test/javascripts/fixtures/site_fixtures.js.es6
@@ -0,0 +1,3 @@
+export default {
+ "site.json": {"site":{"default_archetype":"regular","notification_types":{"mentioned":1,"replied":2,"quoted":3,"edited":4,"liked":5,"private_message":6,"invited_to_private_message":7,"invitee_accepted":8,"posted":9,"moved_post":10,"linked":11,"granted_badge":12},"post_types":{"regular":1,"moderator_action":2},"group_names":["admins","discourse","everyone","mcneel","moderators","newrelic","plugin_authors","sitepoint","staff","translators","trust_level_0","trust_level_1","trust_level_2","trust_level_3","trust_level_4","ubuntu"],"filters":["latest","unread","new","starred","read","posted"],"periods":["yearly","monthly","weekly","daily"],"top_menu_items":["latest","unread","new","starred","read","posted","category","categories","top"],"anonymous_top_menu_items":["latest","top","categories","category","categories","top"],"uncategorized_category_id":17,"is_readonly":false,"categories":[{"id":3,"name":"meta","color":"aaa","text_color":"FFFFFF","slug":"meta","topic_count":122,"post_count":1023,"description":"Discussion about meta.discourse.org itself, the organization of this forum about Discourse, how it works, and how we can improve this site.","topic_url":"/t/category-definition-for-meta/24","read_restricted":false,"permission":null,"notification_level":null,"logo_url":null,"background_url":null},{"id":10,"name":"howto","color":"76923C","text_color":"FFFFFF","slug":"howto","topic_count":72,"post_count":1022,"description":"Tutorial topics that describe how to set up, configure, or install Discourse using a specific platform or environment. Topics in this category may only be created by trust level 2 and up. ","topic_url":"/t/category-definition-for-howto/2629","read_restricted":false,"permission":null,"notification_level":null,"logo_url":null,"background_url":null},{"id":26,"name":"spec","color":"33B0B0","text_color":"FFFFFF","slug":"spec","topic_count":20,"post_count":278,"description":"My idea here is to have mini specs for features we would like built but have no bandwidth to build","topic_url":"/t/about-the-spec-category/13965","read_restricted":false,"permission":null,"parent_category_id":2,"notification_level":null,"logo_url":null,"background_url":null},{"id":7,"name":"dev","color":"000","text_color":"FFFFFF","slug":"dev","topic_count":481,"post_count":3575,"description":"This category is for topics related to hacking on Discourse: submitting pull requests, configuring development environments, coding conventions, and so forth.","topic_url":"/t/category-definition-for-dev/1026","read_restricted":false,"permission":null,"notification_level":null,"logo_url":null,"background_url":null},{"id":6,"name":"support","color":"b99","text_color":"FFFFFF","slug":"support","topic_count":1603,"post_count":11075,"description":"Support on configuring, using, and installing Discourse. Not for software development related topics, but for admins and end users configuring and using Discourse.","topic_url":"/t/category-definition-for-support/389","read_restricted":false,"permission":null,"notification_level":null,"logo_url":null,"background_url":null},{"id":24,"name":"sso","color":"92278F","text_color":"FFFFFF","slug":"sso","topic_count":13,"post_count":53,"description":"Only include actual maintained SSO (single sign on) implementations in this category. See the official documentation on Discourse's SSO support.","topic_url":"/t/about-the-sso-category/13110","read_restricted":false,"permission":null,"parent_category_id":5,"notification_level":null,"logo_url":null,"background_url":null},{"id":28,"name":"hack night","color":"B3B5B4","text_color":"FFFFFF","slug":"hack-night","topic_count":8,"post_count":33,"description":"This is a special, temporary category to organize work on the Discourse Hack Night in Toronto. ","topic_url":"/t/about-the-hack-night-category/17878","read_restricted":false,"permission":null,"parent_category_id":7,"notification_level":null,"logo_url":null,"background_url":null},{"id":27,"name":"translations","color":"808281","text_color":"FFFFFF","slug":"translations","topic_count":95,"post_count":827,"description":"This category is for discussion about localizing Discourse.","topic_url":"/t/about-the-translations-category/14549","read_restricted":false,"permission":null,"parent_category_id":7,"notification_level":null,"logo_url":null,"background_url":null},{"id":4,"name":"faq","color":"33b","text_color":"FFFFFF","slug":"faq","topic_count":48,"post_count":501,"description":"Topics that come up very often when discussing Discourse will eventually be classified into this Frequently Asked Questions category. Should only be added to popular topics.","topic_url":"/t/category-definition-for-faq/25","read_restricted":false,"permission":null,"notification_level":null,"logo_url":null,"background_url":null},{"id":14,"name":"marketplace","color":"8C6238","text_color":"FFFFFF","slug":"marketplace","topic_count":66,"post_count":361,"description":"About commercial Discourse related stuff: jobs or paid gigs, plugins, themes, hosting, etc.","topic_url":"/t/category-definition-for-marketplace/5425","read_restricted":false,"permission":null,"notification_level":null,"logo_url":null,"background_url":null},{"id":12,"name":"discourse hub","color":"b2c79f","text_color":"FFFFFF","slug":"discourse-hub","topic_count":10,"post_count":164,"description":"Topics about current or future Discourse Hub functionality at discourse.org including nickname registration, global user pages, and the site directory.","topic_url":"/t/category-definition-for-discourse-hub/3038","read_restricted":false,"permission":null,"notification_level":null,"logo_url":null,"background_url":null},{"id":13,"name":"blog","color":"ED207B","text_color":"FFFFFF","slug":"blog","topic_count":22,"post_count":390,"description":"Discussion topics generated from the official Discourse Blog. These topics are linked from the bottom of each blog entry where the blog comments would normally be.","topic_url":"/t/category-definition-for-blog/5250","read_restricted":false,"permission":null,"notification_level":null,"logo_url":null,"background_url":null},{"id":5,"name":"extensibility","color":"FE8432","text_color":"FFFFFF","slug":"extensibility","topic_count":226,"post_count":1874,"description":"Topics about extending the functionality of Discourse with plugins, themes, add-ons, or other mechanisms for extensibility. ","topic_url":"/t/about-the-extensibility-category/28","read_restricted":false,"permission":null,"notification_level":null,"logo_url":null,"background_url":null},{"id":11,"name":"login","color":"edb400","text_color":"FFFFFF","slug":"login","topic_count":48,"post_count":357,"description":"Topics about logging in to Discourse, using any standard third party provider (Twitter, Facebook, Google), traditional username and password, or with a custom plugin.","topic_url":"/t/category-definition-for-login/2828","read_restricted":false,"permission":null,"notification_level":null,"logo_url":null,"background_url":null},{"id":22,"name":"plugin","color":"d47711","text_color":"FFFFFF","slug":"plugin","topic_count":40,"post_count":466,"description":"One post per plugin! Only plugin owners should post here. ","topic_url":"/t/about-the-plugin-category/12648","read_restricted":false,"permission":null,"parent_category_id":5,"notification_level":null,"logo_url":null,"background_url":null},{"id":1,"name":"bug","color":"e9dd00","text_color":"000000","slug":"bug","topic_count":1469,"post_count":9295,"description":"A bug report means something is broken, preventing normal/typical use of Discourse. Do be sure to search prior to submitting bugs. Include repro steps, and only describe one bug per topic please.","topic_url":"/t/category-definition-for-bug/2","read_restricted":false,"permission":null,"notification_level":null,"logo_url":null,"background_url":null},{"id":17,"name":"uncategorized","color":"AB9364","text_color":"FFFFFF","slug":"uncategorized","topic_count":342,"post_count":3090,"description":"Topics that don't need a category, or don't fit into any other existing category.","topic_url":null,"read_restricted":false,"permission":null,"notification_level":null,"logo_url":"","background_url":""},{"id":21,"name":"wordpress","color":"1E8CBE","text_color":"FFFFFF","slug":"wordpress","topic_count":26,"post_count":135,"description":"Support for the official Discourse WordPress plugin at https://github.com/discourse/wp-discourse","topic_url":"/t/category-definition-for-wordpress/12282","read_restricted":false,"permission":null,"parent_category_id":6,"notification_level":null,"logo_url":null,"background_url":null},{"id":8,"name":"hosting","color":"74CCED","text_color":"FFFFFF","slug":"hosting","topic_count":100,"post_count":917,"description":"Topics about hosting Discourse, either on your own servers, in the cloud, or with specific hosting services.","topic_url":"/t/category-definition-for-hosting/2626","read_restricted":false,"permission":null,"notification_level":null,"logo_url":null,"background_url":null},{"id":9,"name":"ux","color":"5F497A","text_color":"FFFFFF","slug":"ux","topic_count":452,"post_count":4472,"description":"Discussion about the user interface of Discourse, how features are presented to the user in the client, including language and UI elements.","topic_url":"/t/category-definition-for-ux/2628","read_restricted":false,"permission":null,"notification_level":null,"logo_url":null,"background_url":null},{"id":2,"name":"feature","color":"0E76BD","text_color":"FFFFFF","slug":"feature","topic_count":1367,"post_count":11942,"description":"Discussion about features or potential features of Discourse: how they work, why they work, etc.","topic_url":"/t/category-definition-for-feature/11","read_restricted":false,"permission":null,"notification_level":null,"logo_url":null,"background_url":null}],"post_action_types":[{"name_key":"bookmark","name":"Bookmark","description":"Bookmark this post","long_form":"bookmarked this post","is_flag":false,"icon":null,"id":1,"is_custom_flag":false},{"name_key":"like","name":"Like","description":"Like this post","long_form":"liked this","is_flag":false,"icon":"heart","id":2,"is_custom_flag":false},{"name_key":"off_topic","name":"Off-Topic","description":"This post is radically off-topic in the current topic, and should probably be moved. If this is a topic, perhaps it does not belong here.","long_form":"flagged this as off-topic","is_flag":true,"icon":null,"id":3,"is_custom_flag":false},{"name_key":"inappropriate","name":"Inappropriate","description":"This post contains content that a reasonable person would consider offensive, abusive, or a violation of our community guidelines.","long_form":"flagged this as inappropriate","is_flag":true,"icon":null,"id":4,"is_custom_flag":false},{"name_key":"vote","name":"Vote","description":"Vote for this post","long_form":"voted for this post","is_flag":false,"icon":null,"id":5,"is_custom_flag":false},{"name_key":"spam","name":"Spam","description":"This post is an advertisement. It is not useful or relevant to the current topic, but promotional in nature.","long_form":"flagged this as spam","is_flag":true,"icon":null,"id":8,"is_custom_flag":false},{"name_key":"notify_user","name":"Notify {{username}}","description":"This post contains something I want to talk to this person directly and privately about. Does not cast a flag.","long_form":"notified user","is_flag":true,"icon":null,"id":6,"is_custom_flag":true},{"name_key":"notify_moderators","name":"Notify moderators","description":"This post requires general moderator attention based on the guidelines, TOS, or for another reason not listed above.","long_form":"notified moderators","is_flag":true,"icon":null,"id":7,"is_custom_flag":true}],"topic_flag_types":[{"name_key":"inappropriate","name":"Inappropriate","description":"This topic contains content that a reasonable person would consider offensive, abusive, or a violation of our community guidelines.","long_form":"flagged this as inappropriate","is_flag":true,"icon":null,"id":4,"is_custom_flag":false},{"name_key":"spam","name":"Spam","description":"This topic is an advertisement. It is not useful or relevant to this site, but promotional in nature.","long_form":"flagged this as spam","is_flag":true,"icon":null,"id":8,"is_custom_flag":false},{"name_key":"notify_moderators","name":"Notify moderators","description":"This topic requires general moderator attention based on the guidelines, TOS, or for another reason not listed above.","long_form":"notified moderators","is_flag":true,"icon":null,"id":7,"is_custom_flag":true}],"trust_levels":[{"id":0,"name":"new user"},{"id":1,"name":"basic user"},{"id":2,"name":"member"},{"id":3,"name":"regular"},{"id":4,"name":"leader"}],"archetypes":[{"id":"regular","name":"Regular Topic","options":[]},{"id":"banner","name":"translation missing: en.archetypes.banner.title","options":[]}]}}
+};
diff --git a/test/javascripts/helpers/qunit_helpers.js b/test/javascripts/helpers/qunit-helpers.js.es6
similarity index 70%
rename from test/javascripts/helpers/qunit_helpers.js
rename to test/javascripts/helpers/qunit-helpers.js.es6
index b6450807431..3aa4b2fcc02 100644
--- a/test/javascripts/helpers/qunit_helpers.js
+++ b/test/javascripts/helpers/qunit-helpers.js.es6
@@ -1,9 +1,13 @@
/* global asyncTest */
-/* exported integration, testController, controllerFor, asyncTestDiscourse, fixture */
-function integration(name, options) {
+
+import siteFixtures from 'fixtures/site_fixtures';
+
+export function integration(name, options) {
module("Integration: " + name, {
setup: function() {
Ember.run(Discourse, Discourse.advanceReadiness);
+
+ var siteJson = siteFixtures['site.json'].site;
if (options) {
if (options.setup) {
options.setup.call(this);
@@ -16,7 +20,12 @@ function integration(name, options) {
if (options.settings) {
Discourse.SiteSettings = jQuery.extend(true, Discourse.SiteSettings, options.settings);
}
+
+ if (options.site) {
+ Discourse.Site.resetCurrent(Discourse.Site.create(jQuery.extend(true, {}, siteJson, options.site)));
+ }
}
+
Discourse.reset();
},
@@ -30,13 +39,13 @@ function integration(name, options) {
});
}
-function controllerFor(controller, model) {
+export function controllerFor(controller, model) {
controller = Discourse.__container__.lookup('controller:' + controller);
if (model) { controller.set('model', model ); }
return controller;
}
-function asyncTestDiscourse(text, func) {
+export function asyncTestDiscourse(text, func) {
asyncTest(text, function () {
var self = this;
Ember.run(function () {
@@ -45,7 +54,7 @@ function asyncTestDiscourse(text, func) {
});
}
-function fixture(selector) {
+export function fixture(selector) {
if (selector) {
return $("#qunit-fixture").find(selector);
}
diff --git a/test/javascripts/integration/create-account-user-fields-test.js.es6 b/test/javascripts/integration/create-account-user-fields-test.js.es6
new file mode 100644
index 00000000000..939ea8fcc04
--- /dev/null
+++ b/test/javascripts/integration/create-account-user-fields-test.js.es6
@@ -0,0 +1,46 @@
+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"}]
+ }
+});
+
+test("create account with user fields", function() {
+ visit("/");
+ click("header .sign-up-button");
+
+ andThen(function() {
+ ok(exists('.create-account'), "it shows the create account modal");
+ ok(exists('.user-field'), "it has at least one user field");
+ ok(exists('.modal-footer .btn-primary:disabled'), 'create account is disabled at first');
+ });
+
+ fillIn('#new-account-name', 'Dr. Good Tuna');
+ fillIn('#new-account-password', 'cool password bro');
+ fillIn('#new-account-email', 'good.tuna@test.com');
+ fillIn('#new-account-username', 'goodtuna');
+
+ andThen(function() {
+ ok(exists('#username-validation.good'), 'the username validation is good');
+ 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");
+
+ andThen(function() {
+ ok(exists('.modal-footer .btn-primary:disabled'), 'create account is disabled because field is not checked');
+ });
+
+ click(".user-field input[type=checkbox]");
+ andThen(function() {
+ not(exists('.modal-footer .btn-primary:disabled'), 'create account is disabled because field is not checked');
+ });
+
+ click(".user-field input[type=checkbox]");
+ andThen(function() {
+ ok(exists('.modal-footer .btn-primary:disabled'), 'unclicking the checkbox disables the submit');
+ });
+
+});
diff --git a/test/javascripts/jshint_all.js.erb b/test/javascripts/jshint-test.js.es6.erb
similarity index 100%
rename from test/javascripts/jshint_all.js.erb
rename to test/javascripts/jshint-test.js.es6.erb
diff --git a/test/javascripts/models/topic-test.js.es6 b/test/javascripts/models/topic-test.js.es6
index 1ab2d25963f..e557cf7c1b7 100644
--- a/test/javascripts/models/topic-test.js.es6
+++ b/test/javascripts/models/topic-test.js.es6
@@ -20,16 +20,18 @@ test('has a postStream', function() {
equal(postStream.get('topic'), topic, "the postStream has a reference back to the topic");
});
-var category = _.first(Discourse.Category.list());
test('category relationship', function() {
// It finds the category by id
- var topic = Discourse.Topic.create({id: 1111, category_id: category.get('id') });
+ var category = Discourse.Category.list()[0],
+ topic = Discourse.Topic.create({id: 1111, category_id: category.get('id') });
+
equal(topic.get('category'), category);
});
test("updateFromJson", function() {
- var topic = Discourse.Topic.create({id: 1234});
+ var topic = Discourse.Topic.create({id: 1234}),
+ category = Discourse.Category.list()[0];
topic.updateFromJson({
post_stream: [1,2,3],
diff --git a/test/javascripts/test_helper.js b/test/javascripts/test_helper.js
index 542ac57178d..863d76e452d 100644
--- a/test/javascripts/test_helper.js
+++ b/test/javascripts/test_helper.js
@@ -42,7 +42,7 @@
//= require sinon-qunit-1.0.0
//= require jshint
-//= require helpers/qunit_helpers
+//= require helpers/qunit-helpers
//= require helpers/assertions
//= require helpers/init-ember-qunit
@@ -50,7 +50,6 @@
//= require_tree ./lib
//= require_tree .
//= require_self
-//= require jshint_all
// sinon settings
sinon.config = {
@@ -87,6 +86,7 @@ if (window.Logster) {
var origDebounce = Ember.run.debounce,
createPretendServer = require('helpers/create-pretender', null, null, false).default,
+ fixtures = require('fixtures/site_fixtures', null, null, false).default,
server;
QUnit.testStart(function(ctx) {
@@ -97,6 +97,7 @@ QUnit.testStart(function(ctx) {
Discourse.BaseUri = "/";
Discourse.BaseUrl = "";
Discourse.User.resetCurrent();
+ Discourse.Site.resetCurrent(Discourse.Site.create(fixtures['site.json'].site));
PreloadStore.reset();
window.sandbox = sinon.sandbox.create();
@@ -121,6 +122,15 @@ QUnit.testDone(function() {
});
// Load ES6 tests
+var helpers = require("helpers/qunit-helpers");
+
+// TODO: Replace with proper imports rather than globals
+window.asyncTestDiscourse = helpers.asyncTestDiscourse;
+window.controllerFor = helpers.controllerFor;
+window.fixture = helpers.fixture;
+window.integration = helpers.integration;
+
+
Ember.keys(requirejs.entries).forEach(function(entry) {
if ((/\-test/).test(entry)) {
require(entry, null, null, true);