mirror of
https://github.com/discourse/discourse.git
synced 2025-02-14 09:52:44 +08:00
399 lines
11 KiB
JavaScript
399 lines
11 KiB
JavaScript
/**
|
|
This controller supports all actions related to a topic
|
|
|
|
@class TopicController
|
|
@extends Discourse.ObjectController
|
|
@namespace Discourse
|
|
@module Discourse
|
|
**/
|
|
Discourse.TopicController = Discourse.ObjectController.extend(Discourse.SelectedPostsCount, {
|
|
multiSelect: false,
|
|
summaryCollapsed: true,
|
|
needs: ['header', 'modal', 'composer', 'quoteButton'],
|
|
allPostsSelected: false,
|
|
selectedPosts: new Em.Set(),
|
|
editingTopic: false,
|
|
|
|
jumpTopDisabled: function() {
|
|
return (this.get('progressPosition') === 1);
|
|
}.property('postStream.filteredPostsCount', 'progressPosition'),
|
|
|
|
jumpBottomDisabled: function() {
|
|
return this.get('progressPosition') >= this.get('postStream.filteredPostsCount');
|
|
}.property('postStream.filteredPostsCount', 'progressPosition'),
|
|
|
|
canMergeTopic: function() {
|
|
if (!this.get('details.can_move_posts')) return false;
|
|
return (this.get('selectedPostsCount') > 0);
|
|
}.property('selectedPostsCount'),
|
|
|
|
canSplitTopic: function() {
|
|
if (!this.get('details.can_move_posts')) return false;
|
|
if (this.get('allPostsSelected')) return false;
|
|
return (this.get('selectedPostsCount') > 0);
|
|
}.property('selectedPostsCount'),
|
|
|
|
categories: function() {
|
|
return Discourse.Category.list();
|
|
}.property(),
|
|
|
|
canSelectAll: Em.computed.not('allPostsSelected'),
|
|
|
|
canDeselectAll: function () {
|
|
if (this.get('selectedPostsCount') > 0) return true;
|
|
if (this.get('allPostsSelected')) return true;
|
|
}.property('selectedPostsCount', 'allPostsSelected'),
|
|
|
|
canDeleteSelected: function() {
|
|
var selectedPosts = this.get('selectedPosts');
|
|
|
|
if (this.get('allPostsSelected')) return true;
|
|
if (this.get('selectedPostsCount') === 0) return false;
|
|
|
|
var canDelete = true;
|
|
selectedPosts.forEach(function(p) {
|
|
if (!p.get('can_delete')) {
|
|
canDelete = false;
|
|
return false;
|
|
}
|
|
});
|
|
return canDelete;
|
|
}.property('selectedPostsCount'),
|
|
|
|
hasError: Ember.computed.or('errorBodyHtml', 'message'),
|
|
|
|
streamPercentage: function() {
|
|
if (!this.get('postStream.loaded')) { return 0; }
|
|
if (this.get('postStream.filteredPostsCount') === 0) { return 0; }
|
|
return this.get('progressPosition') / this.get('postStream.filteredPostsCount');
|
|
}.property('postStream.loaded', 'progressPosition', 'postStream.filteredPostsCount'),
|
|
|
|
multiSelectChanged: function() {
|
|
// Deselect all posts when multi select is turned off
|
|
if (!this.get('multiSelect')) {
|
|
this.deselectAll();
|
|
}
|
|
}.observes('multiSelect'),
|
|
|
|
hideProgress: function() {
|
|
if (!this.get('postStream.loaded')) return true;
|
|
if (!this.get('currentPost')) return true;
|
|
if (this.get('postStream.filteredPostsCount') < 2) return true;
|
|
return false;
|
|
}.property('postStream.loaded', 'currentPost', 'postStream.filteredPostsCount'),
|
|
|
|
selectPost: function(post) {
|
|
var selectedPosts = this.get('selectedPosts');
|
|
if (selectedPosts.contains(post)) {
|
|
selectedPosts.removeObject(post);
|
|
this.set('allPostsSelected', 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');
|
|
}
|
|
}
|
|
},
|
|
|
|
selectAll: function() {
|
|
var posts = this.get('postStream.posts');
|
|
var selectedPosts = this.get('selectedPosts');
|
|
if (posts) {
|
|
selectedPosts.addObjects(posts);
|
|
}
|
|
this.set('allPostsSelected', true);
|
|
},
|
|
|
|
deselectAll: function() {
|
|
this.get('selectedPosts').clear();
|
|
this.set('allPostsSelected', false);
|
|
},
|
|
|
|
toggleMultiSelect: function() {
|
|
this.toggleProperty('multiSelect');
|
|
},
|
|
|
|
toggleSummary: function() {
|
|
this.toggleProperty('summaryCollapsed');
|
|
},
|
|
|
|
editTopic: function() {
|
|
if (!this.get('details.can_edit')) return false;
|
|
|
|
this.setProperties({
|
|
editingTopic: true,
|
|
newTitle: this.get('title'),
|
|
newCategoryId: this.get('category_id')
|
|
});
|
|
return false;
|
|
},
|
|
|
|
// close editing mode
|
|
cancelEditingTopic: function() {
|
|
this.set('editingTopic', false);
|
|
},
|
|
|
|
finishedEditingTopic: function() {
|
|
var topicController = this;
|
|
if (this.get('editingTopic')) {
|
|
|
|
var topic = this.get('model');
|
|
|
|
// manually update the titles & category
|
|
topic.setProperties({
|
|
title: this.get('newTitle'),
|
|
category_id: parseInt(this.get('newCategoryId'), 10),
|
|
fancy_title: this.get('newTitle')
|
|
});
|
|
|
|
// save the modifications
|
|
topic.save().then(function(result){
|
|
// update the title if it has been changed (cleaned up) server-side
|
|
var title = result.basic_topic.title;
|
|
var fancy_title = result.basic_topic.fancy_title;
|
|
topic.setProperties({
|
|
title: title,
|
|
fancy_title: fancy_title
|
|
});
|
|
|
|
}, function(error) {
|
|
topicController.set('editingTopic', true);
|
|
if (error && error.responseText) {
|
|
bootbox.alert($.parseJSON(error.responseText).errors[0]);
|
|
} else {
|
|
bootbox.alert(I18n.t('generic_error'));
|
|
}
|
|
});
|
|
|
|
// close editing mode
|
|
topicController.set('editingTopic', false);
|
|
}
|
|
},
|
|
|
|
deleteSelected: function() {
|
|
var topicController = 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();
|
|
}
|
|
|
|
var selectedPosts = topicController.get('selectedPosts');
|
|
Discourse.Post.deleteMany(selectedPosts);
|
|
topicController.get('model.postStream').removePosts(selectedPosts);
|
|
topicController.toggleMultiSelect();
|
|
}
|
|
});
|
|
},
|
|
|
|
jumpTop: function() {
|
|
Discourse.URL.routeTo(this.get('url'));
|
|
},
|
|
|
|
jumpBottom: function() {
|
|
Discourse.URL.routeTo(this.get('lastPostUrl'));
|
|
},
|
|
|
|
|
|
/**
|
|
Toggle a participant for filtering
|
|
|
|
@method toggleParticipant
|
|
**/
|
|
toggleParticipant: function(user) {
|
|
this.get('postStream').toggleParticipant(Em.get(user, 'username'));
|
|
},
|
|
|
|
showFavoriteButton: function() {
|
|
return Discourse.User.current() && !this.get('isPrivateMessage');
|
|
}.property('isPrivateMessage'),
|
|
|
|
recoverTopic: function() {
|
|
this.get('content').recover();
|
|
},
|
|
|
|
deleteTopic: function() {
|
|
this.unsubscribe();
|
|
this.get('content').destroy(Discourse.User.current());
|
|
},
|
|
|
|
resetRead: function() {
|
|
Discourse.ScreenTrack.instance().reset();
|
|
this.unsubscribe();
|
|
|
|
var topicController = this;
|
|
this.get('model').resetRead().then(function() {
|
|
topicController.set('message', I18n.t("topic.read_position_reset"));
|
|
topicController.set('postStream.loaded', false);
|
|
});
|
|
},
|
|
|
|
toggleVisibility: function() {
|
|
this.get('content').toggleStatus('visible');
|
|
},
|
|
|
|
toggleClosed: function() {
|
|
this.get('content').toggleStatus('closed');
|
|
},
|
|
|
|
togglePinned: function() {
|
|
this.get('content').toggleStatus('pinned');
|
|
},
|
|
|
|
toggleArchived: function() {
|
|
this.get('content').toggleStatus('archived');
|
|
},
|
|
|
|
convertToRegular: function() {
|
|
this.get('content').convertArchetype('regular');
|
|
},
|
|
|
|
// Toggle the star on the topic
|
|
toggleStar: function(e) {
|
|
this.get('content').toggleStar();
|
|
},
|
|
|
|
/**
|
|
Clears the pin from a topic for the currently logged in user
|
|
|
|
@method clearPin
|
|
**/
|
|
clearPin: function() {
|
|
this.get('content').clearPin();
|
|
},
|
|
|
|
// Receive notifications for this topic
|
|
subscribe: function() {
|
|
|
|
// Unsubscribe before subscribing again
|
|
this.unsubscribe();
|
|
|
|
var bus = Discourse.MessageBus;
|
|
|
|
var topicController = this;
|
|
bus.subscribe("/topic/" + (this.get('id')), function(data) {
|
|
var topic = topicController.get('model');
|
|
if (data.notification_level_change) {
|
|
topic.set('details.notification_level', data.notification_level_change);
|
|
topic.set('details.notifications_reason_id', data.notifications_reason_id);
|
|
return;
|
|
}
|
|
|
|
// Add the new post into the stream
|
|
topicController.get('postStream').triggerNewPostInStream(data.id);
|
|
});
|
|
},
|
|
|
|
unsubscribe: function() {
|
|
var topicId = this.get('content.id');
|
|
if (!topicId) return;
|
|
|
|
// there is a condition where the view never calls unsubscribe, navigate to a topic from a topic
|
|
Discourse.MessageBus.unsubscribe('/topic/*');
|
|
},
|
|
|
|
// Post related methods
|
|
replyToPost: function(post) {
|
|
var composerController = this.get('controllers.composer');
|
|
var quoteController = this.get('controllers.quoteButton');
|
|
var quotedText = Discourse.BBCode.buildQuoteBBCode(quoteController.get('post'), quoteController.get('buffer'));
|
|
|
|
var topic = post ? post.get('topic') : this.get('model');
|
|
|
|
quoteController.set('buffer', '');
|
|
|
|
if (composerController.get('content.topic.id') === topic.get('id') &&
|
|
composerController.get('content.action') === Discourse.Composer.REPLY) {
|
|
composerController.set('content.post', post);
|
|
composerController.set('content.composeState', Discourse.Composer.OPEN);
|
|
composerController.appendText(quotedText);
|
|
} else {
|
|
|
|
var opts = {
|
|
action: Discourse.Composer.REPLY,
|
|
draftKey: topic.get('draft_key'),
|
|
draftSequence: topic.get('draft_sequence')
|
|
};
|
|
|
|
if(post && post.get("post_number") !== 1){
|
|
opts.post = post;
|
|
} else {
|
|
opts.topic = topic;
|
|
}
|
|
|
|
var promise = composerController.open(opts);
|
|
promise.then(function() { composerController.appendText(quotedText); });
|
|
}
|
|
return false;
|
|
},
|
|
|
|
replyAsNewTopic: function(post) {
|
|
var composerController = this.get('controllers.composer');
|
|
var promise = composerController.open({
|
|
action: Discourse.Composer.CREATE_TOPIC,
|
|
draftKey: Discourse.Composer.REPLY_AS_NEW_TOPIC_KEY
|
|
});
|
|
var postUrl = "" + location.protocol + "//" + location.host + (post.get('url'));
|
|
var postLink = "[" + (this.get('title')) + "](" + postUrl + ")";
|
|
|
|
promise.then(function() {
|
|
Discourse.Post.loadQuote(post.get('id')).then(function(q) {
|
|
composerController.appendText(I18n.t("post.continue_discussion", {
|
|
postLink: postLink
|
|
}) + "\n\n" + q);
|
|
});
|
|
});
|
|
},
|
|
|
|
// Topic related
|
|
reply: function() {
|
|
this.replyToPost();
|
|
},
|
|
|
|
// Edits a post
|
|
editPost: function(post) {
|
|
this.get('controllers.composer').open({
|
|
post: post,
|
|
action: Discourse.Composer.EDIT,
|
|
draftKey: post.get('topic.draft_key'),
|
|
draftSequence: post.get('topic.draft_sequence')
|
|
});
|
|
},
|
|
|
|
toggleBookmark: function(post) {
|
|
if (!Discourse.User.current()) {
|
|
alert(I18n.t("bookmarks.not_bookmarked"));
|
|
return;
|
|
}
|
|
post.toggleProperty('bookmarked');
|
|
return false;
|
|
},
|
|
|
|
clearFlags: function(actionType) {
|
|
actionType.clearFlags();
|
|
},
|
|
|
|
// Who acted on a particular post / action type
|
|
whoActed: function(actionType) {
|
|
actionType.loadUsers();
|
|
},
|
|
|
|
recoverPost: function(post) {
|
|
post.recover();
|
|
},
|
|
|
|
deletePost: function(post) {
|
|
post.destroy(Discourse.User.current());
|
|
},
|
|
|
|
removeAllowedUser: function(username) {
|
|
this.get('details').removeAllowedUser(username);
|
|
}
|
|
});
|
|
|
|
|