mirror of
https://github.com/discourse/discourse.git
synced 2024-11-23 15:05:24 +08:00
Merge pull request #4206 from techAPJ/convert-topic
FEATURE: move a topic from PM to regular topic or vice versa
This commit is contained in:
commit
82daf93eb3
|
@ -545,6 +545,14 @@ export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, {
|
|||
changePostOwner(post) {
|
||||
this.get('selectedPosts').addObject(post);
|
||||
this.send('changeOwner');
|
||||
},
|
||||
|
||||
convertToPublicTopic() {
|
||||
this.get('content').convertTopic("public");
|
||||
},
|
||||
|
||||
convertToPrivateMessage() {
|
||||
this.get('content').convertTopic("private");
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import { propertyEqual } from 'discourse/lib/computed';
|
|||
import { longDate } from 'discourse/lib/formatter';
|
||||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
import ActionSummary from 'discourse/models/action-summary';
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
|
||||
export function loadTopicView(topic, args) {
|
||||
const topicId = topic.get('id');
|
||||
|
@ -446,8 +447,13 @@ const Topic = RestModel.extend({
|
|||
}).finally(()=>this.set('archiving', false));
|
||||
|
||||
return promise;
|
||||
}
|
||||
},
|
||||
|
||||
convertTopic(type) {
|
||||
return Discourse.ajax(`/t/${this.get('id')}/convert-topic/${type}`, {type: 'PUT'}).then(() => {
|
||||
window.location.reload();
|
||||
}).catch(popupAjaxError);
|
||||
}
|
||||
});
|
||||
|
||||
Topic.reopenClass({
|
||||
|
|
|
@ -256,6 +256,16 @@
|
|||
{{/if}}
|
||||
</li>
|
||||
|
||||
{{#if currentUser.admin}}
|
||||
<li class="topic-admin-convert">
|
||||
{{#if model.isPrivateMessage}}
|
||||
{{d-button action="convertToPublicTopic" icon="comment" label="topic.actions.make_public"}}
|
||||
{{else}}
|
||||
{{d-button action="convertToPrivateMessage" icon="envelope" label="topic.actions.make_private"}}
|
||||
{{/if}}
|
||||
</li>
|
||||
{{/if}}
|
||||
|
||||
{{plugin-outlet "topic-admin-menu-buttons"}}
|
||||
{{/popup-menu}}
|
||||
{{/if}}
|
||||
|
|
|
@ -20,7 +20,9 @@ const icons = {
|
|||
'visible.disabled': 'eye-slash',
|
||||
'split_topic': 'sign-out',
|
||||
'invited_user': 'plus-circle',
|
||||
'removed_user': 'minus-circle'
|
||||
'removed_user': 'minus-circle',
|
||||
'public_topic': 'comment',
|
||||
'private_topic': 'envelope'
|
||||
};
|
||||
|
||||
export default createWidget('post-small-action', {
|
||||
|
|
|
@ -27,6 +27,7 @@ class TopicsController < ApplicationController
|
|||
:change_timestamps,
|
||||
:archive_message,
|
||||
:move_to_inbox,
|
||||
:convert_topic,
|
||||
:bookmark]
|
||||
|
||||
before_filter :consider_user_for_promotion, only: :show
|
||||
|
@ -510,6 +511,22 @@ class TopicsController < ApplicationController
|
|||
render nothing: true
|
||||
end
|
||||
|
||||
def convert_topic
|
||||
params.require(:id)
|
||||
params.require(:type)
|
||||
topic = Topic.find_by(id: params[:id])
|
||||
guardian.ensure_can_convert_topic!(topic)
|
||||
|
||||
if params[:type] == "public"
|
||||
converted_topic = topic.convert_to_public_topic(current_user)
|
||||
else
|
||||
converted_topic = topic.convert_to_private_message(current_user)
|
||||
end
|
||||
render_topic_changes(converted_topic)
|
||||
rescue ActiveRecord::RecordInvalid => ex
|
||||
render_json_error(ex)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def toggle_mute
|
||||
|
|
|
@ -1047,6 +1047,18 @@ SQL
|
|||
[result].flatten unless result.blank?
|
||||
end
|
||||
|
||||
def convert_to_public_topic(user)
|
||||
public_topic = TopicConverter.new(self, user).convert_to_public_topic
|
||||
add_small_action(user, "public_topic") if public_topic
|
||||
public_topic
|
||||
end
|
||||
|
||||
def convert_to_private_message(user)
|
||||
private_topic = TopicConverter.new(self, user).convert_to_private_message
|
||||
add_small_action(user, "private_topic") if private_topic
|
||||
private_topic
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def update_category_topic_count_by(num)
|
||||
|
|
69
app/models/topic_converter.rb
Normal file
69
app/models/topic_converter.rb
Normal file
|
@ -0,0 +1,69 @@
|
|||
class TopicConverter
|
||||
|
||||
attr_reader :topic
|
||||
|
||||
def initialize(topic, user)
|
||||
@topic = topic
|
||||
@user = user
|
||||
end
|
||||
|
||||
def convert_to_public_topic
|
||||
Topic.transaction do
|
||||
@topic.category_id = SiteSetting.allow_uncategorized_topics ? SiteSetting.uncategorized_category_id : Category.where(read_restricted: false).first.id
|
||||
@topic.archetype = Archetype.default
|
||||
@topic.save
|
||||
update_user_stats
|
||||
watch_topic(topic)
|
||||
end
|
||||
@topic
|
||||
end
|
||||
|
||||
def convert_to_private_message
|
||||
Topic.transaction do
|
||||
@topic.category_id = nil
|
||||
@topic.archetype = Archetype.private_message
|
||||
add_allowed_users
|
||||
@topic.save
|
||||
watch_topic(topic)
|
||||
end
|
||||
@topic
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def update_user_stats
|
||||
@topic.posts.where(deleted_at: nil).each do |p|
|
||||
user = User.find(p.user_id)
|
||||
# update posts count
|
||||
user.user_stat.post_count += 1
|
||||
user.user_stat.save!
|
||||
end
|
||||
# update topics count
|
||||
@topic.user.user_stat.topic_count += 1
|
||||
@topic.user.user_stat.save!
|
||||
end
|
||||
|
||||
def add_allowed_users
|
||||
@topic.posts.where(deleted_at: nil).each do |p|
|
||||
user = User.find(p.user_id)
|
||||
@topic.topic_allowed_users.build(user_id: user.id) unless @topic.topic_allowed_users.where(user_id: user.id).exists?
|
||||
# update posts count
|
||||
user.user_stat.post_count -= 1
|
||||
user.user_stat.save!
|
||||
end
|
||||
@topic.topic_allowed_users.build(user_id: @user.id)
|
||||
# update topics count
|
||||
@topic.user.user_stat.topic_count -= 1
|
||||
@topic.user.user_stat.save!
|
||||
end
|
||||
|
||||
def watch_topic(topic)
|
||||
@topic.notifier.watch_topic!(topic.user_id)
|
||||
|
||||
@topic.topic_allowed_users(true).each do |tau|
|
||||
next if tau.user_id == -1 || tau.user_id == topic.user_id
|
||||
topic.notifier.watch!(tau.user_id)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -121,6 +121,8 @@ en:
|
|||
email: 'send this link in an email'
|
||||
|
||||
action_codes:
|
||||
public_topic: "made this topic public %{when}"
|
||||
private_topic: "made this topic private %{when}"
|
||||
split_topic: "split this topic %{when}"
|
||||
invited_user: "invited %{who} %{when}"
|
||||
removed_user: "removed %{who} %{when}"
|
||||
|
@ -1344,6 +1346,8 @@ en:
|
|||
invisible: "Make Unlisted"
|
||||
visible: "Make Listed"
|
||||
reset_read: "Reset Read Data"
|
||||
make_public: "Make Public Topic"
|
||||
make_private: "Make Private Message"
|
||||
|
||||
feature:
|
||||
pin: "Pin Topic"
|
||||
|
@ -2983,4 +2987,3 @@ en:
|
|||
top: "There are no more top topics."
|
||||
bookmarks: "There are no more bookmarked topics."
|
||||
search: "There are no more search results."
|
||||
|
||||
|
|
|
@ -486,6 +486,7 @@ Discourse::Application.routes.draw do
|
|||
delete "t/:id" => "topics#destroy"
|
||||
put "t/:id/archive-message" => "topics#archive_message"
|
||||
put "t/:id/move-to-inbox" => "topics#move_to_inbox"
|
||||
put "t/:id/convert-topic/:type" => "topics#convert_topic"
|
||||
put "topics/bulk"
|
||||
put "topics/reset-new" => 'topics#reset_new'
|
||||
post "topics/timings"
|
||||
|
|
|
@ -58,6 +58,10 @@ module TopicGuardian
|
|||
!Discourse.static_doc_topic_ids.include?(topic.id)
|
||||
end
|
||||
|
||||
def can_convert_topic?(topic)
|
||||
topic && !topic.trashed? && is_admin?
|
||||
end
|
||||
|
||||
def can_reply_as_new_topic?(topic)
|
||||
authenticated? && topic && not(topic.private_message?) && @user.has_trust_level?(TrustLevel[1])
|
||||
end
|
||||
|
|
|
@ -872,6 +872,24 @@ describe Guardian do
|
|||
|
||||
end
|
||||
|
||||
context 'can_convert_topic?' do
|
||||
it 'returns false with a nil object' do
|
||||
expect(Guardian.new(user).can_convert_topic?(nil)).to be_falsey
|
||||
end
|
||||
|
||||
it 'returns false when not logged in' do
|
||||
expect(Guardian.new.can_convert_topic?(topic)).to be_falsey
|
||||
end
|
||||
|
||||
it 'returns false when not admin' do
|
||||
expect(Guardian.new(moderator).can_convert_topic?(topic)).to be_falsey
|
||||
end
|
||||
|
||||
it 'returns true when an admin' do
|
||||
expect(Guardian.new(admin).can_convert_topic?(topic)).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
describe 'can_edit?' do
|
||||
|
||||
it 'returns false with a nil object' do
|
||||
|
|
|
@ -1235,4 +1235,63 @@ describe TopicsController do
|
|||
expect(response.headers['X-Robots-Tag']).to eq(nil)
|
||||
end
|
||||
end
|
||||
|
||||
context "convert_topic" do
|
||||
it 'needs you to be logged in' do
|
||||
expect { xhr :put, :convert_topic, id: 111, type: "private" }.to raise_error(Discourse::NotLoggedIn)
|
||||
end
|
||||
|
||||
describe 'converting public topic to private message' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:topic) { Fabricate(:topic, user: user) }
|
||||
|
||||
it "raises an error when the user doesn't have permission to convert topic" do
|
||||
log_in
|
||||
xhr :put, :convert_topic, id: topic.id, type: "private"
|
||||
expect(response).to be_forbidden
|
||||
end
|
||||
|
||||
context "success" do
|
||||
before do
|
||||
admin = log_in(:admin)
|
||||
Topic.any_instance.expects(:convert_to_private_message).with(admin).returns(topic)
|
||||
xhr :put, :convert_topic, id: topic.id, type: "private"
|
||||
end
|
||||
|
||||
it "returns success" do
|
||||
expect(response).to be_success
|
||||
result = ::JSON.parse(response.body)
|
||||
expect(result['success']).to eq(true)
|
||||
expect(result['url']).to be_present
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'converting private message to public topic' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:topic) { Fabricate(:topic, user: user) }
|
||||
|
||||
it "raises an error when the user doesn't have permission to convert topic" do
|
||||
log_in
|
||||
xhr :put, :convert_topic, id: topic.id, type: "public"
|
||||
expect(response).to be_forbidden
|
||||
end
|
||||
|
||||
context "success" do
|
||||
before do
|
||||
admin = log_in(:admin)
|
||||
Topic.any_instance.expects(:convert_to_public_topic).with(admin).returns(topic)
|
||||
xhr :put, :convert_topic, id: topic.id, type: "public"
|
||||
end
|
||||
|
||||
it "returns success" do
|
||||
expect(response).to be_success
|
||||
result = ::JSON.parse(response.body)
|
||||
expect(result['success']).to eq(true)
|
||||
expect(result['url']).to be_present
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
68
spec/models/topic_converter_spec.rb
Normal file
68
spec/models/topic_converter_spec.rb
Normal file
|
@ -0,0 +1,68 @@
|
|||
require 'rails_helper'
|
||||
|
||||
describe TopicConverter do
|
||||
|
||||
context 'convert_to_public_topic' do
|
||||
let(:admin) { Fabricate(:admin) }
|
||||
let(:author) { Fabricate(:user) }
|
||||
let(:private_message) { Fabricate(:private_message_topic, user: author) }
|
||||
|
||||
context 'success' do
|
||||
it "converts private message to regular topic" do
|
||||
topic = private_message.convert_to_public_topic(admin)
|
||||
expect(topic).to be_valid
|
||||
expect(topic.archetype).to eq("regular")
|
||||
end
|
||||
|
||||
it "updates user stats" do
|
||||
topic_user = TopicUser.create!(user_id: author.id, topic_id: private_message.id, posted: true)
|
||||
expect(private_message.user.user_stat.topic_count).to eq(0)
|
||||
private_message.convert_to_public_topic(admin)
|
||||
expect(private_message.reload.user.user_stat.topic_count).to eq(1)
|
||||
expect(topic_user.reload.notification_level).to eq(TopicUser.notification_levels[:watching])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'convert_to_private_message' do
|
||||
let(:admin) { Fabricate(:admin) }
|
||||
let(:author) { Fabricate(:user) }
|
||||
let(:topic) { Fabricate(:topic, user: author) }
|
||||
|
||||
context 'success' do
|
||||
it "converts regular topic to private message" do
|
||||
private_message = topic.convert_to_private_message(admin)
|
||||
expect(private_message).to be_valid
|
||||
expect(topic.archetype).to eq("private_message")
|
||||
end
|
||||
|
||||
it "updates user stats" do
|
||||
Fabricate(:post, topic: topic, user: author)
|
||||
topic_user = TopicUser.create!(user_id: author.id, topic_id: topic.id, posted: true)
|
||||
author.user_stat.topic_count = 1
|
||||
author.user_stat.save
|
||||
expect(topic.user.user_stat.topic_count).to eq(1)
|
||||
topic.convert_to_private_message(admin)
|
||||
|
||||
expect(topic.reload.topic_allowed_users.where(user_id: author.id).count).to eq(1)
|
||||
expect(topic.reload.user.user_stat.topic_count).to eq(0)
|
||||
expect(topic_user.reload.notification_level).to eq(TopicUser.notification_levels[:watching])
|
||||
end
|
||||
end
|
||||
|
||||
context 'topic has replies' do
|
||||
before do
|
||||
@replied_user = Fabricate(:coding_horror)
|
||||
create_post(topic: topic, user: @replied_user)
|
||||
topic.reload
|
||||
end
|
||||
|
||||
it 'adds users who replied to topic in Private Message' do
|
||||
topic.convert_to_private_message(admin)
|
||||
|
||||
expect(topic.reload.topic_allowed_users.where(user_id: @replied_user.id).count).to eq(1)
|
||||
expect(topic.reload.user.user_stat.post_count).to eq(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue
Block a user