mirror of
https://github.com/discourse/discourse.git
synced 2025-02-02 10:23:17 +08:00
FEATURE: Admin interface for adding custom fields for users
This commit is contained in:
parent
a3e2e1fa6e
commit
0fc0533134
|
@ -0,0 +1,43 @@
|
||||||
|
import UserField from 'admin/models/user-field';
|
||||||
|
import BufferedContent from 'discourse/mixins/buffered-content';
|
||||||
|
|
||||||
|
export default Ember.ObjectController.extend(BufferedContent, {
|
||||||
|
needs: ['admin-user-fields'],
|
||||||
|
editing: Ember.computed.empty('id'),
|
||||||
|
|
||||||
|
fieldName: function() {
|
||||||
|
return UserField.fieldTypeById(this.get('field_type')).get('name');
|
||||||
|
}.property('field_type'),
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
save: function() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this.commitBuffer();
|
||||||
|
this.get('model').save().then(function(res) {
|
||||||
|
self.set('model.id', res.user_field.id);
|
||||||
|
self.set('editing', false);
|
||||||
|
}).catch(function() {
|
||||||
|
bootbox.alert(I18n.t('generic_error'));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
edit: function() {
|
||||||
|
this.set('editing', true);
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
this.get('controllers.admin-user-fields').send('destroy', this.get('model'));
|
||||||
|
},
|
||||||
|
|
||||||
|
cancel: function() {
|
||||||
|
var id = this.get('id');
|
||||||
|
if (Ember.empty(id)) {
|
||||||
|
this.get('controllers.admin-user-fields').send('destroy', this.get('model'));
|
||||||
|
} else {
|
||||||
|
this.rollbackBuffer();
|
||||||
|
this.set('editing', false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,36 @@
|
||||||
|
import UserField from 'admin/models/user-field';
|
||||||
|
|
||||||
|
export default Ember.ArrayController.extend({
|
||||||
|
fieldTypes: null,
|
||||||
|
|
||||||
|
createDisabled: Em.computed.gte('model.length', 3),
|
||||||
|
|
||||||
|
_performDestroy: function(f, model) {
|
||||||
|
return f.destroy().then(function() {
|
||||||
|
model.removeObject(f);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
createField: function() {
|
||||||
|
this.pushObject(UserField.create({
|
||||||
|
field_type: 'text',
|
||||||
|
name: I18n.t('admin.user_fields.untitled')
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function(f) {
|
||||||
|
var model = this.get('model'),
|
||||||
|
self = this;
|
||||||
|
|
||||||
|
// 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); }
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
self._performDestroy(f, model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
54
app/assets/javascripts/admin/models/user-field.js.es6
Normal file
54
app/assets/javascripts/admin/models/user-field.js.es6
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
var _fieldTypes = [
|
||||||
|
Ember.Object.create({id: 'text', name: I18n.t('admin.user_fields.field_types.text') }),
|
||||||
|
Ember.Object.create({id: 'confirm', name: I18n.t('admin.user_fields.field_types.confirm') })
|
||||||
|
];
|
||||||
|
|
||||||
|
var UserField = Ember.Object.extend({
|
||||||
|
destroy: function() {
|
||||||
|
var self = this;
|
||||||
|
return new Ember.RSVP.Promise(function(resolve) {
|
||||||
|
var id = self.get('id');
|
||||||
|
if (id) {
|
||||||
|
return Discourse.ajax("/admin/customize/user_fields/" + id, { type: 'DELETE' }).then(function() {
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
save: function() {
|
||||||
|
var id = this.get('id');
|
||||||
|
if (!id) {
|
||||||
|
return Discourse.ajax("/admin/customize/user_fields", {
|
||||||
|
type: "POST",
|
||||||
|
data: { user_field: this.getProperties('name', 'field_type') }
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return Discourse.ajax("/admin/customize/user_fields/" + id, {
|
||||||
|
type: "PUT",
|
||||||
|
data: { user_field: this.getProperties('name', 'field_type') }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
UserField.reopenClass({
|
||||||
|
findAll: function() {
|
||||||
|
return Discourse.ajax("/admin/customize/user_fields").then(function(result) {
|
||||||
|
return result.user_fields.map(function(uf) {
|
||||||
|
return UserField.create(uf);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
fieldTypes: function() {
|
||||||
|
return _fieldTypes;
|
||||||
|
},
|
||||||
|
|
||||||
|
fieldTypeById: function(id) {
|
||||||
|
return _fieldTypes.findBy('id', id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default UserField;
|
14
app/assets/javascripts/admin/routes/admin-user-fields.js.es6
Normal file
14
app/assets/javascripts/admin/routes/admin-user-fields.js.es6
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import UserField from 'admin/models/user-field';
|
||||||
|
|
||||||
|
export default Ember.Route.extend({
|
||||||
|
model: function() {
|
||||||
|
return UserField.findAll();
|
||||||
|
},
|
||||||
|
|
||||||
|
setupController: function(controller, model) {
|
||||||
|
controller.setProperties({
|
||||||
|
model: model,
|
||||||
|
fieldTypes: UserField.fieldTypes()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
|
@ -18,6 +18,8 @@ Discourse.Route.buildRoutes(function() {
|
||||||
this.resource('adminSiteText', { path: '/site_text' }, function() {
|
this.resource('adminSiteText', { path: '/site_text' }, function() {
|
||||||
this.route('edit', {path: '/:text_type'});
|
this.route('edit', {path: '/:text_type'});
|
||||||
});
|
});
|
||||||
|
this.resource('adminUserFields', { path: '/user_fields' }, function() {
|
||||||
|
});
|
||||||
});
|
});
|
||||||
this.route('api');
|
this.route('api');
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,9 @@
|
||||||
<li>{{#link-to 'adminCustomize.colors'}}{{i18n admin.customize.colors.title}}{{/link-to}}</li>
|
<li>{{#link-to 'adminCustomize.colors'}}{{i18n admin.customize.colors.title}}{{/link-to}}</li>
|
||||||
<li>{{#link-to 'adminCustomize.css_html'}}{{i18n admin.customize.css_html.title}}{{/link-to}}</li>
|
<li>{{#link-to 'adminCustomize.css_html'}}{{i18n admin.customize.css_html.title}}{{/link-to}}</li>
|
||||||
<li>{{#link-to 'adminSiteText'}}{{i18n admin.site_text.title}}{{/link-to}}</li>
|
<li>{{#link-to 'adminSiteText'}}{{i18n admin.site_text.title}}{{/link-to}}</li>
|
||||||
|
{{#if USERFIELD_FEATURE_COMPLETE}}
|
||||||
|
<li>{{#link-to 'adminUserFields'}}{{i18n admin.user_fields.title}}{{/link-to}}</li>
|
||||||
|
{{/if}}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
<div class='user-fields'>
|
||||||
|
<h2>{{i18n admin.user_fields.title}}</h2>
|
||||||
|
|
||||||
|
<p class="desc">{{i18n admin.user_fields.description}}</p>
|
||||||
|
|
||||||
|
{{#if model}}
|
||||||
|
{{#each f in model itemController="admin-user-field-item" itemView="admin-user-field-item"}}
|
||||||
|
{{#if f.editing}}
|
||||||
|
<div class='form-element'>
|
||||||
|
<label>{{i18n admin.user_fields.name}}
|
||||||
|
{{input value=f.buffered.name class="user-field-name"}}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class='form-element'>
|
||||||
|
<label>{{i18n admin.user_fields.type}}
|
||||||
|
{{combo-box content=fieldTypes valueAttribute="id" value=f.buffered.field_type}}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class='form-element controls'>
|
||||||
|
<button {{action "save"}}class='btn btn-primary'>{{fa-icon 'check'}} {{i18n admin.user_fields.save}}</button>
|
||||||
|
<button {{action "cancel"}} class='btn btn-danger'>{{fa-icon 'times'}} {{i18n admin.user_fields.cancel}}</button>
|
||||||
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<div class='form-display'>
|
||||||
|
{{f.name}}
|
||||||
|
</div>
|
||||||
|
<div class='form-display'>
|
||||||
|
{{f.fieldName}}
|
||||||
|
</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>
|
||||||
|
{{/if}}
|
||||||
|
<div class='clearfix'></div>
|
||||||
|
{{/each}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<button {{bind-attr disabled="createDisabled"}} class='btn btn-primary' {{action createField}}>
|
||||||
|
{{fa-icon "plus"}}
|
||||||
|
{{i18n admin.user_fields.create}}
|
||||||
|
</button>
|
||||||
|
</div>
|
|
@ -0,0 +1,13 @@
|
||||||
|
export default Ember.View.extend({
|
||||||
|
classNameBindings: [':user-field'],
|
||||||
|
|
||||||
|
_focusOnEdit: function() {
|
||||||
|
if (this.get('controller.editing')) {
|
||||||
|
Ember.run.scheduleOnce('afterRender', this, '_focusName');
|
||||||
|
}
|
||||||
|
}.observes('controller.editing').on('didInsertElement'),
|
||||||
|
|
||||||
|
_focusName: function() {
|
||||||
|
$('.user-field-name').select();
|
||||||
|
}
|
||||||
|
});
|
|
@ -136,7 +136,8 @@ export default Ember.DefaultResolver.extend({
|
||||||
decamelized = decamelized.replace(/^admin\_/, 'admin/templates/');
|
decamelized = decamelized.replace(/^admin\_/, 'admin/templates/');
|
||||||
decamelized = decamelized.replace(/^admin\./, 'admin/templates/');
|
decamelized = decamelized.replace(/^admin\./, 'admin/templates/');
|
||||||
decamelized = decamelized.replace(/\./, '_');
|
decamelized = decamelized.replace(/\./, '_');
|
||||||
return Ember.TEMPLATES[decamelized];
|
var dashed = decamelized.replace(/_/g, '-');
|
||||||
|
return Ember.TEMPLATES[decamelized] || Ember.TEMPLATES[dashed];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
/* global BufferedProxy: true */
|
||||||
|
export default Ember.Mixin.create({
|
||||||
|
buffered: function() {
|
||||||
|
return Em.ObjectProxy.extend(BufferedProxy).create({
|
||||||
|
content: this.get('content')
|
||||||
|
});
|
||||||
|
}.property('content'),
|
||||||
|
|
||||||
|
rollbackBuffer: function() {
|
||||||
|
this.get('buffered').discardBufferedChanges();
|
||||||
|
},
|
||||||
|
|
||||||
|
commitBuffer: function() {
|
||||||
|
this.get('buffered').applyBufferedChanges();
|
||||||
|
}
|
||||||
|
});
|
|
@ -1,3 +1,4 @@
|
||||||
|
//= require admin/models/user-field
|
||||||
//= require admin/controllers/admin-email-skipped
|
//= require admin/controllers/admin-email-skipped
|
||||||
//= require admin/controllers/change-site-customization-details
|
//= require admin/controllers/change-site-customization-details
|
||||||
//= require_tree ./admin
|
//= require_tree ./admin
|
||||||
|
|
|
@ -39,4 +39,5 @@
|
||||||
//= require lock-on.js
|
//= require lock-on.js
|
||||||
//= require ember-cloaking
|
//= require ember-cloaking
|
||||||
//= require break_string
|
//= require break_string
|
||||||
|
//= require buffered-proxy
|
||||||
//= require_tree ./discourse/ember
|
//= require_tree ./discourse/ember
|
||||||
|
|
|
@ -1319,3 +1319,43 @@ tr.not-activated {
|
||||||
color: #bbb;
|
color: #bbb;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.user-fields {
|
||||||
|
h2 {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-field {
|
||||||
|
padding: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-bottom: 1px solid scale-color-diff();
|
||||||
|
|
||||||
|
.form-display {
|
||||||
|
width: 35%;
|
||||||
|
display: inline-block;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-element {
|
||||||
|
float: left;
|
||||||
|
width: 35%;
|
||||||
|
margin-right: 10px;
|
||||||
|
label {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
input, div.combobox {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
float: right;
|
||||||
|
text-align: right;
|
||||||
|
width: 20%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clearfix {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
30
app/controllers/admin/user_fields_controller.rb
Normal file
30
app/controllers/admin/user_fields_controller.rb
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
class Admin::UserFieldsController < Admin::AdminController
|
||||||
|
|
||||||
|
def create
|
||||||
|
field = UserField.create!(params.require(:user_field).permit(:name, :field_type))
|
||||||
|
render_serialized(field, UserFieldSerializer)
|
||||||
|
end
|
||||||
|
|
||||||
|
def index
|
||||||
|
render_serialized(UserField.all, UserFieldSerializer, root: 'user_fields')
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
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.save
|
||||||
|
|
||||||
|
render_serialized(field, UserFieldSerializer)
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
field = UserField.where(id: params.require(:id)).first
|
||||||
|
field.destroy if field.present?
|
||||||
|
render nothing: true
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
4
app/models/user_field.rb
Normal file
4
app/models/user_field.rb
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
class UserField < ActiveRecord::Base
|
||||||
|
validates_presence_of :name, :field_type
|
||||||
|
end
|
||||||
|
|
3
app/models/user_field_serializer.rb
Normal file
3
app/models/user_field_serializer.rb
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
class UserFieldSerializer < ApplicationSerializer
|
||||||
|
attributes :id, :name, :field_type
|
||||||
|
end
|
|
@ -1983,6 +1983,23 @@ en:
|
||||||
external_email: "Email"
|
external_email: "Email"
|
||||||
external_avatar_url: "Avatar URL"
|
external_avatar_url: "Avatar URL"
|
||||||
|
|
||||||
|
user_fields:
|
||||||
|
title: "User Fields"
|
||||||
|
description: "Any fields added here will be required from users when they sign up."
|
||||||
|
create: "Create User Field"
|
||||||
|
untitled: "Untitled"
|
||||||
|
name: "Field Name"
|
||||||
|
type: "Field Type"
|
||||||
|
save: "Save"
|
||||||
|
edit: "Edit"
|
||||||
|
delete: "Delete"
|
||||||
|
cancel: "Cancel"
|
||||||
|
delete_confirm: "Are you sure you want to delete that user field?"
|
||||||
|
|
||||||
|
field_types:
|
||||||
|
text: 'Text Field'
|
||||||
|
confirm: 'Confirmation'
|
||||||
|
|
||||||
site_text:
|
site_text:
|
||||||
none: "Choose a type of content to begin editing."
|
none: "Choose a type of content to begin editing."
|
||||||
title: 'Text Content'
|
title: 'Text Content'
|
||||||
|
|
|
@ -112,6 +112,7 @@ Discourse::Application.routes.draw do
|
||||||
scope "/customize" do
|
scope "/customize" do
|
||||||
resources :site_text, constraints: AdminConstraint.new
|
resources :site_text, constraints: AdminConstraint.new
|
||||||
resources :site_text_types, constraints: AdminConstraint.new
|
resources :site_text_types, constraints: AdminConstraint.new
|
||||||
|
resources :user_fields, constraints: AdminConstraint.new
|
||||||
end
|
end
|
||||||
|
|
||||||
resources :color_schemes, constraints: AdminConstraint.new
|
resources :color_schemes, constraints: AdminConstraint.new
|
||||||
|
|
9
db/migrate/20140925173220_create_user_fields.rb
Normal file
9
db/migrate/20140925173220_create_user_fields.rb
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
class CreateUserFields < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
create_table :user_fields do |t|
|
||||||
|
t.string :name, null: false
|
||||||
|
t.string :field_type, null: false
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -70,7 +70,7 @@ module Tilt
|
||||||
# For backwards compatibility with plugins, for now export the Global format too.
|
# For backwards compatibility with plugins, for now export the Global format too.
|
||||||
# We should eventually have an upgrade system for plugins to use ES6 or some other
|
# We should eventually have an upgrade system for plugins to use ES6 or some other
|
||||||
# resolve based API.
|
# resolve based API.
|
||||||
if ENV['DISCOURSE_NO_CONSTANTS'].nil? && scope.logical_path =~ /(discourse|admin)\/(controllers|components|views|routes|mixins)\/(.*)/
|
if ENV['DISCOURSE_NO_CONSTANTS'].nil? && scope.logical_path =~ /(discourse|admin)\/(controllers|components|views|routes|mixins|models)\/(.*)/
|
||||||
type = Regexp.last_match[2]
|
type = Regexp.last_match[2]
|
||||||
file_name = Regexp.last_match[3].gsub(/[\-\/]/, '_')
|
file_name = Regexp.last_match[3].gsub(/[\-\/]/, '_')
|
||||||
class_name = file_name.classify
|
class_name = file_name.classify
|
||||||
|
@ -87,6 +87,7 @@ module Tilt
|
||||||
# HAX
|
# HAX
|
||||||
result = "Controller" if result == "ControllerController"
|
result = "Controller" if result == "ControllerController"
|
||||||
result.gsub!(/Mixin$/, '')
|
result.gsub!(/Mixin$/, '')
|
||||||
|
result.gsub!(/Model$/, '')
|
||||||
|
|
||||||
@output << "\n\nDiscourse.#{result} = require('#{require_name}').default;\n"
|
@output << "\n\nDiscourse.#{result} = require('#{require_name}').default;\n"
|
||||||
end
|
end
|
||||||
|
|
57
spec/controllers/admin/user_fields_controller_spec.rb
Normal file
57
spec/controllers/admin/user_fields_controller_spec.rb
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Admin::UserFieldsController do
|
||||||
|
|
||||||
|
it "is a subclass of AdminController" do
|
||||||
|
(Admin::ApiController < Admin::AdminController).should == true
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when logged in" do
|
||||||
|
let!(:user) { log_in(:admin) }
|
||||||
|
|
||||||
|
context '.create' do
|
||||||
|
it "creates a user field" do
|
||||||
|
-> {
|
||||||
|
xhr :post, :create, {user_field: {name: 'hello', field_type: 'text'} }
|
||||||
|
response.should be_success
|
||||||
|
}.should change(UserField, :count).by(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context '.index' do
|
||||||
|
let!(:user_field) { Fabricate(:user_field) }
|
||||||
|
|
||||||
|
it "returns a list of user fields" do
|
||||||
|
xhr :get, :index
|
||||||
|
response.should be_success
|
||||||
|
json = ::JSON.parse(response.body)
|
||||||
|
json['user_fields'].should be_present
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context '.destroy' do
|
||||||
|
let!(:user_field) { Fabricate(:user_field) }
|
||||||
|
|
||||||
|
it "returns a list of user fields" do
|
||||||
|
-> {
|
||||||
|
xhr :delete, :destroy, id: user_field.id
|
||||||
|
response.should be_success
|
||||||
|
}.should change(UserField, :count).by(-1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context '.update' do
|
||||||
|
let!(:user_field) { Fabricate(:user_field) }
|
||||||
|
|
||||||
|
it "returns a list of user fields" do
|
||||||
|
xhr :put, :update, id: user_field.id, user_field: {name: 'fraggle', field_type: 'confirm'}
|
||||||
|
response.should be_success
|
||||||
|
user_field.reload
|
||||||
|
user_field.name.should == 'fraggle'
|
||||||
|
user_field.field_type.should == 'confirm'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
4
spec/fabricators/user_field.rb
Normal file
4
spec/fabricators/user_field.rb
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
Fabricator(:user_field) do
|
||||||
|
name { sequence(:name) {|i| "field_#{i}" } }
|
||||||
|
field_type 'text'
|
||||||
|
end
|
87
vendor/assets/javascripts/buffered-proxy.js
vendored
Normal file
87
vendor/assets/javascripts/buffered-proxy.js
vendored
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
(function (global) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function empty(obj) {
|
||||||
|
var key;
|
||||||
|
for (key in obj) if (obj.hasOwnProperty(key)) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var Ember = global.Ember,
|
||||||
|
get = Ember.get, set = Ember.set;
|
||||||
|
|
||||||
|
var BufferedProxy = Ember.Mixin.create({
|
||||||
|
buffer: null,
|
||||||
|
|
||||||
|
hasBufferedChanges: false,
|
||||||
|
|
||||||
|
unknownProperty: function (key) {
|
||||||
|
var buffer = this.buffer;
|
||||||
|
return buffer && buffer.hasOwnProperty(key) ? buffer[key] : this._super(key);
|
||||||
|
},
|
||||||
|
|
||||||
|
setUnknownProperty: function (key, value) {
|
||||||
|
if (!this.buffer) this.buffer = {};
|
||||||
|
|
||||||
|
var buffer = this.buffer,
|
||||||
|
content = this.get('content'),
|
||||||
|
current = content && get(content, key),
|
||||||
|
previous = buffer.hasOwnProperty(key) ? buffer[key] : current;
|
||||||
|
|
||||||
|
if (previous === value) return;
|
||||||
|
|
||||||
|
this.propertyWillChange(key);
|
||||||
|
|
||||||
|
if (current === value) {
|
||||||
|
delete buffer[key];
|
||||||
|
if (empty(buffer)) {
|
||||||
|
this.set('hasBufferedChanges', false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
buffer[key] = value;
|
||||||
|
this.set('hasBufferedChanges', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.propertyDidChange(key);
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
|
||||||
|
applyBufferedChanges: function() {
|
||||||
|
var buffer = this.buffer,
|
||||||
|
content = this.get('content'),
|
||||||
|
key;
|
||||||
|
for (key in buffer) {
|
||||||
|
if (!buffer.hasOwnProperty(key)) continue;
|
||||||
|
set(content, key, buffer[key]);
|
||||||
|
}
|
||||||
|
this.buffer = {};
|
||||||
|
this.set('hasBufferedChanges', false);
|
||||||
|
},
|
||||||
|
|
||||||
|
discardBufferedChanges: function() {
|
||||||
|
var buffer = this.buffer,
|
||||||
|
content = this.get('content'),
|
||||||
|
key;
|
||||||
|
for (key in buffer) {
|
||||||
|
if (!buffer.hasOwnProperty(key)) continue;
|
||||||
|
|
||||||
|
this.propertyWillChange(key);
|
||||||
|
delete buffer[key];
|
||||||
|
this.propertyDidChange(key);
|
||||||
|
}
|
||||||
|
this.set('hasBufferedChanges', false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// CommonJS module
|
||||||
|
if (typeof module !== 'undefined' && module.exports) {
|
||||||
|
module.exports = BufferedProxy;
|
||||||
|
} else if (typeof define === "function" && define.amd) {
|
||||||
|
define("buffered-proxy", function (require, exports, module) {
|
||||||
|
return BufferedProxy;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
global.BufferedProxy = BufferedProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
}(this));
|
Loading…
Reference in New Issue
Block a user