mirror of
https://github.com/discourse/discourse.git
synced 2025-05-23 18:18:13 +08:00
FIX: Unintended edits with composer
This commit is contained in:
@ -107,12 +107,6 @@ Discourse = Ember.Application.createWithMixins({
|
|||||||
this.set('notifyCount', count);
|
this.set('notifyCount', count);
|
||||||
},
|
},
|
||||||
|
|
||||||
openComposer: function(opts) {
|
|
||||||
// TODO, remove container link
|
|
||||||
var composer = Discourse.__container__.lookup('controller:composer');
|
|
||||||
if (composer) composer.open(opts);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Establishes global DOM events and bindings via jQuery.
|
Establishes global DOM events and bindings via jQuery.
|
||||||
|
|
||||||
|
@ -202,6 +202,7 @@ Discourse.ComposerController = Discourse.Controller.extend({
|
|||||||
**/
|
**/
|
||||||
open: function(opts) {
|
open: function(opts) {
|
||||||
if (!opts) opts = {};
|
if (!opts) opts = {};
|
||||||
|
|
||||||
var promise = opts.promise || Ember.Deferred.create();
|
var promise = opts.promise || Ember.Deferred.create();
|
||||||
opts.promise = promise;
|
opts.promise = promise;
|
||||||
this.set('typedReply', false);
|
this.set('typedReply', false);
|
||||||
@ -273,7 +274,8 @@ Discourse.ComposerController = Discourse.Controller.extend({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
composer = composer || Discourse.Composer.open(opts);
|
composer = composer || Discourse.Composer.create();
|
||||||
|
composer.open(opts);
|
||||||
this.set('model', composer);
|
this.set('model', composer);
|
||||||
composer.set('composeState', Discourse.Composer.OPEN);
|
composer.set('composeState', Discourse.Composer.OPEN);
|
||||||
promise.resolve();
|
promise.resolve();
|
||||||
|
@ -7,19 +7,17 @@
|
|||||||
@module Discourse
|
@module Discourse
|
||||||
**/
|
**/
|
||||||
|
|
||||||
var CLOSED, CREATE_TOPIC, DRAFT, EDIT, OPEN, PRIVATE_MESSAGE, REPLY, REPLY_AS_NEW_TOPIC_KEY, SAVING;
|
var CLOSED = 'closed',
|
||||||
|
SAVING = 'saving',
|
||||||
|
OPEN = 'open',
|
||||||
|
DRAFT = 'draft',
|
||||||
|
|
||||||
CLOSED = 'closed';
|
// The actions the composer can take
|
||||||
SAVING = 'saving';
|
CREATE_TOPIC = 'createTopic',
|
||||||
OPEN = 'open';
|
PRIVATE_MESSAGE = 'privateMessage',
|
||||||
DRAFT = 'draft';
|
REPLY = 'reply',
|
||||||
|
EDIT = 'edit',
|
||||||
// The actions the composer can take
|
REPLY_AS_NEW_TOPIC_KEY = "reply_as_new_topic";
|
||||||
CREATE_TOPIC = 'createTopic';
|
|
||||||
PRIVATE_MESSAGE = 'privateMessage';
|
|
||||||
REPLY = 'reply';
|
|
||||||
EDIT = 'edit';
|
|
||||||
REPLY_AS_NEW_TOPIC_KEY = "reply_as_new_topic";
|
|
||||||
|
|
||||||
Discourse.Composer = Discourse.Model.extend({
|
Discourse.Composer = Discourse.Model.extend({
|
||||||
|
|
||||||
@ -70,23 +68,14 @@ Discourse.Composer = Discourse.Model.extend({
|
|||||||
Discourse.KeyValueStore.set({ key: 'composer.showPreview', value: this.get('showPreview') });
|
Discourse.KeyValueStore.set({ key: 'composer.showPreview', value: this.get('showPreview') });
|
||||||
},
|
},
|
||||||
|
|
||||||
// Import a quote from the post
|
|
||||||
importQuote: function() {
|
importQuote: function() {
|
||||||
var post = this.get('post');
|
// If there is no current post, use the post id from the stream
|
||||||
|
var postId = this.get('post.id') || this.get('topic.postStream.firstPostId');
|
||||||
// If we don't have a post, check the topic for the first one
|
if (postId) {
|
||||||
if (!post) {
|
|
||||||
var posts = this.get('topic.posts');
|
|
||||||
if (posts && posts.length > 0) {
|
|
||||||
post = posts[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (post) {
|
|
||||||
this.set('loading', true);
|
this.set('loading', true);
|
||||||
var composer = this;
|
var composer = this;
|
||||||
Discourse.Post.load(post.get('id')).then(function(result) {
|
return Discourse.Post.load(postId).then(function(post) {
|
||||||
composer.appendText(Discourse.BBCode.buildQuoteBBCode(post, result.get('raw')));
|
composer.appendText(Discourse.BBCode.buildQuoteBBCode(post, post.get('raw')));
|
||||||
composer.set('loading', false);
|
composer.set('loading', false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -211,7 +200,6 @@ Discourse.Composer = Discourse.Model.extend({
|
|||||||
*/
|
*/
|
||||||
open: function(opts) {
|
open: function(opts) {
|
||||||
if (!opts) opts = {};
|
if (!opts) opts = {};
|
||||||
|
|
||||||
this.set('loading', false);
|
this.set('loading', false);
|
||||||
|
|
||||||
var replyBlank = Em.isEmpty(this.get("reply"));
|
var replyBlank = Em.isEmpty(this.get("reply"));
|
||||||
@ -221,7 +209,7 @@ Discourse.Composer = Discourse.Model.extend({
|
|||||||
(opts.action !== this.get('action') || ((opts.reply || opts.action === this.EDIT) && this.get('reply') !== this.get('originalText'))) &&
|
(opts.action !== this.get('action') || ((opts.reply || opts.action === this.EDIT) && this.get('reply') !== this.get('originalText'))) &&
|
||||||
!opts.tested) {
|
!opts.tested) {
|
||||||
opts.tested = true;
|
opts.tested = true;
|
||||||
this.cancel(function() { composer.open(opts); });
|
//composer.cancel(function() { composer.open(opts); });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,16 +276,18 @@ Discourse.Composer = Discourse.Model.extend({
|
|||||||
|
|
||||||
// When you edit a post
|
// When you edit a post
|
||||||
editPost: function(opts) {
|
editPost: function(opts) {
|
||||||
var post = this.get('post');
|
var post = this.get('post'),
|
||||||
var oldCooked = post.get('cooked');
|
oldCooked = post.get('cooked'),
|
||||||
var composer = this;
|
composer = this;
|
||||||
|
|
||||||
// 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');
|
var topic = this.get('topic');
|
||||||
topic.set('title', this.get('title'));
|
topic.setProperties({
|
||||||
topic.set('fancy_title', this.get('title'));
|
title: this.get('title'),
|
||||||
topic.set('categoryName', this.get('categoryName'));
|
fancy_title: this.get('title'),
|
||||||
|
categoryName: this.get('categoryName')
|
||||||
|
});
|
||||||
topic.save();
|
topic.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -310,24 +300,9 @@ Discourse.Composer = Discourse.Model.extend({
|
|||||||
|
|
||||||
return Ember.Deferred.promise(function(promise) {
|
return Ember.Deferred.promise(function(promise) {
|
||||||
post.save(function(savedPost) {
|
post.save(function(savedPost) {
|
||||||
var posts = composer.get('topic.posts');
|
composer.set('originalText', '');
|
||||||
|
composer.set('reply', '');
|
||||||
composer.set('originalText', composer.get('reply'));
|
composer.set('post', null);
|
||||||
|
|
||||||
// perhaps our post came from elsewhere eg. draft
|
|
||||||
var idx = -1;
|
|
||||||
var postNumber = post.get('post_number');
|
|
||||||
_.each(posts,function(p,i) {
|
|
||||||
if (p.get('post_number') === postNumber) {
|
|
||||||
idx = i;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (idx > -1) {
|
|
||||||
savedPost.set('topic', composer.get('topic'));
|
|
||||||
posts.replace(idx, 1, [savedPost]);
|
|
||||||
promise.resolve({ post: post });
|
|
||||||
composer.set('topic.draft_sequence', savedPost.draft_sequence);
|
|
||||||
}
|
|
||||||
}, function(error) {
|
}, function(error) {
|
||||||
var response = $.parseJSON(error.responseText);
|
var response = $.parseJSON(error.responseText);
|
||||||
if (response && response.errors) {
|
if (response && response.errors) {
|
||||||
@ -402,8 +377,12 @@ Discourse.Composer = Discourse.Model.extend({
|
|||||||
saving = false;
|
saving = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
composer.set('reply', '');
|
composer.setProperties({
|
||||||
composer.set('createdPost', createdPost);
|
reply: '',
|
||||||
|
createdPost: createdPost,
|
||||||
|
title: ''
|
||||||
|
});
|
||||||
|
|
||||||
if (addedToStream) {
|
if (addedToStream) {
|
||||||
composer.set('composeState', CLOSED);
|
composer.set('composeState', CLOSED);
|
||||||
} else if (saving) {
|
} else if (saving) {
|
||||||
|
@ -24,18 +24,14 @@ Discourse.PostStream = Em.Object.extend({
|
|||||||
|
|
||||||
@property hasPosts
|
@property hasPosts
|
||||||
**/
|
**/
|
||||||
hasPosts: function() {
|
hasPosts: Em.computed.gt('posts.length', 0),
|
||||||
return this.get('posts.length') > 0;
|
|
||||||
}.property('posts.length'),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Do we have a stream list of post ids?
|
Do we have a stream list of post ids?
|
||||||
|
|
||||||
@property hasStream
|
@property hasStream
|
||||||
**/
|
**/
|
||||||
hasStream: function() {
|
hasStream: Em.computed.gt('filteredPostsCount', 0),
|
||||||
return this.get('filteredPostsCount') > 0;
|
|
||||||
}.property('filteredPostsCount'),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Can we append more posts to our current stream?
|
Can we append more posts to our current stream?
|
||||||
@ -44,7 +40,6 @@ Discourse.PostStream = Em.Object.extend({
|
|||||||
**/
|
**/
|
||||||
canAppendMore: Em.computed.and('notLoading', 'hasPosts', 'lastPostNotLoaded'),
|
canAppendMore: Em.computed.and('notLoading', 'hasPosts', 'lastPostNotLoaded'),
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Can we prepend more posts to our current stream?
|
Can we prepend more posts to our current stream?
|
||||||
|
|
||||||
@ -59,11 +54,20 @@ Discourse.PostStream = Em.Object.extend({
|
|||||||
**/
|
**/
|
||||||
firstPostLoaded: function() {
|
firstPostLoaded: function() {
|
||||||
if (!this.get('hasLoadedData')) { return false; }
|
if (!this.get('hasLoadedData')) { return false; }
|
||||||
return !!this.get('posts').findProperty('id', this.get('stream')[0]);
|
return !!this.get('posts').findProperty('id', this.get('firstPostId'));
|
||||||
}.property('hasLoadedData', 'posts.[]', 'stream.@each'),
|
}.property('hasLoadedData', 'posts.[]', 'firstPostId'),
|
||||||
|
|
||||||
firstPostNotLoaded: Em.computed.not('firstPostLoaded'),
|
firstPostNotLoaded: Em.computed.not('firstPostLoaded'),
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns the id of the first post in the set
|
||||||
|
|
||||||
|
@property firstPostId
|
||||||
|
**/
|
||||||
|
firstPostId: function() {
|
||||||
|
return this.get('stream')[0];
|
||||||
|
}.property('stream.@each'),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Returns the id of the last post in the set
|
Returns the id of the last post in the set
|
||||||
|
|
||||||
|
@ -30,7 +30,9 @@ Discourse.TopicFromParamsRoute = Discourse.Route.extend({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var topicController = this.controllerFor('topic');
|
var topicController = this.controllerFor('topic'),
|
||||||
|
composerController = this.controllerFor('composer');
|
||||||
|
|
||||||
postStream.refresh(params).then(function () {
|
postStream.refresh(params).then(function () {
|
||||||
|
|
||||||
// The post we requested might not exist. Let's find the closest post
|
// The post we requested might not exist. Let's find the closest post
|
||||||
@ -43,7 +45,7 @@ Discourse.TopicFromParamsRoute = Discourse.Route.extend({
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (topic.present('draft')) {
|
if (topic.present('draft')) {
|
||||||
Discourse.openComposer({
|
composerController.open({
|
||||||
draft: Discourse.Draft.getLocal(topic.get('draft_key'), topic.get('draft')),
|
draft: Discourse.Draft.getLocal(topic.get('draft_key'), topic.get('draft')),
|
||||||
draftKey: topic.get('draft_key'),
|
draftKey: topic.get('draft_key'),
|
||||||
draftSequence: topic.get('draft_sequence'),
|
draftSequence: topic.get('draft_sequence'),
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
module("Discourse.Composer");
|
module("Discourse.Composer");
|
||||||
|
|
||||||
|
|
||||||
test('replyLength', function() {
|
test('replyLength', function() {
|
||||||
|
|
||||||
var replyLength = function(val, expectedLength, text) {
|
var replyLength = function(val, expectedLength, text) {
|
||||||
var composer = Discourse.Composer.create({ reply: val });
|
var composer = Discourse.Composer.create({ reply: val });
|
||||||
equal(composer.get('replyLength'), expectedLength);
|
equal(composer.get('replyLength'), expectedLength);
|
||||||
@ -13,10 +11,8 @@ test('replyLength', function() {
|
|||||||
replyLength("ba sic\n\nreply", 12, "count only significant whitespaces");
|
replyLength("ba sic\n\nreply", 12, "count only significant whitespaces");
|
||||||
replyLength("1[quote=]not counted[/quote]2[quote=]at all[/quote]3", 3, "removes quotes");
|
replyLength("1[quote=]not counted[/quote]2[quote=]at all[/quote]3", 3, "removes quotes");
|
||||||
replyLength("1[quote=]not[quote=]counted[/quote]yay[/quote]2", 2, "handles nested quotes correctly");
|
replyLength("1[quote=]not[quote=]counted[/quote]yay[/quote]2", 2, "handles nested quotes correctly");
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test('missingReplyCharacters', function() {
|
test('missingReplyCharacters', function() {
|
||||||
var missingReplyCharacters = function(val, isPM, expected, message) {
|
var missingReplyCharacters = function(val, isPM, expected, message) {
|
||||||
var composer = Discourse.Composer.create({ reply: val, creatingPrivateMessage: isPM });
|
var composer = Discourse.Composer.create({ reply: val, creatingPrivateMessage: isPM });
|
||||||
@ -35,4 +31,64 @@ test('missingTitleCharacters', function() {
|
|||||||
|
|
||||||
missingTitleCharacters('hi', false, Discourse.SiteSettings.min_topic_title_length - 2, 'too short post title');
|
missingTitleCharacters('hi', false, Discourse.SiteSettings.min_topic_title_length - 2, 'too short post title');
|
||||||
missingTitleCharacters('z', true, Discourse.SiteSettings.min_private_message_title_length - 1, 'too short pm title');
|
missingTitleCharacters('z', true, Discourse.SiteSettings.min_private_message_title_length - 1, 'too short pm title');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
test('wouldLoseChanges', function() {
|
||||||
|
var composer = Discourse.Composer.create();
|
||||||
|
ok(!composer.get('wouldLoseChanges'), "by default it's false");
|
||||||
|
|
||||||
|
composer.setProperties({
|
||||||
|
originalText: "hello",
|
||||||
|
reply: "hello"
|
||||||
|
});
|
||||||
|
|
||||||
|
ok(!composer.get('wouldLoseChanges'), "it's false when the originalText is the same as the reply");
|
||||||
|
composer.set('reply', 'hello world');
|
||||||
|
ok(composer.get('wouldLoseChanges'), "it's true when the reply changes");
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
test('importQuote with no data', function() {
|
||||||
|
this.stub(Discourse.Post, 'load');
|
||||||
|
var composer = Discourse.Composer.create();
|
||||||
|
composer.importQuote();
|
||||||
|
blank(composer.get('reply'), 'importing with no topic adds nothing');
|
||||||
|
ok(!Discourse.Post.load.calledOnce, "load is not called");
|
||||||
|
|
||||||
|
composer = Discourse.Composer.create({topic: Discourse.Topic.create()});
|
||||||
|
composer.importQuote();
|
||||||
|
blank(composer.get('reply'), 'importing with no posts in a topic adds nothing');
|
||||||
|
ok(!Discourse.Post.load.calledOnce, "load is not called");
|
||||||
|
});
|
||||||
|
|
||||||
|
asyncTest('importQuote with a post', function() {
|
||||||
|
expect(1);
|
||||||
|
|
||||||
|
this.stub(Discourse.Post, 'load').withArgs(123).returns(Em.Deferred.promise(function (p) {
|
||||||
|
p.resolve(Discourse.Post.create({raw: "let's quote"}));
|
||||||
|
}));
|
||||||
|
|
||||||
|
var composer = Discourse.Composer.create({post: Discourse.Post.create({id: 123})});
|
||||||
|
composer.importQuote().then(function () {
|
||||||
|
start();
|
||||||
|
ok(composer.get('reply').indexOf("let's quote") > -1, "it quoted the post");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
asyncTest('importQuote with no post', function() {
|
||||||
|
expect(1);
|
||||||
|
|
||||||
|
this.stub(Discourse.Post, 'load').withArgs(4).returns(Em.Deferred.promise(function (p) {
|
||||||
|
p.resolve(Discourse.Post.create({raw: 'quote me'}));
|
||||||
|
}));
|
||||||
|
|
||||||
|
var composer = Discourse.Composer.create({topic: Discourse.Topic.create()});
|
||||||
|
composer.set('topic.postStream.stream', [4, 5]);
|
||||||
|
composer.importQuote().then(function () {
|
||||||
|
start();
|
||||||
|
ok(composer.get('reply').indexOf('quote me') > -1, "it contains the word quote me");
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
Reference in New Issue
Block a user