diff --git a/app/assets/javascripts/discourse/app/controllers/flag.js b/app/assets/javascripts/discourse/app/controllers/flag.js
index 671130e4822..8d2f6bbbe9d 100644
--- a/app/assets/javascripts/discourse/app/controllers/flag.js
+++ b/app/assets/javascripts/discourse/app/controllers/flag.js
@@ -1,7 +1,5 @@
import { schedule } from "@ember/runloop";
-import ActionSummary from "discourse/models/action-summary";
import Controller from "@ember/controller";
-import EmberObject from "@ember/object";
import I18n from "I18n";
import { MAX_MESSAGE_LENGTH } from "discourse/models/post-action-type";
import ModalFunctionality from "discourse/mixins/modal-functionality";
@@ -10,19 +8,18 @@ import User from "discourse/models/user";
import discourseComputed, { bind } from "discourse-common/utils/decorators";
import { not } from "@ember/object/computed";
import optionalService from "discourse/lib/optional-service";
-import { popupAjaxError } from "discourse/lib/ajax-error";
import { classify } from "@ember/string";
export default Controller.extend(ModalFunctionality, {
adminTools: optionalService(),
userDetails: null,
selected: null,
- flagTopic: null,
message: null,
isWarning: false,
topicActionByName: null,
spammerDetails: null,
flagActions: null,
+ flagTarget: null,
init() {
this._super(...arguments);
@@ -76,13 +73,14 @@ export default Controller.extend(ModalFunctionality, {
_penalize(adminToolMethod, performAction) {
if (this.adminTools) {
return User.findByUsername(this.model.username).then((createdBy) => {
- let postId = this.model.id;
- let postEdit = this.model.cooked;
- return this.adminTools[adminToolMethod](createdBy, {
- postId,
- postEdit,
- before: performAction,
- });
+ const opts = { before: performAction };
+
+ if (this.flagTarget.editable()) {
+ opts.postId = this.model.id;
+ opts.postEdit = this.model.cooked;
+ }
+
+ return this.adminTools[adminToolMethod](createdBy, opts);
});
}
},
@@ -115,47 +113,25 @@ export default Controller.extend(ModalFunctionality, {
return canDeleteSpammer && nameKey === "spam";
},
- @discourseComputed("flagTopic")
- title(flagTopic) {
- return flagTopic ? "flagging_topic.title" : "flagging.title";
+ @discourseComputed("flagTarget")
+ title(flagTarget) {
+ return flagTarget.title();
},
- @discourseComputed("post", "flagTopic", "model.actions_summary.@each.can_act")
+ @discourseComputed(
+ "post",
+ "flagTarget",
+ "model.actions_summary.@each.can_act"
+ )
flagsAvailable() {
- if (!this.flagTopic) {
- // flagging post
- let flagsAvailable = this.get("model.flagsAvailable");
-
- // "message user" option should be at the top
- const notifyUserIndex = flagsAvailable.indexOf(
- flagsAvailable.filterBy("name_key", "notify_user")[0]
- );
- if (notifyUserIndex !== -1) {
- const notifyUser = flagsAvailable[notifyUserIndex];
- flagsAvailable.splice(notifyUserIndex, 1);
- flagsAvailable.splice(0, 0, notifyUser);
- }
- return flagsAvailable;
- } else {
- // flagging topic
- let lookup = EmberObject.create();
- let model = this.model;
- model.get("actions_summary").forEach((a) => {
- a.flagTopic = model;
- a.actionType = this.site.topicFlagTypeById(a.id);
- lookup.set(a.actionType.get("name_key"), ActionSummary.create(a));
- });
- this.set("topicActionByName", lookup);
-
- return this.site.get("topic_flag_types").filter((item) => {
- return this.get("model.actions_summary").some((a) => {
- return a.id === item.get("id") && a.can_act;
- });
- });
- }
+ return this.flagTarget.flagsAvailable(this, this.site, this.model);
},
- @discourseComputed("post", "flagTopic", "model.actions_summary.@each.can_act")
+ @discourseComputed(
+ "post",
+ "flagTarget",
+ "model.actions_summary.@each.can_act"
+ )
staffFlagsAvailable() {
return (
this.get("model.flagsAvailable") &&
@@ -190,9 +166,13 @@ export default Controller.extend(ModalFunctionality, {
},
// Staff accounts can "take action"
- @discourseComputed("flagTopic", "selected.is_custom_flag")
- canTakeAction(flagTopic, isCustomFlag) {
- return !flagTopic && !isCustomFlag && this.currentUser.get("staff");
+ @discourseComputed("flagTarget", "selected.is_custom_flag")
+ canTakeAction(flagTarget, isCustomFlag) {
+ return (
+ !flagTarget.targetsTopic() &&
+ !isCustomFlag &&
+ this.currentUser.get("staff")
+ );
},
@discourseComputed("selected.is_custom_flag")
@@ -200,14 +180,13 @@ export default Controller.extend(ModalFunctionality, {
return isCustomFlag ? "envelope" : "flag";
},
- @discourseComputed("selected.is_custom_flag", "flagTopic")
- submitLabel(isCustomFlag, flagTopic) {
+ @discourseComputed("selected.is_custom_flag", "flagTarget")
+ submitLabel(isCustomFlag, flagTarget) {
if (isCustomFlag) {
- return flagTopic
- ? "flagging_topic.notify_action"
- : "flagging.notify_action";
+ return flagTarget.customSubmitLabel();
}
- return flagTopic ? "flagging_topic.action" : "flagging.action";
+
+ return flagTarget.submitLabel();
},
actions: {
@@ -243,59 +222,13 @@ export default Controller.extend(ModalFunctionality, {
},
createFlag(opts) {
- let postAction; // an instance of ActionSummary
+ const params = opts || {};
- if (!this.flagTopic) {
- postAction = this.get("model.actions_summary").findBy(
- "id",
- this.get("selected.id")
- );
- } else {
- postAction = this.get(
- "topicActionByName." + this.get("selected.name_key")
- );
+ if (this.get("selected.is_custom_flag")) {
+ params.message = this.message;
}
- let params = this.get("selected.is_custom_flag")
- ? { message: this.message }
- : {};
-
- if (opts) {
- params = Object.assign(params, opts);
- }
-
- this.appEvents.trigger(
- this.flagTopic ? "topic:flag-created" : "post:flag-created",
- this.model,
- postAction,
- params
- );
-
- this.send("hideModal");
-
- postAction
- .act(this.model, params)
- .then(() => {
- if (this.isDestroying || this.isDestroyed) {
- return;
- }
-
- if (!params.skipClose) {
- this.send("closeModal");
- }
- if (params.message) {
- this.set("message", "");
- }
- this.appEvents.trigger("post-stream:refresh", {
- id: this.get("model.id"),
- });
- })
- .catch((error) => {
- if (!this.isDestroying && !this.isDestroyed) {
- this.send("closeModal");
- }
- popupAjaxError(error);
- });
+ this.flagTarget.create(this, params);
},
createFlagAsWarning() {
@@ -304,7 +237,10 @@ export default Controller.extend(ModalFunctionality, {
},
flagForReview() {
- this.set("selected", this.get("notifyModeratorsFlag"));
+ if (!this.selected) {
+ this.set("selected", this.get("notifyModeratorsFlag"));
+ }
+
this.send("createFlag", { queue_for_review: true });
this.set("model.hidden", true);
},
@@ -314,10 +250,12 @@ export default Controller.extend(ModalFunctionality, {
},
},
- @discourseComputed("flagTopic", "selected.name_key")
- canSendWarning(flagTopic, nameKey) {
+ @discourseComputed("flagTarget", "selected.name_key")
+ canSendWarning(flagTarget, nameKey) {
return (
- !flagTopic && this.currentUser.get("staff") && nameKey === "notify_user"
+ !flagTarget.targetsTopic() &&
+ this.currentUser.get("staff") &&
+ nameKey === "notify_user"
);
},
});
diff --git a/app/assets/javascripts/discourse/app/lib/flag-targets/flag.js b/app/assets/javascripts/discourse/app/lib/flag-targets/flag.js
new file mode 100644
index 00000000000..b2affe83496
--- /dev/null
+++ b/app/assets/javascripts/discourse/app/lib/flag-targets/flag.js
@@ -0,0 +1,49 @@
+import { popupAjaxError } from "discourse/lib/ajax-error";
+
+export default class Flag {
+ targetsTopic() {
+ return false;
+ }
+
+ editable() {
+ return true;
+ }
+
+ create(controller, opts) {
+ // an instance of ActionSummary
+ let postAction = this.postActionFor(controller);
+
+ controller.appEvents.trigger(
+ this.flagCreatedEvent,
+ controller.model,
+ postAction,
+ opts
+ );
+
+ controller.send("hideModal");
+
+ postAction
+ .act(controller.model, opts)
+ .then(() => {
+ if (controller.isDestroying || controller.isDestroyed) {
+ return;
+ }
+
+ if (!opts.skipClose) {
+ controller.send("closeModal");
+ }
+ if (opts.message) {
+ controller.set("message", "");
+ }
+ controller.appEvents.trigger("post-stream:refresh", {
+ id: controller.get("model.id"),
+ });
+ })
+ .catch((error) => {
+ if (!controller.isDestroying && !controller.isDestroyed) {
+ controller.send("closeModal");
+ }
+ popupAjaxError(error);
+ });
+ }
+}
diff --git a/app/assets/javascripts/discourse/app/lib/flag-targets/post-flag.js b/app/assets/javascripts/discourse/app/lib/flag-targets/post-flag.js
new file mode 100644
index 00000000000..f30fd72ba27
--- /dev/null
+++ b/app/assets/javascripts/discourse/app/lib/flag-targets/post-flag.js
@@ -0,0 +1,42 @@
+import Flag from "discourse/lib/flag-targets/flag";
+
+export default class PostFlag extends Flag {
+ title() {
+ return "flagging.title";
+ }
+
+ customSubmitLabel() {
+ return "flagging.notify_action";
+ }
+
+ submitLabel() {
+ return "flagging.action";
+ }
+
+ flagCreatedEvent() {
+ return "post:flag-created";
+ }
+
+ flagsAvailable(_flagController, _site, model) {
+ let flagsAvailable = model.flagsAvailable;
+
+ // "message user" option should be at the top
+ const notifyUserIndex = flagsAvailable.indexOf(
+ flagsAvailable.filterBy("name_key", "notify_user")[0]
+ );
+
+ if (notifyUserIndex !== -1) {
+ const notifyUser = flagsAvailable[notifyUserIndex];
+ flagsAvailable.splice(notifyUserIndex, 1);
+ flagsAvailable.splice(0, 0, notifyUser);
+ }
+
+ return flagsAvailable;
+ }
+
+ postActionFor(controller) {
+ return controller
+ .get("model.actions_summary")
+ .findBy("id", controller.get("selected.id"));
+ }
+}
diff --git a/app/assets/javascripts/discourse/app/lib/flag-targets/topic-flag.js b/app/assets/javascripts/discourse/app/lib/flag-targets/topic-flag.js
new file mode 100644
index 00000000000..e8df5440fd9
--- /dev/null
+++ b/app/assets/javascripts/discourse/app/lib/flag-targets/topic-flag.js
@@ -0,0 +1,46 @@
+import ActionSummary from "discourse/models/action-summary";
+import EmberObject from "@ember/object";
+import Flag from "discourse/lib/flag-targets/flag";
+
+export default class TopicFlag extends Flag {
+ title() {
+ return "flagging_topic.title";
+ }
+
+ targetsTopic() {
+ return true;
+ }
+
+ customSubmitLabel() {
+ return "flagging_topic.notify_action";
+ }
+
+ submitLabel() {
+ return "flagging_topic.action";
+ }
+
+ flagCreatedEvent() {
+ return "topic:flag-created";
+ }
+
+ flagsAvailable(flagController, site, model) {
+ let lookup = EmberObject.create();
+
+ model.actions_summary.forEach((a) => {
+ a.flagTopic = model;
+ a.actionType = site.topicFlagTypeById(a.id);
+ lookup.set(a.actionType.name_key, ActionSummary.create(a));
+ });
+ flagController.set("topicActionByName", lookup);
+
+ return site.topic_flag_types.filter((item) => {
+ return model.actions_summary.some((a) => {
+ return a.id === item.id && a.can_act;
+ });
+ });
+ }
+
+ postActionFor(controller) {
+ return controller.get(`topicActionByName.${controller.selected.name_key}`);
+ }
+}
diff --git a/app/assets/javascripts/discourse/app/lib/transform-post.js b/app/assets/javascripts/discourse/app/lib/transform-post.js
index cd02dc7ca4a..ea7674d9258 100644
--- a/app/assets/javascripts/discourse/app/lib/transform-post.js
+++ b/app/assets/javascripts/discourse/app/lib/transform-post.js
@@ -57,7 +57,7 @@ export function transformBasicPost(post) {
showFlagDelete: false,
canRecover: post.can_recover,
canEdit: post.can_edit,
- canFlag: !isEmpty(post.get("flagsAvailable")),
+ canFlag: !post.get("topic.deleted") && !isEmpty(post.get("flagsAvailable")),
canReviewTopic: false,
reviewableId: post.reviewable_id,
reviewableScoreCount: post.reviewable_score_count,
diff --git a/app/assets/javascripts/discourse/app/routes/topic.js b/app/assets/javascripts/discourse/app/routes/topic.js
index 89e6e7a1f95..6764143a011 100644
--- a/app/assets/javascripts/discourse/app/routes/topic.js
+++ b/app/assets/javascripts/discourse/app/routes/topic.js
@@ -8,6 +8,8 @@ import { isEmpty } from "@ember/utils";
import { inject as service } from "@ember/service";
import { setTopicId } from "discourse/lib/topic-list-tracker";
import showModal from "discourse/lib/show-modal";
+import TopicFlag from "discourse/lib/flag-targets/topic-flag";
+import PostFlag from "discourse/lib/flag-targets/post-flag";
const SCROLL_DELAY = 500;
@@ -94,14 +96,14 @@ const TopicRoute = DiscourseRoute.extend({
@action
showFlags(model) {
let controller = showModal("flag", { model });
- controller.setProperties({ flagTopic: false });
+ controller.setProperties({ flagTarget: new PostFlag() });
},
@action
showFlagTopic() {
const model = this.modelFor("topic");
let controller = showModal("flag", { model });
- controller.setProperties({ flagTopic: true });
+ controller.setProperties({ flagTarget: new TopicFlag() });
},
@action
diff --git a/app/assets/javascripts/discourse/app/templates/modal/flag.hbs b/app/assets/javascripts/discourse/app/templates/modal/flag.hbs
index 863c4828954..a890ce311ee 100644
--- a/app/assets/javascripts/discourse/app/templates/modal/flag.hbs
+++ b/app/assets/javascripts/discourse/app/templates/modal/flag.hbs
@@ -18,7 +18,7 @@
{{#if this.canTakeAction}}
-
+
{{/if}}
{{#if this.showDeleteSpammer}}
diff --git a/app/assets/javascripts/select-kit/addon/components/select-kit.js b/app/assets/javascripts/select-kit/addon/components/select-kit.js
index 7d1b4003a38..11348538cd8 100644
--- a/app/assets/javascripts/select-kit/addon/components/select-kit.js
+++ b/app/assets/javascripts/select-kit/addon/components/select-kit.js
@@ -294,6 +294,7 @@ export default Component.extend(
mobilePlacementStrategy: null,
desktopPlacementStrategy: null,
hiddenValues: null,
+ disabled: false,
},
autoFilterable: computed("content.[]", "selectKit.filter", function () {
diff --git a/lib/guardian/post_guardian.rb b/lib/guardian/post_guardian.rb
index a4f9e536ecf..1e884d5e588 100644
--- a/lib/guardian/post_guardian.rb
+++ b/lib/guardian/post_guardian.rb
@@ -232,7 +232,7 @@ module PostGuardian
def can_delete_post_action?(post_action)
return false unless is_my_own?(post_action) && !post_action.is_private_message?
- post_action.created_at > SiteSetting.post_undo_action_window_mins.minutes.ago && !post_action.post.topic&.archived?
+ post_action.created_at > SiteSetting.post_undo_action_window_mins.minutes.ago && !post_action.post&.topic&.archived?
end
def can_see_post?(post)