Refactor topic title/category saving to support easier changing of

attributes via plugins.
This commit is contained in:
Robin Ward 2015-01-02 13:47:07 -05:00
parent 9523b26af2
commit 7182767349
8 changed files with 49 additions and 86 deletions

View File

@ -1,7 +1,8 @@
import ObjectController from 'discourse/controllers/object'; import ObjectController from 'discourse/controllers/object';
import BufferedContent from 'discourse/mixins/buffered-content';
import { spinnerHTML } from 'discourse/helpers/loading-spinner'; import { spinnerHTML } from 'discourse/helpers/loading-spinner';
export default ObjectController.extend(Discourse.SelectedPostsCount, { export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedContent, {
multiSelect: false, multiSelect: false,
needs: ['header', 'modal', 'composer', 'quote-button', 'search', 'topic-progress', 'application'], needs: ['header', 'modal', 'composer', 'quote-button', 'search', 'topic-progress', 'application'],
allPostsSelected: false, allPostsSelected: false,
@ -235,11 +236,6 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, {
this.set('allPostsSelected', false); this.set('allPostsSelected', false);
}, },
/**
Toggle a participant for filtering
@method toggleParticipant
**/
toggleParticipant: function(user) { toggleParticipant: function(user) {
this.get('postStream').toggleParticipant(Em.get(user, 'username')); this.get('postStream').toggleParticipant(Em.get(user, 'username'));
}, },
@ -247,17 +243,13 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, {
editTopic: function() { editTopic: function() {
if (!this.get('details.can_edit')) return false; if (!this.get('details.can_edit')) return false;
this.setProperties({ this.set('editingTopic', true);
editingTopic: true,
newTitle: this.get('title'),
newCategoryId: this.get('category_id')
});
return false; return false;
}, },
// close editing mode
cancelEditingTopic: function() { cancelEditingTopic: function() {
this.set('editingTopic', false); this.set('editingTopic', false);
this.rollbackBuffer();
}, },
toggleMultiSelect: function() { toggleMultiSelect: function() {
@ -265,39 +257,25 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, {
}, },
finishedEditingTopic: function() { finishedEditingTopic: function() {
if (this.get('editingTopic')) { if (!this.get('editingTopic')) { return; }
var topic = this.get('model');
// Topic title hasn't been sanitized yet, so the template shouldn't trust it.
this.set('topicSaving', true);
// manually update the titles & category
var backup = topic.setPropertiesBackup({
title: this.get('newTitle'),
category_id: parseInt(this.get('newCategoryId'), 10),
fancy_title: this.get('newTitle')
});
// save the modifications // save the modifications
var self = this; var self = this,
topic.save().then(function(result){ props = this.get('buffered.buffer');
// update the title if it has been changed (cleaned up) server-side
topic.setProperties(Em.getProperties(result.basic_topic, 'title', 'fancy_title')); Discourse.Topic.update(this.get('model'), props).then(function() {
self.set('topicSaving', false); self.set('editingTopic', false);
}, function(error) { }).catch(function(error) {
self.setProperties({ editingTopic: true, topicSaving: false });
topic.setProperties(backup);
if (error && error.responseText) { if (error && error.responseText) {
bootbox.alert($.parseJSON(error.responseText).errors[0]); bootbox.alert($.parseJSON(error.responseText).errors[0]);
} else { } else {
bootbox.alert(I18n.t('generic_error')); bootbox.alert(I18n.t('generic_error'));
} }
}).finally(function() {
// Note we even roll back on success here because `update` saves
// the properties to the topic.
self.rollbackBuffer();
}); });
// close editing mode
self.set('editingTopic', false);
}
}, },
toggledSelectedPost: function(post) { toggledSelectedPost: function(post) {

View File

@ -463,13 +463,10 @@ Discourse.Composer = Discourse.Model.extend({
// Update the title if we've changed it // Update the title if we've changed it
if (this.get('title') && post.get('post_number') === 1) { if (this.get('title') && post.get('post_number') === 1) {
var topic = this.get('topic'); Discourse.Topic.update(this.get('topic'), {
topic.setProperties({
title: this.get('title'), title: this.get('title'),
fancy_title: Handlebars.Utils.escapeExpression(this.get('title')), category_id: this.get('categoryId')
category_id: parseInt(this.get('categoryId'), 10)
}); });
topic.save();
} }
post.setProperties({ post.setProperties({

View File

@ -1,12 +1,4 @@
Discourse.Model = Ember.Object.extend(Discourse.Presence, { Discourse.Model = Ember.Object.extend(Discourse.Presence);
// Like `setProperties` but returns the original values in case
// we want to roll back
setPropertiesBackup: function(obj) {
var backup = this.getProperties(Ember.keys(obj));
this.setProperties(obj);
return backup;
}
});
Discourse.Model.reopenClass({ Discourse.Model.reopenClass({
extractByKey: function(collection, klass) { extractByKey: function(collection, klass) {

View File

@ -202,23 +202,6 @@ Discourse.Topic = Discourse.Model.extend({
}); });
}, },
// Save any changes we've made to the model
save: function() {
// Don't save unless we can
if (!this.get('details.can_edit')) return;
var data = { title: this.get('title') };
if(this.get('category')){
data.category_id = this.get('category.id');
}
return Discourse.ajax(this.get('url'), {
type: 'PUT',
data: data
});
},
/** /**
Invite a user to this topic Invite a user to this topic
@ -373,6 +356,17 @@ Discourse.Topic.reopenClass({
} }
}, },
update: function(topic, props) {
return Discourse.ajax(topic.get('url'), { type: 'PUT', data: props }).then(function(result) {
// The title can be cleaned up server side
props.title = result.basic_topic.title;
props.fancy_title = result.basic_topic.fancy_title;
topic.setProperties(props);
});
},
create: function() { create: function() {
var result = this._super.apply(this, arguments); var result = this._super.apply(this, arguments);
this.createActionSummary(result); this.createActionSummary(result);

View File

@ -16,11 +16,11 @@
{{#if editingTopic}} {{#if editingTopic}}
{{#if isPrivateMessage}} {{#if isPrivateMessage}}
<span class="private-message-glyph">{{fa-icon envelope}}</span> <span class="private-message-glyph">{{fa-icon envelope}}</span>
{{autofocus-text-field id='edit-title' value=newTitle maxLength=maxTitleLength}} {{autofocus-text-field id='edit-title' value=buffered.title maxLength=maxTitleLength}}
{{else}} {{else}}
{{autofocus-text-field id='edit-title' value=newTitle maxLength=maxTitleLength}} {{autofocus-text-field id='edit-title' value=buffered.title maxLength=maxTitleLength}}
</br> </br>
{{category-chooser valueAttribute="id" value=newCategoryId source=category_id}} {{category-chooser valueAttribute="id" value=buffered.category_id source=buffered.category_id}}
{{/if}} {{/if}}
<button class='btn btn-primary btn-small no-text' {{action "finishedEditingTopic"}}>{{fa-icon check}}</button> <button class='btn btn-primary btn-small no-text' {{action "finishedEditingTopic"}}>{{fa-icon check}}</button>
@ -34,11 +34,7 @@
{{#if details.loaded}} {{#if details.loaded}}
{{topic-status topic=model}} {{topic-status topic=model}}
<a href='{{unbound url}}' {{action "jumpTop"}}> <a href='{{unbound url}}' {{action "jumpTop"}}>
{{#if topicSaving}}
{{fancy_title}}
{{else}}
{{{fancy_title}}} {{{fancy_title}}}
{{/if}}
</a> </a>
{{/if}} {{/if}}

View File

@ -7,6 +7,7 @@ export default ComboboxView.extend({
overrideWidths: true, overrideWidths: true,
dataAttributes: ['id', 'description_text'], dataAttributes: ['id', 'description_text'],
valueBinding: Ember.Binding.oneWay('source'), valueBinding: Ember.Binding.oneWay('source'),
castInteger: true,
content: function() { content: function() {
var scopedCategoryId = this.get('scopedCategoryId'); var scopedCategoryId = this.get('scopedCategoryId');

View File

@ -63,7 +63,7 @@ export default Discourse.View.extend({
this.rerender(); this.rerender();
}.observes('content.@each'), }.observes('content.@each'),
didInsertElement: function() { _initializeCombo: function() {
var $elem = this.$(), var $elem = this.$(),
self = this; self = this;
@ -75,10 +75,15 @@ export default Discourse.View.extend({
$elem.select2({formatResult: this.template, minimumResultsForSearch: 5, width: 'resolve'}); $elem.select2({formatResult: this.template, minimumResultsForSearch: 5, width: 'resolve'});
var castInteger = this.get('castInteger');
$elem.on("change", function (e) { $elem.on("change", function (e) {
self.set('value', $(e.target).val()); var val = $(e.target).val();
if (val.length && castInteger) {
val = parseInt(val, 10);
}
self.set('value', val);
}); });
}, }.on('didInsertElement'),
willClearRender: function() { willClearRender: function() {
var elementId = "s2id_" + this.$().attr('id'); var elementId = "s2id_" + this.$().attr('id');

View File

@ -28,8 +28,8 @@ test("editingMode", function() {
topicController.set('model.details.can_edit', true); topicController.set('model.details.can_edit', true);
topicController.send('editTopic'); topicController.send('editTopic');
ok(topicController.get('editingTopic'), "calling editTopic enables editing if the user can edit"); ok(topicController.get('editingTopic'), "calling editTopic enables editing if the user can edit");
equal(topicController.get('newTitle'), topic.get('title')); equal(topicController.get('buffered.title'), topic.get('title'));
equal(topicController.get('newCategoryId'), topic.get('category_id')); equal(topicController.get('buffered.category_id'), topic.get('category_id'));
topicController.send('cancelEditingTopic'); topicController.send('cancelEditingTopic');
ok(!topicController.get('editingTopic'), "cancelling edit mode reverts the property value"); ok(!topicController.get('editingTopic'), "cancelling edit mode reverts the property value");