mirror of
https://github.com/discourse/discourse.git
synced 2024-11-25 18:03:43 +08:00
DEV: move post flags into database (#27125)
This is preparation for a feature that will allow admins to define their custom flags. Current behaviour should stay untouched.
This commit is contained in:
parent
312a930ac8
commit
cfbbfd177c
|
@ -14,7 +14,9 @@ export default Component.extend({
|
||||||
|
|
||||||
@discourseComputed("flag.name_key")
|
@discourseComputed("flag.name_key")
|
||||||
customPlaceholder(nameKey) {
|
customPlaceholder(nameKey) {
|
||||||
return I18n.t("flagging.custom_placeholder_" + nameKey);
|
return I18n.t("flagging.custom_placeholder_" + nameKey, {
|
||||||
|
defaultValue: I18n.t("flagging.custom_placeholder_notify_moderators"),
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@discourseComputed("flag.name", "flag.name_key", "username")
|
@discourseComputed("flag.name", "flag.name_key", "username")
|
||||||
|
@ -22,7 +24,9 @@ export default Component.extend({
|
||||||
if (["notify_user", "notify_moderators"].includes(nameKey)) {
|
if (["notify_user", "notify_moderators"].includes(nameKey)) {
|
||||||
return name.replace(/{{username}}|%{username}/, username);
|
return name.replace(/{{username}}|%{username}/, username);
|
||||||
} else {
|
} else {
|
||||||
return I18n.t("flagging.formatted_name." + nameKey);
|
return I18n.t("flagging.formatted_name." + nameKey, {
|
||||||
|
defaultValue: name,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -254,7 +254,11 @@ export default function transformPost(
|
||||||
postId: post.id,
|
postId: post.id,
|
||||||
action,
|
action,
|
||||||
canUndo: a.can_undo,
|
canUndo: a.can_undo,
|
||||||
description: I18n.t(`post.actions.by_you.${action}`),
|
description: I18n.t(`post.actions.by_you.${action}`, {
|
||||||
|
defaultValue: I18n.t(`post.actions.by_you.custom`, {
|
||||||
|
custom: a.actionType.name,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
65
app/models/flag.rb
Normal file
65
app/models/flag.rb
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Flag < ActiveRecord::Base
|
||||||
|
MAX_SYSTEM_FLAG_ID = 1000
|
||||||
|
scope :enabled, -> { where(enabled: true) }
|
||||||
|
scope :system, -> { where("id < 1000") }
|
||||||
|
|
||||||
|
before_save :set_position
|
||||||
|
before_save :set_name_key
|
||||||
|
after_save :reset_flag_settings!
|
||||||
|
after_destroy :reset_flag_settings!
|
||||||
|
|
||||||
|
default_scope { order(:position) }
|
||||||
|
|
||||||
|
def used?
|
||||||
|
PostAction.exists?(post_action_type_id: self.id) ||
|
||||||
|
ReviewableScore.exists?(reviewable_score_type: self.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.reset_flag_settings!
|
||||||
|
# Flags are memoized for better performance. After the update, we need to reload them in all processes.
|
||||||
|
PostActionType.reload_types
|
||||||
|
DiscourseEvent.trigger(:reload_post_action_types)
|
||||||
|
end
|
||||||
|
|
||||||
|
def system?
|
||||||
|
self.id < MAX_SYSTEM_FLAG_ID
|
||||||
|
end
|
||||||
|
|
||||||
|
def applies_to?(type)
|
||||||
|
self.applies_to.include?(type)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def reset_flag_settings!
|
||||||
|
self.class.reset_flag_settings!
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_position
|
||||||
|
self.position = Flag.maximum(:position).to_i + 1 if !self.position
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_name_key
|
||||||
|
self.name_key = self.name.squeeze(" ").gsub(" ", "_").gsub(/[^\w]/, "").downcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: flags
|
||||||
|
#
|
||||||
|
# id :bigint not null, primary key
|
||||||
|
# name :string
|
||||||
|
# name_key :string
|
||||||
|
# description :text
|
||||||
|
# notify_type :boolean default(FALSE), not null
|
||||||
|
# auto_action_type :boolean default(FALSE), not null
|
||||||
|
# custom_type :boolean default(FALSE), not null
|
||||||
|
# applies_to :string not null, is an Array
|
||||||
|
# position :integer not null
|
||||||
|
# enabled :boolean default(TRUE), not null
|
||||||
|
# created_at :datetime not null
|
||||||
|
# updated_at :datetime not null
|
||||||
|
#
|
|
@ -11,39 +11,47 @@ class PostActionType < ActiveRecord::Base
|
||||||
ApplicationSerializer.expire_cache_fragment!(/\Apost_action_flag_types_/)
|
ApplicationSerializer.expire_cache_fragment!(/\Apost_action_flag_types_/)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
DiscourseEvent.on(:reload_post_action_types) { self.reload_types }
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
attr_reader :flag_settings
|
attr_reader :flag_settings
|
||||||
|
|
||||||
def replace_flag_settings(settings)
|
def initialize_flag_settings
|
||||||
if settings
|
@flag_settings = FlagSettings.new
|
||||||
@flag_settings = settings
|
|
||||||
else
|
|
||||||
initialize_flag_settings
|
|
||||||
end
|
|
||||||
@types = nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def ordered
|
def replace_flag_settings(settings)
|
||||||
order("position asc")
|
Discourse.deprecate("Flags should not be replaced. Insert custom flags as database records.")
|
||||||
|
@flag_settings = settings || FlagSettings.new
|
||||||
|
@all_flags = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def types
|
def types
|
||||||
unless @types
|
return Enum.new(like: 2).merge!(flag_settings.flag_types) if overridden_by_plugin?
|
||||||
# NOTE: Previously bookmark was type 1 but that has been superseded
|
Enum.new(like: 2).merge(flag_types)
|
||||||
# by the separate Bookmark model and functionality
|
end
|
||||||
@types = Enum.new(like: 2)
|
|
||||||
@types.merge!(flag_settings.flag_types)
|
|
||||||
end
|
|
||||||
|
|
||||||
@types
|
def reload_types
|
||||||
|
@all_flags = nil
|
||||||
|
@flag_settings = FlagSettings.new
|
||||||
|
ReviewableScore.reload_types
|
||||||
|
end
|
||||||
|
|
||||||
|
def overridden_by_plugin?
|
||||||
|
flag_settings.flag_types.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def all_flags
|
||||||
|
@all_flags ||= Flag.all
|
||||||
end
|
end
|
||||||
|
|
||||||
def auto_action_flag_types
|
def auto_action_flag_types
|
||||||
flag_settings.auto_action_types
|
return flag_settings.auto_action_types if overridden_by_plugin?
|
||||||
|
flag_enum(all_flags.select(&:auto_action_type))
|
||||||
end
|
end
|
||||||
|
|
||||||
def public_types
|
def public_types
|
||||||
@public_types ||= types.except(*flag_types.keys << :notify_user)
|
types.except(*flag_types.keys << :notify_user)
|
||||||
end
|
end
|
||||||
|
|
||||||
def public_type_ids
|
def public_type_ids
|
||||||
|
@ -51,11 +59,13 @@ class PostActionType < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def flag_types_without_custom
|
def flag_types_without_custom
|
||||||
flag_settings.without_custom_types
|
return flag_settings.without_custom_types if overridden_by_plugin?
|
||||||
|
flag_enum(all_flags.reject(&:custom_type))
|
||||||
end
|
end
|
||||||
|
|
||||||
def flag_types
|
def flag_types
|
||||||
flag_settings.flag_types
|
return flag_settings.flag_types if overridden_by_plugin?
|
||||||
|
flag_enum(all_flags)
|
||||||
end
|
end
|
||||||
|
|
||||||
# flags resulting in mod notifications
|
# flags resulting in mod notifications
|
||||||
|
@ -64,15 +74,25 @@ class PostActionType < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def notify_flag_types
|
def notify_flag_types
|
||||||
flag_settings.notify_types
|
return flag_settings.notify_types if overridden_by_plugin?
|
||||||
|
flag_enum(all_flags.select(&:notify_type))
|
||||||
end
|
end
|
||||||
|
|
||||||
def topic_flag_types
|
def topic_flag_types
|
||||||
flag_settings.topic_flag_types
|
if overridden_by_plugin?
|
||||||
|
flag_settings.topic_flag_types
|
||||||
|
else
|
||||||
|
flag_enum(all_flags.select { |flag| flag.applies_to?("Topic") })
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def custom_types
|
def custom_types
|
||||||
flag_settings.custom_types
|
return flag_settings.custom_types if overridden_by_plugin?
|
||||||
|
flag_enum(all_flags.select(&:custom_type))
|
||||||
|
end
|
||||||
|
|
||||||
|
def names
|
||||||
|
all_flags.pluck(:id, :name).to_h
|
||||||
end
|
end
|
||||||
|
|
||||||
def is_flag?(sym)
|
def is_flag?(sym)
|
||||||
|
@ -81,28 +101,8 @@ class PostActionType < ActiveRecord::Base
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def initialize_flag_settings
|
def flag_enum(scope)
|
||||||
@flag_settings = FlagSettings.new
|
Enum.new(scope.map { |flag| [flag.name_key.to_sym, flag.id] }.to_h)
|
||||||
@flag_settings.add(3, :off_topic, notify_type: true, auto_action_type: true)
|
|
||||||
@flag_settings.add(
|
|
||||||
4,
|
|
||||||
:inappropriate,
|
|
||||||
topic_type: true,
|
|
||||||
notify_type: true,
|
|
||||||
auto_action_type: true,
|
|
||||||
)
|
|
||||||
@flag_settings.add(8, :spam, topic_type: true, notify_type: true, auto_action_type: true)
|
|
||||||
@flag_settings.add(6, :notify_user, topic_type: false, notify_type: false, custom_type: true)
|
|
||||||
@flag_settings.add(
|
|
||||||
7,
|
|
||||||
:notify_moderators,
|
|
||||||
topic_type: true,
|
|
||||||
notify_type: true,
|
|
||||||
custom_type: true,
|
|
||||||
)
|
|
||||||
@flag_settings.add(10, :illegal, topic_type: true, notify_type: true, custom_type: true)
|
|
||||||
# When adding a new ID here, check that it doesn't clash with any added in
|
|
||||||
# `ReviewableScore.types`. You can thank me later.
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -14,25 +14,25 @@ class PostActionTypeSerializer < ApplicationSerializer
|
||||||
end
|
end
|
||||||
|
|
||||||
def name
|
def name
|
||||||
i18n("title")
|
i18n("title", default: object.class.names[object.id])
|
||||||
end
|
end
|
||||||
|
|
||||||
def description
|
def description
|
||||||
i18n("description", tos_url: tos_url, base_path: Discourse.base_path)
|
i18n("description", vars: { tos_url:, base_path: Discourse.base_path })
|
||||||
end
|
end
|
||||||
|
|
||||||
def short_description
|
def short_description
|
||||||
i18n("short_description", tos_url: tos_url, base_path: Discourse.base_path)
|
i18n("short_description", vars: { tos_url: tos_url, base_path: Discourse.base_path })
|
||||||
end
|
end
|
||||||
|
|
||||||
def name_key
|
def name_key
|
||||||
PostActionType.types[object.id]
|
PostActionType.types[object.id].to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def i18n(field, vars = nil)
|
def i18n(field, default: nil, vars: nil)
|
||||||
key = "post_action_types.#{name_key}.#{field}"
|
key = "post_action_types.#{name_key}.#{field}"
|
||||||
vars ? I18n.t(key, vars) : I18n.t(key)
|
vars ? I18n.t(key, vars, default: default) : I18n.t(key, default: default)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,8 +9,9 @@ class ReviewableScoreTypeSerializer < ApplicationSerializer
|
||||||
|
|
||||||
# Allow us to share post action type translations for backwards compatibility
|
# Allow us to share post action type translations for backwards compatibility
|
||||||
def title
|
def title
|
||||||
I18n.t("post_action_types.#{ReviewableScore.types[id]}.title", default: nil) ||
|
I18n.t("post_action_types.#{type}.title", default: nil) ||
|
||||||
I18n.t("reviewable_score_types.#{ReviewableScore.types[id]}.title")
|
I18n.t("reviewable_score_types.#{type}.title", default: nil) ||
|
||||||
|
PostActionType.flag_settings.names[id]
|
||||||
end
|
end
|
||||||
|
|
||||||
def reviewable_priority
|
def reviewable_priority
|
||||||
|
|
|
@ -350,13 +350,6 @@ class SiteSerializer < ApplicationSerializer
|
||||||
private
|
private
|
||||||
|
|
||||||
def ordered_flags(flags)
|
def ordered_flags(flags)
|
||||||
notify_moderators_type = PostActionType.flag_types[:notify_moderators]
|
flags.map { |id| PostActionType.new(id: id) }
|
||||||
types = flags
|
|
||||||
|
|
||||||
if notify_moderators_flag = types.index(notify_moderators_type)
|
|
||||||
types.insert(types.length, types.delete_at(notify_moderators_flag))
|
|
||||||
end
|
|
||||||
|
|
||||||
types.map { |id| PostActionType.new(id: id) }
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
class TopicFlagTypeSerializer < PostActionTypeSerializer
|
class TopicFlagTypeSerializer < PostActionTypeSerializer
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def i18n(field, vars = nil)
|
def i18n(field, default: nil, vars: nil)
|
||||||
key = "topic_flag_types.#{name_key}.#{field}"
|
key = "topic_flag_types.#{name_key}.#{field}"
|
||||||
vars ? I18n.t(key, vars) : I18n.t(key)
|
vars ? I18n.t(key, vars, default: default) : I18n.t(key, default: default)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3771,6 +3771,7 @@ en:
|
||||||
illegal: "You flagged this as illegal"
|
illegal: "You flagged this as illegal"
|
||||||
notify_moderators: "You flagged this for moderation"
|
notify_moderators: "You flagged this for moderation"
|
||||||
notify_user: "You sent a message to this user"
|
notify_user: "You sent a message to this user"
|
||||||
|
custom: "You flagged this topic as %{custom}"
|
||||||
|
|
||||||
delete:
|
delete:
|
||||||
confirm:
|
confirm:
|
||||||
|
|
56
db/fixtures/003_flags.rb
Normal file
56
db/fixtures/003_flags.rb
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
Flag.seed do |s|
|
||||||
|
s.id = 3
|
||||||
|
s.name = "off_topic"
|
||||||
|
s.position = 2
|
||||||
|
s.notify_type = true
|
||||||
|
s.auto_action_type = true
|
||||||
|
s.custom_type = false
|
||||||
|
s.applies_to = %w[Post Chat::Message]
|
||||||
|
end
|
||||||
|
Flag.seed do |s|
|
||||||
|
s.id = 4
|
||||||
|
s.name = "inappropriate"
|
||||||
|
s.position = 3
|
||||||
|
s.notify_type = true
|
||||||
|
s.auto_action_type = true
|
||||||
|
s.custom_type = false
|
||||||
|
s.applies_to = %w[Post Topic Chat::Message]
|
||||||
|
end
|
||||||
|
Flag.seed do |s|
|
||||||
|
s.id = 8
|
||||||
|
s.name = "spam"
|
||||||
|
s.position = 4
|
||||||
|
s.notify_type = true
|
||||||
|
s.auto_action_type = true
|
||||||
|
s.custom_type = false
|
||||||
|
s.applies_to = %w[Post Topic Chat::Message]
|
||||||
|
end
|
||||||
|
Flag.seed do |s|
|
||||||
|
s.id = 6
|
||||||
|
s.name = "notify_user"
|
||||||
|
s.position = 0
|
||||||
|
s.notify_type = false
|
||||||
|
s.auto_action_type = false
|
||||||
|
s.custom_type = true
|
||||||
|
s.applies_to = %w[Post Chat::Message]
|
||||||
|
end
|
||||||
|
Flag.seed do |s|
|
||||||
|
s.id = 7
|
||||||
|
s.name = "notify_moderators"
|
||||||
|
s.position = 1
|
||||||
|
s.notify_type = true
|
||||||
|
s.auto_action_type = false
|
||||||
|
s.custom_type = true
|
||||||
|
s.applies_to = %w[Post Topic Chat::Message]
|
||||||
|
end
|
||||||
|
Flag.seed do |s|
|
||||||
|
s.id = 10
|
||||||
|
s.name = "illegal"
|
||||||
|
s.position = 5
|
||||||
|
s.notify_type = true
|
||||||
|
s.auto_action_type = false
|
||||||
|
s.custom_type = true
|
||||||
|
s.applies_to = %w[Post Topic Chat::Message]
|
||||||
|
end
|
|
@ -18,7 +18,7 @@ class AddFlagStatsToUser < ActiveRecord::Migration[5.2]
|
||||||
SUM(CASE WHEN pa.deferred_at IS NOT NULL THEN 1 ELSE 0 END) as flags_ignored
|
SUM(CASE WHEN pa.deferred_at IS NOT NULL THEN 1 ELSE 0 END) as flags_ignored
|
||||||
FROM post_actions AS pa
|
FROM post_actions AS pa
|
||||||
INNER JOIN users AS u ON u.id = pa.user_id
|
INNER JOIN users AS u ON u.id = pa.user_id
|
||||||
WHERE pa.post_action_type_id IN (#{PostActionType.notify_flag_types.values.join(", ")})
|
WHERE pa.post_action_type_id IN (3, 4, 8, 7, 10)
|
||||||
AND pa.user_id > 0
|
AND pa.user_id > 0
|
||||||
GROUP BY u.id
|
GROUP BY u.id
|
||||||
) AS x
|
) AS x
|
||||||
|
|
20
db/migrate/20240423054323_create_flags.rb
Normal file
20
db/migrate/20240423054323_create_flags.rb
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class CreateFlags < ActiveRecord::Migration[7.0]
|
||||||
|
def change
|
||||||
|
create_table :flags do |t|
|
||||||
|
t.string :name, unique: true
|
||||||
|
t.string :name_key, unique: true
|
||||||
|
t.text :description
|
||||||
|
t.boolean :notify_type, default: false, null: false
|
||||||
|
t.boolean :auto_action_type, default: false, null: false
|
||||||
|
t.boolean :custom_type, default: false, null: false
|
||||||
|
t.string :applies_to, array: true, null: false
|
||||||
|
t.integer :position, null: false
|
||||||
|
t.boolean :enabled, default: true, null: false
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
# IDs below 1000 are reserved for system flags
|
||||||
|
DB.exec("SELECT setval('flags_id_seq', #{Flag::MAX_SYSTEM_FLAG_ID + 1}, FALSE);")
|
||||||
|
end
|
||||||
|
end
|
|
@ -7,6 +7,7 @@ class FlagSettings
|
||||||
:topic_flag_types,
|
:topic_flag_types,
|
||||||
:auto_action_types,
|
:auto_action_types,
|
||||||
:custom_types,
|
:custom_types,
|
||||||
|
:names,
|
||||||
)
|
)
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
|
@ -16,18 +17,28 @@ class FlagSettings
|
||||||
@auto_action_types = Enum.new
|
@auto_action_types = Enum.new
|
||||||
@custom_types = Enum.new
|
@custom_types = Enum.new
|
||||||
@without_custom_types = Enum.new
|
@without_custom_types = Enum.new
|
||||||
|
@names = Enum.new
|
||||||
end
|
end
|
||||||
|
|
||||||
def add(id, name, topic_type: nil, notify_type: nil, auto_action_type: nil, custom_type: nil)
|
def add(
|
||||||
@all_flag_types[name] = id
|
id,
|
||||||
@topic_flag_types[name] = id if !!topic_type
|
name_key,
|
||||||
@notify_types[name] = id if !!notify_type
|
topic_type: nil,
|
||||||
@auto_action_types[name] = id if !!auto_action_type
|
notify_type: nil,
|
||||||
|
auto_action_type: nil,
|
||||||
|
custom_type: nil,
|
||||||
|
name: nil
|
||||||
|
)
|
||||||
|
@all_flag_types[name_key] = id
|
||||||
|
@topic_flag_types[name_key] = id if !!topic_type
|
||||||
|
@notify_types[name_key] = id if !!notify_type
|
||||||
|
@auto_action_types[name_key] = id if !!auto_action_type
|
||||||
|
@names[id] = name if name
|
||||||
|
|
||||||
if !!custom_type
|
if !!custom_type
|
||||||
@custom_types[name] = id
|
@custom_types[name_key] = id
|
||||||
else
|
else
|
||||||
@without_custom_types[name] = id
|
@without_custom_types[name_key] = id
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ require "guardian/bookmark_guardian"
|
||||||
require "guardian/category_guardian"
|
require "guardian/category_guardian"
|
||||||
require "guardian/ensure_magic"
|
require "guardian/ensure_magic"
|
||||||
require "guardian/group_guardian"
|
require "guardian/group_guardian"
|
||||||
|
require "guardian/flag_guardian"
|
||||||
require "guardian/post_guardian"
|
require "guardian/post_guardian"
|
||||||
require "guardian/post_revision_guardian"
|
require "guardian/post_revision_guardian"
|
||||||
require "guardian/sidebar_guardian"
|
require "guardian/sidebar_guardian"
|
||||||
|
@ -16,6 +17,7 @@ class Guardian
|
||||||
include BookmarkGuardian
|
include BookmarkGuardian
|
||||||
include CategoryGuardian
|
include CategoryGuardian
|
||||||
include EnsureMagic
|
include EnsureMagic
|
||||||
|
include FlagGuardian
|
||||||
include GroupGuardian
|
include GroupGuardian
|
||||||
include PostGuardian
|
include PostGuardian
|
||||||
include PostRevisionGuardian
|
include PostRevisionGuardian
|
||||||
|
|
7
lib/guardian/flag_guardian.rb
Normal file
7
lib/guardian/flag_guardian.rb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module FlagGuardian
|
||||||
|
def can_edit_flag?(flag)
|
||||||
|
@user.admin? && !flag.system? && !flag.used?
|
||||||
|
end
|
||||||
|
end
|
|
@ -197,6 +197,9 @@ class Plugin::Instance
|
||||||
|
|
||||||
# Applies to all sites in a multisite environment. Ignores plugin.enabled?
|
# Applies to all sites in a multisite environment. Ignores plugin.enabled?
|
||||||
def replace_flags(settings: ::FlagSettings.new, score_type_names: [])
|
def replace_flags(settings: ::FlagSettings.new, score_type_names: [])
|
||||||
|
Discourse.deprecate(
|
||||||
|
"replace flags should not be used as flags were moved to the database. Instead, a flag record should be added to the database. Alternatively, soon, the admin will be able to do this in the admin panel.",
|
||||||
|
)
|
||||||
next_flag_id = ReviewableScore.types.values.max + 1
|
next_flag_id = ReviewableScore.types.values.max + 1
|
||||||
|
|
||||||
yield(settings, next_flag_id) if block_given?
|
yield(settings, next_flag_id) if block_given?
|
||||||
|
|
|
@ -35,7 +35,10 @@ export default class ChatMessageFlag {
|
||||||
return flags.map((flag) => {
|
return flags.map((flag) => {
|
||||||
flag.set(
|
flag.set(
|
||||||
"description",
|
"description",
|
||||||
I18n.t(`chat.flags.${flag.name_key}`, { basePath: getURL("") })
|
I18n.t(`chat.flags.${flag.name_key}`, {
|
||||||
|
basePath: getURL(""),
|
||||||
|
defaultValue: "",
|
||||||
|
})
|
||||||
);
|
);
|
||||||
return flag;
|
return flag;
|
||||||
});
|
});
|
||||||
|
|
|
@ -22,7 +22,7 @@ RSpec.describe Chat::FlagMessage do
|
||||||
let(:guardian) { Guardian.new(current_user) }
|
let(:guardian) { Guardian.new(current_user) }
|
||||||
let(:channel_id) { channel_1.id }
|
let(:channel_id) { channel_1.id }
|
||||||
let(:message_id) { message_1.id }
|
let(:message_id) { message_1.id }
|
||||||
let(:flag_type_id) { ReviewableScore.types.values.first }
|
let(:flag_type_id) { ReviewableScore.types[:off_topic] }
|
||||||
let(:message) { nil }
|
let(:message) { nil }
|
||||||
let(:is_warning) { nil }
|
let(:is_warning) { nil }
|
||||||
let(:take_action) { nil }
|
let(:take_action) { nil }
|
||||||
|
|
|
@ -1032,7 +1032,7 @@ RSpec.describe DiscourseNarrativeBot::NewUserNarrative do
|
||||||
let(:another_post) { Fabricate(:post, user: discobot_user, topic: topic) }
|
let(:another_post) { Fabricate(:post, user: discobot_user, topic: topic) }
|
||||||
let(:flag) do
|
let(:flag) do
|
||||||
Fabricate(
|
Fabricate(
|
||||||
:flag,
|
:flag_post_action,
|
||||||
post: post,
|
post: post,
|
||||||
user: user,
|
user: user,
|
||||||
post_action_type_id: PostActionType.types[:inappropriate],
|
post_action_type_id: PostActionType.types[:inappropriate],
|
||||||
|
@ -1040,7 +1040,7 @@ RSpec.describe DiscourseNarrativeBot::NewUserNarrative do
|
||||||
end
|
end
|
||||||
let(:other_flag) do
|
let(:other_flag) do
|
||||||
Fabricate(
|
Fabricate(
|
||||||
:flag,
|
:flag_post_action,
|
||||||
post: another_post,
|
post: another_post,
|
||||||
user: user,
|
user: user,
|
||||||
post_action_type_id: PostActionType.types[:spam],
|
post_action_type_id: PostActionType.types[:spam],
|
||||||
|
@ -1166,7 +1166,7 @@ RSpec.describe DiscourseNarrativeBot::NewUserNarrative do
|
||||||
|
|
||||||
describe "when post contain the right answer" do
|
describe "when post contain the right answer" do
|
||||||
let(:post) { Fabricate(:post, user: discobot_user, topic: topic) }
|
let(:post) { Fabricate(:post, user: discobot_user, topic: topic) }
|
||||||
let(:flag) { Fabricate(:flag, post: post, user: user) }
|
let(:flag) { Fabricate(:flag_post_action, post: post, user: user) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
narrative.set_data(user, state: :tutorial_flag, topic_id: topic.id)
|
narrative.set_data(user, state: :tutorial_flag, topic_id: topic.id)
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
Fabricator(:flag, from: :post_action) do
|
Fabricator(:flag) { name "offtopic", applies_to { %w[Post Chat::Message] } }
|
||||||
user
|
|
||||||
post
|
|
||||||
post_action_type_id PostActionType.types[:spam]
|
|
||||||
end
|
|
||||||
|
|
7
spec/fabricators/flag_post_action_fabricator.rb
Normal file
7
spec/fabricators/flag_post_action_fabricator.rb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
Fabricator(:flag_post_action, from: :post_action) do
|
||||||
|
user
|
||||||
|
post
|
||||||
|
post_action_type_id PostActionType.types[:spam]
|
||||||
|
end
|
|
@ -360,7 +360,7 @@ RSpec.describe ComposerMessagesFinder do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "shows a message when the replier has already flagged the post" do
|
it "shows a message when the replier has already flagged the post" do
|
||||||
Fabricate(:flag, post: self_flagged_post, user: user)
|
Fabricate(:flag_post_action, post: self_flagged_post, user: user)
|
||||||
finder =
|
finder =
|
||||||
ComposerMessagesFinder.new(
|
ComposerMessagesFinder.new(
|
||||||
user,
|
user,
|
||||||
|
@ -372,13 +372,13 @@ RSpec.describe ComposerMessagesFinder do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "shows a message when replying to flagged topic (first post)" do
|
it "shows a message when replying to flagged topic (first post)" do
|
||||||
Fabricate(:flag, post: original_post, user: user)
|
Fabricate(:flag_post_action, post: original_post, user: user)
|
||||||
finder = ComposerMessagesFinder.new(user, composer_action: "reply", topic_id: topic.id)
|
finder = ComposerMessagesFinder.new(user, composer_action: "reply", topic_id: topic.id)
|
||||||
expect(finder.check_dont_feed_the_trolls).to be_present
|
expect(finder.check_dont_feed_the_trolls).to be_present
|
||||||
end
|
end
|
||||||
|
|
||||||
it "does not show a message when not enough others have flagged the post" do
|
it "does not show a message when not enough others have flagged the post" do
|
||||||
Fabricate(:flag, post: under_flagged_post, user: other_user)
|
Fabricate(:flag_post_action, post: under_flagged_post, user: other_user)
|
||||||
finder =
|
finder =
|
||||||
ComposerMessagesFinder.new(
|
ComposerMessagesFinder.new(
|
||||||
user,
|
user,
|
||||||
|
@ -392,7 +392,12 @@ RSpec.describe ComposerMessagesFinder do
|
||||||
it "does not show a message when the flag has already been resolved" do
|
it "does not show a message when the flag has already been resolved" do
|
||||||
SiteSetting.dont_feed_the_trolls_threshold = 1
|
SiteSetting.dont_feed_the_trolls_threshold = 1
|
||||||
|
|
||||||
Fabricate(:flag, post: resolved_flag_post, user: other_user, disagreed_at: 1.hour.ago)
|
Fabricate(
|
||||||
|
:flag_post_action,
|
||||||
|
post: resolved_flag_post,
|
||||||
|
user: other_user,
|
||||||
|
disagreed_at: 1.hour.ago,
|
||||||
|
)
|
||||||
finder =
|
finder =
|
||||||
ComposerMessagesFinder.new(
|
ComposerMessagesFinder.new(
|
||||||
user,
|
user,
|
||||||
|
@ -404,8 +409,8 @@ RSpec.describe ComposerMessagesFinder do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "shows a message when enough others have already flagged the post" do
|
it "shows a message when enough others have already flagged the post" do
|
||||||
Fabricate(:flag, post: over_flagged_post, user: other_user)
|
Fabricate(:flag_post_action, post: over_flagged_post, user: other_user)
|
||||||
Fabricate(:flag, post: over_flagged_post, user: third_user)
|
Fabricate(:flag_post_action, post: over_flagged_post, user: third_user)
|
||||||
finder =
|
finder =
|
||||||
ComposerMessagesFinder.new(
|
ComposerMessagesFinder.new(
|
||||||
user,
|
user,
|
||||||
|
|
28
spec/lib/guardian/flag_guardian_spec.rb
Normal file
28
spec/lib/guardian/flag_guardian_spec.rb
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
RSpec.describe FlagGuardian do
|
||||||
|
fab!(:user)
|
||||||
|
fab!(:admin)
|
||||||
|
fab!(:flag)
|
||||||
|
|
||||||
|
describe "#can_edit_flag?" do
|
||||||
|
it "returns true for admin and false for regular user" do
|
||||||
|
expect(Guardian.new(admin).can_edit_flag?(flag)).to eq(true)
|
||||||
|
expect(Guardian.new(user).can_edit_flag?(flag)).to eq(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns false when flag is system" do
|
||||||
|
expect(Guardian.new(admin).can_edit_flag?(Flag.system.first)).to eq(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns false when flag was already used with post action" do
|
||||||
|
Fabricate(:post_action, post_action_type_id: flag.id)
|
||||||
|
expect(Guardian.new(admin).can_edit_flag?(flag)).to eq(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns false when flag was already used with reviewable" do
|
||||||
|
Fabricate(:reviewable_score, reviewable_score_type: flag.id)
|
||||||
|
expect(Guardian.new(admin).can_edit_flag?(flag)).to eq(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -673,10 +673,7 @@ TEXT
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#replace_flags" do
|
describe "#replace_flags" do
|
||||||
after do
|
after { PostActionType.replace_flag_settings(nil) }
|
||||||
PostActionType.replace_flag_settings(nil)
|
|
||||||
ReviewableScore.reload_types
|
|
||||||
end
|
|
||||||
|
|
||||||
let(:original_flags) { PostActionType.flag_settings }
|
let(:original_flags) { PostActionType.flag_settings }
|
||||||
|
|
||||||
|
|
|
@ -484,7 +484,7 @@ RSpec.describe PostRevisor do
|
||||||
|
|
||||||
post = Fabricate(:post, raw: "hello world")
|
post = Fabricate(:post, raw: "hello world")
|
||||||
|
|
||||||
Fabricate(:flag, post: post, user: user)
|
Fabricate(:flag_post_action, post: post, user: user)
|
||||||
|
|
||||||
revisor = PostRevisor.new(post)
|
revisor = PostRevisor.new(post)
|
||||||
revisor.revise!(
|
revisor.revise!(
|
||||||
|
|
69
spec/models/flag_spec.rb
Normal file
69
spec/models/flag_spec.rb
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
RSpec.describe Flag, type: :model do
|
||||||
|
before { Flag.reset_flag_settings! }
|
||||||
|
|
||||||
|
it "has id lower than 1000 for system flags" do
|
||||||
|
flag = Fabricate(:flag, id: 1)
|
||||||
|
expect(flag.system?).to be true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "has id greater than 1000 for non-system flags" do
|
||||||
|
flag = Fabricate(:flag)
|
||||||
|
expect(flag.system?).to be false
|
||||||
|
expect(flag.id).to be > 1000
|
||||||
|
end
|
||||||
|
|
||||||
|
it "has correct name key" do
|
||||||
|
flag = Fabricate(:flag, name: "CuStOm Flag!!!")
|
||||||
|
expect(flag.name_key).to eq("custom_flag")
|
||||||
|
|
||||||
|
flag.update!(name: "It's Illegal")
|
||||||
|
expect(flag.name_key).to eq("its_illegal")
|
||||||
|
|
||||||
|
flag.update!(name: "THIS IS SPaM!+)(*&^%$#@@@!)")
|
||||||
|
expect(flag.name_key).to eq("this_is_spam")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "updates post action types when created, modified or destroyed" do
|
||||||
|
expect(PostActionType.flag_types.keys).to eq(
|
||||||
|
%i[notify_user notify_moderators off_topic inappropriate spam illegal],
|
||||||
|
)
|
||||||
|
expect(ReviewableScore.types.keys).to eq(
|
||||||
|
%i[notify_user notify_moderators off_topic inappropriate spam illegal needs_approval],
|
||||||
|
)
|
||||||
|
|
||||||
|
flag = Fabricate(:flag, name: "custom")
|
||||||
|
expect(PostActionType.flag_types.keys).to eq(
|
||||||
|
%i[notify_user notify_moderators off_topic inappropriate spam illegal custom],
|
||||||
|
)
|
||||||
|
expect(ReviewableScore.types.keys).to eq(
|
||||||
|
%i[notify_user notify_moderators off_topic inappropriate spam illegal custom needs_approval],
|
||||||
|
)
|
||||||
|
|
||||||
|
flag.update!(name: "edited_custom")
|
||||||
|
expect(PostActionType.flag_types.keys).to eq(
|
||||||
|
%i[notify_user notify_moderators off_topic inappropriate spam illegal edited_custom],
|
||||||
|
)
|
||||||
|
expect(ReviewableScore.types.keys).to eq(
|
||||||
|
%i[
|
||||||
|
notify_user
|
||||||
|
notify_moderators
|
||||||
|
off_topic
|
||||||
|
inappropriate
|
||||||
|
spam
|
||||||
|
illegal
|
||||||
|
edited_custom
|
||||||
|
needs_approval
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
flag.destroy!
|
||||||
|
expect(PostActionType.flag_types.keys).to eq(
|
||||||
|
%i[notify_user notify_moderators off_topic inappropriate spam illegal],
|
||||||
|
)
|
||||||
|
expect(ReviewableScore.types.keys).to eq(
|
||||||
|
%i[notify_user notify_moderators off_topic inappropriate spam illegal needs_approval],
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
|
@ -359,7 +359,7 @@ RSpec.describe TrustLevel3Requirements do
|
||||||
flags =
|
flags =
|
||||||
%i[off_topic inappropriate notify_user notify_moderators spam].map do |t|
|
%i[off_topic inappropriate notify_user notify_moderators spam].map do |t|
|
||||||
Fabricate(
|
Fabricate(
|
||||||
:flag,
|
:flag_post_action,
|
||||||
post: Fabricate(:post, user: user),
|
post: Fabricate(:post, user: user),
|
||||||
post_action_type_id: PostActionType.types[t],
|
post_action_type_id: PostActionType.types[t],
|
||||||
agreed_at: 1.minute.ago,
|
agreed_at: 1.minute.ago,
|
||||||
|
@ -369,7 +369,7 @@ RSpec.describe TrustLevel3Requirements do
|
||||||
_deferred_flags =
|
_deferred_flags =
|
||||||
%i[off_topic inappropriate notify_user notify_moderators spam].map do |t|
|
%i[off_topic inappropriate notify_user notify_moderators spam].map do |t|
|
||||||
Fabricate(
|
Fabricate(
|
||||||
:flag,
|
:flag_post_action,
|
||||||
post: Fabricate(:post, user: user),
|
post: Fabricate(:post, user: user),
|
||||||
post_action_type_id: PostActionType.types[t],
|
post_action_type_id: PostActionType.types[t],
|
||||||
deferred_at: 1.minute.ago,
|
deferred_at: 1.minute.ago,
|
||||||
|
@ -379,7 +379,7 @@ RSpec.describe TrustLevel3Requirements do
|
||||||
_deleted_flags =
|
_deleted_flags =
|
||||||
%i[off_topic inappropriate notify_user notify_moderators spam].map do |t|
|
%i[off_topic inappropriate notify_user notify_moderators spam].map do |t|
|
||||||
Fabricate(
|
Fabricate(
|
||||||
:flag,
|
:flag_post_action,
|
||||||
post: Fabricate(:post, user: user),
|
post: Fabricate(:post, user: user),
|
||||||
post_action_type_id: PostActionType.types[t],
|
post_action_type_id: PostActionType.types[t],
|
||||||
deleted_at: 1.minute.ago,
|
deleted_at: 1.minute.ago,
|
||||||
|
@ -388,7 +388,7 @@ RSpec.describe TrustLevel3Requirements do
|
||||||
|
|
||||||
# Same post, different user:
|
# Same post, different user:
|
||||||
Fabricate(
|
Fabricate(
|
||||||
:flag,
|
:flag_post_action,
|
||||||
post: flags[1].post,
|
post: flags[1].post,
|
||||||
post_action_type_id: PostActionType.types[:spam],
|
post_action_type_id: PostActionType.types[:spam],
|
||||||
agreed_at: 1.minute.ago,
|
agreed_at: 1.minute.ago,
|
||||||
|
@ -396,7 +396,7 @@ RSpec.describe TrustLevel3Requirements do
|
||||||
|
|
||||||
# Flagged their own post:
|
# Flagged their own post:
|
||||||
Fabricate(
|
Fabricate(
|
||||||
:flag,
|
:flag_post_action,
|
||||||
user: user,
|
user: user,
|
||||||
post: Fabricate(:post, user: user),
|
post: Fabricate(:post, user: user),
|
||||||
post_action_type_id: PostActionType.types[:spam],
|
post_action_type_id: PostActionType.types[:spam],
|
||||||
|
@ -405,7 +405,7 @@ RSpec.describe TrustLevel3Requirements do
|
||||||
|
|
||||||
# More than 100 days ago:
|
# More than 100 days ago:
|
||||||
Fabricate(
|
Fabricate(
|
||||||
:flag,
|
:flag_post_action,
|
||||||
post: Fabricate(:post, user: user, created_at: 101.days.ago),
|
post: Fabricate(:post, user: user, created_at: 101.days.ago),
|
||||||
post_action_type_id: PostActionType.types[:spam],
|
post_action_type_id: PostActionType.types[:spam],
|
||||||
created_at: 101.days.ago,
|
created_at: 101.days.ago,
|
||||||
|
|
|
@ -72,7 +72,11 @@ RSpec.describe SpamRule::AutoSilence do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns 0 when there is one flag that has a reason other than spam" do
|
it "returns 0 when there is one flag that has a reason other than spam" do
|
||||||
Fabricate(:flag, post: post, post_action_type_id: PostActionType.types[:off_topic])
|
Fabricate(
|
||||||
|
:flag_post_action,
|
||||||
|
post: post,
|
||||||
|
post_action_type_id: PostActionType.types[:off_topic],
|
||||||
|
)
|
||||||
expect(count).to eq(0)
|
expect(count).to eq(0)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ describe "Composer don't feed the trolls popup", type: :system do
|
||||||
fab!(:topic) { Fabricate(:topic, user: user) }
|
fab!(:topic) { Fabricate(:topic, user: user) }
|
||||||
fab!(:post) { Fabricate(:post, user: user, topic: topic) }
|
fab!(:post) { Fabricate(:post, user: user, topic: topic) }
|
||||||
fab!(:reply) { Fabricate(:post, user: troll, topic: topic) }
|
fab!(:reply) { Fabricate(:post, user: troll, topic: topic) }
|
||||||
fab!(:flag) { Fabricate(:flag, post: reply, user: user) }
|
fab!(:flag) { Fabricate(:flag_post_action, post: reply, user: user) }
|
||||||
let(:topic_page) { PageObjects::Pages::Topic.new }
|
let(:topic_page) { PageObjects::Pages::Topic.new }
|
||||||
|
|
||||||
before { sign_in user }
|
before { sign_in user }
|
||||||
|
|
|
@ -12,7 +12,7 @@ describe "Flagging post", type: :system do
|
||||||
|
|
||||||
describe "Using Take Action" do
|
describe "Using Take Action" do
|
||||||
it "can select the default action to hide the post, agree with other flags, and reach the flag threshold" do
|
it "can select the default action to hide the post, agree with other flags, and reach the flag threshold" do
|
||||||
other_flag = Fabricate(:flag, post: post_to_flag, user: Fabricate(:moderator))
|
other_flag = Fabricate(:flag_post_action, post: post_to_flag, user: Fabricate(:moderator))
|
||||||
other_flag_reviewable =
|
other_flag_reviewable =
|
||||||
Fabricate(:reviewable_flagged_post, target: post_to_flag, created_by: other_flag.user)
|
Fabricate(:reviewable_flagged_post, target: post_to_flag, created_by: other_flag.user)
|
||||||
expect(other_flag.reload.agreed_at).to be_nil
|
expect(other_flag.reload.agreed_at).to be_nil
|
||||||
|
|
Loading…
Reference in New Issue
Block a user