Support saving posts via Store
This commit is contained in:
parent
d4a05825da
commit
76f7786d0d
|
@ -1,11 +1,20 @@
|
|||
import RestAdapter from 'discourse/adapters/rest';
|
||||
import { Result } from 'discourse/adapters/rest';
|
||||
|
||||
export default RestAdapter.extend({
|
||||
|
||||
// GET /posts doesn't include a type
|
||||
find(store, type, findArgs) {
|
||||
return this._super(store, type, findArgs).then(function(result) {
|
||||
return {post: result};
|
||||
});
|
||||
},
|
||||
|
||||
createRecord(store, type, args) {
|
||||
const typeField = Ember.String.underscore(type);
|
||||
args.nested_post = true;
|
||||
return Discourse.ajax(this.pathFor(store, type), { method: 'POST', data: args }).then(function (json) {
|
||||
return new Result(json[typeField], json);
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
const ADMIN_MODELS = ['plugin'];
|
||||
|
||||
export function Result(payload, responseJson) {
|
||||
this.payload = payload;
|
||||
this.responseJson = responseJson;
|
||||
this.target = null;
|
||||
}
|
||||
|
||||
export default Ember.Object.extend({
|
||||
pathFor(store, type, findArgs) {
|
||||
let path = "/" + Ember.String.underscore(store.pluralize(type));
|
||||
|
@ -35,7 +41,18 @@ export default Ember.Object.extend({
|
|||
update(store, type, id, attrs) {
|
||||
const data = {};
|
||||
data[Ember.String.underscore(type)] = attrs;
|
||||
return Discourse.ajax(this.pathFor(store, type, id), { method: 'PUT', data });
|
||||
return Discourse.ajax(this.pathFor(store, type, id), { method: 'PUT', data }).then(function(json) {
|
||||
return new Result(json[type], json);
|
||||
});
|
||||
},
|
||||
|
||||
createRecord(store, type, attrs) {
|
||||
const data = {};
|
||||
const typeField = Ember.String.underscore(type);
|
||||
data[typeField] = attrs;
|
||||
return Discourse.ajax(this.pathFor(store, type), { method: 'POST', data }).then(function (json) {
|
||||
return new Result(json[typeField], json);
|
||||
});
|
||||
},
|
||||
|
||||
destroyRecord(store, type, record) {
|
||||
|
|
|
@ -219,6 +219,7 @@ export default DiscourseController.extend({
|
|||
imageSizes: this.get('view').imageSizes(),
|
||||
editReason: this.get("editReason")
|
||||
}).then(function(opts) {
|
||||
|
||||
// If we replied as a new topic successfully, remove the draft.
|
||||
if (self.get('replyAsNewTopicDraft')) {
|
||||
self.destroyDraft();
|
||||
|
@ -246,7 +247,6 @@ export default DiscourseController.extend({
|
|||
bootbox.alert(error);
|
||||
});
|
||||
|
||||
|
||||
if (this.get('controllers.application.currentRouteName').split('.')[0] === 'topic' &&
|
||||
composer.get('topic.id') === this.get('controllers.topic.model.id')) {
|
||||
staged = composer.get('stagedPost');
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
export function throwAjaxError(undoCallback) {
|
||||
return function(error) {
|
||||
if (error instanceof Error) {
|
||||
Ember.Logger.error(error.stack);
|
||||
}
|
||||
|
||||
if (typeof error === "string") {
|
||||
Ember.Logger.error(error);
|
||||
}
|
||||
|
||||
// If we provided an `undo` callback
|
||||
if (undoCallback) { undoCallback(error); }
|
||||
|
||||
let parsedError;
|
||||
if (error.responseText) {
|
||||
try {
|
||||
const parsedJSON = $.parseJSON(error.responseText);
|
||||
if (parsedJSON.errors) {
|
||||
parsedError = parsedJSON.errors[0];
|
||||
} else if (parsedJSON.failed) {
|
||||
parsedError = parsedJSON.message;
|
||||
}
|
||||
} catch(ex) {
|
||||
// in case the JSON doesn't parse
|
||||
Ember.Logger.error(ex.stack);
|
||||
}
|
||||
}
|
||||
throw parsedError || I18n.t('generic_error');
|
||||
};
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import RestModel from 'discourse/models/rest';
|
||||
import Post from 'discourse/models/post';
|
||||
import Topic from 'discourse/models/topic';
|
||||
import { throwAjaxError } from 'discourse/lib/ajax-error';
|
||||
|
||||
const CLOSED = 'closed',
|
||||
SAVING = 'saving',
|
||||
|
@ -430,25 +430,22 @@ const Composer = RestModel.extend({
|
|||
promise = Ember.RSVP.resolve();
|
||||
}
|
||||
|
||||
post.setProperties({
|
||||
const props = {
|
||||
raw: this.get('reply'),
|
||||
editReason: opts.editReason,
|
||||
imageSizes: opts.imageSizes,
|
||||
edit_reason: opts.editReason,
|
||||
image_sizes: opts.imageSizes,
|
||||
cooked: this.getCookedHtml()
|
||||
});
|
||||
};
|
||||
|
||||
this.set('composeState', CLOSED);
|
||||
|
||||
return promise.then(function() {
|
||||
return post.save(function(result) {
|
||||
post.updateFromPost(result);
|
||||
return post.save(props).then(function() {
|
||||
self.clearState();
|
||||
}, function (error) {
|
||||
}).catch(throwAjaxError(function() {
|
||||
post.set('cooked', oldCooked);
|
||||
self.set('composeState', OPEN);
|
||||
const response = $.parseJSON(error.responseText);
|
||||
throw response && response.errors ? response.errors[0] : I18n.t('generic_error');
|
||||
});
|
||||
}));
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -473,7 +470,7 @@ const Composer = RestModel.extend({
|
|||
let addedToStream = false;
|
||||
|
||||
// Build the post object
|
||||
const createdPost = Post.create({
|
||||
const createdPost = this.store.createRecord('post', {
|
||||
imageSizes: opts.imageSizes,
|
||||
cooked: this.getCookedHtml(),
|
||||
reply_count: 0,
|
||||
|
@ -489,7 +486,6 @@ const Composer = RestModel.extend({
|
|||
moderator: user.get('moderator'),
|
||||
admin: user.get('admin'),
|
||||
yours: true,
|
||||
newPost: true,
|
||||
read: true
|
||||
});
|
||||
|
||||
|
@ -521,11 +517,7 @@ const Composer = RestModel.extend({
|
|||
// engine, staging will just cause a blank post to render
|
||||
if (!_.isEmpty(createdPost.get('cooked'))) {
|
||||
state = postStream.stagePost(createdPost, user);
|
||||
|
||||
if(state === "alreadyStaging"){
|
||||
return;
|
||||
}
|
||||
|
||||
if (state === "alreadyStaging") { return; }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -534,13 +526,12 @@ const Composer = RestModel.extend({
|
|||
composer.set("stagedPost", state === "staged" && createdPost);
|
||||
|
||||
return createdPost.save().then(function(result) {
|
||||
|
||||
let saving = true;
|
||||
createdPost.updateFromJson(result);
|
||||
|
||||
if (topic) {
|
||||
// It's no longer a new post
|
||||
createdPost.set('newPost', false);
|
||||
topic.set('draft_sequence', result.draft_sequence);
|
||||
topic.set('draft_sequence', result.target.draft_sequence);
|
||||
postStream.commitPost(createdPost);
|
||||
addedToStream = true;
|
||||
} else {
|
||||
|
@ -563,30 +554,13 @@ const Composer = RestModel.extend({
|
|||
composer.set('composeState', SAVING);
|
||||
}
|
||||
|
||||
return { post: result };
|
||||
}).catch(function(error) {
|
||||
|
||||
// If an error occurs
|
||||
return { post: createdPost };
|
||||
}).catch(throwAjaxError(function() {
|
||||
if (postStream) {
|
||||
postStream.undoPost(createdPost);
|
||||
}
|
||||
composer.set('composeState', OPEN);
|
||||
|
||||
// TODO extract error handling code
|
||||
let parsedError;
|
||||
try {
|
||||
const parsedJSON = $.parseJSON(error.responseText);
|
||||
if (parsedJSON.errors) {
|
||||
parsedError = parsedJSON.errors[0];
|
||||
} else if (parsedJSON.failed) {
|
||||
parsedError = parsedJSON.message;
|
||||
}
|
||||
}
|
||||
catch(ex) {
|
||||
parsedError = "Unknown error saving post, try again. Error: " + error.status + " " + error.statusText;
|
||||
}
|
||||
throw parsedError;
|
||||
});
|
||||
}));
|
||||
},
|
||||
|
||||
getCookedHtml() {
|
||||
|
|
|
@ -113,47 +113,35 @@ const Post = RestModel.extend({
|
|||
});
|
||||
}.property('actions_summary.@each.users', 'actions_summary.@each.count'),
|
||||
|
||||
save() {
|
||||
const self = this;
|
||||
if (!this.get('newPost')) {
|
||||
// We're updating a post
|
||||
return Discourse.ajax("/posts/" + (this.get('id')), {
|
||||
type: 'PUT',
|
||||
dataType: 'json',
|
||||
data: {
|
||||
post: { raw: this.get('raw'), edit_reason: this.get('editReason') },
|
||||
image_sizes: this.get('imageSizes')
|
||||
}
|
||||
}).then(function(result) {
|
||||
// If we received a category update, update it
|
||||
self.set('version', result.post.version);
|
||||
if (result.category) Discourse.Site.current().updateCategory(result.category);
|
||||
return Discourse.Post.create(result.post);
|
||||
});
|
||||
|
||||
} else {
|
||||
// We're saving a post
|
||||
const data = this.getProperties(Discourse.Composer.serializedFieldsForCreate());
|
||||
data.reply_to_post_number = this.get('reply_to_post_number');
|
||||
data.image_sizes = this.get('imageSizes');
|
||||
data.nested_post = true;
|
||||
|
||||
const metaData = this.get('metaData');
|
||||
// Put the metaData into the request
|
||||
if (metaData) {
|
||||
data.meta_data = {};
|
||||
Ember.keys(metaData).forEach(function(key) { data.meta_data[key] = metaData.get(key); });
|
||||
}
|
||||
|
||||
return Discourse.ajax("/posts", {
|
||||
type: 'POST',
|
||||
data: data
|
||||
}).then(function(result) {
|
||||
return Discourse.Post.create(result.post);
|
||||
});
|
||||
afterUpdate(res) {
|
||||
if (res.category) {
|
||||
Discourse.Site.current().updateCategory(res.category);
|
||||
}
|
||||
},
|
||||
|
||||
updateProperties() {
|
||||
return {
|
||||
post: { raw: this.get('raw'), edit_reason: this.get('editReason') },
|
||||
image_sizes: this.get('imageSizes')
|
||||
};
|
||||
},
|
||||
|
||||
createProperties() {
|
||||
const data = this.getProperties(Discourse.Composer.serializedFieldsForCreate());
|
||||
data.reply_to_post_number = this.get('reply_to_post_number');
|
||||
data.image_sizes = this.get('imageSizes');
|
||||
|
||||
const metaData = this.get('metaData');
|
||||
|
||||
// Put the metaData into the request
|
||||
if (metaData) {
|
||||
data.meta_data = {};
|
||||
Ember.keys(metaData).forEach(function(key) { data.meta_data[key] = metaData.get(key); });
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
|
||||
// Expands the first post's content, if embedded and shortened.
|
||||
expand() {
|
||||
const self = this;
|
||||
|
@ -266,50 +254,6 @@ const Post = RestModel.extend({
|
|||
});
|
||||
},
|
||||
|
||||
/**
|
||||
Updates a post from a JSON packet. This is normally done after the post is saved to refresh any
|
||||
attributes.
|
||||
**/
|
||||
updateFromJson(obj) {
|
||||
if (!obj) return;
|
||||
|
||||
let skip, oldVal;
|
||||
|
||||
// Update all the properties
|
||||
const post = this;
|
||||
_.each(obj, function(val,key) {
|
||||
if (key !== 'actions_summary'){
|
||||
oldVal = post[key];
|
||||
skip = false;
|
||||
|
||||
if (val && val !== oldVal) {
|
||||
|
||||
if (key === "reply_to_user" && val && oldVal) {
|
||||
skip = val.username === oldVal.username || Em.get(val, "username") === Em.get(oldVal, "username");
|
||||
}
|
||||
|
||||
if(!skip) {
|
||||
post.set(key, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Rebuild actions summary
|
||||
this.set('actions_summary', Em.A());
|
||||
if (obj.actions_summary) {
|
||||
const lookup = Em.Object.create();
|
||||
_.each(obj.actions_summary,function(a) {
|
||||
a.post = post;
|
||||
a.actionType = Discourse.Site.current().postActionTypeById(a.id);
|
||||
const actionSummary = Discourse.ActionSummary.create(a);
|
||||
post.get('actions_summary').pushObject(actionSummary);
|
||||
lookup.set(a.actionType.get('name_key'), actionSummary);
|
||||
});
|
||||
this.set('actionByName', lookup);
|
||||
}
|
||||
},
|
||||
|
||||
// Load replies to this post
|
||||
loadReplies() {
|
||||
if(this.get('loadingReplies')){
|
||||
|
|
|
@ -1,22 +1,52 @@
|
|||
import Presence from 'discourse/mixins/presence';
|
||||
|
||||
const RestModel = Ember.Object.extend(Presence, {
|
||||
update(attrs) {
|
||||
const self = this,
|
||||
type = this.get('__type');
|
||||
isNew: Ember.computed.equal('__state', 'new'),
|
||||
isCreated: Ember.computed.equal('__state', 'created'),
|
||||
|
||||
const munge = this.__munge;
|
||||
return this.store.update(type, this.get('id'), attrs).then(function(result) {
|
||||
if (result && result[type]) {
|
||||
Object.keys(result).forEach(function(k) {
|
||||
attrs[k] = result[k];
|
||||
});
|
||||
}
|
||||
self.setProperties(munge(attrs));
|
||||
return result;
|
||||
afterUpdate: Ember.K,
|
||||
|
||||
update(props) {
|
||||
props = props || this.updateProperties();
|
||||
|
||||
const type = this.get('__type'),
|
||||
store = this.get('store');
|
||||
|
||||
const self = this;
|
||||
return store.update(type, this.get('id'), props).then(function(res) {
|
||||
self.setProperties(self.__munge(res.payload || res.responseJson));
|
||||
self.afterUpdate(res);
|
||||
return res;
|
||||
});
|
||||
},
|
||||
|
||||
_saveNew(props) {
|
||||
props = props || this.createProperties();
|
||||
|
||||
const type = this.get('__type'),
|
||||
store = this.get('store'),
|
||||
adapter = store.adapterFor(type);
|
||||
|
||||
const self = this;
|
||||
return adapter.createRecord(store, type, props).then(function(res) {
|
||||
if (!res) { throw "Received no data back from createRecord"; }
|
||||
self.setProperties(self.__munge(res.payload));
|
||||
|
||||
self.set('__state', 'created');
|
||||
|
||||
res.target = self;
|
||||
return res;
|
||||
});
|
||||
},
|
||||
|
||||
createProperties() {
|
||||
throw "You must overwrite `createProperties()` before saving a record";
|
||||
},
|
||||
|
||||
save(props) {
|
||||
return this.get('isNew') ? this._saveNew(props) : this.update(props);
|
||||
},
|
||||
|
||||
destroyRecord() {
|
||||
const type = this.get('__type');
|
||||
return this.store.destroyRecord(type, this);
|
||||
|
@ -34,7 +64,7 @@ RestModel.reopenClass({
|
|||
args = args || {};
|
||||
if (!args.store) {
|
||||
const container = Discourse.__container__;
|
||||
Ember.warn('Use `store.createRecord` to create records instead of `.create()`');
|
||||
// Ember.warn('Use `store.createRecord` to create records instead of `.create()`');
|
||||
args.store = container.lookup('store:main');
|
||||
}
|
||||
|
||||
|
|
|
@ -16,26 +16,23 @@ export default Ember.Object.extend({
|
|||
},
|
||||
|
||||
findAll(type) {
|
||||
const adapter = this.container.lookup('adapter:' + type) || this.container.lookup('adapter:rest');
|
||||
const self = this;
|
||||
return adapter.findAll(this, type).then(function(result) {
|
||||
return this.adapterFor(type).findAll(this, type).then(function(result) {
|
||||
return self._resultSet(type, result);
|
||||
});
|
||||
},
|
||||
|
||||
// Mostly for legacy, things like TopicList without ResultSets
|
||||
findFiltered(type, findArgs) {
|
||||
const adapter = this.container.lookup('adapter:' + type) || this.container.lookup('adapter:rest');
|
||||
const self = this;
|
||||
return adapter.find(this, type, findArgs).then(function(result) {
|
||||
return this.adapterFor(type).find(this, type, findArgs).then(function(result) {
|
||||
return self._build(type, result);
|
||||
});
|
||||
},
|
||||
|
||||
find(type, findArgs) {
|
||||
const adapter = this.container.lookup('adapter:' + type) || this.container.lookup('adapter:rest');
|
||||
const self = this;
|
||||
return adapter.find(this, type, findArgs).then(function(result) {
|
||||
return this.adapterFor(type).find(this, type, findArgs).then(function(result) {
|
||||
if (typeof findArgs === "object") {
|
||||
return self._resultSet(type, result);
|
||||
} else {
|
||||
|
@ -64,8 +61,7 @@ export default Ember.Object.extend({
|
|||
},
|
||||
|
||||
update(type, id, attrs) {
|
||||
const adapter = this.container.lookup('adapter:' + type) || this.container.lookup('adapter:rest');
|
||||
return adapter.update(this, type, id, attrs, function(result) {
|
||||
return this.adapterFor(type).update(this, type, id, attrs, function(result) {
|
||||
if (result && result[type] && result[type].id) {
|
||||
const oldRecord = _identityMap[type][id];
|
||||
delete _identityMap[type][id];
|
||||
|
@ -81,8 +77,7 @@ export default Ember.Object.extend({
|
|||
},
|
||||
|
||||
destroyRecord(type, record) {
|
||||
const adapter = this.container.lookup('adapter:' + type) || this.container.lookup('adapter:rest');
|
||||
return adapter.destroyRecord(this, type, record).then(function(result) {
|
||||
return this.adapterFor(type).destroyRecord(this, type, record).then(function(result) {
|
||||
const forType = _identityMap[type];
|
||||
if (forType) { delete forType[record.get('id')]; }
|
||||
return result;
|
||||
|
@ -101,6 +96,7 @@ export default Ember.Object.extend({
|
|||
_build(type, obj) {
|
||||
obj.store = this;
|
||||
obj.__type = type;
|
||||
obj.__state = obj.id ? "created" : "new";
|
||||
|
||||
const klass = this.container.lookupFactory('model:' + type) || RestModel;
|
||||
const model = klass.create(obj);
|
||||
|
@ -111,6 +107,10 @@ export default Ember.Object.extend({
|
|||
return model;
|
||||
},
|
||||
|
||||
adapterFor(type) {
|
||||
return this.container.lookup('adapter:' + type) || this.container.lookup('adapter:rest');
|
||||
},
|
||||
|
||||
_hydrate(type, obj) {
|
||||
if (!obj) { throw "Can't hydrate " + type + " of `null`"; }
|
||||
if (!obj.id) { throw "Can't hydrate " + type + " without an `id`"; }
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
//= require ./discourse/helpers/register-unbound
|
||||
//= require ./discourse/mixins/scrolling
|
||||
//= require_tree ./discourse/mixins
|
||||
//= require ./discourse/lib/ajax-error
|
||||
//= require ./discourse/lib/markdown
|
||||
//= require ./discourse/lib/search-for-term
|
||||
//= require ./discourse/lib/user-search
|
||||
|
|
|
@ -100,6 +100,8 @@ test("Create a Reply", () => {
|
|||
test("Edit the first post", () => {
|
||||
visit("/t/internationalization-localization/280");
|
||||
|
||||
ok(!exists('.topic-post:eq(0) .post-info.edits'), 'it has no edits icon at first');
|
||||
|
||||
click('.topic-post:eq(0) button[data-action=showMoreActions]');
|
||||
click('.topic-post:eq(0) button[data-action=edit]');
|
||||
andThen(() => {
|
||||
|
@ -111,6 +113,7 @@ test("Edit the first post", () => {
|
|||
click('#reply-control button.create');
|
||||
andThen(() => {
|
||||
ok(!exists('#wmd-input'), 'it closes the composer');
|
||||
ok(exists('.topic-post:eq(0) .post-info.edits'), 'it has the edits icon');
|
||||
ok(find('#topic-title h1').text().indexOf('This is the new text for the title') !== -1, 'it shows the new title');
|
||||
ok(find('.topic-post:eq(0) .cooked').text().indexOf('This is the new text for the post') !== -1, 'it updates the post');
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export default {
|
||||
"/posts/398": {"id":398,"name":"Uwe Keim","username":"uwe_keim","avatar_template":"/user_avatar/meta.discourse.org/uwe_keim/{size}/5697.png","uploaded_avatar_id":5697,"created_at":"2013-02-05T21:29:00.280Z","cooked":"<p>Any plans to support localization of UI elements, so that I (for example) could set up a completely German speaking forum?</p>","post_number":1,"post_type":1,"updated_at":"2013-02-05T21:29:00.280Z","like_count":0,"reply_count":1,"reply_to_post_number":null,"quote_count":0,"avg_time":25,"incoming_link_count":314,"reads":475,"score":1702.25,"yours":false,"topic_id":280,"topic_slug":"internationalization-localization","display_username":"Uwe Keim","primary_group_name":null,"version":2,"can_edit":true,"can_delete":false,"can_recover":true,"user_title":null,"raw":"Any plans to support localization of UI elements, so that I (for example) could set up a completely German speaking forum?","actions_summary":[{"id":2,"count":0,"hidden":false,"can_act":true,"can_defer_flags":false},{"id":3,"count":0,"hidden":false,"can_act":true,"can_defer_flags":false},{"id":4,"count":0,"hidden":false,"can_act":true,"can_defer_flags":false},{"id":5,"count":0,"hidden":true,"can_act":true,"can_defer_flags":false},{"id":6,"count":0,"hidden":false,"can_act":true,"can_defer_flags":false},{"id":7,"count":0,"hidden":false,"can_act":true,"can_defer_flags":false},{"id":8,"count":0,"hidden":false,"can_act":true,"can_defer_flags":false}],"moderator":false,"admin":false,"staff":false,"user_id":255,"hidden":false,"hidden_reason_id":null,"trust_level":2,"deleted_at":null,"user_deleted":false,"edit_reason":null,"can_view_edit_history":true,"wiki":false}
|
||||
"/posts/398": {"id":398,"name":"Uwe Keim","username":"uwe_keim","avatar_template":"/user_avatar/meta.discourse.org/uwe_keim/{size}/5697.png","uploaded_avatar_id":5697,"created_at":"2013-02-05T21:29:00.280Z","cooked":"<p>Any plans to support localization of UI elements, so that I (for example) could set up a completely German speaking forum?</p>","post_number":1,"post_type":1,"updated_at":"2013-02-05T21:29:00.280Z","like_count":0,"reply_count":1,"reply_to_post_number":null,"quote_count":0,"avg_time":25,"incoming_link_count":314,"reads":475,"score":1702.25,"yours":false,"topic_id":280,"topic_slug":"internationalization-localization","display_username":"Uwe Keim","primary_group_name":null,"version":1,"can_edit":true,"can_delete":false,"can_recover":true,"user_title":null,"raw":"Any plans to support localization of UI elements, so that I (for example) could set up a completely German speaking forum?","actions_summary":[{"id":2,"count":0,"hidden":false,"can_act":true,"can_defer_flags":false},{"id":3,"count":0,"hidden":false,"can_act":true,"can_defer_flags":false},{"id":4,"count":0,"hidden":false,"can_act":true,"can_defer_flags":false},{"id":5,"count":0,"hidden":true,"can_act":true,"can_defer_flags":false},{"id":6,"count":0,"hidden":false,"can_act":true,"can_defer_flags":false},{"id":7,"count":0,"hidden":false,"can_act":true,"can_defer_flags":false},{"id":8,"count":0,"hidden":false,"can_act":true,"can_defer_flags":false}],"moderator":false,"admin":false,"staff":false,"user_id":255,"hidden":false,"hidden_reason_id":null,"trust_level":2,"deleted_at":null,"user_deleted":false,"edit_reason":null,"can_view_edit_history":true,"wiki":false}
|
||||
};
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -2,15 +2,21 @@ function parsePostData(query) {
|
|||
const result = {};
|
||||
query.split("&").forEach(function(part) {
|
||||
const item = part.split("=");
|
||||
result[item[0]] = decodeURIComponent(item[1]).replace(/\+/g, ' ');
|
||||
const firstSeg = decodeURIComponent(item[0]);
|
||||
const m = /^([^\[]+)\[([^\]]+)\]/.exec(firstSeg);
|
||||
|
||||
const val = decodeURIComponent(item[1]).replace(/\+/g, ' ');
|
||||
if (m) {
|
||||
result[m[1]] = result[m[1]] || {};
|
||||
result[m[1]][m[2]] = val;
|
||||
} else {
|
||||
result[firstSeg] = val;
|
||||
}
|
||||
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function clone(obj) {
|
||||
return JSON.parse(JSON.stringify(obj));
|
||||
}
|
||||
|
||||
function response(code, obj) {
|
||||
if (typeof code === "object") {
|
||||
obj = code;
|
||||
|
@ -122,7 +128,10 @@ export default function() {
|
|||
this.put('/posts/:post_id/recover', success);
|
||||
|
||||
this.put('/posts/:post_id', (request) => {
|
||||
return response({ post: {id: request.params.post_id, version: 2 } });
|
||||
const data = parsePostData(request.requestBody);
|
||||
data.post.id = request.params.post_id;
|
||||
data.post.version = 2;
|
||||
return response(200, data.post);
|
||||
});
|
||||
|
||||
this.put('/t/:slug/:id', (request) => {
|
||||
|
@ -157,9 +166,15 @@ export default function() {
|
|||
}
|
||||
});
|
||||
|
||||
this.post('/widgets', function(request) {
|
||||
const widget = parsePostData(request.requestBody).widget;
|
||||
widget.id = 100;
|
||||
return response(200, {widget});
|
||||
});
|
||||
|
||||
this.put('/widgets/:widget_id', function(request) {
|
||||
const w = _widgets.findBy('id', parseInt(request.params.widget_id));
|
||||
return response({ widget: clone(w) });
|
||||
const widget = parsePostData(request.requestBody).widget;
|
||||
return response({ widget });
|
||||
});
|
||||
|
||||
this.get('/widgets', function(request) {
|
||||
|
|
|
@ -28,6 +28,21 @@ test('update', function() {
|
|||
});
|
||||
});
|
||||
|
||||
test('save new', function() {
|
||||
const store = createStore();
|
||||
const widget = store.createRecord('widget');
|
||||
|
||||
ok(widget.get('isNew'), 'it is a new record');
|
||||
ok(!widget.get('isCreated'), 'it is not created');
|
||||
|
||||
widget.save({ name: 'Evil Widget' }).then(function() {
|
||||
ok(widget.get('id'), 'it has an id');
|
||||
ok(widget.get('name'), 'Evil Widget');
|
||||
ok(widget.get('isCreated'), 'it is created');
|
||||
ok(!widget.get('isNew'), 'it is no longer new');
|
||||
});
|
||||
});
|
||||
|
||||
test('destroyRecord', function() {
|
||||
const store = createStore();
|
||||
store.find('widget', 123).then(function(widget) {
|
||||
|
|
|
@ -5,6 +5,8 @@ import createStore from 'helpers/create-store';
|
|||
test('createRecord', function() {
|
||||
const store = createStore();
|
||||
const widget = store.createRecord('widget', {id: 111, name: 'hello'});
|
||||
|
||||
ok(!widget.get('isNew'), 'it is not a new record');
|
||||
equal(widget.get('name'), 'hello');
|
||||
equal(widget.get('id'), 111);
|
||||
});
|
||||
|
@ -13,6 +15,7 @@ test('createRecord without an `id`', function() {
|
|||
const store = createStore();
|
||||
const widget = store.createRecord('widget', {name: 'hello'});
|
||||
|
||||
ok(widget.get('isNew'), 'it is a new record');
|
||||
ok(!widget.get('id'), 'there is no id');
|
||||
});
|
||||
|
||||
|
@ -21,6 +24,7 @@ test('createRecord without attributes', function() {
|
|||
const widget = store.createRecord('widget');
|
||||
|
||||
ok(!widget.get('id'), 'there is no id');
|
||||
ok(widget.get('isNew'), 'it is a new record');
|
||||
});
|
||||
|
||||
test('createRecord with a record as attributes returns that record from the map', function() {
|
||||
|
@ -36,6 +40,7 @@ test('find', function() {
|
|||
store.find('widget', 123).then(function(w) {
|
||||
equal(w.get('name'), 'Trout Lure');
|
||||
equal(w.get('id'), 123);
|
||||
ok(!w.get('isNew'), 'found records are not new');
|
||||
|
||||
// A second find by id returns the same object
|
||||
store.find('widget', 123).then(function(w2) {
|
||||
|
@ -70,6 +75,7 @@ test('findAll', function() {
|
|||
store.findAll('widget').then(function(result) {
|
||||
equal(result.get('length'), 2);
|
||||
const w = result.findBy('id', 124);
|
||||
ok(!w.get('isNew'), 'found records are not new');
|
||||
equal(w.get('name'), 'Evil Repellant');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -437,9 +437,10 @@ var bootbox = window.bootbox || (function(document, $) {
|
|||
|
||||
// wire up button handlers
|
||||
div.on('click', '.modal-footer a', function(e) {
|
||||
var self = this;
|
||||
Ember.run(function() {
|
||||
|
||||
var handler = $(this).data("handler"),
|
||||
var handler = $(self).data("handler"),
|
||||
cb = callbacks[handler],
|
||||
hideModal = null;
|
||||
|
||||
|
|
Loading…
Reference in New Issue