FEATURE: move a topic from PM to regular topic or vice versa

This commit is contained in:
Arpit Jalan 2016-05-01 17:18:43 +05:30
parent e2928f78d2
commit acfb540952
13 changed files with 280 additions and 3 deletions

View file

@ -545,6 +545,14 @@ export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, {
changePostOwner(post) { changePostOwner(post) {
this.get('selectedPosts').addObject(post); this.get('selectedPosts').addObject(post);
this.send('changeOwner'); this.send('changeOwner');
},
convertToPublicTopic() {
this.get('content').convertTopic("public");
},
convertToPrivateMessage() {
this.get('content').convertTopic("private");
} }
}, },

View file

@ -4,6 +4,7 @@ import { propertyEqual } from 'discourse/lib/computed';
import { longDate } from 'discourse/lib/formatter'; import { longDate } from 'discourse/lib/formatter';
import computed from 'ember-addons/ember-computed-decorators'; import computed from 'ember-addons/ember-computed-decorators';
import ActionSummary from 'discourse/models/action-summary'; import ActionSummary from 'discourse/models/action-summary';
import { popupAjaxError } from 'discourse/lib/ajax-error';
export function loadTopicView(topic, args) { export function loadTopicView(topic, args) {
const topicId = topic.get('id'); const topicId = topic.get('id');
@ -446,8 +447,13 @@ const Topic = RestModel.extend({
}).finally(()=>this.set('archiving', false)); }).finally(()=>this.set('archiving', false));
return promise; return promise;
} },
convertTopic(type) {
return Discourse.ajax(`/t/${this.get('id')}/convert-topic/${type}`, {type: 'PUT'}).then(() => {
window.location.reload();
}).catch(popupAjaxError);
}
}); });
Topic.reopenClass({ Topic.reopenClass({

View file

@ -256,6 +256,16 @@
{{/if}} {{/if}}
</li> </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"}} {{plugin-outlet "topic-admin-menu-buttons"}}
{{/popup-menu}} {{/popup-menu}}
{{/if}} {{/if}}

View file

@ -20,7 +20,9 @@ const icons = {
'visible.disabled': 'eye-slash', 'visible.disabled': 'eye-slash',
'split_topic': 'sign-out', 'split_topic': 'sign-out',
'invited_user': 'plus-circle', 'invited_user': 'plus-circle',
'removed_user': 'minus-circle' 'removed_user': 'minus-circle',
'public_topic': 'comment',
'private_topic': 'envelope'
}; };
export default createWidget('post-small-action', { export default createWidget('post-small-action', {

View file

@ -27,6 +27,7 @@ class TopicsController < ApplicationController
:change_timestamps, :change_timestamps,
:archive_message, :archive_message,
:move_to_inbox, :move_to_inbox,
:convert_topic,
:bookmark] :bookmark]
before_filter :consider_user_for_promotion, only: :show before_filter :consider_user_for_promotion, only: :show
@ -510,6 +511,22 @@ class TopicsController < ApplicationController
render nothing: true render nothing: true
end 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 private
def toggle_mute def toggle_mute

View file

@ -1047,6 +1047,18 @@ SQL
[result].flatten unless result.blank? [result].flatten unless result.blank?
end 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 private
def update_category_topic_count_by(num) def update_category_topic_count_by(num)

View 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

View file

@ -121,6 +121,8 @@ en:
email: 'send this link in an email' email: 'send this link in an email'
action_codes: action_codes:
public_topic: "made this topic public %{when}"
private_topic: "made this topic private %{when}"
split_topic: "split this topic %{when}" split_topic: "split this topic %{when}"
invited_user: "invited %{who} %{when}" invited_user: "invited %{who} %{when}"
removed_user: "removed %{who} %{when}" removed_user: "removed %{who} %{when}"
@ -1344,6 +1346,8 @@ en:
invisible: "Make Unlisted" invisible: "Make Unlisted"
visible: "Make Listed" visible: "Make Listed"
reset_read: "Reset Read Data" reset_read: "Reset Read Data"
make_public: "Make Public Topic"
make_private: "Make Private Message"
feature: feature:
pin: "Pin Topic" pin: "Pin Topic"
@ -2982,4 +2986,3 @@ en:
top: "There are no more top topics." top: "There are no more top topics."
bookmarks: "There are no more bookmarked topics." bookmarks: "There are no more bookmarked topics."
search: "There are no more search results." search: "There are no more search results."

View file

@ -485,6 +485,7 @@ Discourse::Application.routes.draw do
delete "t/:id" => "topics#destroy" delete "t/:id" => "topics#destroy"
put "t/:id/archive-message" => "topics#archive_message" put "t/:id/archive-message" => "topics#archive_message"
put "t/:id/move-to-inbox" => "topics#move_to_inbox" put "t/:id/move-to-inbox" => "topics#move_to_inbox"
put "t/:id/convert-topic/:type" => "topics#convert_topic"
put "topics/bulk" put "topics/bulk"
put "topics/reset-new" => 'topics#reset_new' put "topics/reset-new" => 'topics#reset_new'
post "topics/timings" post "topics/timings"

View file

@ -58,6 +58,10 @@ module TopicGuardian
!Discourse.static_doc_topic_ids.include?(topic.id) !Discourse.static_doc_topic_ids.include?(topic.id)
end end
def can_convert_topic?(topic)
topic && !topic.trashed? && is_admin?
end
def can_reply_as_new_topic?(topic) def can_reply_as_new_topic?(topic)
authenticated? && topic && not(topic.private_message?) && @user.has_trust_level?(TrustLevel[1]) authenticated? && topic && not(topic.private_message?) && @user.has_trust_level?(TrustLevel[1])
end end

View file

@ -872,6 +872,24 @@ describe Guardian do
end 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 describe 'can_edit?' do
it 'returns false with a nil object' do it 'returns false with a nil object' do

View file

@ -1235,4 +1235,63 @@ describe TopicsController do
expect(response.headers['X-Robots-Tag']).to eq(nil) expect(response.headers['X-Robots-Tag']).to eq(nil)
end end
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 end

View 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