diff --git a/app/assets/javascripts/discourse/app/components/modal/move-to-topic.hbs b/app/assets/javascripts/discourse/app/components/modal/move-to-topic.hbs
index db9922572e4..c06d7fe910c 100644
--- a/app/assets/javascripts/discourse/app/components/modal/move-to-topic.hbs
+++ b/app/assets/javascripts/discourse/app/components/modal/move-to-topic.hbs
@@ -131,6 +131,8 @@
{{/if}}
+
{{html-safe diff --git a/app/assets/javascripts/discourse/app/components/modal/move-to-topic.js b/app/assets/javascripts/discourse/app/components/modal/move-to-topic.js index 419048cbf7c..5cea8e3fd06 100644 --- a/app/assets/javascripts/discourse/app/components/modal/move-to-topic.js +++ b/app/assets/javascripts/discourse/app/components/modal/move-to-topic.js @@ -3,6 +3,7 @@ import { tracked } from "@glimmer/tracking"; import { action } from "@ember/object"; import { service } from "@ember/service"; import { isEmpty } from "@ember/utils"; +import { applyValueTransformer } from "discourse/lib/transformer"; import DiscourseURL from "discourse/lib/url"; import { mergeTopic, movePosts } from "discourse/models/topic"; import { i18n } from "discourse-i18n"; @@ -145,6 +146,15 @@ export default class MoveToTopic extends Component { }; } + mergeOptions = applyValueTransformer( + "move-to-topic-merge-options", + mergeOptions + ); + moveOptions = applyValueTransformer( + "move-to-topic-move-options", + moveOptions + ); + try { let result; if (this.args.model.selectedAllPosts) { diff --git a/app/assets/javascripts/discourse/app/lib/transformer/registry.js b/app/assets/javascripts/discourse/app/lib/transformer/registry.js index f4d1803fd60..376638004b4 100644 --- a/app/assets/javascripts/discourse/app/lib/transformer/registry.js +++ b/app/assets/javascripts/discourse/app/lib/transformer/registry.js @@ -13,6 +13,8 @@ export const VALUE_TRANSFORMERS = Object.freeze([ "invite-simple-mode-topic", "mentions-class", "more-topics-tabs", + "move-to-topic-merge-options", + "move-to-topic-move-options", "parent-category-row-class-mobile", "parent-category-row-class", "post-menu-buttons", diff --git a/app/assets/javascripts/discourse/tests/acceptance/modal/move-to-topic-test.js b/app/assets/javascripts/discourse/tests/acceptance/modal/move-to-topic-test.js new file mode 100644 index 00000000000..662aeac3c9b --- /dev/null +++ b/app/assets/javascripts/discourse/tests/acceptance/modal/move-to-topic-test.js @@ -0,0 +1,49 @@ +import { click, fillIn, visit } from "@ember/test-helpers"; +import { test } from "qunit"; +import { withPluginApi } from "discourse/lib/plugin-api"; +import pretender, { + parsePostData, + response, +} from "discourse/tests/helpers/create-pretender"; +import { acceptance } from "discourse/tests/helpers/qunit-helpers"; + +acceptance("Modal - move-to-topic", function (needs) { + needs.user({ admin: true }); + + test("Transformer can modify merge/move options sent in request", async function (assert) { + withPluginApi("1.24.0", (api) => { + ["move-to-topic-merge-options", "move-to-topic-move-options"].forEach( + (transformerName) => { + api.registerValueTransformer(transformerName, (transformer) => { + transformer.value.sillyVal = true; + return transformer.value; + }); + } + ); + }); + + await visit("/t/internationalization-localization/280"); + + // Open admin menu, select a post, and open move to topic modal + await click(".topic-admin-menu-trigger"); + await click(".topic-admin-menu-content .topic-admin-multi-select button"); + await click(".select-posts .select-post"); + await click(".selected-posts .move-to-topic"); + + // Choose existing topic, and pick the first topic. + await click("input#move-to-existing-topic"); + await fillIn("input#choose-topic-title", 1); + await click(".choose-topic-list .existing-topic input"); + + pretender.post("/t/280/move-posts", (request) => { + assert.step("request"); + const data = parsePostData(request.requestBody); + assert.strictEqual(data.sillyVal, "true"); + return response({ success: true }); + }); + + // Submit! + await click(".d-modal__footer .btn-primary"); + assert.verifySteps(["request"]); + }); +}); diff --git a/app/controllers/topics_controller.rb b/app/controllers/topics_controller.rb index 114be53d0a6..07de1538911 100644 --- a/app/controllers/topics_controller.rb +++ b/app/controllers/topics_controller.rb @@ -857,6 +857,7 @@ class TopicsController < ApplicationController params.permit(:participants) params.permit(:chronological_order) params.permit(:archetype) + params.permit(:freeze_original) raise Discourse::InvalidAccess if params[:archetype] == "private_message" && !guardian.is_staff? @@ -869,6 +870,7 @@ class TopicsController < ApplicationController args = {} args[:destination_topic_id] = destination_topic_id.to_i args[:chronological_order] = params[:chronological_order] == "true" + args[:freeze_original] = params[:freeze_original] == "true" if params[:archetype].present? args[:archetype] = params[:archetype] @@ -891,6 +893,7 @@ class TopicsController < ApplicationController params.permit(:participants) params.permit(:chronological_order) params.permit(:archetype) + params.permit(:freeze_original) topic = Topic.with_deleted.find_by(id: topic_id) guardian.ensure_can_move_posts!(topic) @@ -1399,6 +1402,7 @@ class TopicsController < ApplicationController ].present? args[:tags] = params[:tags] if params[:tags].present? args[:chronological_order] = params[:chronological_order] == "true" + args[:freeze_original] = true if params[:freeze_original] == "true" if params[:archetype].present? args[:archetype] = params[:archetype] diff --git a/app/models/topic.rb b/app/models/topic.rb index c15302d5d2f..40e22fb82a6 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -1298,6 +1298,9 @@ class Topic < ActiveRecord::Base moved_by, post_ids, move_to_pm: opts[:archetype].present? && opts[:archetype] == "private_message", + options: { + freeze_original: opts[:freeze_original], + }, ) if opts[:destination_topic_id] diff --git a/spec/requests/topics_controller_spec.rb b/spec/requests/topics_controller_spec.rb index 68d5ef2160d..04aaea156e8 100644 --- a/spec/requests/topics_controller_spec.rb +++ b/spec/requests/topics_controller_spec.rb @@ -166,6 +166,21 @@ RSpec.describe TopicsController do expect(Tag.all.pluck(:name)).to include("foo", "bar") end + describe "with freeze_original param" do + it "duplicates post to new topic and keeps original post in place" do + expect do + post "/t/#{topic.id}/move-posts.json", + params: { + title: "Logan is a good movie", + post_ids: [p2.id], + freeze_original: true, + } + end.to change { Topic.count }.by(1) + expect(response.status).to eq(200) + expect(topic.post_ids).to include(p2.id) + end + end + describe "when topic has been deleted" do it "should still be able to move posts" do PostDestroyer.new(admin, topic.first_post).destroy @@ -308,6 +323,22 @@ RSpec.describe TopicsController do expect(result["url"]).to be_present end + describe "with freeze_original param" do + it "duplicates post to topic and keeps original post in place" do + expect do + post "/t/#{topic.id}/move-posts.json", + params: { + post_ids: [p2.id], + destination_topic_id: dest_topic.id, + freeze_original: true, + } + end.to change { dest_topic.posts.count }.by(1) + expect(response.status).to eq(200) + expect(topic.post_ids).to include(p2.id) + expect(dest_topic.posts.find_by(raw: p2.raw)).to be_present + end + end + it "triggers an event on merge" do begin called = false