mirror of
https://github.com/codeninjasllc/discourse.git
synced 2024-11-23 23:58:31 -05:00
Select +Replies for bulk operations
This commit is contained in:
parent
dba1d79de2
commit
f157ec1f91
20 changed files with 282 additions and 77 deletions
|
@ -12,6 +12,7 @@ Discourse.MergeTopicController = Discourse.ObjectController.extend(Discourse.Sel
|
|||
|
||||
topicController: Em.computed.alias('controllers.topic'),
|
||||
selectedPosts: Em.computed.alias('topicController.selectedPosts'),
|
||||
selectedReplies: Em.computed.alias('topicController.selectedReplies'),
|
||||
allPostsSelected: Em.computed.alias('topicController.allPostsSelected'),
|
||||
|
||||
buttonDisabled: function() {
|
||||
|
@ -31,10 +32,13 @@ Discourse.MergeTopicController = Discourse.ObjectController.extend(Discourse.Sel
|
|||
if (this.get('allPostsSelected')) {
|
||||
promise = Discourse.Topic.mergeTopic(this.get('id'), this.get('selectedTopicId'));
|
||||
} else {
|
||||
var postIds = this.get('selectedPosts').map(function(p) { return p.get('id'); });
|
||||
var postIds = this.get('selectedPosts').map(function(p) { return p.get('id'); }),
|
||||
replyPostIds = this.get('selectedReplies').map(function(p) { return p.get('id'); });
|
||||
|
||||
promise = Discourse.Topic.movePosts(this.get('id'), {
|
||||
destination_topic_id: this.get('selectedTopicId'),
|
||||
post_ids: postIds
|
||||
post_ids: postIds,
|
||||
reply_post_ids: replyPostIds
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ Discourse.SplitTopicController = Discourse.ObjectController.extend(Discourse.Sel
|
|||
|
||||
topicController: Em.computed.alias('controllers.topic'),
|
||||
selectedPosts: Em.computed.alias('topicController.selectedPosts'),
|
||||
selectedReplies: Em.computed.alias('topicController.selectedReplies'),
|
||||
|
||||
buttonDisabled: function() {
|
||||
if (this.get('saving')) return true;
|
||||
|
@ -30,21 +31,23 @@ Discourse.SplitTopicController = Discourse.ObjectController.extend(Discourse.Sel
|
|||
movePostsToNewTopic: function() {
|
||||
this.set('saving', true);
|
||||
|
||||
var postIds = this.get('selectedPosts').map(function(p) { return p.get('id'); });
|
||||
var splitTopicController = this;
|
||||
var postIds = this.get('selectedPosts').map(function(p) { return p.get('id'); }),
|
||||
replyPostIds = this.get('selectedReplies').map(function(p) { return p.get('id'); }),
|
||||
self = this;
|
||||
|
||||
Discourse.Topic.movePosts(this.get('id'), {
|
||||
title: this.get('topicName'),
|
||||
post_ids: postIds
|
||||
post_ids: postIds,
|
||||
reply_post_ids: replyPostIds
|
||||
}).then(function(result) {
|
||||
// Posts moved
|
||||
splitTopicController.send('closeModal');
|
||||
splitTopicController.get('topicController').toggleMultiSelect();
|
||||
self.send('closeModal');
|
||||
self.get('topicController').toggleMultiSelect();
|
||||
Em.run.next(function() { Discourse.URL.routeTo(result.url); });
|
||||
}, function() {
|
||||
// Error moving posts
|
||||
splitTopicController.flash(I18n.t('topic.split_topic.error'));
|
||||
splitTopicController.set('saving', false);
|
||||
self.flash(I18n.t('topic.split_topic.error'));
|
||||
self.set('saving', false);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -11,8 +11,15 @@ Discourse.TopicController = Discourse.ObjectController.extend(Discourse.Selected
|
|||
summaryCollapsed: true,
|
||||
needs: ['header', 'modal', 'composer', 'quoteButton'],
|
||||
allPostsSelected: false,
|
||||
selectedPosts: new Em.Set(),
|
||||
editingTopic: false,
|
||||
selectedPosts: null,
|
||||
selectedReplies: null,
|
||||
|
||||
init: function() {
|
||||
this._super();
|
||||
this.set('selectedPosts', new Em.Set());
|
||||
this.set('selectedReplies', new Em.Set());
|
||||
},
|
||||
|
||||
jumpTopDisabled: function() {
|
||||
return (this.get('progressPosition') === 1);
|
||||
|
@ -82,18 +89,49 @@ Discourse.TopicController = Discourse.ObjectController.extend(Discourse.Selected
|
|||
return false;
|
||||
}.property('postStream.loaded', 'currentPost', 'postStream.filteredPostsCount'),
|
||||
|
||||
selectPost: function(post) {
|
||||
var selectedPosts = this.get('selectedPosts');
|
||||
if (selectedPosts.contains(post)) {
|
||||
selectedPosts.removeObject(post);
|
||||
deselectPost: function(post) {
|
||||
this.get('selectedPosts').removeObject(post);
|
||||
|
||||
var selectedReplies = this.get('selectedReplies');
|
||||
selectedReplies.removeObject(post);
|
||||
|
||||
var selectedReply = selectedReplies.findProperty('post_number', post.get('reply_to_post_number'));
|
||||
if (selectedReply) { selectedReplies.removeObject(selectedReply); }
|
||||
|
||||
this.set('allPostsSelected', false);
|
||||
},
|
||||
|
||||
postSelected: function(post) {
|
||||
if (this.get('allPostsSelected')) { return true; }
|
||||
if (this.get('selectedPosts').contains(post)) { return true; }
|
||||
|
||||
if (this.get('selectedReplies').findProperty('post_number', post.get('reply_to_post_number'))) { return true; }
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
toggledSelectedPost: function(post) {
|
||||
var selectedPosts = this.get('selectedPosts');
|
||||
if (this.postSelected(post)) {
|
||||
this.deselectPost(post);
|
||||
return false;
|
||||
} else {
|
||||
selectedPosts.addObject(post);
|
||||
|
||||
// If the user manually selects all posts, all posts are selected
|
||||
if (selectedPosts.length === this.get('posts_count')) {
|
||||
this.set('allPostsSelected');
|
||||
this.set('allPostsSelected', true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
toggledSelectedPostReplies: function(post) {
|
||||
var selectedReplies = this.get('selectedReplies');
|
||||
if (this.toggledSelectedPost(post)) {
|
||||
selectedReplies.addObject(post);
|
||||
} else {
|
||||
selectedReplies.removeObject(post);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -108,6 +146,7 @@ Discourse.TopicController = Discourse.ObjectController.extend(Discourse.Selected
|
|||
|
||||
deselectAll: function() {
|
||||
this.get('selectedPosts').clear();
|
||||
this.get('selectedReplies').clear();
|
||||
this.set('allPostsSelected', false);
|
||||
},
|
||||
|
||||
|
@ -177,19 +216,28 @@ Discourse.TopicController = Discourse.ObjectController.extend(Discourse.Selected
|
|||
},
|
||||
|
||||
deleteSelected: function() {
|
||||
var topicController = this;
|
||||
var self = this;
|
||||
bootbox.confirm(I18n.t("post.delete.confirm", { count: this.get('selectedPostsCount')}), function(result) {
|
||||
if (result) {
|
||||
|
||||
// If all posts are selected, it's the same thing as deleting the topic
|
||||
if (topicController.get('allPostsSelected')) {
|
||||
return topicController.deleteTopic();
|
||||
if (self.get('allPostsSelected')) {
|
||||
return self.deleteTopic();
|
||||
}
|
||||
|
||||
var selectedPosts = topicController.get('selectedPosts');
|
||||
Discourse.Post.deleteMany(selectedPosts);
|
||||
topicController.get('model.postStream').removePosts(selectedPosts);
|
||||
topicController.toggleMultiSelect();
|
||||
var selectedPosts = self.get('selectedPosts'),
|
||||
selectedReplies = self.get('selectedReplies'),
|
||||
postStream = self.get('postStream'),
|
||||
toRemove = new Ember.Set();
|
||||
|
||||
|
||||
Discourse.Post.deleteMany(selectedPosts, selectedReplies);
|
||||
postStream.get('posts').forEach(function (p) {
|
||||
if (self.postSelected(p)) { toRemove.addObject(p); }
|
||||
});
|
||||
|
||||
postStream.removePosts(toRemove);
|
||||
self.toggleMultiSelect();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
|
|
@ -11,10 +11,15 @@ Discourse.SelectedPostsCount = Em.Mixin.create({
|
|||
selectedPostsCount: function() {
|
||||
if (this.get('allPostsSelected')) return this.get('posts_count') || this.get('topic.posts_count');
|
||||
|
||||
if (!this.get('selectedPosts')) return 0;
|
||||
var sum = this.get('selectedPosts.length') || 0;
|
||||
if (this.get('selectedReplies')) {
|
||||
this.get('selectedReplies').forEach(function (p) {
|
||||
sum += p.get('reply_count') || 0;
|
||||
});
|
||||
}
|
||||
|
||||
return this.get('selectedPosts.length');
|
||||
}.property('selectedPosts.length', 'allPostsSelected')
|
||||
return sum;
|
||||
}.property('selectedPosts.length', 'allPostsSelected', 'selectedReplies.length')
|
||||
|
||||
});
|
||||
|
||||
|
|
|
@ -328,8 +328,7 @@ Discourse.Post = Discourse.Model.extend({
|
|||
|
||||
// Whether to show replies directly below
|
||||
showRepliesBelow: function() {
|
||||
var reply_count, topic;
|
||||
reply_count = this.get('reply_count');
|
||||
var reply_count = this.get('reply_count');
|
||||
|
||||
// We don't show replies if there aren't any
|
||||
if (reply_count === 0) return false;
|
||||
|
@ -341,7 +340,7 @@ Discourse.Post = Discourse.Model.extend({
|
|||
if (reply_count > 1) return true;
|
||||
|
||||
// If we have *exactly* one reply, we have to consider if it's directly below us
|
||||
topic = this.get('topic');
|
||||
var topic = this.get('topic');
|
||||
return !topic.isReplyDirectlyBelow(this);
|
||||
|
||||
}.property('reply_count'),
|
||||
|
@ -377,11 +376,12 @@ Discourse.Post.reopenClass({
|
|||
return result;
|
||||
},
|
||||
|
||||
deleteMany: function(posts) {
|
||||
deleteMany: function(selectedPosts, selectedReplies) {
|
||||
return Discourse.ajax("/posts/destroy_many", {
|
||||
type: 'DELETE',
|
||||
data: {
|
||||
post_ids: posts.map(function(p) { return p.get('id'); })
|
||||
post_ids: selectedPosts.map(function(p) { return p.get('id'); }),
|
||||
reply_post_ids: selectedReplies.map(function(p) { return p.get('id'); })
|
||||
}
|
||||
});
|
||||
},
|
||||
|
|
|
@ -32,7 +32,10 @@
|
|||
</div>
|
||||
|
||||
<div class='topic-body span14'>
|
||||
<button {{action selectPost this}} {{bindAttr class=":post-select controller.multiSelect::hidden"}}>{{view.selectText}}</button>
|
||||
<div {{bindAttr class=":select-posts controller.multiSelect::hidden"}}>
|
||||
<button {{action toggledSelectedPostReplies this}} {{bindAttr class="view.canSelectReplies::hidden"}}>{{i18n topic.multi_select.select_replies}}</button>
|
||||
<button {{action toggledSelectedPost this}} class="select-post">{{view.selectPostText}}</button>
|
||||
</div>
|
||||
|
||||
<div {{bindAttr class="showUserReplyTab:avoid-tab view.repliesShown::bottom-round :contents :regular view.extraClass"}}>
|
||||
{{#unless controller.multiSelect}}
|
||||
|
|
|
@ -29,17 +29,20 @@ Discourse.PostView = Discourse.GroupedView.extend({
|
|||
|
||||
mouseUp: function(e) {
|
||||
if (this.get('controller.multiSelect') && (e.metaKey || e.ctrlKey)) {
|
||||
this.get('controller').selectPost(this.get('post'));
|
||||
this.get('controller').toggledSelectedPost(this.get('post'));
|
||||
}
|
||||
},
|
||||
|
||||
selected: function() {
|
||||
var selectedPosts = this.get('controller.selectedPosts');
|
||||
if (!selectedPosts) return false;
|
||||
return selectedPosts.contains(this.get('post'));
|
||||
return this.get('controller').postSelected(this.get('post'));
|
||||
}.property('controller.selectedPostsCount'),
|
||||
|
||||
selectText: function() {
|
||||
canSelectReplies: function() {
|
||||
if (this.get('post.reply_count') === 0) { return false; }
|
||||
return !this.get('selected');
|
||||
}.property('post.reply_count', 'selected'),
|
||||
|
||||
selectPostText: function() {
|
||||
return this.get('selected') ? I18n.t('topic.multi_select.selected', { count: this.get('controller.selectedPostsCount') }) : I18n.t('topic.multi_select.select');
|
||||
}.property('selected', 'controller.selectedPostsCount'),
|
||||
|
||||
|
|
|
@ -500,10 +500,12 @@
|
|||
}
|
||||
&.selected {
|
||||
article.boxed {
|
||||
.post-select {
|
||||
.select-posts {
|
||||
button.select-post {
|
||||
background-color: $blue;
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
.topic-body {
|
||||
.contents {
|
||||
@include box-shadow(0px 0px 7px $blue);
|
||||
|
@ -519,7 +521,13 @@
|
|||
font-size: 16px;
|
||||
line-height: 20px;
|
||||
|
||||
.post-select {
|
||||
.select-posts {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
z-index: 490;
|
||||
top: 4px;
|
||||
|
||||
button {
|
||||
@include border-radius-all(4px);
|
||||
background-color: $light_gray;
|
||||
border-top: 1px solid $white;
|
||||
|
@ -527,12 +535,9 @@
|
|||
border-bottom: 1px solid $gray;
|
||||
border-right: 1px solid $gray;
|
||||
color: $darkish_gray;
|
||||
top: 4px;
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
font-size: 12px;
|
||||
padding: 2px 5px;
|
||||
z-index: 490;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
|
|
|
@ -191,6 +191,16 @@ class ApplicationController < ActionController::Base
|
|||
user
|
||||
end
|
||||
|
||||
def post_ids_including_replies
|
||||
post_ids = params[:post_ids].map {|p| p.to_i}
|
||||
if params[:reply_post_ids]
|
||||
post_ids << PostReply.where(post_id: params[:reply_post_ids].map {|p| p.to_i}).pluck(:reply_id)
|
||||
post_ids.flatten!
|
||||
post_ids.uniq!
|
||||
end
|
||||
post_ids
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def preload_anonymous_data
|
||||
|
|
|
@ -150,10 +150,11 @@ class PostsController < ApplicationController
|
|||
|
||||
params.require(:post_ids)
|
||||
|
||||
posts = Post.where(id: params[:post_ids])
|
||||
posts = Post.where(id: post_ids_including_replies)
|
||||
raise Discourse::InvalidParameters.new(:post_ids) if posts.blank?
|
||||
|
||||
# Make sure we can delete the posts
|
||||
|
||||
posts.each {|p| guardian.ensure_can_delete!(p) }
|
||||
|
||||
Post.transaction do
|
||||
|
|
|
@ -244,7 +244,7 @@ class TopicsController < ApplicationController
|
|||
topic = Topic.where(id: params[:topic_id]).first
|
||||
guardian.ensure_can_move_posts!(topic)
|
||||
|
||||
dest_topic = move_post_to_destination(topic)
|
||||
dest_topic = move_posts_to_destination(topic)
|
||||
render_topic_changes(dest_topic)
|
||||
end
|
||||
|
||||
|
@ -333,12 +333,12 @@ class TopicsController < ApplicationController
|
|||
|
||||
private
|
||||
|
||||
def move_post_to_destination(topic)
|
||||
def move_posts_to_destination(topic)
|
||||
args = {}
|
||||
args[:title] = params[:title] if params[:title].present?
|
||||
args[:destination_topic_id] = params[:destination_topic_id].to_i if params[:destination_topic_id].present?
|
||||
|
||||
topic.move_posts(current_user, params[:post_ids].map {|p| p.to_i}, args)
|
||||
topic.move_posts(current_user, post_ids_including_replies, args)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -92,6 +92,7 @@ predef:
|
|||
- find
|
||||
- sinon
|
||||
- controllerFor
|
||||
- testController
|
||||
- Favcount
|
||||
|
||||
browser: true # true if the standard browser globals should be predefined
|
||||
|
|
|
@ -763,6 +763,7 @@ en:
|
|||
multi_select:
|
||||
select: 'select'
|
||||
selected: 'selected ({{count}})'
|
||||
select_replies: 'select +replies'
|
||||
delete: delete selected
|
||||
cancel: cancel selecting
|
||||
description:
|
||||
|
|
|
@ -163,7 +163,7 @@ describe PostsController do
|
|||
|
||||
let!(:poster) { log_in(:moderator) }
|
||||
let!(:post1) { Fabricate(:post, user: poster, post_number: 2) }
|
||||
let!(:post2) { Fabricate(:post, topic_id: post1.topic_id, user: poster, post_number: 3) }
|
||||
let!(:post2) { Fabricate(:post, topic_id: post1.topic_id, user: poster, post_number: 3, reply_to_post_number: post1.post_number) }
|
||||
|
||||
it "raises invalid parameters no post_ids" do
|
||||
lambda { xhr :delete, :destroy_many }.should raise_error(ActionController::ParameterMissing)
|
||||
|
@ -189,6 +189,19 @@ describe PostsController do
|
|||
xhr :delete, :destroy_many, post_ids: [post1.id, post2.id]
|
||||
end
|
||||
|
||||
describe "can delete replies" do
|
||||
|
||||
before do
|
||||
PostReply.create(post_id: post1.id, reply_id: post2.id)
|
||||
end
|
||||
|
||||
it "deletes the post and the reply to it" do
|
||||
Post.any_instance.expects(:destroy).twice
|
||||
xhr :delete, :destroy_many, post_ids: [post1.id], reply_post_ids: [post1.id]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -93,6 +93,27 @@ describe TopicsController do
|
|||
end
|
||||
end
|
||||
|
||||
describe "moving replied posts" do
|
||||
let!(:user) { log_in(:moderator) }
|
||||
let!(:p1) { Fabricate(:post, user: user) }
|
||||
let!(:topic) { p1.topic }
|
||||
let!(:p2) { Fabricate(:post, topic: topic, user: user, reply_to_post_number: p1.post_number ) }
|
||||
|
||||
context 'success' do
|
||||
|
||||
before do
|
||||
PostReply.create(post_id: p1.id, reply_id: p2.id)
|
||||
end
|
||||
|
||||
it "moves the child posts too" do
|
||||
Topic.any_instance.expects(:move_posts).with(user, [p1.id, p2.id], title: 'blah').returns(topic)
|
||||
xhr :post, :move_posts, topic_id: topic.id, title: 'blah', post_ids: [p1.id], reply_post_ids: [p1.id]
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
describe 'moving to an existing topic' do
|
||||
let!(:user) { log_in(:moderator) }
|
||||
let(:p1) { Fabricate(:post, user: user) }
|
||||
|
|
|
@ -7,7 +7,7 @@ var avatarSelector = Em.Object.create({
|
|||
module("Discourse.AvatarSelectorController");
|
||||
|
||||
test("avatarTemplate", function() {
|
||||
var avatarSelectorController = controllerFor("avatarSelector");
|
||||
var avatarSelectorController = testController(Discourse.AvatarSelectorController);
|
||||
avatarSelectorController.setProperties(avatarSelector);
|
||||
|
||||
equal(avatarSelectorController.get("avatarTemplate"),
|
||||
|
|
|
@ -16,7 +16,7 @@ var buildAdminUser = function(args) {
|
|||
module("Discourse.FlagController canDeleteSpammer");
|
||||
|
||||
test("canDeleteSpammer not staff", function(){
|
||||
var flagController = controllerFor('flag', buildPost());
|
||||
var flagController = testController(Discourse.FlagController, buildPost());
|
||||
this.stub(Discourse.User, 'currentProp').withArgs('staff').returns(false);
|
||||
flagController.set('selected', Discourse.PostActionType.create({name_key: 'spam'}));
|
||||
equal(flagController.get('canDeleteSpammer'), false, 'false if current user is not staff');
|
||||
|
@ -29,7 +29,7 @@ var canDeleteSpammer = function(test, postActionType, expected, testName) {
|
|||
|
||||
test("canDeleteSpammer spam not selected", function(){
|
||||
this.stub(Discourse.User, 'currentProp').withArgs('staff').returns(true);
|
||||
this.flagController = controllerFor('flag', buildPost());
|
||||
this.flagController = testController(Discourse.FlagController, buildPost());
|
||||
this.flagController.set('userDetails', buildAdminUser({can_delete_all_posts: true, can_be_deleted: true}));
|
||||
canDeleteSpammer(this, 'off_topic', false, 'false if current user is staff, but selected is off_topic');
|
||||
canDeleteSpammer(this, 'inappropriate', false, 'false if current user is staff, but selected is inappropriate');
|
||||
|
@ -39,7 +39,7 @@ test("canDeleteSpammer spam not selected", function(){
|
|||
|
||||
test("canDeleteSpammer spam selected", function(){
|
||||
this.stub(Discourse.User, 'currentProp').withArgs('staff').returns(true);
|
||||
this.flagController = controllerFor('flag', buildPost());
|
||||
this.flagController = testController(Discourse.FlagController, buildPost());
|
||||
|
||||
this.flagController.set('userDetails', buildAdminUser({can_delete_all_posts: true, can_be_deleted: true}));
|
||||
canDeleteSpammer(this, 'spam', true, 'true if current user is staff, selected is spam, posts and user can be deleted');
|
||||
|
|
|
@ -1,22 +1,19 @@
|
|||
module("Discourse.TopicController");
|
||||
|
||||
var topic = Discourse.Topic.create({
|
||||
var buildTopic = function() {
|
||||
return Discourse.Topic.create({
|
||||
title: "Qunit Test Topic",
|
||||
participants: [
|
||||
{id: 1234,
|
||||
post_count: 4,
|
||||
username: "eviltrout"}
|
||||
]
|
||||
});
|
||||
|
||||
|
||||
module("Discourse.TopicController", {
|
||||
setup: function() {
|
||||
this.topicController = controllerFor('topic', topic);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
test("editingMode", function() {
|
||||
var topicController = this.topicController;
|
||||
var topic = buildTopic(),
|
||||
topicController = testController(Discourse.TopicController, topic);
|
||||
|
||||
ok(!topicController.get('editingTopic'), "we are not editing by default");
|
||||
|
||||
|
@ -33,3 +30,88 @@ test("editingMode", function() {
|
|||
topicController.cancelEditingTopic();
|
||||
ok(!topicController.get('editingTopic'), "cancelling edit mode reverts the property value");
|
||||
});
|
||||
|
||||
test("toggledSelectedPost", function() {
|
||||
var tc = testController(Discourse.TopicController, buildTopic()),
|
||||
post = Discourse.Post.create({id: 123, post_number: 2}),
|
||||
postStream = tc.get('postStream');
|
||||
|
||||
postStream.appendPost(post);
|
||||
postStream.appendPost(Discourse.Post.create({id: 124, post_number: 3}));
|
||||
|
||||
blank(tc.get('selectedPosts'), "there are no selected posts by default");
|
||||
equal(tc.get('selectedPostsCount'), 0, "there is a selected post count of 0");
|
||||
ok(!tc.postSelected(post), "the post is not selected by default");
|
||||
|
||||
tc.toggledSelectedPost(post);
|
||||
present(tc.get('selectedPosts'), "there is a selectedPosts collection");
|
||||
equal(tc.get('selectedPostsCount'), 1, "there is a selected post now");
|
||||
ok(tc.postSelected(post), "the post is now selected");
|
||||
|
||||
tc.toggledSelectedPost(post);
|
||||
ok(!tc.postSelected(post), "the post is no longer selected");
|
||||
|
||||
});
|
||||
|
||||
test("selectAll", function() {
|
||||
var tc = testController(Discourse.TopicController, buildTopic()),
|
||||
post = Discourse.Post.create({id: 123, post_number: 2}),
|
||||
postStream = tc.get('postStream');
|
||||
|
||||
postStream.appendPost(post);
|
||||
|
||||
ok(!tc.postSelected(post), "the post is not selected by default");
|
||||
tc.selectAll();
|
||||
ok(tc.postSelected(post), "the post is now selected");
|
||||
ok(tc.get('allPostsSelected'), "all posts are selected");
|
||||
tc.deselectAll();
|
||||
ok(!tc.postSelected(post), "the post is deselected again");
|
||||
ok(!tc.get('allPostsSelected'), "all posts are not selected");
|
||||
|
||||
});
|
||||
|
||||
test("Automating setting of allPostsSelected", function() {
|
||||
var topic = buildTopic(),
|
||||
tc = testController(Discourse.TopicController, topic),
|
||||
post = Discourse.Post.create({id: 123, post_number: 2}),
|
||||
postStream = tc.get('postStream');
|
||||
|
||||
topic.set('posts_count', 1);
|
||||
postStream.appendPost(post);
|
||||
ok(!tc.get('allPostsSelected'), "all posts are not selected by default");
|
||||
|
||||
tc.toggledSelectedPost(post);
|
||||
ok(tc.get('allPostsSelected'), "all posts are selected if we select the only post");
|
||||
|
||||
tc.toggledSelectedPost(post);
|
||||
ok(!tc.get('allPostsSelected'), "the posts are no longer automatically selected");
|
||||
});
|
||||
|
||||
test("Select Replies when present", function() {
|
||||
var topic = buildTopic(),
|
||||
tc = testController(Discourse.TopicController, topic),
|
||||
p1 = Discourse.Post.create({id: 1, post_number: 1, reply_count: 1}),
|
||||
p2 = Discourse.Post.create({id: 2, post_number: 2}),
|
||||
p3 = Discourse.Post.create({id: 2, post_number: 3, reply_to_post_number: 1}),
|
||||
postStream = tc.get('postStream');
|
||||
|
||||
ok(!tc.postSelected(p3), "replies are not selected by default");
|
||||
tc.toggledSelectedPostReplies(p1);
|
||||
ok(tc.postSelected(p1), "it selects the post");
|
||||
ok(!tc.postSelected(p2), "it doesn't select a post that's not a reply");
|
||||
ok(tc.postSelected(p3), "it selects a post that is a reply");
|
||||
equal(tc.get('selectedPostsCount'), 2, "it has a selected posts count of two");
|
||||
|
||||
// If we deselected the post whose replies are selected...
|
||||
tc.toggledSelectedPost(p1);
|
||||
ok(!tc.postSelected(p1), "it deselects the post");
|
||||
ok(!tc.postSelected(p3), "it deselects the replies too");
|
||||
|
||||
// If we deselect a reply, it should deselect the parent's replies selected attribute. Weird but what else would make sense?
|
||||
tc.toggledSelectedPostReplies(p1);
|
||||
tc.toggledSelectedPost(p3);
|
||||
ok(tc.postSelected(p1), "the post stays selected");
|
||||
ok(!tc.postSelected(p3), "it deselects the replies too");
|
||||
|
||||
});
|
||||
|
||||
|
|
|
@ -14,6 +14,10 @@ function integration(name) {
|
|||
});
|
||||
}
|
||||
|
||||
function testController(klass, model) {
|
||||
return klass.create({model: model, container: Discourse.__container__});
|
||||
}
|
||||
|
||||
function controllerFor(controller, model) {
|
||||
var controller = Discourse.__container__.lookup('controller:' + controller);
|
||||
if (model) { controller.set('model', model ); }
|
||||
|
|
|
@ -122,6 +122,7 @@ var jsHintOpts = {
|
|||
"console",
|
||||
"alert",
|
||||
"controllerFor",
|
||||
"testController",
|
||||
"containsInstance",
|
||||
"deepEqual",
|
||||
"notEqual",
|
||||
|
|
Loading…
Reference in a new issue