diff --git a/app/assets/javascripts/admin/components/admin-user-field-item.js.es6 b/app/assets/javascripts/admin/components/admin-user-field-item.js.es6 index 337b10557..bccec3093 100644 --- a/app/assets/javascripts/admin/components/admin-user-field-item.js.es6 +++ b/app/assets/javascripts/admin/components/admin-user-field-item.js.es6 @@ -6,6 +6,9 @@ export default Ember.Component.extend(bufferedProperty('userField'), { editing: Ember.computed.empty('userField.id'), classNameBindings: [':user-field'], + cantMoveUp: Discourse.computed.propertyEqual('userField', 'firstField'), + cantMoveDown: Discourse.computed.propertyEqual('userField', 'lastField'), + userFieldsDescription: function() { return I18n.t('admin.user_fields.description'); }.property(), @@ -55,13 +58,20 @@ export default Ember.Component.extend(bufferedProperty('userField'), { 'show_on_profile', 'options'); - this.get('userField').save(attrs).then(function(res) { - self.set('userField.id', res.user_field.id); + this.get('userField').save(attrs).then(function() { self.set('editing', false); self.commitBuffer(); }).catch(popupAjaxError); }, + moveUp() { + this.sendAction('moveUpAction', this.get('userField')); + }, + + moveDown() { + this.sendAction('moveDownAction', this.get('userField')); + }, + edit() { this.set('editing', true); }, diff --git a/app/assets/javascripts/admin/controllers/admin-user-fields.js.es6 b/app/assets/javascripts/admin/controllers/admin-user-fields.js.es6 index 2990dc180..70963a147 100644 --- a/app/assets/javascripts/admin/controllers/admin-user-fields.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-user-fields.js.es6 @@ -1,31 +1,60 @@ -import UserField from 'admin/models/user-field'; +import { popupAjaxError } from 'discourse/lib/ajax-error'; -export default Ember.ArrayController.extend({ +const MAX_FIELDS = 20; + +export default Ember.Controller.extend({ fieldTypes: null, - createDisabled: Em.computed.gte('model.length', 20), + createDisabled: Em.computed.gte('model.length', MAX_FIELDS), - _performDestroy(f, model) { - return f.destroy().then(function() { - model.removeObject(f); + arrangedContent: function() { + return Ember.ArrayProxy.extend(Ember.SortableMixin).create({ + sortProperties: ['position'], + content: this.get('model') }); - }, + }.property('model'), actions: { createField() { - this.pushObject(UserField.create({ field_type: 'text' })); + const f = this.store.createRecord('user-field', { field_type: 'text', position: MAX_FIELDS }); + this.get('model').pushObject(f); + }, + + moveUp(f) { + const idx = this.get('arrangedContent').indexOf(f); + if (idx) { + const prev = this.get('arrangedContent').objectAt(idx-1); + const prevPos = prev.get('position'); + + prev.update({ position: f.get('position') }); + f.update({ position: prevPos }); + } + }, + + moveDown(f) { + const idx = this.get('arrangedContent').indexOf(f); + if (idx > -1) { + const next = this.get('arrangedContent').objectAt(idx+1); + const nextPos = next.get('position'); + + next.update({ position: f.get('position') }); + f.update({ position: nextPos }); + } }, destroy(f) { - const model = this.get('model'), - self = this; + const model = this.get('model'); // Only confirm if we already been saved if (f.get('id')) { bootbox.confirm(I18n.t("admin.user_fields.delete_confirm"), function(result) { - if (result) { self._performDestroy(f, model); } + if (result) { + f.destroyRecord().then(function() { + model.removeObject(f); + }).catch(popupAjaxError); + } }); } else { - self._performDestroy(f, model); + model.removeObject(f); } } } diff --git a/app/assets/javascripts/admin/models/user-field.js.es6 b/app/assets/javascripts/admin/models/user-field.js.es6 index 35ee8e0bd..c6a1a1f6b 100644 --- a/app/assets/javascripts/admin/models/user-field.js.es6 +++ b/app/assets/javascripts/admin/models/user-field.js.es6 @@ -1,47 +1,12 @@ -const UserField = Ember.Object.extend({ +import RestModel from 'discourse/models/rest'; - destroy() { - const self = this; - return new Ember.RSVP.Promise(function(resolve) { - const id = self.get('id'); - if (id) { - return Discourse.ajax("/admin/customize/user_fields/" + id, { type: 'DELETE' }).then(function() { - resolve(); - }); - } - resolve(); - }); - }, - - save(attrs) { - const id = this.get('id'); - if (!id) { - return Discourse.ajax("/admin/customize/user_fields", { - type: "POST", - data: { user_field: attrs } - }); - } else { - return Discourse.ajax("/admin/customize/user_fields/" + id, { - type: "PUT", - data: { user_field: attrs } - }); - } - } -}); +const UserField = RestModel.extend(); const UserFieldType = Ember.Object.extend({ name: Discourse.computed.i18n('id', 'admin.user_fields.field_types.%@') }); UserField.reopenClass({ - findAll() { - return Discourse.ajax("/admin/customize/user_fields").then(function(result) { - return result.user_fields.map(function(uf) { - return UserField.create(uf); - }); - }); - }, - fieldTypes() { if (!this._fieldTypes) { this._fieldTypes = [ diff --git a/app/assets/javascripts/admin/routes/admin-user-fields.js.es6 b/app/assets/javascripts/admin/routes/admin-user-fields.js.es6 index a540f343c..5770165ff 100644 --- a/app/assets/javascripts/admin/routes/admin-user-fields.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-user-fields.js.es6 @@ -2,13 +2,10 @@ import UserField from 'admin/models/user-field'; export default Discourse.Route.extend({ model: function() { - return UserField.findAll(); + return this.store.findAll('user-field'); }, setupController: function(controller, model) { - controller.setProperties({ - model: model, - fieldTypes: UserField.fieldTypes() - }); + controller.setProperties({ model, fieldTypes: UserField.fieldTypes() }); } }); diff --git a/app/assets/javascripts/admin/templates/components/admin-user-field-item.hbs b/app/assets/javascripts/admin/templates/components/admin-user-field-item.hbs index 4bb52c9e6..36fa8c3b8 100644 --- a/app/assets/javascripts/admin/templates/components/admin-user-field-item.hbs +++ b/app/assets/javascripts/admin/templates/components/admin-user-field-item.hbs @@ -35,13 +35,17 @@ {{/admin-form-row}} {{else}}
-
{{userField.name}}
-
{{{userField.description}}}
+
+ {{userField.name}} +
+ {{{userField.description}}} +
{{fieldName}}
-
{{d-button action="edit" class="btn-default" icon="pencil" label="admin.user_fields.edit"}} {{d-button action="destroy" class="btn-danger" icon="trash-o" label="admin.user_fields.delete"}} + {{d-button action="moveUp" icon="arrow-up" disabled=cantMoveUp}} + {{d-button action="moveDown" icon="arrow-down" disabled=cantMoveDown}}
{{flags}}
diff --git a/app/assets/javascripts/admin/templates/user-fields.hbs b/app/assets/javascripts/admin/templates/user-fields.hbs index 20ce1beb3..3a89a0e06 100644 --- a/app/assets/javascripts/admin/templates/user-fields.hbs +++ b/app/assets/javascripts/admin/templates/user-fields.hbs @@ -4,8 +4,14 @@

{{i18n 'admin.user_fields.help'}}

{{#if model}} - {{#each model as |uf|}} - {{admin-user-field-item userField=uf fieldTypes=fieldTypes destroyAction="destroy"}} + {{#each arrangedContent as |uf|}} + {{admin-user-field-item userField=uf + fieldTypes=fieldTypes + firstField=arrangedContent.firstObject + lastField=arrangedContent.lastObject + destroyAction="destroy" + moveUpAction="moveUp" + moveDownAction="moveDown"}} {{/each}} {{/if}} diff --git a/app/assets/javascripts/discourse/adapters/rest.js.es6 b/app/assets/javascripts/discourse/adapters/rest.js.es6 index ec25f2d13..5a64730ab 100644 --- a/app/assets/javascripts/discourse/adapters/rest.js.es6 +++ b/app/assets/javascripts/discourse/adapters/rest.js.es6 @@ -17,10 +17,14 @@ function rethrow(error) { } export default Ember.Object.extend({ - pathFor(store, type, findArgs) { - let path = "/" + Ember.String.underscore(store.pluralize(type)); - if (ADMIN_MODELS.indexOf(type) !== -1) { path = "/admin" + path; } + basePath(store, type) { + if (ADMIN_MODELS.indexOf(type) !== -1) { return "/admin/"; } + return "/"; + }, + + pathFor(store, type, findArgs) { + let path = this.basePath(store, type, findArgs) + Ember.String.underscore(store.pluralize(type)); if (findArgs) { if (typeof findArgs === "object") { @@ -51,9 +55,10 @@ export default Ember.Object.extend({ update(store, type, id, attrs) { const data = {}; - data[Ember.String.underscore(type)] = attrs; + const typeField = Ember.String.underscore(type); + data[typeField] = attrs; return ajax(this.pathFor(store, type, id), { method: 'PUT', data }).then(function(json) { - return new Result(json[type], json); + return new Result(json[typeField], json); }); }, diff --git a/app/assets/javascripts/discourse/adapters/user-field.js.es6 b/app/assets/javascripts/discourse/adapters/user-field.js.es6 new file mode 100644 index 000000000..f57240a11 --- /dev/null +++ b/app/assets/javascripts/discourse/adapters/user-field.js.es6 @@ -0,0 +1,7 @@ +import RestAdapter from 'discourse/adapters/rest'; + +export default RestAdapter.extend({ + basePath() { + return "/admin/customize/"; + } +}); diff --git a/app/assets/javascripts/discourse/controllers/create-account.js.es6 b/app/assets/javascripts/discourse/controllers/create-account.js.es6 index b5812eb6a..a668d4cc8 100644 --- a/app/assets/javascripts/discourse/controllers/create-account.js.es6 +++ b/app/assets/javascripts/discourse/controllers/create-account.js.es6 @@ -394,11 +394,8 @@ export default DiscourseController.extend(ModalFunctionality, { let userFields = this.site.get('user_fields'); if (userFields) { - userFields = userFields.map(function(f) { - return Ember.Object.create({ - value: null, - field: f - }); + userFields = _.sortBy(userFields, 'position').map(function(f) { + return Ember.Object.create({ value: null, field: f }); }); } this.set('userFields', userFields); diff --git a/app/assets/javascripts/discourse/models/rest.js.es6 b/app/assets/javascripts/discourse/models/rest.js.es6 index 843e22eb1..6396f8e54 100644 --- a/app/assets/javascripts/discourse/models/rest.js.es6 +++ b/app/assets/javascripts/discourse/models/rest.js.es6 @@ -18,7 +18,6 @@ const RestModel = Ember.Object.extend(Presence, { const self = this; self.set('isSaving', true); return store.update(type, this.get('id'), props).then(function(res) { - const payload = self.__munge(res.payload || res.responseJson); if (payload.success === "OK") { diff --git a/app/assets/stylesheets/common/admin/admin_base.scss b/app/assets/stylesheets/common/admin/admin_base.scss index d9a06c315..166829bb1 100644 --- a/app/assets/stylesheets/common/admin/admin_base.scss +++ b/app/assets/stylesheets/common/admin/admin_base.scss @@ -1460,7 +1460,6 @@ tr.not-activated { .controls { float: right; text-align: right; - width: 20%; } .clearfix { diff --git a/app/controllers/admin/user_fields_controller.rb b/app/controllers/admin/user_fields_controller.rb index 6770d67bd..0980381ec 100644 --- a/app/controllers/admin/user_fields_controller.rb +++ b/app/controllers/admin/user_fields_controller.rb @@ -1,11 +1,13 @@ class Admin::UserFieldsController < Admin::AdminController def self.columns - [:name, :field_type, :editable, :description, :required, :show_on_profile] + [:name, :field_type, :editable, :description, :required, :show_on_profile, :position] end def create field = UserField.new(params.require(:user_field).permit(*Admin::UserFieldsController.columns)) + + field.position = (UserField.maximum(:position) || 0) + 1 field.required = params[:required] == "true" fetch_options(field) @@ -15,33 +17,35 @@ class Admin::UserFieldsController < Admin::AdminController end def index - user_fields = UserField.all.includes(:user_field_options) + user_fields = UserField.all.includes(:user_field_options).order(:position) render_serialized(user_fields, UserFieldSerializer, root: 'user_fields') end def update - field_params = params.require(:user_field) - + field_params = params[:user_field] field = UserField.where(id: params.require(:id)).first Admin::UserFieldsController.columns.each do |col| - field.send("#{col}=", field_params[col] || false) + unless field_params[col].nil? + field.send("#{col}=", field_params[col]) + end end UserFieldOption.where(user_field_id: field.id).delete_all fetch_options(field) - json_result(field, serializer: UserFieldSerializer) do - field.save + if field.save + render_serialized(field, UserFieldSerializer, root: 'user_field') + else + render_json_error(field) end end def destroy field = UserField.where(id: params.require(:id)).first field.destroy if field.present? - render nothing: true + render json: success_json end - protected def fetch_options(field) diff --git a/app/serializers/user_field_serializer.rb b/app/serializers/user_field_serializer.rb index 0c8f6f721..7c0689806 100644 --- a/app/serializers/user_field_serializer.rb +++ b/app/serializers/user_field_serializer.rb @@ -6,6 +6,7 @@ class UserFieldSerializer < ApplicationSerializer :editable, :required, :show_on_profile, + :position, :options def options diff --git a/db/migrate/20150730154830_add_position_to_user_fields.rb b/db/migrate/20150730154830_add_position_to_user_fields.rb new file mode 100644 index 000000000..0f0fd0499 --- /dev/null +++ b/db/migrate/20150730154830_add_position_to_user_fields.rb @@ -0,0 +1,6 @@ +class AddPositionToUserFields < ActiveRecord::Migration + def change + add_column :user_fields, :position, :integer, default: 0 + execute "UPDATE user_fields SET position = (SELECT COUNT(*) from user_fields as uf2 where uf2.id < user_fields.id)" + end +end diff --git a/test/javascripts/helpers/create-pretender.js.es6 b/test/javascripts/helpers/create-pretender.js.es6 index 60994d163..fe00b4838 100644 --- a/test/javascripts/helpers/create-pretender.js.es6 +++ b/test/javascripts/helpers/create-pretender.js.es6 @@ -249,6 +249,12 @@ export default function() { return response({ widget }); }); + this.put('/cool_things/:cool_thing_id', function(request) { + const cool_thing = parsePostData(request.requestBody).cool_thing; + return response({ cool_thing }); + }); + + this.get('/widgets', function(request) { let result = _widgets; diff --git a/test/javascripts/models/store-test.js.es6 b/test/javascripts/models/store-test.js.es6 index f0dca5ad7..bac22ecd5 100644 --- a/test/javascripts/models/store-test.js.es6 +++ b/test/javascripts/models/store-test.js.es6 @@ -70,6 +70,14 @@ test('update', function() { }); }); +test('update with a multi world name', function(assert) { + const store = createStore(); + return store.update('cool-thing', 123, {name: 'hello'}).then(function(result) { + assert.ok(result); + assert.equal(result.payload.name, 'hello'); + }); +}); + test('findAll', function() { const store = createStore(); return store.findAll('widget').then(function(result) { @@ -84,7 +92,7 @@ test('destroyRecord', function(assert) { const store = createStore(); return store.find('widget', 123).then(function(w) { store.destroyRecord('widget', w).then(function(result) { - ok(result); + assert.ok(result); }); }); });