diff --git a/app/assets/javascripts/discourse/components/small-action.js.es6 b/app/assets/javascripts/discourse/components/small-action.js.es6 index 977b408c807..4ffa7ac63f2 100644 --- a/app/assets/javascripts/discourse/components/small-action.js.es6 +++ b/app/assets/javascripts/discourse/components/small-action.js.es6 @@ -10,7 +10,8 @@ const icons = { 'pinned_globally.enabled': 'thumb-tack', 'pinned_globally.disabled': 'thumb-tack unpinned', 'visible.enabled': 'eye', - 'visible.disabled': 'eye-slash' + 'visible.disabled': 'eye-slash', + 'split_topic': 'sign-out' }; export function actionDescription(actionCode, createdAt) { diff --git a/app/assets/javascripts/discourse/controllers/merge-topic.js.es6 b/app/assets/javascripts/discourse/controllers/merge-topic.js.es6 index c923aaf2dab..44f374ddbf8 100644 --- a/app/assets/javascripts/discourse/controllers/merge-topic.js.es6 +++ b/app/assets/javascripts/discourse/controllers/merge-topic.js.es6 @@ -1,12 +1,15 @@ import Presence from 'discourse/mixins/presence'; import SelectedPostsCount from 'discourse/mixins/selected-posts-count'; import ModalFunctionality from 'discourse/mixins/modal-functionality'; -import ObjectController from 'discourse/controllers/object'; +import { movePosts, mergeTopic } from 'discourse/models/topic'; // Modal related to merging of topics -export default ObjectController.extend(SelectedPostsCount, ModalFunctionality, Presence, { +export default Ember.Controller.extend(SelectedPostsCount, ModalFunctionality, Presence, { needs: ['topic'], + saving: false, + selectedTopicId: null, + topicController: Em.computed.alias('controllers.topic'), selectedPosts: Em.computed.alias('topicController.selectedPosts'), selectedReplies: Em.computed.alias('topicController.selectedReplies'), @@ -22,38 +25,40 @@ export default ObjectController.extend(SelectedPostsCount, ModalFunctionality, P return I18n.t('topic.merge_topic.title'); }.property('saving'), - onShow: function() { + onShow() { this.set('controllers.modal.modalClass', 'split-modal'); }, actions: { - movePostsToExistingTopic: function() { + movePostsToExistingTopic() { + const topicId = this.get('model.id'); + this.set('saving', true); - var promise = null; + let promise = null; if (this.get('allPostsSelected')) { - promise = Discourse.Topic.mergeTopic(this.get('id'), this.get('selectedTopicId')); + promise = mergeTopic(topicId, this.get('selectedTopicId')); } else { - var postIds = this.get('selectedPosts').map(function(p) { return p.get('id'); }), - replyPostIds = this.get('selectedReplies').map(function(p) { return p.get('id'); }); + const postIds = this.get('selectedPosts').map(function(p) { return p.get('id'); }); + const replyPostIds = this.get('selectedReplies').map(function(p) { return p.get('id'); }); - promise = Discourse.Topic.movePosts(this.get('id'), { + promise = movePosts(topicId, { destination_topic_id: this.get('selectedTopicId'), post_ids: postIds, reply_post_ids: replyPostIds }); } - var mergeTopicController = this; + const self = this; promise.then(function(result) { // Posts moved - mergeTopicController.send('closeModal'); - mergeTopicController.get('topicController').send('toggleMultiSelect'); + self.send('closeModal'); + self.get('topicController').send('toggleMultiSelect'); Em.run.next(function() { Discourse.URL.routeTo(result.url); }); - }, function() { - // Error moving posts - mergeTopicController.flash(I18n.t('topic.merge_topic.error')); - mergeTopicController.set('saving', false); + }).catch(function() { + self.flash(I18n.t('topic.merge_topic.error')); + }).finally(function() { + self.set('saving', false); }); return false; } diff --git a/app/assets/javascripts/discourse/controllers/split-topic.js.es6 b/app/assets/javascripts/discourse/controllers/split-topic.js.es6 index 98aed2dccea..0c75ae35037 100644 --- a/app/assets/javascripts/discourse/controllers/split-topic.js.es6 +++ b/app/assets/javascripts/discourse/controllers/split-topic.js.es6 @@ -1,15 +1,20 @@ import Presence from 'discourse/mixins/presence'; import SelectedPostsCount from 'discourse/mixins/selected-posts-count'; import ModalFunctionality from 'discourse/mixins/modal-functionality'; -import ObjectController from 'discourse/controllers/object'; +import { extractError } from 'discourse/lib/ajax-error'; +import { movePosts } from 'discourse/models/topic'; // Modal related to auto closing of topics -export default ObjectController.extend(SelectedPostsCount, ModalFunctionality, Presence, { +export default Ember.Controller.extend(SelectedPostsCount, ModalFunctionality, Presence, { needs: ['topic'], + topicName: null, + saving: false, + categoryId: null, 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() { if (this.get('saving')) return true; @@ -21,7 +26,7 @@ export default ObjectController.extend(SelectedPostsCount, ModalFunctionality, P return I18n.t('topic.split_topic.action'); }.property('saving'), - onShow: function() { + onShow() { this.setProperties({ 'controllers.modal.modalClass': 'split-modal', saving: false, @@ -31,39 +36,29 @@ export default ObjectController.extend(SelectedPostsCount, ModalFunctionality, P }, actions: { - movePostsToNewTopic: function() { + movePostsToNewTopic() { this.set('saving', true); - 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, - categoryId = this.get('categoryId'), - saveOpts = { - title: this.get('topicName'), - post_ids: postIds, - reply_post_ids: replyPostIds - }; + const postIds = this.get('selectedPosts').map(function(p) { return p.get('id'); }), + replyPostIds = this.get('selectedReplies').map(function(p) { return p.get('id'); }), + self = this, + categoryId = this.get('categoryId'), + saveOpts = { + title: this.get('topicName'), + post_ids: postIds, + reply_post_ids: replyPostIds + }; if (!Ember.isNone(categoryId)) { saveOpts.category_id = categoryId; } - Discourse.Topic.movePosts(this.get('id'), saveOpts).then(function(result) { + movePosts(this.get('model.id'), saveOpts).then(function(result) { // Posts moved self.send('closeModal'); self.get('topicController').send('toggleMultiSelect'); - Em.run.next(function() { Discourse.URL.routeTo(result.url); }); + Ember.run.next(function() { Discourse.URL.routeTo(result.url); }); }).catch(function(xhr) { - - var error = I18n.t('topic.split_topic.error'); - - if (xhr) { - var json = xhr.responseJSON; - if (json && json.errors) { - error = json.errors[0]; - } - } - - // Error moving posts - self.flash(error); + self.flash(extractError(xhr, I18n.t('topic.split_topic.error'))); + }).finally(function() { self.set('saving', false); }); return false; diff --git a/app/assets/javascripts/discourse/lib/ajax-error.js.es6 b/app/assets/javascripts/discourse/lib/ajax-error.js.es6 index 04d6d0dbc7e..bb2a915727f 100644 --- a/app/assets/javascripts/discourse/lib/ajax-error.js.es6 +++ b/app/assets/javascripts/discourse/lib/ajax-error.js.es6 @@ -1,4 +1,4 @@ -function extractError(error) { +export function extractError(error, defaultMessage) { if (error instanceof Error) { Ember.Logger.error(error.stack); } @@ -42,7 +42,7 @@ function extractError(error) { } } - return parsedError || I18n.t('generic_error'); + return parsedError || defaultMessage || I18n.t('generic_error'); } export function throwAjaxError(undoCallback) { diff --git a/app/assets/javascripts/discourse/models/topic.js.es6 b/app/assets/javascripts/discourse/models/topic.js.es6 index 0f06dc48471..24550874112 100644 --- a/app/assets/javascripts/discourse/models/topic.js.es6 +++ b/app/assets/javascripts/discourse/models/topic.js.es6 @@ -1,3 +1,4 @@ +import { flushMap } from 'discourse/models/store'; import RestModel from 'discourse/models/rest'; const Topic = RestModel.extend({ @@ -462,28 +463,6 @@ Topic.reopenClass({ return Discourse.ajax(url + ".json", {data: data}); }, - mergeTopic(topicId, destinationTopicId) { - const promise = Discourse.ajax("/t/" + topicId + "/merge-topic", { - type: 'POST', - data: {destination_topic_id: destinationTopicId} - }).then(function (result) { - if (result.success) return result; - promise.reject(new Error("error merging topic")); - }); - return promise; - }, - - movePosts(topicId, opts) { - const promise = Discourse.ajax("/t/" + topicId + "/move-posts", { - type: 'POST', - data: opts - }).then(function (result) { - if (result.success) return result; - promise.reject(new Error("error moving posts topic")); - }); - return promise; - }, - changeOwners(topicId, opts) { const promise = Discourse.ajax("/t/" + topicId + "/change-owner", { type: 'POST', @@ -523,4 +502,24 @@ Topic.reopenClass({ } }); +function moveResult(result) { + if (result.success) { + // We should be hesitant to flush the map but moving ids is one rare case + flushMap(); + return result; + } + throw "error moving posts topic"; +} + +export function movePosts(topicId, data) { + return Discourse.ajax("/t/" + topicId + "/move-posts", { type: 'POST', data }).then(moveResult); +} + +export function mergeTopic(topicId, destinationTopicId) { + return Discourse.ajax("/t/" + topicId + "/merge-topic", { + type: 'POST', + data: {destination_topic_id: destinationTopicId} + }).then(moveResult); +} + export default Topic; diff --git a/app/assets/javascripts/discourse/templates/modal/merge-topic.hbs b/app/assets/javascripts/discourse/templates/modal/merge-topic.hbs index a05ac14f22d..e4bf364fc2e 100644 --- a/app/assets/javascripts/discourse/templates/modal/merge-topic.hbs +++ b/app/assets/javascripts/discourse/templates/modal/merge-topic.hbs @@ -1,10 +1,4 @@ diff --git a/app/assets/javascripts/discourse/templates/modal/split_topic.hbs b/app/assets/javascripts/discourse/templates/modal/split-topic.hbs similarity index 59% rename from app/assets/javascripts/discourse/templates/modal/split_topic.hbs rename to app/assets/javascripts/discourse/templates/modal/split-topic.hbs index ff72aab89c5..fe80f88174a 100644 --- a/app/assets/javascripts/discourse/templates/modal/split_topic.hbs +++ b/app/assets/javascripts/discourse/templates/modal/split-topic.hbs @@ -1,10 +1,4 @@ diff --git a/app/assets/javascripts/discourse/views/split-topic.js.es6 b/app/assets/javascripts/discourse/views/split-topic.js.es6 index 5ee0d765548..9291495adc8 100644 --- a/app/assets/javascripts/discourse/views/split-topic.js.es6 +++ b/app/assets/javascripts/discourse/views/split-topic.js.es6 @@ -2,6 +2,6 @@ import SelectedPostsCount from 'discourse/mixins/selected-posts-count'; import ModalBodyView from "discourse/views/modal-body"; export default ModalBodyView.extend(SelectedPostsCount, { - templateName: 'modal/split_topic', + templateName: 'modal/split-topic', title: I18n.t('topic.split_topic.title') }); diff --git a/app/assets/javascripts/main_include.js b/app/assets/javascripts/main_include.js index 668234d4faa..8338d7921f7 100644 --- a/app/assets/javascripts/main_include.js +++ b/app/assets/javascripts/main_include.js @@ -28,6 +28,8 @@ //= require_tree ./discourse/adapters //= require ./discourse/models/rest //= require ./discourse/models/model +//= require ./discourse/models/result-set +//= require ./discourse/models/store //= require ./discourse/models/post-action-type //= require ./discourse/models/action-summary //= require ./discourse/models/post diff --git a/app/models/post_mover.rb b/app/models/post_mover.rb index 77111f6151a..272eb1c3f4f 100644 --- a/app/models/post_mover.rb +++ b/app/models/post_mover.rb @@ -123,11 +123,15 @@ class PostMover end def create_moderator_post_in_original_topic + move_type_str = PostMover.move_types[@move_type].to_s + original_topic.add_moderator_post( user, - I18n.t("move_posts.#{PostMover.move_types[@move_type]}_moderator_post", + I18n.t("move_posts.#{move_type_str}_moderator_post", count: post_ids.count, - topic_link: "[#{destination_topic.title}](#{destination_topic.url})"), + topic_link: "[#{destination_topic.title}](#{destination_topic.relative_url})"), + post_type: Post.types[:small_action], + action_code: "split_topic", post_number: @first_post_number_moved ) end diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index f6034fac9a4..ce2a7574a27 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -120,6 +120,7 @@ en: email: 'send this link in an email' action_codes: + split_topic: "split this topic" autoclosed: enabled: 'closed this topic %{when}' disabled: 'opened this topic %{when}' diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 685aa844739..a390ee8a556 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -1242,11 +1242,11 @@ en: move_posts: new_topic_moderator_post: - one: "I moved a post to a new topic: %{topic_link}" - other: "I moved %{count} posts to a new topic: %{topic_link}" + one: "A post was split to a new topic: %{topic_link}" + other: "%{count} posts were split to a new topic: %{topic_link}" existing_topic_moderator_post: - one: "I moved a post to an existing topic: %{topic_link}" - other: "I moved %{count} posts to an existing topic: %{topic_link}" + one: "A post was merged into an existing topic: %{topic_link}" + other: "%{count} posts were merged into an existing topic: %{topic_link}" change_owner: post_revision_text: "Ownership transferred from %{old_user} to %{new_user}"