DEV: Apply syntax_tree formatting

This commit is contained in:
Rafael dos Santos Silva 2022-07-25 14:45:41 -03:00
parent 8c11d4d6ed
commit 6116f26642
52 changed files with 1867 additions and 1280 deletions

View File

@ -7,9 +7,10 @@ module DiscourseAssign
def suggestions def suggestions
users = [current_user] users = [current_user]
users += User users +=
.where('users.id <> ?', current_user.id) User
.joins(<<~SQL .where("users.id <> ?", current_user.id)
.joins(<<~SQL)
JOIN( JOIN(
SELECT assigned_to_id user_id, MAX(created_at) last_assigned SELECT assigned_to_id user_id, MAX(created_at) last_assigned
FROM assignments FROM assignments
@ -18,15 +19,21 @@ module DiscourseAssign
HAVING COUNT(*) < #{SiteSetting.max_assigned_topics} HAVING COUNT(*) < #{SiteSetting.max_assigned_topics}
) as X ON X.user_id = users.id ) as X ON X.user_id = users.id
SQL SQL
)
.assign_allowed .assign_allowed
.order('X.last_assigned DESC') .order("X.last_assigned DESC")
.limit(6) .limit(6)
render json: { render json: {
assign_allowed_on_groups: Group.visible_groups(current_user).assign_allowed_groups.pluck(:name), assign_allowed_on_groups:
assign_allowed_for_groups: Group.visible_groups(current_user).assignable(current_user).pluck(:name), Group.visible_groups(current_user).assign_allowed_groups.pluck(:name),
suggestions: ActiveModel::ArraySerializer.new(users, scope: guardian, each_serializer: BasicUserSerializer), assign_allowed_for_groups:
Group.visible_groups(current_user).assignable(current_user).pluck(:name),
suggestions:
ActiveModel::ArraySerializer.new(
users,
scope: guardian,
each_serializer: BasicUserSerializer,
),
} }
end end
@ -46,11 +53,18 @@ module DiscourseAssign
def assign def assign
target_id = params.require(:target_id) target_id = params.require(:target_id)
target_type = params.require(:target_type) target_type = params.require(:target_type)
username = params.permit(:username)['username'] username = params.permit(:username)["username"]
group_name = params.permit(:group_name)['group_name'] group_name = params.permit(:group_name)["group_name"]
note = params.permit(:note)['note'].presence note = params.permit(:note)["note"].presence
assign_to = username.present? ? User.find_by(username_lower: username.downcase) : Group.where("LOWER(name) = ?", group_name.downcase).first assign_to =
(
if username.present?
User.find_by(username_lower: username.downcase)
else
Group.where("LOWER(name) = ?", group_name.downcase).first
end
)
raise Discourse::NotFound unless assign_to raise Discourse::NotFound unless assign_to
raise Discourse::NotFound if !Assignment.valid_type?(target_type) raise Discourse::NotFound if !Assignment.valid_type?(target_type)
@ -72,19 +86,27 @@ module DiscourseAssign
offset = (params[:offset] || 0).to_i offset = (params[:offset] || 0).to_i
limit = (params[:limit] || 100).to_i limit = (params[:limit] || 100).to_i
topics = Topic topics =
Topic
.includes(:tags) .includes(:tags)
.includes(:user) .includes(:user)
.joins("JOIN assignments a ON a.target_id = topics.id AND a.target_type = 'Topic' AND a.assigned_to_id IS NOT NULL") .joins(
"JOIN assignments a ON a.target_id = topics.id AND a.target_type = 'Topic' AND a.assigned_to_id IS NOT NULL",
)
.order("a.assigned_to_id, topics.bumped_at desc") .order("a.assigned_to_id, topics.bumped_at desc")
.offset(offset) .offset(offset)
.limit(limit) .limit(limit)
Topic.preload_custom_fields(topics, TopicList.preloaded_custom_fields) Topic.preload_custom_fields(topics, TopicList.preloaded_custom_fields)
topic_assignments = Assignment.where(target_id: topics.map(&:id), target_type: 'Topic', active: true).pluck(:target_id, :assigned_to_id).to_h topic_assignments =
Assignment
.where(target_id: topics.map(&:id), target_type: "Topic", active: true)
.pluck(:target_id, :assigned_to_id)
.to_h
users = User users =
User
.where("users.id IN (?)", topic_assignments.values.uniq) .where("users.id IN (?)", topic_assignments.values.uniq)
.joins("join user_emails on user_emails.user_id = users.id AND user_emails.primary") .joins("join user_emails on user_emails.user_id = users.id AND user_emails.primary")
.select(UserLookup.lookup_columns) .select(UserLookup.lookup_columns)
@ -114,40 +136,40 @@ module DiscourseAssign
guardian.ensure_can_see_group_members!(group) guardian.ensure_can_see_group_members!(group)
members = User members =
User
.joins("LEFT OUTER JOIN group_users g ON g.user_id = users.id") .joins("LEFT OUTER JOIN group_users g ON g.user_id = users.id")
.joins("LEFT OUTER JOIN assignments a ON a.assigned_to_id = users.id AND a.assigned_to_type = 'User'") .joins(
"LEFT OUTER JOIN assignments a ON a.assigned_to_id = users.id AND a.assigned_to_type = 'User'",
)
.joins("LEFT OUTER JOIN topics t ON t.id = a.target_id AND a.target_type = 'Topic'") .joins("LEFT OUTER JOIN topics t ON t.id = a.target_id AND a.target_type = 'Topic'")
.where("g.group_id = ? AND users.id > 0 AND t.deleted_at IS NULL", group.id) .where("g.group_id = ? AND users.id > 0 AND t.deleted_at IS NULL", group.id)
.where("a.assigned_to_id IS NOT NULL AND a.active") .where("a.assigned_to_id IS NOT NULL AND a.active")
.order('COUNT(users.id) DESC') .order("COUNT(users.id) DESC")
.group('users.id') .group("users.id")
.select('users.*, COUNT(users.id) as "assignments_count"') .select('users.*, COUNT(users.id) as "assignments_count"')
.limit(limit) .limit(limit)
.offset(offset) .offset(offset)
if params[:filter] members = members.where(<<~SQL, pattern: "%#{params[:filter]}%") if params[:filter]
members = members.where(<<~SQL, pattern: "%#{params[:filter]}%")
users.name ILIKE :pattern OR users.username_lower ILIKE :pattern users.name ILIKE :pattern OR users.username_lower ILIKE :pattern
SQL SQL
end
group_assignments = Topic group_assignments =
Topic
.joins("JOIN assignments a ON a.topic_id = topics.id") .joins("JOIN assignments a ON a.topic_id = topics.id")
.where(<<~SQL, group_id: group.id) .where(<<~SQL, group_id: group.id)
a.assigned_to_id = :group_id AND a.assigned_to_type = 'Group' AND a.active a.assigned_to_id = :group_id AND a.assigned_to_type = 'Group' AND a.active
SQL SQL
.pluck(:topic_id) .pluck(:topic_id)
assignments = TopicQuery assignments =
.new(current_user) TopicQuery.new(current_user).group_topics_assigned_results(group).pluck("topics.id")
.group_topics_assigned_results(group)
.pluck('topics.id')
render json: { render json: {
members: serialize_data(members, GroupUserAssignedSerializer), members: serialize_data(members, GroupUserAssignedSerializer),
assignment_count: (assignments | group_assignments).count, assignment_count: (assignments | group_assignments).count,
group_assignment_count: group_assignments.count group_assignment_count: group_assignments.count,
} }
end end
@ -156,26 +178,59 @@ module DiscourseAssign
def translate_failure(reason, assign_to) def translate_failure(reason, assign_to)
case reason case reason
when :already_assigned when :already_assigned
{ error: I18n.t('discourse_assign.already_assigned', username: assign_to.username) } { error: I18n.t("discourse_assign.already_assigned", username: assign_to.username) }
when :forbidden_assign_to when :forbidden_assign_to
{ error: I18n.t('discourse_assign.forbidden_assign_to', username: assign_to.username) } { error: I18n.t("discourse_assign.forbidden_assign_to", username: assign_to.username) }
when :forbidden_assignee_not_pm_participant when :forbidden_assignee_not_pm_participant
{ error: I18n.t('discourse_assign.forbidden_assignee_not_pm_participant', username: assign_to.username) } {
error:
I18n.t(
"discourse_assign.forbidden_assignee_not_pm_participant",
username: assign_to.username,
),
}
when :forbidden_assignee_cant_see_topic when :forbidden_assignee_cant_see_topic
{ error: I18n.t('discourse_assign.forbidden_assignee_cant_see_topic', username: assign_to.username) } {
error:
I18n.t(
"discourse_assign.forbidden_assignee_cant_see_topic",
username: assign_to.username,
),
}
when :group_already_assigned when :group_already_assigned
{ error: I18n.t('discourse_assign.group_already_assigned', group: assign_to.name) } { error: I18n.t("discourse_assign.group_already_assigned", group: assign_to.name) }
when :forbidden_group_assign_to when :forbidden_group_assign_to
{ error: I18n.t('discourse_assign.forbidden_group_assign_to', group: assign_to.name) } { error: I18n.t("discourse_assign.forbidden_group_assign_to", group: assign_to.name) }
when :forbidden_group_assignee_not_pm_participant when :forbidden_group_assignee_not_pm_participant
{ error: I18n.t('discourse_assign.forbidden_group_assignee_not_pm_participant', group: assign_to.name) } {
error:
I18n.t(
"discourse_assign.forbidden_group_assignee_not_pm_participant",
group: assign_to.name,
),
}
when :forbidden_group_assignee_cant_see_topic when :forbidden_group_assignee_cant_see_topic
{ error: I18n.t('discourse_assign.forbidden_group_assignee_cant_see_topic', group: assign_to.name) } {
error:
I18n.t(
"discourse_assign.forbidden_group_assignee_cant_see_topic",
group: assign_to.name,
),
}
when :too_many_assigns_for_topic when :too_many_assigns_for_topic
{ error: I18n.t('discourse_assign.too_many_assigns_for_topic', limit: Assigner::ASSIGNMENTS_PER_TOPIC_LIMIT) } {
error:
I18n.t(
"discourse_assign.too_many_assigns_for_topic",
limit: Assigner::ASSIGNMENTS_PER_TOPIC_LIMIT,
),
}
else else
max = SiteSetting.max_assigned_topics max = SiteSetting.max_assigned_topics
{ error: I18n.t('discourse_assign.too_many_assigns', username: assign_to.username, max: max) } {
error:
I18n.t("discourse_assign.too_many_assigns", username: assign_to.username, max: max),
}
end end
end end

View File

@ -1,23 +1,21 @@
# frozen_string_literal: true # frozen_string_literal: true
require_dependency 'email/message_builder' require_dependency "email/message_builder"
class AssignMailer < ActionMailer::Base class AssignMailer < ActionMailer::Base
include Email::BuildEmailHelper include Email::BuildEmailHelper
def self.levels def self.levels
@levels ||= Enum.new(never: 'never', @levels ||= Enum.new(never: "never", different_users: "different_users", always: "always")
different_users: 'different_users',
always: 'always')
end end
def send_assignment(to_address, topic, assigned_by) def send_assignment(to_address, topic, assigned_by)
opts = { opts = {
template: 'assign_mailer', template: "assign_mailer",
topic_title: topic.title, topic_title: topic.title,
assignee_name: assigned_by.username, assignee_name: assigned_by.username,
topic_excerpt: topic.excerpt, topic_excerpt: topic.excerpt,
topic_link: topic.url topic_link: topic.url,
} }
build_email(to_address, opts) build_email(to_address, opts)
end end

View File

@ -1,18 +1,20 @@
# frozen_string_literal: true # frozen_string_literal: true
require_dependency 'enum_site_setting' require_dependency "enum_site_setting"
class AssignMailerSiteSettings < EnumSiteSetting class AssignMailerSiteSettings < EnumSiteSetting
def self.valid_value?(val) def self.valid_value?(val)
values.any? { |v| v[:value].to_s == val.to_s } values.any? { |v| v[:value].to_s == val.to_s }
end end
def self.values def self.values
@values ||= [ @values ||= [
{ name: 'discourse_assign.assign_mailer.never', value: AssignMailer.levels[:never] }, { name: "discourse_assign.assign_mailer.never", value: AssignMailer.levels[:never] },
{ name: 'discourse_assign.assign_mailer.different_users', value: AssignMailer.levels[:different_users] }, {
{ name: 'discourse_assign.assign_mailer.always', value: AssignMailer.levels[:always] } name: "discourse_assign.assign_mailer.different_users",
value: AssignMailer.levels[:different_users],
},
{ name: "discourse_assign.assign_mailer.always", value: AssignMailer.levels[:always] },
] ]
end end

View File

@ -1,25 +1,30 @@
# frozen_string_literal: true # frozen_string_literal: true
class Assignment < ActiveRecord::Base class Assignment < ActiveRecord::Base
VALID_TYPES = %w(topic post).freeze VALID_TYPES = %w[topic post].freeze
belongs_to :topic belongs_to :topic
belongs_to :assigned_to, polymorphic: true belongs_to :assigned_to, polymorphic: true
belongs_to :assigned_by_user, class_name: "User" belongs_to :assigned_by_user, class_name: "User"
belongs_to :target, polymorphic: true belongs_to :target, polymorphic: true
scope :joins_with_topics, -> { joins("INNER JOIN topics ON topics.id = assignments.target_id AND assignments.target_type = 'Topic' AND topics.deleted_at IS NULL") } scope :joins_with_topics,
-> {
joins(
"INNER JOIN topics ON topics.id = assignments.target_id AND assignments.target_type = 'Topic' AND topics.deleted_at IS NULL",
)
}
def self.valid_type?(type) def self.valid_type?(type)
VALID_TYPES.include?(type.downcase) VALID_TYPES.include?(type.downcase)
end end
def assigned_to_user? def assigned_to_user?
assigned_to_type == 'User' assigned_to_type == "User"
end end
def assigned_to_group? def assigned_to_group?
assigned_to_type == 'Group' assigned_to_type == "Group"
end end
end end

View File

@ -1,12 +1,10 @@
# frozen_string_literal: true # frozen_string_literal: true
require_dependency 'enum_site_setting' require_dependency "enum_site_setting"
class RemindAssignsFrequencySiteSettings < EnumSiteSetting class RemindAssignsFrequencySiteSettings < EnumSiteSetting
def self.valid_value?(val) def self.valid_value?(val)
val.to_i.to_s == val.to_s && val.to_i.to_s == val.to_s && values.any? { |v| v[:value] == val.to_i }
values.any? { |v| v[:value] == val.to_i }
end end
DAILY_MINUTES = 24 * 60 * 1 DAILY_MINUTES = 24 * 60 * 1
@ -16,25 +14,11 @@ class RemindAssignsFrequencySiteSettings < EnumSiteSetting
def self.values def self.values
@values ||= [ @values ||= [
{ { name: "discourse_assign.reminders_frequency.never", value: 0 },
name: 'discourse_assign.reminders_frequency.never', value: 0 { name: "discourse_assign.reminders_frequency.daily", value: DAILY_MINUTES },
}, { name: "discourse_assign.reminders_frequency.weekly", value: WEEKLY_MINUTES },
{ { name: "discourse_assign.reminders_frequency.monthly", value: MONTHLY_MINUTES },
name: 'discourse_assign.reminders_frequency.daily', { name: "discourse_assign.reminders_frequency.quarterly", value: QUARTERLY_MINUTES },
value: DAILY_MINUTES
},
{
name: 'discourse_assign.reminders_frequency.weekly',
value: WEEKLY_MINUTES
},
{
name: 'discourse_assign.reminders_frequency.monthly',
value: MONTHLY_MINUTES
},
{
name: 'discourse_assign.reminders_frequency.quarterly',
value: QUARTERLY_MINUTES
}
] ]
end end
@ -45,7 +29,7 @@ class RemindAssignsFrequencySiteSettings < EnumSiteSetting
def self.frequency_for(minutes) def self.frequency_for(minutes)
value = values.detect { |v| v[:value] == minutes } value = values.detect { |v| v[:value] == minutes }
raise Discourse::InvalidParameters(:task_reminders_frequency) if value.nil? raise Discourse.InvalidParameters(:task_reminders_frequency) if value.nil?
I18n.t(value[:name]) I18n.t(value[:name])
end end

View File

@ -3,12 +3,7 @@
class AssignedTopicSerializer < BasicTopicSerializer class AssignedTopicSerializer < BasicTopicSerializer
include TopicTagsMixin include TopicTagsMixin
attributes :excerpt, attributes :excerpt, :category_id, :created_at, :updated_at, :assigned_to_user, :assigned_to_group
:category_id,
:created_at,
:updated_at,
:assigned_to_user,
:assigned_to_group
has_one :user, serializer: BasicUserSerializer, embed: :objects has_one :user, serializer: BasicUserSerializer, embed: :objects

View File

@ -1,12 +1,9 @@
# frozen_string_literal: true # frozen_string_literal: true
class GroupUserAssignedSerializer < BasicUserSerializer class GroupUserAssignedSerializer < BasicUserSerializer
attributes :assignments_count, :username_lower
attributes :assignments_count,
:username_lower
def include_assignments_count def include_assignments_count
object.can_assign? object.can_assign?
end end
end end

View File

@ -2,7 +2,10 @@
class AddLastRemindedAtIndex < ActiveRecord::Migration[5.2] class AddLastRemindedAtIndex < ActiveRecord::Migration[5.2]
def change def change
add_index :user_custom_fields, %i[name user_id], name: :idx_user_custom_fields_last_reminded_at, add_index :user_custom_fields,
unique: true, where: "name = 'last_reminded_at'" %i[name user_id],
name: :idx_user_custom_fields_last_reminded_at,
unique: true,
where: "name = 'last_reminded_at'"
end end
end end

View File

@ -2,7 +2,10 @@
class AddRemindsAssignFrequencyIndex < ActiveRecord::Migration[5.2] class AddRemindsAssignFrequencyIndex < ActiveRecord::Migration[5.2]
def change def change
add_index :user_custom_fields, %i[name user_id], name: :idx_user_custom_fields_remind_assigns_frequency, add_index :user_custom_fields,
unique: true, where: "name = 'remind_assigns_frequency'" %i[name user_id],
name: :idx_user_custom_fields_remind_assigns_frequency,
unique: true,
where: "name = 'remind_assigns_frequency'"
end end
end end

View File

@ -2,15 +2,19 @@
class SetAssignAllowedOnGroupsDefault < ActiveRecord::Migration[5.2] class SetAssignAllowedOnGroupsDefault < ActiveRecord::Migration[5.2]
def up def up
current_values = DB.query_single("SELECT value FROM site_settings WHERE name = 'assign_allowed_on_groups'").first current_values =
DB.query_single(
"SELECT value FROM site_settings WHERE name = 'assign_allowed_on_groups'",
).first
# Dynamically sets the default value, supports older versions. # Dynamically sets the default value, supports older versions.
if current_values.nil? if current_values.nil?
min_version = 201_907_171_337_43 min_version = 201_907_171_337_43
migrated_site_setting = DB.query_single( migrated_site_setting =
"SELECT schema_migrations.version FROM schema_migrations WHERE schema_migrations.version = '#{min_version}'" DB.query_single(
"SELECT schema_migrations.version FROM schema_migrations WHERE schema_migrations.version = '#{min_version}'",
).first ).first
default = migrated_site_setting.present? ? Group::AUTO_GROUPS[:staff] : 'staff' default = migrated_site_setting.present? ? Group::AUTO_GROUPS[:staff] : "staff"
execute "INSERT INTO site_settings (name, data_type, value, created_at, updated_at) execute "INSERT INTO site_settings (name, data_type, value, created_at, updated_at)
VALUES ('assign_allowed_on_groups', #{SiteSettings::TypeSupervisor.types[:group_list]}, '#{default}', now(), now())" VALUES ('assign_allowed_on_groups', #{SiteSettings::TypeSupervisor.types[:group_list]}, '#{default}', now(), now())"

View File

@ -2,6 +2,6 @@
class AddIndexToAssignedToIdAndType < ActiveRecord::Migration[6.1] class AddIndexToAssignedToIdAndType < ActiveRecord::Migration[6.1]
def change def change
add_index :assignments, [:assigned_to_id, :assigned_to_type] add_index :assignments, %i[assigned_to_id assigned_to_type]
end end
end end

View File

@ -14,8 +14,11 @@ class AddTargetToAssignments < ActiveRecord::Migration[6.1]
change_column :assignments, :target_id, :integer, null: false change_column :assignments, :target_id, :integer, null: false
change_column :assignments, :target_type, :string, null: false change_column :assignments, :target_type, :string, null: false
add_index :assignments, [:target_id, :target_type], unique: true add_index :assignments, %i[target_id target_type], unique: true
add_index :assignments, [:assigned_to_id, :assigned_to_type, :target_id, :target_type], unique: true, name: 'unique_target_and_assigned' add_index :assignments,
%i[assigned_to_id assigned_to_type target_id target_type],
unique: true,
name: "unique_target_and_assigned"
end end
def down def down

View File

@ -41,7 +41,6 @@ class CorrectlyMoveAssignmentsFromCustomFieldsToATable < ActiveRecord::Migration
ORDER BY assigned_by.created_at DESC ORDER BY assigned_by.created_at DESC
ON CONFLICT DO NOTHING ON CONFLICT DO NOTHING
SQL SQL
else else
execute <<~SQL execute <<~SQL
INSERT INTO assignments (assigned_to_id, assigned_by_user_id, topic_id, created_at, updated_at) INSERT INTO assignments (assigned_to_id, assigned_by_user_id, topic_id, created_at, updated_at)

View File

@ -8,12 +8,21 @@ module Jobs
raise Discourse::InvalidParameters.new(:assigned_to_id) if args[:assigned_to_id].nil? raise Discourse::InvalidParameters.new(:assigned_to_id) if args[:assigned_to_id].nil?
raise Discourse::InvalidParameters.new(:assigned_to_type) if args[:assigned_to_type].nil? raise Discourse::InvalidParameters.new(:assigned_to_type) if args[:assigned_to_type].nil?
raise Discourse::InvalidParameters.new(:assigned_by_id) if args[:assigned_by_id].nil? raise Discourse::InvalidParameters.new(:assigned_by_id) if args[:assigned_by_id].nil?
raise Discourse::InvalidParameters.new(:skip_small_action_post) if args[:skip_small_action_post].nil? if args[:skip_small_action_post].nil?
raise Discourse::InvalidParameters.new(:skip_small_action_post)
end
topic = Topic.find(args[:topic_id]) topic = Topic.find(args[:topic_id])
post = Post.find(args[:post_id]) post = Post.find(args[:post_id])
assigned_by = User.find(args[:assigned_by_id]) assigned_by = User.find(args[:assigned_by_id])
assigned_to = args[:assigned_to_type] == "User" ? User.find(args[:assigned_to_id]) : Group.find(args[:assigned_to_id]) assigned_to =
(
if args[:assigned_to_type] == "User"
User.find(args[:assigned_to_id])
else
Group.find(args[:assigned_to_id])
end
)
assigned_to_users = args[:assigned_to_type] == "User" ? [assigned_to] : assigned_to.users assigned_to_users = args[:assigned_to_type] == "User" ? [assigned_to] : assigned_to.users
assigned_to_users.each do |user| assigned_to_users.each do |user|
@ -30,11 +39,17 @@ module Jobs
notification_type: Notification.types[:assigned] || Notification.types[:custom], notification_type: Notification.types[:assigned] || Notification.types[:custom],
excerpt: excerpt:
I18n.t( I18n.t(
(assigned_to_user ? "discourse_assign.topic_assigned_excerpt" : "discourse_assign.topic_group_assigned_excerpt"), (
if assigned_to_user
"discourse_assign.topic_assigned_excerpt"
else
"discourse_assign.topic_group_assigned_excerpt"
end
),
title: topic.title, title: topic.title,
group: assigned_to.name, group: assigned_to.name,
locale: user.effective_locale locale: user.effective_locale,
) ),
) )
next if args[:skip_small_action_post] next if args[:skip_small_action_post]
@ -45,10 +60,17 @@ module Jobs
post_number: post.post_number, post_number: post.post_number,
high_priority: true, high_priority: true,
data: { data: {
message: assigned_to_user ? 'discourse_assign.assign_notification' : 'discourse_assign.assign_group_notification', message:
(
if assigned_to_user
"discourse_assign.assign_notification"
else
"discourse_assign.assign_group_notification"
end
),
display_username: assigned_to_user ? assigned_by.username : assigned_to.name, display_username: assigned_to_user ? assigned_by.username : assigned_to.name,
topic_title: topic.title topic_title: topic.title,
}.to_json }.to_json,
) )
end end
end end

View File

@ -2,7 +2,7 @@
module Jobs module Jobs
class RemindUser < ::Jobs::Base class RemindUser < ::Jobs::Base
sidekiq_options queue: 'low' sidekiq_options queue: "low"
def execute(args) def execute(args)
user = User.find_by(id: args[:user_id]) user = User.find_by(id: args[:user_id])

View File

@ -8,16 +8,28 @@ module Jobs
raise Discourse::InvalidParameters.new(:assigned_to_type) if args[:assigned_to_type].nil? raise Discourse::InvalidParameters.new(:assigned_to_type) if args[:assigned_to_type].nil?
topic = Topic.find(args[:topic_id]) topic = Topic.find(args[:topic_id])
assigned_to_users = args[:assigned_to_type] == "User" ? [User.find(args[:assigned_to_id])] : Group.find(args[:assigned_to_id]).users assigned_to_users =
(
if args[:assigned_to_type] == "User"
[User.find(args[:assigned_to_id])]
else
Group.find(args[:assigned_to_id]).users
end
)
assigned_to_users.each do |user| assigned_to_users.each do |user|
Assigner.publish_topic_tracking_state(topic, user.id) Assigner.publish_topic_tracking_state(topic, user.id)
Notification.where( Notification
.where(
notification_type: Notification.types[:assigned] || Notification.types[:custom], notification_type: Notification.types[:assigned] || Notification.types[:custom],
user_id: user.id, user_id: user.id,
topic_id: topic.id, topic_id: topic.id,
).where("data like '%discourse_assign.assign_notification%' OR data like '%discourse_assign.assign_group_notification%'").destroy_all )
.where(
"data like '%discourse_assign.assign_notification%' OR data like '%discourse_assign.assign_group_notification%'",
)
.destroy_all
end end
end end
end end

View File

@ -12,18 +12,22 @@ module Jobs
private private
def skip_enqueue? def skip_enqueue?
SiteSetting.remind_assigns_frequency.nil? || !SiteSetting.assign_enabled? || SiteSetting.assign_allowed_on_groups.blank? SiteSetting.remind_assigns_frequency.nil? || !SiteSetting.assign_enabled? ||
SiteSetting.assign_allowed_on_groups.blank?
end end
def allowed_group_ids def allowed_group_ids
Group.assign_allowed_groups.pluck(:id).join(',') Group.assign_allowed_groups.pluck(:id).join(",")
end end
def user_ids def user_ids
global_frequency = SiteSetting.remind_assigns_frequency global_frequency = SiteSetting.remind_assigns_frequency
frequency = ActiveRecord::Base.sanitize_sql("COALESCE(user_frequency.value, '#{global_frequency}')::INT") frequency =
ActiveRecord::Base.sanitize_sql(
"COALESCE(user_frequency.value, '#{global_frequency}')::INT",
)
DB.query_single(<<~SQL DB.query_single(<<~SQL)
SELECT assignments.assigned_to_id SELECT assignments.assigned_to_id
FROM assignments FROM assignments
@ -50,7 +54,6 @@ module Jobs
GROUP BY assignments.assigned_to_id GROUP BY assignments.assigned_to_id
HAVING COUNT(assignments.assigned_to_id) > 1 HAVING COUNT(assignments.assigned_to_id) > 1
SQL SQL
)
end end
end end
end end

View File

@ -1,17 +1,18 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'email/sender' require "email/sender"
require 'nokogiri' require "nokogiri"
class ::Assigner class ::Assigner
ASSIGNMENTS_PER_TOPIC_LIMIT = 5 ASSIGNMENTS_PER_TOPIC_LIMIT = 5
def self.backfill_auto_assign def self.backfill_auto_assign
staff_mention = User staff_mention =
User
.assign_allowed .assign_allowed
.pluck('username') .pluck("username")
.map { |name| "p.cooked ILIKE '%mention%@#{name}%'" } .map { |name| "p.cooked ILIKE '%mention%@#{name}%'" }
.join(' OR ') .join(" OR ")
sql = <<~SQL sql = <<~SQL
SELECT p.topic_id, MAX(post_number) post_number SELECT p.topic_id, MAX(post_number) post_number
@ -29,7 +30,12 @@ class ::Assigner
puts puts
assigned = 0 assigned = 0
ActiveRecord::Base.connection.raw_connection.exec(sql).to_a.each do |row| ActiveRecord::Base
.connection
.raw_connection
.exec(sql)
.to_a
.each do |row|
post = Post.find_by(post_number: row["post_number"].to_i, topic_id: row["topic_id"].to_i) post = Post.find_by(post_number: row["post_number"].to_i, topic_id: row["topic_id"].to_i)
assigned += 1 if post && auto_assign(post) assigned += 1 if post && auto_assign(post)
putc "." putc "."
@ -41,13 +47,23 @@ class ::Assigner
def self.assigned_self?(text) def self.assigned_self?(text)
return false if text.blank? || SiteSetting.assign_self_regex.blank? return false if text.blank? || SiteSetting.assign_self_regex.blank?
regex = Regexp.new(SiteSetting.assign_self_regex) rescue nil regex =
begin
Regexp.new(SiteSetting.assign_self_regex)
rescue StandardError
nil
end
!!(regex && text[regex]) !!(regex && text[regex])
end end
def self.assigned_other?(text) def self.assigned_other?(text)
return false if text.blank? || SiteSetting.assign_other_regex.blank? return false if text.blank? || SiteSetting.assign_other_regex.blank?
regex = Regexp.new(SiteSetting.assign_other_regex) rescue nil regex =
begin
Regexp.new(SiteSetting.assign_other_regex)
rescue StandardError
nil
end
!!(regex && text[regex]) !!(regex && text[regex])
end end
@ -78,7 +94,7 @@ class ::Assigner
end end
def self.is_last_staff_post?(post) def self.is_last_staff_post?(post)
allowed_user_ids = User.assign_allowed.pluck(:id).join(',') allowed_user_ids = User.assign_allowed.pluck(:id).join(",")
sql = <<~SQL sql = <<~SQL
SELECT 1 SELECT 1
@ -90,10 +106,7 @@ class ::Assigner
HAVING MAX(post_number) = :post_number HAVING MAX(post_number) = :post_number
SQL SQL
args = { args = { topic_id: post.topic_id, post_number: post.post_number }
topic_id: post.topic_id,
post_number: post.post_number
}
DB.exec(sql, args) == 1 DB.exec(sql, args) == 1
end end
@ -101,20 +114,13 @@ class ::Assigner
def self.mentioned_staff(post) def self.mentioned_staff(post)
mentions = post.raw_mentions mentions = post.raw_mentions
if mentions.present? if mentions.present?
User.human_users User.human_users.assign_allowed.where("username_lower IN (?)", mentions.map(&:downcase)).first
.assign_allowed
.where('username_lower IN (?)', mentions.map(&:downcase))
.first
end end
end end
def self.publish_topic_tracking_state(topic, user_id) def self.publish_topic_tracking_state(topic, user_id)
if topic.private_message? if topic.private_message?
MessageBus.publish( MessageBus.publish("/private-messages/assigned", { topic_id: topic.id }, user_ids: [user_id])
"/private-messages/assigned",
{ topic_id: topic.id },
user_ids: [user_id]
)
end end
end end
@ -135,7 +141,8 @@ class ::Assigner
return true if assign_to.is_a?(Group) return true if assign_to.is_a?(Group)
return true if @assigned_by.id == assign_to.id return true if @assigned_by.id == assign_to.id
assigned_total = Assignment assigned_total =
Assignment
.joins_with_topics .joins_with_topics
.where(topics: { deleted_at: nil }) .where(topics: { deleted_at: nil })
.where(assigned_to_id: assign_to.id, active: true) .where(assigned_to_id: assign_to.id, active: true)
@ -165,7 +172,10 @@ class ::Assigner
end end
def can_assignee_see_target?(assignee) def can_assignee_see_target?(assignee)
return false if (topic_target? || post_target?) && topic.private_message? && !private_message_allowed_user_ids.include?(assignee.id) if (topic_target? || post_target?) && topic.private_message? &&
!private_message_allowed_user_ids.include?(assignee.id)
return false
end
return Guardian.new(assignee).can_see_topic?(@target) if topic_target? return Guardian.new(assignee).can_see_topic?(@target) if topic_target?
return Guardian.new(assignee).can_see_post?(@target) if post_target? return Guardian.new(assignee).can_see_post?(@target) if post_target?
@ -188,9 +198,17 @@ class ::Assigner
def forbidden_reasons(assign_to:, type:, note:) def forbidden_reasons(assign_to:, type:, note:)
case case
when assign_to.is_a?(User) && !can_assignee_see_target?(assign_to) when assign_to.is_a?(User) && !can_assignee_see_target?(assign_to)
topic.private_message? ? :forbidden_assignee_not_pm_participant : :forbidden_assignee_cant_see_topic if topic.private_message?
:forbidden_assignee_not_pm_participant
else
:forbidden_assignee_cant_see_topic
end
when assign_to.is_a?(Group) && assign_to.users.any? { |user| !can_assignee_see_target?(user) } when assign_to.is_a?(Group) && assign_to.users.any? { |user| !can_assignee_see_target?(user) }
topic.private_message? ? :forbidden_group_assignee_not_pm_participant : :forbidden_group_assignee_cant_see_topic if topic.private_message?
:forbidden_group_assignee_not_pm_participant
else
:forbidden_group_assignee_cant_see_topic
end
when !can_be_assigned?(assign_to) when !can_be_assigned?(assign_to)
assign_to.is_a?(User) ? :forbidden_assign_to : :forbidden_group_assign_to assign_to.is_a?(User) ? :forbidden_assign_to : :forbidden_group_assign_to
when topic_same_assignee_and_note(assign_to, type, note) when topic_same_assignee_and_note(assign_to, type, note)
@ -240,7 +258,14 @@ class ::Assigner
@target.assignment&.destroy! @target.assignment&.destroy!
assignment = @target.create_assignment!(assigned_to_id: assign_to.id, assigned_to_type: assigned_to_type, assigned_by_user_id: @assigned_by.id, topic_id: topic.id, note: note) assignment =
@target.create_assignment!(
assigned_to_id: assign_to.id,
assigned_to_type: assigned_to_type,
assigned_by_user_id: @assigned_by.id,
topic_id: topic.id,
note: note,
)
first_post.publish_change_to_clients!(:revised, reload_topic: true) first_post.publish_change_to_clients!(:revised, reload_topic: true)
@ -252,17 +277,21 @@ class ::Assigner
if !TopicUser.exists?( if !TopicUser.exists?(
user_id: assign_to.id, user_id: assign_to.id,
topic_id: topic.id, topic_id: topic.id,
notification_level: TopicUser.notification_levels[:watching] notification_level: TopicUser.notification_levels[:watching],
) )
TopicUser.change( TopicUser.change(
assign_to.id, assign_to.id,
topic.id, topic.id,
notification_level: TopicUser.notification_levels[:watching], notification_level: TopicUser.notification_levels[:watching],
notifications_reason_id: TopicUser.notification_reasons[:plugin_changed] notifications_reason_id: TopicUser.notification_reasons[:plugin_changed],
) )
end end
if SiteSetting.assign_mailer == AssignMailer.levels[:always] || (SiteSetting.assign_mailer == AssignMailer.levels[:different_users] && @assigned_by.id != assign_to.id) if SiteSetting.assign_mailer == AssignMailer.levels[:always] ||
(
SiteSetting.assign_mailer == AssignMailer.levels[:different_users] &&
@assigned_by.id != assign_to.id
)
if !topic.muted?(assign_to) if !topic.muted?(assign_to)
message = AssignMailer.send_assignment(assign_to.email, topic, @assigned_by) message = AssignMailer.send_assignment(assign_to.email, topic, @assigned_by)
Email::Sender.new(message, :assign_message).send Email::Sender.new(message, :assign_message).send
@ -283,18 +312,14 @@ class ::Assigner
topic_id: topic.id, topic_id: topic.id,
topic_title: topic.title, topic_title: topic.title,
assigned_by_id: @assigned_by.id, assigned_by_id: @assigned_by.id,
assigned_by_username: @assigned_by.username assigned_by_username: @assigned_by.username,
} }
if assignment.assigned_to_user? if assignment.assigned_to_user?
payload.merge!({ payload.merge!({ assigned_to_id: assign_to.id, assigned_to_username: assign_to.username })
assigned_to_id: assign_to.id,
assigned_to_username: assign_to.username,
})
else else
payload.merge!({ payload.merge!(
assigned_to_group_id: assign_to.id, { assigned_to_group_id: assign_to.id, assigned_to_group_name: assign_to.name },
assigned_to_group_name: assign_to.name, )
})
end end
WebHook.enqueue_assign_hooks(assigned_to_type, payload.to_json) WebHook.enqueue_assign_hooks(assigned_to_type, payload.to_json)
end end
@ -310,24 +335,25 @@ class ::Assigner
first_post.publish_change_to_clients!(:revised, reload_topic: true) first_post.publish_change_to_clients!(:revised, reload_topic: true)
Jobs.enqueue(:unassign_notification, Jobs.enqueue(
:unassign_notification,
topic_id: topic.id, topic_id: topic.id,
assigned_to_id: assignment.assigned_to.id, assigned_to_id: assignment.assigned_to.id,
assigned_to_type: assignment.assigned_to_type) assigned_to_type: assignment.assigned_to_type,
)
if assignment.assigned_to_user? if assignment.assigned_to_user?
if TopicUser.exists?( if TopicUser.exists?(
user_id: assignment.assigned_to_id, user_id: assignment.assigned_to_id,
topic: topic, topic: topic,
notification_level: TopicUser.notification_levels[:watching], notification_level: TopicUser.notification_levels[:watching],
notifications_reason_id: TopicUser.notification_reasons[:plugin_changed] notifications_reason_id: TopicUser.notification_reasons[:plugin_changed],
) )
TopicUser.change( TopicUser.change(
assignment.assigned_to_id, assignment.assigned_to_id,
topic.id, topic.id,
notification_level: TopicUser.notification_levels[:tracking], notification_level: TopicUser.notification_levels[:tracking],
notifications_reason_id: TopicUser.notification_reasons[:plugin_changed] notifications_reason_id: TopicUser.notification_reasons[:plugin_changed],
) )
end end
end end
@ -337,7 +363,9 @@ class ::Assigner
if SiteSetting.unassign_creates_tracking_post && !silent if SiteSetting.unassign_creates_tracking_post && !silent
post_type = SiteSetting.assigns_public ? Post.types[:small_action] : Post.types[:whisper] post_type = SiteSetting.assigns_public ? Post.types[:small_action] : Post.types[:whisper]
custom_fields = { "action_code_who" => assigned_to.is_a?(User) ? assigned_to.username : assigned_to.name } custom_fields = {
"action_code_who" => assigned_to.is_a?(User) ? assigned_to.username : assigned_to.name,
}
if post_target? if post_target?
custom_fields.merge!("action_code_path" => "/p/#{@target.id}") custom_fields.merge!("action_code_path" => "/p/#{@target.id}")
@ -345,7 +373,8 @@ class ::Assigner
end end
topic.add_moderator_post( topic.add_moderator_post(
@assigned_by, nil, @assigned_by,
nil,
bump: false, bump: false,
post_type: post_type, post_type: post_type,
custom_fields: custom_fields, custom_fields: custom_fields,
@ -361,18 +390,16 @@ class ::Assigner
topic_id: topic.id, topic_id: topic.id,
topic_title: topic.title, topic_title: topic.title,
unassigned_by_id: @assigned_by.id, unassigned_by_id: @assigned_by.id,
unassigned_by_username: @assigned_by.username unassigned_by_username: @assigned_by.username,
} }
if assignment.assigned_to_user? if assignment.assigned_to_user?
payload.merge!({ payload.merge!(
unassigned_to_id: assigned_to.id, { unassigned_to_id: assigned_to.id, unassigned_to_username: assigned_to.username },
unassigned_to_username: assigned_to.username, )
})
else else
payload.merge!({ payload.merge!(
unassigned_to_group_id: assigned_to.id, { unassigned_to_group_id: assigned_to.id, unassigned_to_group_name: assigned_to.name },
unassigned_to_group_name: assigned_to.name, )
})
end end
WebHook.enqueue_assign_hooks(type, payload.to_json) WebHook.enqueue_assign_hooks(type, payload.to_json)
end end
@ -380,14 +407,14 @@ class ::Assigner
MessageBus.publish( MessageBus.publish(
"/staff/topic-assignment", "/staff/topic-assignment",
{ {
type: 'unassigned', type: "unassigned",
topic_id: topic.id, topic_id: topic.id,
post_id: post_target? && @target.id, post_id: post_target? && @target.id,
post_number: post_target? && @target.post_number, post_number: post_target? && @target.post_number,
assigned_type: assignment.assigned_to.is_a?(User) ? "User" : "Group", assigned_type: assignment.assigned_to.is_a?(User) ? "User" : "Group",
assignment_note: nil, assignment_note: nil,
}, },
user_ids: allowed_user_ids user_ids: allowed_user_ids,
) )
end end
end end
@ -395,20 +422,26 @@ class ::Assigner
private private
def queue_notification(assign_to, skip_small_action_post) def queue_notification(assign_to, skip_small_action_post)
Jobs.enqueue(:assign_notification, Jobs.enqueue(
:assign_notification,
topic_id: topic.id, topic_id: topic.id,
post_id: topic_target? ? first_post.id : @target.id, post_id: topic_target? ? first_post.id : @target.id,
assigned_to_id: assign_to.id, assigned_to_id: assign_to.id,
assigned_to_type: assign_to.is_a?(User) ? "User" : "Group", assigned_to_type: assign_to.is_a?(User) ? "User" : "Group",
assigned_by_id: @assigned_by.id, assigned_by_id: @assigned_by.id,
skip_small_action_post: skip_small_action_post) skip_small_action_post: skip_small_action_post,
)
end end
def add_small_action_post(action_code, assign_to, note) def add_small_action_post(action_code, assign_to, note)
custom_fields = { "action_code_who" => assign_to.is_a?(User) ? assign_to.username : assign_to.name } custom_fields = {
"action_code_who" => assign_to.is_a?(User) ? assign_to.username : assign_to.name,
}
if post_target? if post_target?
custom_fields.merge!({ "action_code_path" => "/p/#{@target.id}", "action_code_post_id" => @target.id }) custom_fields.merge!(
{ "action_code_path" => "/p/#{@target.id}", "action_code_post_id" => @target.id },
)
end end
topic.add_moderator_post( topic.add_moderator_post(
@ -417,7 +450,7 @@ class ::Assigner
bump: false, bump: false,
post_type: SiteSetting.assigns_public ? Post.types[:small_action] : Post.types[:whisper], post_type: SiteSetting.assigns_public ? Post.types[:small_action] : Post.types[:whisper],
action_code: action_code, action_code: action_code,
custom_fields: custom_fields custom_fields: custom_fields,
) )
end end
@ -434,7 +467,7 @@ class ::Assigner
assigned_to: serializer.new(assign_to, scope: Guardian.new, root: false).as_json, assigned_to: serializer.new(assign_to, scope: Guardian.new, root: false).as_json,
assignment_note: note, assignment_note: note,
}, },
user_ids: allowed_user_ids user_ids: allowed_user_ids,
) )
end end
@ -460,16 +493,16 @@ class ::Assigner
def topic_same_assignee_and_note(assign_to, type, note) def topic_same_assignee_and_note(assign_to, type, note)
topic.assignment&.assigned_to_id == assign_to.id && topic.assignment&.assigned_to_id == assign_to.id &&
topic.assignment&.assigned_to_type == type && topic.assignment&.assigned_to_type == type && topic.assignment.active == true &&
topic.assignment.active == true &&
topic.assignment&.note == note topic.assignment&.note == note
end end
def post_same_assignee_and_note(assign_to, type, note) def post_same_assignee_and_note(assign_to, type, note)
@target.is_a?(Topic) && @target.is_a?(Topic) &&
Assignment.where(topic_id: topic.id, target_type: "Post", active: true).any? do |assignment| Assignment
assignment.assigned_to_id == assign_to.id && .where(topic_id: topic.id, target_type: "Post", active: true)
assignment.assigned_to_type == type && .any? do |assignment|
assignment.assigned_to_id == assign_to.id && assignment.assigned_to_type == type &&
assignment&.note == note assignment&.note == note
end end
end end

View File

@ -8,7 +8,7 @@ module DiscourseAssign
username: user.username, username: user.username,
name: user.name, name: user.name,
avatar_template: user.avatar_template, avatar_template: user.avatar_template,
assign_icon: 'user-plus', assign_icon: "user-plus",
assign_path: SiteSetting.assigns_user_url_path.gsub("{username}", user.username), assign_path: SiteSetting.assigns_user_url_path.gsub("{username}", user.username),
} }
end end
@ -22,23 +22,39 @@ module DiscourseAssign
flair_color: group.flair_color, flair_color: group.flair_color,
flair_icon: group.flair_icon, flair_icon: group.flair_icon,
flair_upload_id: group.flair_upload_id, flair_upload_id: group.flair_upload_id,
assign_icon: 'group-plus', assign_icon: "group-plus",
assign_path: "/g/#{group.name}/assigned/everyone", assign_path: "/g/#{group.name}/assigned/everyone",
} }
end end
def self.build_indirectly_assigned_to(post_assignments, topic) def self.build_indirectly_assigned_to(post_assignments, topic)
post_assignments.map do |post_id, assigned_map| post_assignments
.map do |post_id, assigned_map|
assigned_to = assigned_map[:assigned_to] assigned_to = assigned_map[:assigned_to]
note = assigned_map[:assignment_note] note = assigned_map[:assignment_note]
post_number = assigned_map[:post_number] post_number = assigned_map[:post_number]
if (assigned_to.is_a?(User)) if (assigned_to.is_a?(User))
[post_id, { assigned_to: build_assigned_to_user(assigned_to, topic), post_number: post_number, assignment_note: note }] [
post_id,
{
assigned_to: build_assigned_to_user(assigned_to, topic),
post_number: post_number,
assignment_note: note,
},
]
elsif assigned_to.is_a?(Group) elsif assigned_to.is_a?(Group)
[post_id, { assigned_to: build_assigned_to_group(assigned_to, topic), post_number: post_number, assignment_note: note }] [
post_id,
{
assigned_to: build_assigned_to_group(assigned_to, topic),
post_number: post_number,
assignment_note: note,
},
]
end end
end.to_h end
.to_h
end end
end end
end end

View File

@ -1,9 +1,9 @@
# frozen_string_literal: true # frozen_string_literal: true
class PendingAssignsReminder class PendingAssignsReminder
REMINDED_AT = 'last_reminded_at' REMINDED_AT = "last_reminded_at"
REMINDERS_FREQUENCY = 'remind_assigns_frequency' REMINDERS_FREQUENCY = "remind_assigns_frequency"
CUSTOM_FIELD_NAME = 'assigns_reminder' CUSTOM_FIELD_NAME = "assigns_reminder"
REMINDER_THRESHOLD = 2 REMINDER_THRESHOLD = 2
def remind(user) def remind(user)
@ -14,7 +14,7 @@ class PendingAssignsReminder
oldest_topics = assigned_topics(user, order: :asc).where.not(id: newest_topics.map(&:id)) oldest_topics = assigned_topics(user, order: :asc).where.not(id: newest_topics.map(&:id))
assigned_topics_count = assigned_count_for(user) assigned_topics_count = assigned_count_for(user)
title = I18n.t('pending_assigns_reminder.title', pending_assignments: assigned_topics_count) title = I18n.t("pending_assigns_reminder.title", pending_assignments: assigned_topics_count)
PostCreator.create!( PostCreator.create!(
Discourse.system_user, Discourse.system_user,
@ -23,7 +23,9 @@ class PendingAssignsReminder
archetype: Archetype.private_message, archetype: Archetype.private_message,
subtype: TopicSubtype.system_message, subtype: TopicSubtype.system_message,
target_usernames: user.username, target_usernames: user.username,
custom_fields: { CUSTOM_FIELD_NAME => true } custom_fields: {
CUSTOM_FIELD_NAME => true,
},
) )
update_last_reminded(user) update_last_reminded(user)
@ -32,38 +34,46 @@ class PendingAssignsReminder
private private
def delete_previous_reminders(user) def delete_previous_reminders(user)
posts = Post posts =
Post
.joins(topic: { topic_allowed_users: :user }) .joins(topic: { topic_allowed_users: :user })
.where(topic: { .where(
topic: {
posts_count: 1, posts_count: 1,
user_id: Discourse.system_user, user_id: Discourse.system_user,
archetype: Archetype.private_message, archetype: Archetype.private_message,
subtype: TopicSubtype.system_message, subtype: TopicSubtype.system_message,
topic_allowed_users: { topic_allowed_users: {
users: { id: user.id } users: {
} id: user.id,
}) },
},
},
)
.joins(topic: :_custom_fields) .joins(topic: :_custom_fields)
.where(topic_custom_fields: { .where(topic_custom_fields: { name: CUSTOM_FIELD_NAME })
name: CUSTOM_FIELD_NAME
})
posts.find_each do |post| posts.find_each { |post| PostDestroyer.new(Discourse.system_user, post).destroy }
PostDestroyer.new(Discourse.system_user, post).destroy
end
end end
def assigned_count_for(user) def assigned_count_for(user)
Assignment.joins_with_topics.where(assigned_to_id: user.id, assigned_to_type: 'User', active: true).count Assignment
.joins_with_topics
.where(assigned_to_id: user.id, assigned_to_type: "User", active: true)
.count
end end
def assigned_topics(user, order:) def assigned_topics(user, order:)
secure = Topic.listable_topics.secured(Guardian.new(user)).or(Topic.private_messages_for_user(user)) secure =
Topic.listable_topics.secured(Guardian.new(user)).or(Topic.private_messages_for_user(user))
Topic Topic
.joins(:assignment) .joins(:assignment)
.select(:slug, :id, :title, :fancy_title, 'assignments.created_at AS assigned_at') .select(:slug, :id, :title, :fancy_title, "assignments.created_at AS assigned_at")
.where("assignments.assigned_to_id = ? AND assignments.assigned_to_type = 'User' AND assignments.active", user.id) .where(
"assignments.assigned_to_id = ? AND assignments.assigned_to_type = 'User' AND assignments.active",
user.id,
)
.merge(secure) .merge(secure)
.order("assignments.created_at #{order}") .order("assignments.created_at #{order}")
.limit(3) .limit(3)
@ -74,20 +84,25 @@ class PendingAssignsReminder
oldest_list = build_list_for(:oldest, last_three_topics) oldest_list = build_list_for(:oldest, last_three_topics)
I18n.t( I18n.t(
'pending_assigns_reminder.body', "pending_assigns_reminder.body",
pending_assignments: assigned_topics_count, pending_assignments: assigned_topics_count,
assignments_link: "#{Discourse.base_url}/u/#{user.username_lower}/activity/assigned", assignments_link: "#{Discourse.base_url}/u/#{user.username_lower}/activity/assigned",
newest_assignments: newest_list, newest_assignments: newest_list,
oldest_assignments: oldest_list, oldest_assignments: oldest_list,
frequency: frequency_in_words(user) frequency: frequency_in_words(user),
) )
end end
def build_list_for(key, topics) def build_list_for(key, topics)
return '' if topics.empty? return "" if topics.empty?
initial_list = { 'topic_0' => '', 'topic_1' => '', 'topic_2' => '' } initial_list = { "topic_0" => "", "topic_1" => "", "topic_2" => "" }
items = topics.each_with_index.reduce(initial_list) do |memo, (t, index)| items =
memo["topic_#{index}"] = "- [#{Emoji.gsub_emoji_to_unicode(t.fancy_title)}](#{t.relative_url}) - assigned #{time_in_words_for(t)}" topics
.each_with_index
.reduce(initial_list) do |memo, (t, index)|
memo[
"topic_#{index}"
] = "- [#{Emoji.gsub_emoji_to_unicode(t.fancy_title)}](#{t.relative_url}) - assigned #{time_in_words_for(t)}"
memo memo
end end
@ -96,12 +111,16 @@ class PendingAssignsReminder
def time_in_words_for(topic) def time_in_words_for(topic)
FreedomPatches::Rails4.distance_of_time_in_words( FreedomPatches::Rails4.distance_of_time_in_words(
Time.zone.now, topic.assigned_at.to_time, false, scope: 'datetime.distance_in_words_verbose' Time.zone.now,
topic.assigned_at.to_time,
false,
scope: "datetime.distance_in_words_verbose",
) )
end end
def frequency_in_words(user) def frequency_in_words(user)
frequency = if user.custom_fields&.has_key?(REMINDERS_FREQUENCY) frequency =
if user.custom_fields&.has_key?(REMINDERS_FREQUENCY)
user.custom_fields[REMINDERS_FREQUENCY] user.custom_fields[REMINDERS_FREQUENCY]
else else
SiteSetting.remind_assigns_frequency SiteSetting.remind_assigns_frequency

View File

@ -10,11 +10,9 @@ class RandomAssignUtils
end end
def self.automation_script!(context, fields, automation) def self.automation_script!(context, fields, automation)
unless SiteSetting.assign_enabled? raise_error(automation, "discourse-assign is not enabled") unless SiteSetting.assign_enabled?
raise_error(automation, "discourse-assign is not enabled")
end
unless topic_id = fields.dig('assigned_topic', 'value') unless topic_id = fields.dig("assigned_topic", "value")
raise_error(automation, "`assigned_topic` not provided") raise_error(automation, "`assigned_topic` not provided")
end end
@ -22,16 +20,17 @@ class RandomAssignUtils
raise_error(automation, "Topic(#{topic_id}) not found") raise_error(automation, "Topic(#{topic_id}) not found")
end end
min_hours = fields.dig('minimum_time_between_assignments', 'value').presence min_hours = fields.dig("minimum_time_between_assignments", "value").presence
if min_hours && TopicCustomField if min_hours &&
.where(name: 'assigned_to_id', topic_id: topic_id) TopicCustomField
.where('created_at < ?', min_hours.to_i.hours.ago) .where(name: "assigned_to_id", topic_id: topic_id)
.where("created_at < ?", min_hours.to_i.hours.ago)
.exists? .exists?
log_info(automation, "Topic(#{topic_id}) has already been assigned recently") log_info(automation, "Topic(#{topic_id}) has already been assigned recently")
return return
end end
unless group_id = fields.dig('assignees_group', 'value') unless group_id = fields.dig("assignees_group", "value")
raise_error(automation, "`assignees_group` not provided") raise_error(automation, "`assignees_group` not provided")
end end
@ -39,19 +38,18 @@ class RandomAssignUtils
raise_error(automation, "Group(#{group_id}) not found") raise_error(automation, "Group(#{group_id}) not found")
end end
users_on_holiday = Set.new( users_on_holiday =
User Set.new(
.where(id: User.where(
UserCustomField id: UserCustomField.where(name: "on_holiday", value: "t").select(:user_id),
.where(name: 'on_holiday', value: 't') ).pluck(:id),
.select(:user_id)
).pluck(:id)
) )
group_users_ids = group group_users_ids =
group
.group_users .group_users
.joins(:user) .joins(:user)
.pluck('users.id') .pluck("users.id")
.reject { |user_id| users_on_holiday.include?(user_id) } .reject { |user_id| users_on_holiday.include?(user_id) }
if group_users_ids.empty? if group_users_ids.empty?
@ -59,12 +57,16 @@ class RandomAssignUtils
return return
end end
max_recently_assigned_days = (fields.dig('max_recently_assigned_days', 'value').presence || 180).to_i.days.ago max_recently_assigned_days =
last_assignees_ids = RandomAssignUtils.recently_assigned_users_ids(topic_id, max_recently_assigned_days) (fields.dig("max_recently_assigned_days", "value").presence || 180).to_i.days.ago
last_assignees_ids =
RandomAssignUtils.recently_assigned_users_ids(topic_id, max_recently_assigned_days)
users_ids = group_users_ids - last_assignees_ids users_ids = group_users_ids - last_assignees_ids
if users_ids.blank? if users_ids.blank?
min_recently_assigned_days = (fields.dig('min_recently_assigned_days', 'value').presence || 14).to_i.days.ago min_recently_assigned_days =
recently_assigned_users_ids = RandomAssignUtils.recently_assigned_users_ids(topic_id, min_recently_assigned_days) (fields.dig("min_recently_assigned_days", "value").presence || 14).to_i.days.ago
recently_assigned_users_ids =
RandomAssignUtils.recently_assigned_users_ids(topic_id, min_recently_assigned_days)
users_ids = group_users_ids - recently_assigned_users_ids users_ids = group_users_ids - recently_assigned_users_ids
end end
@ -73,10 +75,9 @@ class RandomAssignUtils
return return
end end
if fields.dig('in_working_hours', 'value') if fields.dig("in_working_hours", "value")
assign_to_user_id = users_ids.shuffle.find do |user_id| assign_to_user_id =
RandomAssignUtils.in_working_hours?(user_id) users_ids.shuffle.find { |user_id| RandomAssignUtils.in_working_hours?(user_id) }
end
end end
assign_to_user_id ||= users_ids.sample assign_to_user_id ||= users_ids.sample
@ -87,35 +88,34 @@ class RandomAssignUtils
assign_to = User.find(assign_to_user_id) assign_to = User.find(assign_to_user_id)
result = nil result = nil
if raw = fields.dig('post_template', 'value').presence if raw = fields.dig("post_template", "value").presence
post = PostCreator.new( post =
PostCreator.new(
Discourse.system_user, Discourse.system_user,
raw: raw, raw: raw,
skip_validations: true, skip_validations: true,
topic_id: topic.id topic_id: topic.id,
).create! ).create!
result = Assigner.new(post, Discourse.system_user).assign(assign_to) result = Assigner.new(post, Discourse.system_user).assign(assign_to)
if !result[:success] PostDestroyer.new(Discourse.system_user, post).destroy if !result[:success]
PostDestroyer.new(Discourse.system_user, post).destroy
end
else else
result = Assigner.new(topic, Discourse.system_user).assign(assign_to) result = Assigner.new(topic, Discourse.system_user).assign(assign_to)
end end
if !result[:success] RandomAssignUtils.no_one!(topic_id, group.name) if !result[:success]
RandomAssignUtils.no_one!(topic_id, group.name)
end
end end
def self.recently_assigned_users_ids(topic_id, from) def self.recently_assigned_users_ids(topic_id, from)
posts = Post posts =
Post
.joins(:user) .joins(:user)
.where(topic_id: topic_id, action_code: ['assigned', 'reassigned', 'assigned_to_post']) .where(topic_id: topic_id, action_code: %w[assigned reassigned assigned_to_post])
.where('posts.created_at > ?', from) .where("posts.created_at > ?", from)
.order(created_at: :desc) .order(created_at: :desc)
usernames = Post.custom_fields_for_ids(posts, [:action_code_who]).map { |_, v| v['action_code_who'] }.uniq usernames =
Post.custom_fields_for_ids(posts, [:action_code_who]).map { |_, v| v["action_code_who"] }.uniq
User.where(username: usernames).limit(100).pluck(:id) User.where(username: usernames).limit(100).pluck(:id)
end end
@ -126,7 +126,9 @@ class RandomAssignUtils
begin begin
tzinfo = ActiveSupport::TimeZone.find_tzinfo(timezone) tzinfo = ActiveSupport::TimeZone.find_tzinfo(timezone)
rescue TZInfo::InvalidTimezoneIdentifier rescue TZInfo::InvalidTimezoneIdentifier
Rails.logger.warn("#{User.find_by(id: user_id)&.username} has the timezone #{timezone} set, we do not know how to parse it in Rails (assuming UTC)") Rails.logger.warn(
"#{User.find_by(id: user_id)&.username} has the timezone #{timezone} set, we do not know how to parse it in Rails (assuming UTC)",
)
timezone = "UTC" timezone = "UTC"
tzinfo = ActiveSupport::TimeZone.find_tzinfo(timezone) tzinfo = ActiveSupport::TimeZone.find_tzinfo(timezone)
end end
@ -139,7 +141,7 @@ class RandomAssignUtils
Discourse.system_user, Discourse.system_user,
topic_id: topic_id, topic_id: topic_id,
raw: I18n.t("discourse_automation.scriptables.random_assign.no_one", group: group), raw: I18n.t("discourse_automation.scriptables.random_assign.no_one", group: group),
validate: false validate: false,
) )
end end
@ -147,9 +149,6 @@ class RandomAssignUtils
tzinfo = RandomAssignUtils.user_tzinfo(user_id) tzinfo = RandomAssignUtils.user_tzinfo(user_id)
tztime = tzinfo.now tztime = tzinfo.now
!tztime.saturday? && !tztime.saturday? && !tztime.sunday? && tztime.hour > 7 && tztime.hour < 11
!tztime.sunday? &&
tztime.hour > 7 &&
tztime.hour < 11
end end
end end

View File

@ -37,6 +37,10 @@ class ::TopicAssigner
end end
def self.deprecation_note def self.deprecation_note
Discourse.deprecate("TopicAssigner class is deprecated, use Assigner", since: "2.8", drop_from: "2.9") Discourse.deprecate(
"TopicAssigner class is deprecated, use Assigner",
since: "2.8",
drop_from: "2.9",
)
end end
end end

424
plugin.rb
View File

@ -9,34 +9,46 @@
enabled_site_setting :assign_enabled enabled_site_setting :assign_enabled
register_asset 'stylesheets/assigns.scss' register_asset "stylesheets/assigns.scss"
register_asset 'stylesheets/mobile/assigns.scss', :mobile register_asset "stylesheets/mobile/assigns.scss", :mobile
register_svg_icon "user-plus" register_svg_icon "user-plus"
register_svg_icon "user-times" register_svg_icon "user-times"
%w[user-plus user-times group-plus group-times].each { |i| register_svg_icon(i) } %w[user-plus user-times group-plus group-times].each { |i| register_svg_icon(i) }
load File.expand_path('../lib/discourse_assign/engine.rb', __FILE__) load File.expand_path("../lib/discourse_assign/engine.rb", __FILE__)
load File.expand_path('../lib/discourse_assign/helpers.rb', __FILE__) load File.expand_path("../lib/discourse_assign/helpers.rb", __FILE__)
Discourse::Application.routes.append do Discourse::Application.routes.append do
mount ::DiscourseAssign::Engine, at: "/assign" mount ::DiscourseAssign::Engine, at: "/assign"
get "topics/private-messages-assigned/:username" => "list#private_messages_assigned", as: "topics_private_messages_assigned", constraints: { username: ::RouteFormat.username } get "topics/private-messages-assigned/:username" => "list#private_messages_assigned",
get "/topics/messages-assigned/:username" => "list#messages_assigned", constraints: { username: ::RouteFormat.username }, as: "messages_assigned" :as => "topics_private_messages_assigned",
get "/topics/group-topics-assigned/:groupname" => "list#group_topics_assigned", constraints: { username: ::RouteFormat.username }, as: "group_topics_assigned" :constraints => {
username: ::RouteFormat.username,
}
get "/topics/messages-assigned/:username" => "list#messages_assigned",
:constraints => {
username: ::RouteFormat.username,
},
:as => "messages_assigned"
get "/topics/group-topics-assigned/:groupname" => "list#group_topics_assigned",
:constraints => {
username: ::RouteFormat.username,
},
:as => "group_topics_assigned"
get "/g/:id/assigned" => "groups#index" get "/g/:id/assigned" => "groups#index"
get "/g/:id/assigned/:route_type" => "groups#index" get "/g/:id/assigned/:route_type" => "groups#index"
end end
after_initialize do after_initialize do
require File.expand_path('../jobs/scheduled/enqueue_reminders.rb', __FILE__) require File.expand_path("../jobs/scheduled/enqueue_reminders.rb", __FILE__)
require File.expand_path('../jobs/regular/remind_user.rb', __FILE__) require File.expand_path("../jobs/regular/remind_user.rb", __FILE__)
require File.expand_path('../jobs/regular/assign_notification.rb', __FILE__) require File.expand_path("../jobs/regular/assign_notification.rb", __FILE__)
require File.expand_path('../jobs/regular/unassign_notification.rb', __FILE__) require File.expand_path("../jobs/regular/unassign_notification.rb", __FILE__)
require 'topic_assigner' require "topic_assigner"
require 'assigner' require "assigner"
require 'pending_assigns_reminder' require "pending_assigns_reminder"
register_group_param(:assignable_level) register_group_param(:assignable_level)
register_groups_callback_for_users_search_controller_action(:assignable_groups) do |groups, user| register_groups_callback_for_users_search_controller_action(:assignable_groups) do |groups, user|
@ -53,15 +65,20 @@ after_initialize do
end end
class ::Group class ::Group
scope :assignable, ->(user) { scope :assignable,
where("assignable_level in (:levels) OR ->(user) {
where(
"assignable_level in (:levels) OR
( (
assignable_level = #{ALIAS_LEVELS[:members_mods_and_admins]} AND id in ( assignable_level = #{ALIAS_LEVELS[:members_mods_and_admins]} AND id in (
SELECT group_id FROM group_users WHERE user_id = :user_id) SELECT group_id FROM group_users WHERE user_id = :user_id)
) OR ( ) OR (
assignable_level = #{ALIAS_LEVELS[:owners_mods_and_admins]} AND id in ( assignable_level = #{ALIAS_LEVELS[:owners_mods_and_admins]} AND id in (
SELECT group_id FROM group_users WHERE user_id = :user_id AND owner IS TRUE) SELECT group_id FROM group_users WHERE user_id = :user_id AND owner IS TRUE)
)", levels: alias_levels(user), user_id: user && user.id) )",
levels: alias_levels(user),
user_id: user && user.id,
)
} }
end end
end end
@ -70,17 +87,13 @@ after_initialize do
register_editable_user_custom_field frequency_field register_editable_user_custom_field frequency_field
User.register_custom_field_type frequency_field, :integer User.register_custom_field_type frequency_field, :integer
DiscoursePluginRegistry.serialized_current_user_fields << frequency_field DiscoursePluginRegistry.serialized_current_user_fields << frequency_field
add_to_serializer(:user, :reminders_frequency) do add_to_serializer(:user, :reminders_frequency) { RemindAssignsFrequencySiteSettings.values }
RemindAssignsFrequencySiteSettings.values
end
add_to_serializer(:group_show, :assignment_count) do add_to_serializer(:group_show, :assignment_count) do
Topic Topic.joins(<<~SQL).where(<<~SQL, group_id: object.id).where("topics.deleted_at IS NULL").count
.joins(<<~SQL)
JOIN assignments a JOIN assignments a
ON topics.id = a.topic_id AND a.assigned_to_id IS NOT NULL ON topics.id = a.topic_id AND a.assigned_to_id IS NOT NULL
SQL SQL
.where(<<~SQL, group_id: object.id)
a.active AND a.active AND
(( ((
a.assigned_to_type = 'User' AND a.assigned_to_id IN ( a.assigned_to_type = 'User' AND a.assigned_to_id IN (
@ -92,28 +105,20 @@ after_initialize do
a.assigned_to_type = 'Group' AND a.assigned_to_id = :group_id a.assigned_to_type = 'Group' AND a.assigned_to_id = :group_id
)) ))
SQL SQL
.where("topics.deleted_at IS NULL")
.count
end end
add_to_serializer(:group_show, 'include_assignment_count?') do add_to_serializer(:group_show, "include_assignment_count?") { scope.can_assign? }
scope.can_assign?
end
add_to_serializer(:group_show, :assignable_level) do add_to_serializer(:group_show, :assignable_level) { object.assignable_level }
object.assignable_level
end
add_to_serializer(:group_show, :can_show_assigned_tab?) do add_to_serializer(:group_show, :can_show_assigned_tab?) { object.can_show_assigned_tab? }
object.can_show_assigned_tab?
end
add_model_callback(UserCustomField, :before_save) do add_model_callback(UserCustomField, :before_save) do
self.value = self.value.to_i if self.name == frequency_field self.value = self.value.to_i if self.name == frequency_field
end end
add_class_method(:group, :assign_allowed_groups) do add_class_method(:group, :assign_allowed_groups) do
allowed_groups = SiteSetting.assign_allowed_on_groups.split('|') allowed_groups = SiteSetting.assign_allowed_on_groups.split("|")
where(id: allowed_groups) where(id: allowed_groups)
end end
@ -121,21 +126,24 @@ after_initialize do
@can_assign ||= @can_assign ||=
begin begin
return true if admin? return true if admin?
allowed_groups = SiteSetting.assign_allowed_on_groups.split('|').compact allowed_groups = SiteSetting.assign_allowed_on_groups.split("|").compact
allowed_groups.present? && groups.where(id: allowed_groups).exists? ? allowed_groups.present? && groups.where(id: allowed_groups).exists? ? :true : :false
:true : :false
end end
@can_assign == :true @can_assign == :true
end end
add_to_serializer(:current_user, :never_auto_track_topics) do add_to_serializer(:current_user, :never_auto_track_topics) do
(user.user_option.auto_track_topics_after_msecs || SiteSetting.default_other_auto_track_topics_after_msecs) < 0 (
user.user_option.auto_track_topics_after_msecs ||
SiteSetting.default_other_auto_track_topics_after_msecs
) < 0
end end
add_to_class(:group, :can_show_assigned_tab?) do add_to_class(:group, :can_show_assigned_tab?) do
allowed_group_ids = SiteSetting.assign_allowed_on_groups.split("|") allowed_group_ids = SiteSetting.assign_allowed_on_groups.split("|")
group_has_disallowed_users = DB.query_single(<<~SQL, allowed_group_ids: allowed_group_ids, current_group_id: self.id)[0] group_has_disallowed_users =
DB.query_single(<<~SQL, allowed_group_ids: allowed_group_ids, current_group_id: self.id)[0]
SELECT EXISTS( SELECT EXISTS(
SELECT 1 FROM users SELECT 1 FROM users
JOIN group_users current_group_users JOIN group_users current_group_users
@ -154,11 +162,12 @@ after_initialize do
add_to_class(:guardian, :can_assign?) { user && user.can_assign? } add_to_class(:guardian, :can_assign?) { user && user.can_assign? }
add_class_method(:user, :assign_allowed) do add_class_method(:user, :assign_allowed) do
allowed_groups = SiteSetting.assign_allowed_on_groups.split('|') allowed_groups = SiteSetting.assign_allowed_on_groups.split("|")
# The UNION against admin users is necessary because bot users like the system user are given the admin status but # The UNION against admin users is necessary because bot users like the system user are given the admin status but
# are not added into the admin group. # are not added into the admin group.
where("users.id IN ( where(
"users.id IN (
SELECT SELECT
user_id user_id
FROM group_users FROM group_users
@ -169,39 +178,46 @@ after_initialize do
SELECT id SELECT id
FROM users FROM users
WHERE users.admin WHERE users.admin
)", allowed_groups) )",
allowed_groups,
)
end end
add_model_callback(Group, :before_update) do add_model_callback(Group, :before_update) do
if name_changed? if name_changed?
SiteSetting.assign_allowed_on_groups = SiteSetting.assign_allowed_on_groups.gsub(name_was, name) SiteSetting.assign_allowed_on_groups =
SiteSetting.assign_allowed_on_groups.gsub(name_was, name)
end end
end end
add_model_callback(Group, :before_destroy) do add_model_callback(Group, :before_destroy) do
new_setting = SiteSetting.assign_allowed_on_groups.gsub(/#{id}[|]?/, '') new_setting = SiteSetting.assign_allowed_on_groups.gsub(/#{id}[|]?/, "")
new_setting = new_setting.chomp('|') if new_setting.ends_with?('|') new_setting = new_setting.chomp("|") if new_setting.ends_with?("|")
SiteSetting.assign_allowed_on_groups = new_setting SiteSetting.assign_allowed_on_groups = new_setting
end end
on(:assign_topic) do |topic, user, assigning_user, force| on(:assign_topic) do |topic, user, assigning_user, force|
if force || !Assignment.exists?(target: topic) Assigner.new(topic, assigning_user).assign(user) if force || !Assignment.exists?(target: topic)
Assigner.new(topic, assigning_user).assign(user)
end
end end
on(:unassign_topic) do |topic, unassigning_user| on(:unassign_topic) { |topic, unassigning_user| Assigner.new(topic, unassigning_user).unassign }
Assigner.new(topic, unassigning_user).unassign
end
Site.preloaded_category_custom_fields << "enable_unassigned_filter" Site.preloaded_category_custom_fields << "enable_unassigned_filter"
BookmarkQuery.on_preload do |bookmarks, bookmark_query| BookmarkQuery.on_preload do |bookmarks, bookmark_query|
if SiteSetting.assign_enabled? if SiteSetting.assign_enabled?
topics = Bookmark.select_type(bookmarks, "Topic").map(&:bookmarkable).concat( topics =
Bookmark.select_type(bookmarks, "Post").map { |bm| bm.bookmarkable.topic } Bookmark
).uniq .select_type(bookmarks, "Topic")
assignments = Assignment.strict_loading.where(topic_id: topics).includes(:assigned_to).index_by(&:topic_id) .map(&:bookmarkable)
.concat(Bookmark.select_type(bookmarks, "Post").map { |bm| bm.bookmarkable.topic })
.uniq
assignments =
Assignment
.strict_loading
.where(topic_id: topics)
.includes(:assigned_to)
.index_by(&:topic_id)
topics.each do |topic| topics.each do |topic|
assigned_to = assignments[topic.id]&.assigned_to assigned_to = assignments[topic.id]&.assigned_to
@ -223,22 +239,40 @@ after_initialize do
assignments = Assignment.strict_loading.where(topic: topics, active: true).includes(:target) assignments = Assignment.strict_loading.where(topic: topics, active: true).includes(:target)
assignments_map = assignments.group_by(&:topic_id) assignments_map = assignments.group_by(&:topic_id)
user_ids = assignments.filter { |assignment| assignment.assigned_to_user? }.map(&:assigned_to_id) user_ids =
assignments.filter { |assignment| assignment.assigned_to_user? }.map(&:assigned_to_id)
users_map = User.where(id: user_ids).select(UserLookup.lookup_columns).index_by(&:id) users_map = User.where(id: user_ids).select(UserLookup.lookup_columns).index_by(&:id)
group_ids = assignments.filter { |assignment| assignment.assigned_to_group? }.map(&:assigned_to_id) group_ids =
assignments.filter { |assignment| assignment.assigned_to_group? }.map(&:assigned_to_id)
groups_map = Group.where(id: group_ids).index_by(&:id) groups_map = Group.where(id: group_ids).index_by(&:id)
topics.each do |topic| topics.each do |topic|
assignments = assignments_map[topic.id] assignments = assignments_map[topic.id]
direct_assignment = assignments&.find { |assignment| assignment.target_type == "Topic" && assignment.target_id == topic.id } direct_assignment =
assignments&.find do |assignment|
assignment.target_type == "Topic" && assignment.target_id == topic.id
end
indirectly_assigned_to = {} indirectly_assigned_to = {}
assignments&.each do |assignment| assignments
&.each do |assignment|
next if assignment.target_type == "Topic" next if assignment.target_type == "Topic"
next if !assignment.target next if !assignment.target
next indirectly_assigned_to[assignment.target_id] = { assigned_to: users_map[assignment.assigned_to_id], post_number: assignment.target.post_number } if assignment&.assigned_to_user? next(
next indirectly_assigned_to[assignment.target_id] = { assigned_to: groups_map[assignment.assigned_to_id], post_number: assignment.target.post_number } if assignment&.assigned_to_group? indirectly_assigned_to[assignment.target_id] = {
end&.compact&.uniq assigned_to: users_map[assignment.assigned_to_id],
post_number: assignment.target.post_number,
}
) if assignment&.assigned_to_user?
next(
indirectly_assigned_to[assignment.target_id] = {
assigned_to: groups_map[assignment.assigned_to_id],
post_number: assignment.target.post_number,
}
) if assignment&.assigned_to_group?
end
&.compact
&.uniq
assigned_to = assigned_to =
if direct_assignment&.assigned_to_user? if direct_assignment&.assigned_to_user?
@ -260,20 +294,31 @@ after_initialize do
if allowed_access && results.posts.length > 0 if allowed_access && results.posts.length > 0
topics = results.posts.map(&:topic) topics = results.posts.map(&:topic)
assignments = Assignment.strict_loading.where(topic: topics, active: true).includes(:assigned_to, :target).group_by(&:topic_id) assignments =
Assignment
.strict_loading
.where(topic: topics, active: true)
.includes(:assigned_to, :target)
.group_by(&:topic_id)
results.posts.each do |post| results.posts.each do |post|
topic_assignments = assignments[post.topic.id] topic_assignments = assignments[post.topic.id]
direct_assignment = topic_assignments&.find { |assignment| assignment.target_type == "Topic" } direct_assignment =
indirect_assignments = topic_assignments&.select { |assignment| assignment.target_type == "Post" } topic_assignments&.find { |assignment| assignment.target_type == "Topic" }
indirect_assignments =
topic_assignments&.select { |assignment| assignment.target_type == "Post" }
if direct_assignment if direct_assignment
assigned_to = direct_assignment.assigned_to assigned_to = direct_assignment.assigned_to
post.topic.preload_assigned_to(assigned_to) post.topic.preload_assigned_to(assigned_to)
end end
if indirect_assignments.present? if indirect_assignments.present?
indirect_assignment_map = indirect_assignments.reduce({}) do |acc, assignment| indirect_assignment_map =
indirect_assignments.reduce({}) do |acc, assignment|
if assignment.target if assignment.target
acc[assignment.target_id] = { assigned_to: assignment.assigned_to, post_number: assignment.target.post_number } acc[assignment.target_id] = {
assigned_to: assignment.assigned_to,
post_number: assignment.target.post_number,
}
end end
acc acc
end end
@ -285,7 +330,7 @@ after_initialize do
end end
# TopicQuery # TopicQuery
require_dependency 'topic_query' require_dependency "topic_query"
TopicQuery.add_custom_filter(:assigned) do |results, topic_query| TopicQuery.add_custom_filter(:assigned) do |results, topic_query|
name = topic_query.options[:assigned] name = topic_query.options[:assigned]
next results if name.blank? next results if name.blank?
@ -293,32 +338,42 @@ after_initialize do
next results if !topic_query.guardian.can_assign? && !SiteSetting.assigns_public next results if !topic_query.guardian.can_assign? && !SiteSetting.assigns_public
if name == "nobody" if name == "nobody"
next results next(
.joins("LEFT JOIN assignments a ON a.topic_id = topics.id AND active") results.joins("LEFT JOIN assignments a ON a.topic_id = topics.id AND active").where(
.where("a.assigned_to_id IS NULL") "a.assigned_to_id IS NULL",
)
)
end end
if name == "*" if name == "*"
next results next(
.joins("JOIN assignments a ON a.topic_id = topics.id AND active") results.joins("JOIN assignments a ON a.topic_id = topics.id AND active").where(
.where("a.assigned_to_id IS NOT NULL") "a.assigned_to_id IS NOT NULL",
)
)
end end
user_id = topic_query.guardian.user.id if name == "me" user_id = topic_query.guardian.user.id if name == "me"
user_id ||= User.where(username_lower: name.downcase).pluck_first(:id) user_id ||= User.where(username_lower: name.downcase).pluck_first(:id)
if user_id if user_id
next results next(
.joins("JOIN assignments a ON a.topic_id = topics.id AND active") results.joins("JOIN assignments a ON a.topic_id = topics.id AND active").where(
.where("a.assigned_to_id = ? AND a.assigned_to_type = 'User'", user_id) "a.assigned_to_id = ? AND a.assigned_to_type = 'User'",
user_id,
)
)
end end
group_id = Group.where(name: name.downcase).pluck_first(:id) group_id = Group.where(name: name.downcase).pluck_first(:id)
if group_id if group_id
next results next(
.joins("JOIN assignments a ON a.topic_id = topics.id AND active") results.joins("JOIN assignments a ON a.topic_id = topics.id AND active").where(
.where("a.assigned_to_id = ? AND a.assigned_to_type = 'Group'", group_id) "a.assigned_to_id = ? AND a.assigned_to_type = 'Group'",
group_id,
)
)
end end
next results next results
@ -334,11 +389,9 @@ after_initialize do
(assigned_to_id = :user_id AND assigned_to_type = 'User' AND active) (assigned_to_id = :user_id AND assigned_to_type = 'User' AND active)
SQL SQL
if @options[:filter] != :direct topic_ids_sql << <<~SQL if @options[:filter] != :direct
topic_ids_sql << <<~SQL
OR (assigned_to_id IN (group_users.group_id) AND assigned_to_type = 'Group' AND active) OR (assigned_to_id IN (group_users.group_id) AND assigned_to_type = 'Group' AND active)
SQL SQL
end
sql = "topics.id IN (#{topic_ids_sql})" sql = "topics.id IN (#{topic_ids_sql})"
@ -357,13 +410,11 @@ after_initialize do
) )
SQL SQL
if @options[:filter] != :direct topic_ids_sql << <<~SQL if @options[:filter] != :direct
topic_ids_sql << <<~SQL
OR ( OR (
assigned_to_id IN (SELECT user_id from group_users where group_id = :group_id) AND assigned_to_type = 'User' AND active assigned_to_id IN (SELECT user_id from group_users where group_id = :group_id) AND assigned_to_type = 'User' AND active
) )
SQL SQL
end
sql = "topics.id IN (#{topic_ids_sql})" sql = "topics.id IN (#{topic_ids_sql})"
@ -395,7 +446,7 @@ after_initialize do
end end
# ListController # ListController
require_dependency 'list_controller' require_dependency "list_controller"
class ::ListController class ::ListController
generate_message_route(:private_messages_assigned) generate_message_route(:private_messages_assigned)
end end
@ -441,15 +492,21 @@ after_initialize do
add_to_class(:topic, :indirectly_assigned_to) do add_to_class(:topic, :indirectly_assigned_to) do
return @indirectly_assigned_to if defined?(@indirectly_assigned_to) return @indirectly_assigned_to if defined?(@indirectly_assigned_to)
@indirectly_assigned_to = Assignment.where(topic_id: id, target_type: "Post", active: true).includes(:target).inject({}) do |acc, assignment| @indirectly_assigned_to =
acc[assignment.target_id] = { assigned_to: assignment.assigned_to, post_number: assignment.target.post_number, assignment_note: assignment.note } if assignment.target Assignment
.where(topic_id: id, target_type: "Post", active: true)
.includes(:target)
.inject({}) do |acc, assignment|
acc[assignment.target_id] = {
assigned_to: assignment.assigned_to,
post_number: assignment.target.post_number,
assignment_note: assignment.note,
} if assignment.target
acc acc
end end
end end
add_to_class(:topic, :preload_assigned_to) do |assigned_to| add_to_class(:topic, :preload_assigned_to) { |assigned_to| @assigned_to = assigned_to }
@assigned_to = assigned_to
end
add_to_class(:topic, :preload_indirectly_assigned_to) do |indirectly_assigned_to| add_to_class(:topic, :preload_indirectly_assigned_to) do |indirectly_assigned_to|
@indirectly_assigned_to = indirectly_assigned_to @indirectly_assigned_to = indirectly_assigned_to
@ -457,17 +514,17 @@ after_initialize do
# TopicList serializer # TopicList serializer
add_to_serializer(:topic_list, :assigned_messages_count) do add_to_serializer(:topic_list, :assigned_messages_count) do
TopicQuery.new(object.current_user, guardian: scope, limit: false) TopicQuery
.new(object.current_user, guardian: scope, limit: false)
.private_messages_assigned_query(object.current_user) .private_messages_assigned_query(object.current_user)
.count .count
end end
add_to_serializer(:topic_list, 'include_assigned_messages_count?') do add_to_serializer(:topic_list, "include_assigned_messages_count?") do
options = object.instance_variable_get(:@opts) options = object.instance_variable_get(:@opts)
if assigned_user = options.dig(:assigned) if assigned_user = options.dig(:assigned)
scope.can_assign? || scope.can_assign? || assigned_user.downcase == scope.current_user&.username_lower
assigned_user.downcase == scope.current_user&.username_lower
end end
end end
@ -489,16 +546,18 @@ after_initialize do
end end
add_to_serializer(:topic_view, :indirectly_assigned_to) do add_to_serializer(:topic_view, :indirectly_assigned_to) do
DiscourseAssign::Helpers.build_indirectly_assigned_to(object.topic.indirectly_assigned_to, object.topic) DiscourseAssign::Helpers.build_indirectly_assigned_to(
object.topic.indirectly_assigned_to,
object.topic,
)
end end
add_to_serializer(:topic_view, :include_indirectly_assigned_to?) do add_to_serializer(:topic_view, :include_indirectly_assigned_to?) do
(SiteSetting.assigns_public || scope.can_assign?) && object.topic.indirectly_assigned_to.present? (SiteSetting.assigns_public || scope.can_assign?) &&
object.topic.indirectly_assigned_to.present?
end end
add_to_serializer(:topic_view, :assignment_note, false) do add_to_serializer(:topic_view, :assignment_note, false) { object.topic.assignment.note }
object.topic.assignment.note
end
add_to_serializer(:topic_view, :include_assignment_note?, false) do add_to_serializer(:topic_view, :include_assignment_note?, false) do
(SiteSetting.assigns_public || scope.can_assign?) && object.topic.assignment.present? (SiteSetting.assigns_public || scope.can_assign?) && object.topic.assignment.present?
@ -559,7 +618,7 @@ after_initialize do
DiscourseAssign::Helpers.build_assigned_to_user(object.assigned_to, object) DiscourseAssign::Helpers.build_assigned_to_user(object.assigned_to, object)
end end
add_to_serializer(:search_topic_list_item, 'include_assigned_to_user?') do add_to_serializer(:search_topic_list_item, "include_assigned_to_user?") do
(SiteSetting.assigns_public || scope.can_assign?) && object.assigned_to&.is_a?(User) (SiteSetting.assigns_public || scope.can_assign?) && object.assigned_to&.is_a?(User)
end end
@ -567,7 +626,7 @@ after_initialize do
BasicGroupSerializer.new(object.assigned_to, scope: scope, root: false).as_json BasicGroupSerializer.new(object.assigned_to, scope: scope, root: false).as_json
end end
add_to_serializer(:search_topic_list_item, 'include_assigned_to_group?') do add_to_serializer(:search_topic_list_item, "include_assigned_to_group?") do
(SiteSetting.assigns_public || scope.can_assign?) && object.assigned_to&.is_a?(Group) (SiteSetting.assigns_public || scope.can_assign?) && object.assigned_to&.is_a?(Group)
end end
@ -583,30 +642,25 @@ after_initialize do
TopicsBulkAction.register_operation("assign") do TopicsBulkAction.register_operation("assign") do
if @user.can_assign? if @user.can_assign?
assign_user = User.find_by_username(@operation[:username]) assign_user = User.find_by_username(@operation[:username])
topics.each do |topic| topics.each { |topic| Assigner.new(topic, @user).assign(assign_user) }
Assigner.new(topic, @user).assign(assign_user)
end
end end
end end
TopicsBulkAction.register_operation("unassign") do TopicsBulkAction.register_operation("unassign") do
if @user.can_assign? if @user.can_assign?
topics.each do |topic| topics.each { |topic| Assigner.new(topic, @user).unassign if guardian.can_assign? }
if guardian.can_assign?
Assigner.new(topic, @user).unassign
end
end
end end
end end
register_permitted_bulk_action_parameter :username register_permitted_bulk_action_parameter :username
add_to_class(:user_bookmark_base_serializer, :assigned_to) do add_to_class(:user_bookmark_base_serializer, :assigned_to) do
@assigned_to ||= bookmarkable_type == "Topic" ? bookmarkable.assigned_to : bookmarkable.topic.assigned_to @assigned_to ||=
bookmarkable_type == "Topic" ? bookmarkable.assigned_to : bookmarkable.topic.assigned_to
end end
add_to_class(:user_bookmark_base_serializer, :can_have_assignment?) do add_to_class(:user_bookmark_base_serializer, :can_have_assignment?) do
["Post", "Topic"].include?(bookmarkable_type) %w[Post Topic].include?(bookmarkable_type)
end end
add_to_serializer(:user_bookmark_base, :assigned_to_user, false) do add_to_serializer(:user_bookmark_base, :assigned_to_user, false) do
@ -614,7 +668,7 @@ after_initialize do
BasicUserSerializer.new(assigned_to, scope: scope, root: false).as_json BasicUserSerializer.new(assigned_to, scope: scope, root: false).as_json
end end
add_to_serializer(:user_bookmark_base, 'include_assigned_to_user?') do add_to_serializer(:user_bookmark_base, "include_assigned_to_user?") do
return false if !can_have_assignment? return false if !can_have_assignment?
(SiteSetting.assigns_public || scope.can_assign?) && assigned_to&.is_a?(User) (SiteSetting.assigns_public || scope.can_assign?) && assigned_to&.is_a?(User)
end end
@ -624,56 +678,48 @@ after_initialize do
BasicGroupSerializer.new(assigned_to, scope: scope, root: false).as_json BasicGroupSerializer.new(assigned_to, scope: scope, root: false).as_json
end end
add_to_serializer(:user_bookmark_base, 'include_assigned_to_group?') do add_to_serializer(:user_bookmark_base, "include_assigned_to_group?") do
return false if !can_have_assignment? return false if !can_have_assignment?
(SiteSetting.assigns_public || scope.can_assign?) && assigned_to&.is_a?(Group) (SiteSetting.assigns_public || scope.can_assign?) && assigned_to&.is_a?(Group)
end end
add_to_serializer(:basic_user, :assign_icon) do add_to_serializer(:basic_user, :assign_icon) { "user-plus" }
'user-plus'
end
add_to_serializer(:basic_user, :assign_path) do add_to_serializer(:basic_user, :assign_path) do
return if !object.is_a?(User) return if !object.is_a?(User)
SiteSetting.assigns_user_url_path.gsub("{username}", object.username) SiteSetting.assigns_user_url_path.gsub("{username}", object.username)
end end
add_to_serializer(:basic_group, :assign_icon) do add_to_serializer(:basic_group, :assign_icon) { "group-plus" }
'group-plus'
end
add_to_serializer(:basic_group, :assign_path) do add_to_serializer(:basic_group, :assign_path) { "/g/#{object.name}/assigned/everyone" }
"/g/#{object.name}/assigned/everyone"
end
# PostSerializer # PostSerializer
add_to_serializer(:post, :assigned_to_user) do add_to_serializer(:post, :assigned_to_user) do
BasicUserSerializer.new(object.assignment.assigned_to, scope: scope, root: false).as_json BasicUserSerializer.new(object.assignment.assigned_to, scope: scope, root: false).as_json
end end
add_to_serializer(:post, 'include_assigned_to_user?') do add_to_serializer(:post, "include_assigned_to_user?") do
(SiteSetting.assigns_public || scope.can_assign?) && object.assignment&.assigned_to&.is_a?(User) && object.assignment.active (SiteSetting.assigns_public || scope.can_assign?) &&
object.assignment&.assigned_to&.is_a?(User) && object.assignment.active
end end
add_to_serializer(:post, :assigned_to_group, false) do add_to_serializer(:post, :assigned_to_group, false) do
BasicGroupSerializer.new(object.assignment.assigned_to, scope: scope, root: false).as_json BasicGroupSerializer.new(object.assignment.assigned_to, scope: scope, root: false).as_json
end end
add_to_serializer(:post, 'include_assigned_to_group?') do add_to_serializer(:post, "include_assigned_to_group?") do
(SiteSetting.assigns_public || scope.can_assign?) && object.assignment&.assigned_to&.is_a?(Group) && object.assignment.active (SiteSetting.assigns_public || scope.can_assign?) &&
object.assignment&.assigned_to&.is_a?(Group) && object.assignment.active
end end
add_to_serializer(:post, :assignment_note, false) do add_to_serializer(:post, :assignment_note, false) { object.assignment.note }
object.assignment.note
end
add_to_serializer(:post, :include_assignment_note?, false) do add_to_serializer(:post, :include_assignment_note?, false) do
(SiteSetting.assigns_public || scope.can_assign?) && object.assignment.present? (SiteSetting.assigns_public || scope.can_assign?) && object.assignment.present?
end end
# CurrentUser serializer # CurrentUser serializer
add_to_serializer(:current_user, :can_assign) do add_to_serializer(:current_user, :can_assign) { object.can_assign? }
object.can_assign?
end
# FlaggedTopic serializer # FlaggedTopic serializer
add_to_serializer(:flagged_topic, :assigned_to_user) do add_to_serializer(:flagged_topic, :assigned_to_user) do
@ -697,59 +743,47 @@ after_initialize do
[ [
:assigned_to, :assigned_to,
Proc.new do |results, value| Proc.new do |results, value|
results.joins(<<~SQL results.joins(<<~SQL).where(target_type: Post.name).where("u.username = ?", value)
INNER JOIN posts p ON p.id = target_id INNER JOIN posts p ON p.id = target_id
INNER JOIN topics t ON t.id = p.topic_id INNER JOIN topics t ON t.id = p.topic_id
INNER JOIN assignments a ON a.topic_id = t.id AND a.assigned_to_type = 'User' INNER JOIN assignments a ON a.topic_id = t.id AND a.assigned_to_type = 'User'
INNER JOIN users u ON u.id = a.assigned_to_id INNER JOIN users u ON u.id = a.assigned_to_id
SQL SQL
) end,
.where(target_type: Post.name) ],
.where('u.username = ?', value)
end
]
) )
# TopicTrackingState # TopicTrackingState
add_class_method(:topic_tracking_state, :publish_assigned_private_message) do |topic, assignee| add_class_method(:topic_tracking_state, :publish_assigned_private_message) do |topic, assignee|
return unless topic.private_message? return unless topic.private_message?
opts = opts = (assignee.is_a?(User) ? { user_ids: [assignee.id] } : { group_ids: [assignee.id] })
if assignee.is_a?(User)
{ user_ids: [assignee.id] }
else
{ group_ids: [assignee.id] }
end
MessageBus.publish( MessageBus.publish("/private-messages/assigned", { topic_id: topic.id }, opts)
"/private-messages/assigned",
{ topic_id: topic.id },
opts
)
end end
# Event listeners # Event listeners
on(:post_created) do |post| on(:post_created) { |post| ::Assigner.auto_assign(post, force: true) }
::Assigner.auto_assign(post, force: true)
end
on(:post_edited) do |post, topic_changed| on(:post_edited) { |post, topic_changed| ::Assigner.auto_assign(post, force: true) }
::Assigner.auto_assign(post, force: true)
end
on(:topic_status_updated) do |topic, status, enabled| on(:topic_status_updated) do |topic, status, enabled|
if SiteSetting.unassign_on_close && (status == 'closed' || status == 'autoclosed') && enabled && Assignment.exists?(topic_id: topic.id, active: true) if SiteSetting.unassign_on_close && (status == "closed" || status == "autoclosed") && enabled &&
Assignment.exists?(topic_id: topic.id, active: true)
assigner = ::Assigner.new(topic, Discourse.system_user) assigner = ::Assigner.new(topic, Discourse.system_user)
assigner.unassign(silent: true, deactivate: true) assigner.unassign(silent: true, deactivate: true)
topic.posts.joins(:assignment).find_each do |post| topic
.posts
.joins(:assignment)
.find_each do |post|
assigner = ::Assigner.new(post, Discourse.system_user) assigner = ::Assigner.new(post, Discourse.system_user)
assigner.unassign(silent: true, deactivate: true) assigner.unassign(silent: true, deactivate: true)
end end
MessageBus.publish("/topic/#{topic.id}", reload_topic: true, refresh_stream: true) MessageBus.publish("/topic/#{topic.id}", reload_topic: true, refresh_stream: true)
end end
if SiteSetting.reassign_on_open && (status == 'closed' || status == 'autoclosed') && !enabled && Assignment.exists?(topic_id: topic.id, active: false) if SiteSetting.reassign_on_open && (status == "closed" || status == "autoclosed") && !enabled &&
Assignment.exists?(topic_id: topic.id, active: false)
Assignment.where(topic_id: topic.id, target_type: "Topic").update_all(active: true) Assignment.where(topic_id: topic.id, target_type: "Topic").update_all(active: true)
Assignment Assignment
.where(topic_id: topic.id, target_type: "Post") .where(topic_id: topic.id, target_type: "Post")
@ -766,14 +800,21 @@ after_initialize do
end end
# small actions have to be destroyed as link is incorrect # small actions have to be destroyed as link is incorrect
PostCustomField.where(name: "action_code_post_id", value: post.id).find_each do |post_custom_field| PostCustomField
next if ![Post.types[:small_action], Post.types[:whisper]].include?(post_custom_field.post.post_type) .where(name: "action_code_post_id", value: post.id)
.find_each do |post_custom_field|
if ![Post.types[:small_action], Post.types[:whisper]].include?(
post_custom_field.post.post_type,
)
next
end
post_custom_field.post.destroy post_custom_field.post.destroy
end end
end end
on(:post_recovered) do |post| on(:post_recovered) do |post|
if SiteSetting.reassign_on_open && Assignment.where(target_type: "Post", target_id: post.id, active: false) if SiteSetting.reassign_on_open &&
Assignment.where(target_type: "Post", target_id: post.id, active: false)
Assignment.where(target_type: "Post", target_id: post.id).update_all(active: true) Assignment.where(target_type: "Post", target_id: post.id).update_all(active: true)
MessageBus.publish("/topic/#{post.topic_id}", reload_topic: true, refresh_stream: true) MessageBus.publish("/topic/#{post.topic_id}", reload_topic: true, refresh_stream: true)
end end
@ -782,21 +823,27 @@ after_initialize do
on(:move_to_inbox) do |info| on(:move_to_inbox) do |info|
topic = info[:topic] topic = info[:topic]
TopicTrackingState.publish_assigned_private_message(topic, topic.assignment.assigned_to) if topic.assignment if topic.assignment
TopicTrackingState.publish_assigned_private_message(topic, topic.assignment.assigned_to)
end
next if !SiteSetting.unassign_on_group_archive next if !SiteSetting.unassign_on_group_archive
next if !info[:group] next if !info[:group]
Assignment.where(topic_id: topic.id, active: false).find_each do |assignment| Assignment
.where(topic_id: topic.id, active: false)
.find_each do |assignment|
next unless assignment.target next unless assignment.target
assignment.update!(active: true) assignment.update!(active: true)
Jobs.enqueue(:assign_notification, Jobs.enqueue(
:assign_notification,
topic_id: topic.id, topic_id: topic.id,
post_id: assignment.target_type.is_a?(Topic) ? topic.first_post.id : assignment.target.id, post_id: assignment.target_type.is_a?(Topic) ? topic.first_post.id : assignment.target.id,
assigned_to_id: assignment.assigned_to_id, assigned_to_id: assignment.assigned_to_id,
assigned_to_type: assignment.assigned_to_type, assigned_to_type: assignment.assigned_to_type,
assigned_by_id: assignment.assigned_by_user_id, assigned_by_id: assignment.assigned_by_user_id,
skip_small_action_post: true) skip_small_action_post: true,
)
end end
end end
@ -809,33 +856,36 @@ after_initialize do
next if !SiteSetting.unassign_on_group_archive next if !SiteSetting.unassign_on_group_archive
next if !info[:group] next if !info[:group]
Assignment.where(topic_id: topic.id, active: true).find_each do |assignment| Assignment
.where(topic_id: topic.id, active: true)
.find_each do |assignment|
assignment.update!(active: false) assignment.update!(active: false)
Jobs.enqueue(:unassign_notification, Jobs.enqueue(
:unassign_notification,
topic_id: topic.id, topic_id: topic.id,
assigned_to_id: assignment.assigned_to.id, assigned_to_id: assignment.assigned_to.id,
assigned_to_type: assignment.assigned_to_type) assigned_to_type: assignment.assigned_to_type,
)
end end
end end
on(:user_removed_from_group) do |user, group| on(:user_removed_from_group) do |user, group|
assign_allowed_groups = SiteSetting.assign_allowed_on_groups.split('|').map(&:to_i) assign_allowed_groups = SiteSetting.assign_allowed_on_groups.split("|").map(&:to_i)
if assign_allowed_groups.include?(group.id) if assign_allowed_groups.include?(group.id)
groups = GroupUser.where(user: user).pluck(:group_id) groups = GroupUser.where(user: user).pluck(:group_id)
if (groups & assign_allowed_groups).empty? if (groups & assign_allowed_groups).empty?
topics = Topic.joins(:assignment).where('assignments.assigned_to_id = ?', user.id) topics = Topic.joins(:assignment).where("assignments.assigned_to_id = ?", user.id)
topics.each do |topic| topics.each { |topic| Assigner.new(topic, Discourse.system_user).unassign }
Assigner.new(topic, Discourse.system_user).unassign
end
end end
end end
end end
on(:post_moved) do |post, original_topic_id| on(:post_moved) do |post, original_topic_id|
assignment = Assignment.where(topic_id: original_topic_id, target_type: "Post", target_id: post.id).first assignment =
Assignment.where(topic_id: original_topic_id, target_type: "Post", target_id: post.id).first
next if !assignment next if !assignment
if post.is_first_post? if post.is_first_post?
assignment.update!(topic_id: post.topic_id, target_type: "Topic", target_id: post.topic_id) assignment.update!(topic_id: post.topic_id, target_type: "Topic", target_id: post.topic_id)
@ -846,33 +896,25 @@ after_initialize do
class ::WebHook class ::WebHook
def self.enqueue_assign_hooks(event, payload) def self.enqueue_assign_hooks(event, payload)
if active_web_hooks('assign').exists? WebHook.enqueue_hooks(:assign, event, payload: payload) if active_web_hooks("assign").exists?
WebHook.enqueue_hooks(:assign, event,
payload: payload
)
end
end end
end end
register_search_advanced_filter(/in:assigned/) do |posts| register_search_advanced_filter(/in:assigned/) do |posts|
if @guardian.can_assign? posts.where(<<~SQL) if @guardian.can_assign?
posts.where(<<~SQL)
topics.id IN ( topics.id IN (
SELECT a.topic_id FROM assignments a WHERE a.active SELECT a.topic_id FROM assignments a WHERE a.active
) )
SQL SQL
end end
end
register_search_advanced_filter(/in:unassigned/) do |posts| register_search_advanced_filter(/in:unassigned/) do |posts|
if @guardian.can_assign? posts.where(<<~SQL) if @guardian.can_assign?
posts.where(<<~SQL)
topics.id NOT IN ( topics.id NOT IN (
SELECT a.topic_id FROM assignments a WHERE a.active SELECT a.topic_id FROM assignments a WHERE a.active
) )
SQL SQL
end end
end
register_search_advanced_filter(/assigned:(.+)$/) do |posts, match| register_search_advanced_filter(/assigned:(.+)$/) do |posts, match|
if @guardian.can_assign? if @guardian.can_assign?
@ -893,9 +935,9 @@ after_initialize do
end end
if defined?(DiscourseAutomation) if defined?(DiscourseAutomation)
require 'random_assign_utils' require "random_assign_utils"
add_automation_scriptable('random_assign') do add_automation_scriptable("random_assign") do
field :assignees_group, component: :group, required: true field :assignees_group, component: :group, required: true
field :assigned_topic, component: :text, required: true field :assigned_topic, component: :text, required: true
field :minimum_time_between_assignments, component: :text field :minimum_time_between_assignments, component: :text

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'rails_helper' require "rails_helper"
require_relative '../support/assign_allowed_group' require_relative "../support/assign_allowed_group"
describe Search do describe Search do
fab!(:user) { Fabricate(:active_user) } fab!(:user) { Fabricate(:active_user) }
@ -12,8 +12,8 @@ describe Search do
SiteSetting.assign_enabled = true SiteSetting.assign_enabled = true
end end
context 'Advanced search' do context "Advanced search" do
include_context 'A group that is allowed to assign' include_context "A group that is allowed to assign"
let(:post1) { Fabricate(:post) } let(:post1) { Fabricate(:post) }
let(:post2) { Fabricate(:post) } let(:post2) { Fabricate(:post) }
@ -30,24 +30,36 @@ describe Search do
Assigner.new(post2.topic, user).assign(user2) Assigner.new(post2.topic, user).assign(user2)
Assigner.new(post3.topic, user).assign(user) Assigner.new(post3.topic, user).assign(user)
Assigner.new(post5, user).assign(user) Assigner.new(post5, user).assign(user)
Assignment.create!(assigned_to: user, assigned_by_user: user, target: post6, topic_id: post6.topic.id, active: false) Assignment.create!(
assigned_to: user,
assigned_by_user: user,
target: post6,
topic_id: post6.topic.id,
active: false,
)
end end
it 'can find by status' do it "can find by status" do
expect(Search.execute('in:assigned', guardian: Guardian.new(user)).posts.length).to eq(4) expect(Search.execute("in:assigned", guardian: Guardian.new(user)).posts.length).to eq(4)
Assigner.new(post3.topic, user).unassign Assigner.new(post3.topic, user).unassign
expect(Search.execute('in:unassigned', guardian: Guardian.new(user)).posts.length).to eq(2) expect(Search.execute("in:unassigned", guardian: Guardian.new(user)).posts.length).to eq(2)
expect(Search.execute("assigned:#{user.username}", guardian: Guardian.new(user)).posts.length).to eq(2) expect(
Search.execute("assigned:#{user.username}", guardian: Guardian.new(user)).posts.length,
).to eq(2)
end end
it 'serializes results' do it "serializes results" do
guardian = Guardian.new(user) guardian = Guardian.new(user)
result = Search.execute('in:assigned', guardian: guardian) result = Search.execute("in:assigned", guardian: guardian)
serializer = GroupedSearchResultSerializer.new(result, scope: guardian) serializer = GroupedSearchResultSerializer.new(result, scope: guardian)
indirectly_assigned_to = serializer.as_json[:topics].find { |topic| topic[:id] == post5.topic.id }[:indirectly_assigned_to] indirectly_assigned_to =
expect(indirectly_assigned_to).to eq(post5.id => { serializer.as_json[:topics].find { |topic| topic[:id] == post5.topic.id }[
:indirectly_assigned_to
]
expect(indirectly_assigned_to).to eq(
post5.id => {
assigned_to: { assigned_to: {
assign_icon: "user-plus", assign_icon: "user-plus",
assign_path: "/u/#{user.username}/activity/assigned", assign_path: "/u/#{user.username}/activity/assigned",
@ -57,7 +69,8 @@ describe Search do
}, },
post_number: post5.post_number, post_number: post5.post_number,
assignment_note: nil, assignment_note: nil,
}) },
)
end end
end end
end end

View File

@ -1,27 +1,23 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'rails_helper' require "rails_helper"
require_relative '../support/assign_allowed_group' require_relative "../support/assign_allowed_group"
describe TopicQuery do describe TopicQuery do
before do before { SiteSetting.assign_enabled = true }
SiteSetting.assign_enabled = true
end
fab!(:user) { Fabricate(:user) } fab!(:user) { Fabricate(:user) }
fab!(:user2) { Fabricate(:user) } fab!(:user2) { Fabricate(:user) }
include_context 'A group that is allowed to assign' include_context "A group that is allowed to assign"
before do before do
add_to_assign_allowed_group(user) add_to_assign_allowed_group(user)
add_to_assign_allowed_group(user2) add_to_assign_allowed_group(user2)
end end
describe '#list_messages_assigned' do describe "#list_messages_assigned" do
fab!(:private_message) do fab!(:private_message) { Fabricate(:private_message_post, user: user).topic }
Fabricate(:private_message_post, user: user).topic
end
fab!(:topic) { Fabricate(:post, user: user).topic } fab!(:topic) { Fabricate(:post, user: user).topic }
fab!(:group_topic) { Fabricate(:post, user: user).topic } fab!(:group_topic) { Fabricate(:post, user: user).topic }
@ -32,13 +28,13 @@ describe TopicQuery do
assign_to(group_topic, user, assign_allowed_group) assign_to(group_topic, user, assign_allowed_group)
end end
it 'Includes topics and PMs assigned to user' do it "Includes topics and PMs assigned to user" do
assigned_messages = TopicQuery.new(user, { page: 0 }).list_messages_assigned(user).topics assigned_messages = TopicQuery.new(user, { page: 0 }).list_messages_assigned(user).topics
expect(assigned_messages).to contain_exactly(private_message, topic, group_topic) expect(assigned_messages).to contain_exactly(private_message, topic, group_topic)
end end
it 'Excludes inactive assignments' do it "Excludes inactive assignments" do
Assignment.update_all(active: false) Assignment.update_all(active: false)
assigned_messages = TopicQuery.new(user, { page: 0 }).list_messages_assigned(user).topics assigned_messages = TopicQuery.new(user, { page: 0 }).list_messages_assigned(user).topics
@ -46,19 +42,20 @@ describe TopicQuery do
expect(assigned_messages).to eq([]) expect(assigned_messages).to eq([])
end end
it 'Excludes topics and PMs not assigned to user' do it "Excludes topics and PMs not assigned to user" do
assigned_messages = TopicQuery.new(user2, { page: 0 }).list_messages_assigned(user2).topics assigned_messages = TopicQuery.new(user2, { page: 0 }).list_messages_assigned(user2).topics
expect(assigned_messages).to contain_exactly(group_topic) expect(assigned_messages).to contain_exactly(group_topic)
end end
it 'direct filter excludes group assignment' do it "direct filter excludes group assignment" do
assigned_messages = TopicQuery.new(user, { page: 0, filter: :direct }).list_messages_assigned(user).topics assigned_messages =
TopicQuery.new(user, { page: 0, filter: :direct }).list_messages_assigned(user).topics
expect(assigned_messages).to contain_exactly(private_message, topic) expect(assigned_messages).to contain_exactly(private_message, topic)
end end
it 'Returns the results ordered by the bumped_at field' do it "Returns the results ordered by the bumped_at field" do
topic.update(bumped_at: 2.weeks.ago) topic.update(bumped_at: 2.weeks.ago)
assigned_messages = TopicQuery.new(user, { page: 0 }).list_messages_assigned(user).topics assigned_messages = TopicQuery.new(user, { page: 0 }).list_messages_assigned(user).topics
@ -67,11 +64,8 @@ describe TopicQuery do
end end
end end
describe '#list_group_topics_assigned' do describe "#list_group_topics_assigned" do
fab!(:private_message) { Fabricate(:private_message_post, user: user).topic }
fab!(:private_message) do
Fabricate(:private_message_post, user: user).topic
end
fab!(:topic) { Fabricate(:post, user: user).topic } fab!(:topic) { Fabricate(:post, user: user).topic }
@ -83,33 +77,40 @@ describe TopicQuery do
assign_to(group_topic, user, assign_allowed_group) assign_to(group_topic, user, assign_allowed_group)
end end
it 'Includes topics and PMs assigned to user' do it "Includes topics and PMs assigned to user" do
assigned_messages = TopicQuery.new(user, { page: 0 }).list_group_topics_assigned(assign_allowed_group).topics assigned_messages =
TopicQuery.new(user, { page: 0 }).list_group_topics_assigned(assign_allowed_group).topics
expect(assigned_messages).to contain_exactly(private_message, topic, group_topic) expect(assigned_messages).to contain_exactly(private_message, topic, group_topic)
end end
it 'Returns the results ordered by the bumped_at field' do it "Returns the results ordered by the bumped_at field" do
topic.update(bumped_at: 2.weeks.ago) topic.update(bumped_at: 2.weeks.ago)
assigned_messages = TopicQuery.new(user, { page: 0 }).list_group_topics_assigned(assign_allowed_group).topics assigned_messages =
TopicQuery.new(user, { page: 0 }).list_group_topics_assigned(assign_allowed_group).topics
expect(assigned_messages).to eq([group_topic, private_message, topic]) expect(assigned_messages).to eq([group_topic, private_message, topic])
end end
it 'direct filter shows only group assignments' do it "direct filter shows only group assignments" do
assigned_messages = TopicQuery.new(user, { page: 0, filter: :direct }).list_group_topics_assigned(assign_allowed_group).topics assigned_messages =
TopicQuery
.new(user, { page: 0, filter: :direct })
.list_group_topics_assigned(assign_allowed_group)
.topics
expect(assigned_messages).to contain_exactly(group_topic) expect(assigned_messages).to contain_exactly(group_topic)
end end
end end
describe '#list_private_messages_assigned' do describe "#list_private_messages_assigned" do
let(:user_topic) do let(:user_topic) do
topic = create_post( topic =
create_post(
user: Fabricate(:user), user: Fabricate(:user),
target_usernames: [user.username, user2.username], target_usernames: [user.username, user2.username],
archetype: Archetype.private_message archetype: Archetype.private_message,
).topic ).topic
create_post(topic_id: topic.id, user: user) create_post(topic_id: topic.id, user: user)
@ -118,61 +119,63 @@ describe TopicQuery do
end end
let(:assigned_topic) do let(:assigned_topic) do
topic = create_post( topic =
create_post(
user: Fabricate(:user), user: Fabricate(:user),
target_usernames: [user.username, user2.username], target_usernames: [user.username, user2.username],
archetype: Archetype.private_message archetype: Archetype.private_message,
).topic ).topic
assign_to(topic, user, user) assign_to(topic, user, user)
end end
let(:group2) do let(:group2) { Fabricate(:group, messageable_level: Group::ALIAS_LEVELS[:everyone]) }
Fabricate(:group, messageable_level: Group::ALIAS_LEVELS[:everyone])
end
let(:group_assigned_topic) do let(:group_assigned_topic) do
topic = create_post( topic =
create_post(
user: user, user: user,
target_group_names: [assign_allowed_group.name, group2.name], target_group_names: [assign_allowed_group.name, group2.name],
archetype: Archetype.private_message archetype: Archetype.private_message,
).topic ).topic
assign_to(topic, user, user) assign_to(topic, user, user)
end end
before do before do
assign_allowed_group.update!( assign_allowed_group.update!(messageable_level: Group::ALIAS_LEVELS[:everyone])
messageable_level: Group::ALIAS_LEVELS[:everyone]
)
user_topic user_topic
assigned_topic assigned_topic
group_assigned_topic group_assigned_topic
end end
it 'should return the right topics' do it "should return the right topics" do
expect( expect(TopicQuery.new(user).list_private_messages_assigned(user).topics).to contain_exactly(
TopicQuery.new(user).list_private_messages_assigned(user).topics assigned_topic,
).to contain_exactly(assigned_topic, group_assigned_topic) group_assigned_topic,
)
UserArchivedMessage.archive!(user.id, assigned_topic) UserArchivedMessage.archive!(user.id, assigned_topic)
expect( expect(TopicQuery.new(user).list_private_messages_assigned(user).topics).to contain_exactly(
TopicQuery.new(user).list_private_messages_assigned(user).topics assigned_topic,
).to contain_exactly(assigned_topic, group_assigned_topic) group_assigned_topic,
)
GroupArchivedMessage.archive!(group2.id, group_assigned_topic) GroupArchivedMessage.archive!(group2.id, group_assigned_topic)
expect( expect(TopicQuery.new(user).list_private_messages_assigned(user).topics).to contain_exactly(
TopicQuery.new(user).list_private_messages_assigned(user).topics assigned_topic,
).to contain_exactly(assigned_topic, group_assigned_topic) group_assigned_topic,
)
GroupArchivedMessage.archive!(assign_allowed_group.id, group_assigned_topic) GroupArchivedMessage.archive!(assign_allowed_group.id, group_assigned_topic)
expect( expect(TopicQuery.new(user).list_private_messages_assigned(user).topics).to contain_exactly(
TopicQuery.new(user).list_private_messages_assigned(user).topics assigned_topic,
).to contain_exactly(assigned_topic, group_assigned_topic) group_assigned_topic,
)
end end
end end
@ -225,8 +228,6 @@ describe TopicQuery do
end end
def assign_to(topic, user, assignee) def assign_to(topic, user, assignee)
topic.tap do |t| topic.tap { |t| Assigner.new(t, user).assign(assignee) }
Assigner.new(t, user).assign(assignee)
end
end end
end end

View File

@ -1,29 +1,29 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'rails_helper' require "rails_helper"
require_relative '../support/assign_allowed_group' require_relative "../support/assign_allowed_group"
describe TopicsBulkAction do describe TopicsBulkAction do
fab!(:post) { Fabricate(:post) } fab!(:post) { Fabricate(:post) }
fab!(:post1) { Fabricate(:post) } fab!(:post1) { Fabricate(:post) }
fab!(:post2) { Fabricate(:post) } fab!(:post2) { Fabricate(:post) }
before do before { SiteSetting.assign_enabled = true }
SiteSetting.assign_enabled = true
end
let(:user) { Fabricate(:user) } let(:user) { Fabricate(:user) }
let(:user2) { Fabricate(:user) } let(:user2) { Fabricate(:user) }
include_context 'A group that is allowed to assign' include_context "A group that is allowed to assign"
before do before { add_to_assign_allowed_group(user) }
add_to_assign_allowed_group(user)
end
describe "assign_topics" do describe "assign_topics" do
it "assigns multiple topics to user" do it "assigns multiple topics to user" do
TopicsBulkAction.new(user, [post.topic.id, post1.topic.id], { type: 'assign', username: user.username }).perform! TopicsBulkAction.new(
user,
[post.topic.id, post1.topic.id],
{ type: "assign", username: user.username },
).perform!
assigned_topics = TopicQuery.new(user, { page: 0 }).list_messages_assigned(user).topics assigned_topics = TopicQuery.new(user, { page: 0 }).list_messages_assigned(user).topics
@ -33,7 +33,11 @@ describe TopicsBulkAction do
end end
it "doesn't allows to assign to user not in assign_allowed_group" do it "doesn't allows to assign to user not in assign_allowed_group" do
TopicsBulkAction.new(user, [post.topic.id, post1.topic.id], { type: 'assign', username: user2.username }).perform! TopicsBulkAction.new(
user,
[post.topic.id, post1.topic.id],
{ type: "assign", username: user2.username },
).perform!
assigned_topics = TopicQuery.new(user, { page: 0 }).list_messages_assigned(user2).topics assigned_topics = TopicQuery.new(user, { page: 0 }).list_messages_assigned(user2).topics
@ -41,7 +45,11 @@ describe TopicsBulkAction do
end end
it "user who is not in assign_allowed_group can't assign topics" do it "user who is not in assign_allowed_group can't assign topics" do
TopicsBulkAction.new(user2, [post.topic.id, post1.topic.id, post2.topic.id], { type: 'assign', username: user.username }).perform! TopicsBulkAction.new(
user2,
[post.topic.id, post1.topic.id, post2.topic.id],
{ type: "assign", username: user.username },
).perform!
assigned_topics = TopicQuery.new(user, { page: 0 }).list_messages_assigned(user).topics assigned_topics = TopicQuery.new(user, { page: 0 }).list_messages_assigned(user).topics
@ -51,9 +59,13 @@ describe TopicsBulkAction do
describe "unassign_topics" do describe "unassign_topics" do
it "unassigns multiple topics assigned to user" do it "unassigns multiple topics assigned to user" do
TopicsBulkAction.new(user, [post.topic.id, post1.topic.id, post2.topic.id], { type: 'assign', username: user.username }).perform! TopicsBulkAction.new(
user,
[post.topic.id, post1.topic.id, post2.topic.id],
{ type: "assign", username: user.username },
).perform!
TopicsBulkAction.new(user, [post.topic.id, post1.topic.id], type: 'unassign').perform! TopicsBulkAction.new(user, [post.topic.id, post1.topic.id], type: "unassign").perform!
assigned_topics = TopicQuery.new(user, { page: 0 }).list_messages_assigned(user).topics assigned_topics = TopicQuery.new(user, { page: 0 }).list_messages_assigned(user).topics
@ -63,9 +75,13 @@ describe TopicsBulkAction do
end end
it "user who is not in assign_allowed_group can't unassign topics" do it "user who is not in assign_allowed_group can't unassign topics" do
TopicsBulkAction.new(user, [post.topic.id, post1.topic.id, post2.topic.id], { type: 'assign', username: user.username }).perform! TopicsBulkAction.new(
user,
[post.topic.id, post1.topic.id, post2.topic.id],
{ type: "assign", username: user.username },
).perform!
TopicsBulkAction.new(user2, [post.topic.id, post1.topic.id], type: 'unassign').perform! TopicsBulkAction.new(user2, [post.topic.id, post1.topic.id], type: "unassign").perform!
assigned_topics = TopicQuery.new(user, { page: 0 }).list_messages_assigned(user).topics assigned_topics = TopicQuery.new(user, { page: 0 }).list_messages_assigned(user).topics
@ -73,6 +89,5 @@ describe TopicsBulkAction do
expect(assigned_topics).to contain_exactly(post.topic, post1.topic, post2.topic) expect(assigned_topics).to contain_exactly(post.topic, post1.topic, post2.topic)
end end
end end
end end

View File

@ -1,9 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
Fabricator(:assign_web_hook, from: :web_hook) do Fabricator(:assign_web_hook, from: :web_hook) do
transient assign_hook: WebHookEventType.find_by(name: 'assign') transient assign_hook: WebHookEventType.find_by(name: "assign")
after_build do |web_hook, transients| after_build { |web_hook, transients| web_hook.web_hook_event_types = [transients[:assign_hook]] }
web_hook.web_hook_event_types = [transients[:assign_hook]]
end
end end

View File

@ -1,15 +1,13 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'rails_helper' require "rails_helper"
require_relative '../support/assign_allowed_group' require_relative "../support/assign_allowed_group"
require_relative '../fabricators/assign_hook_fabricator.rb' require_relative "../fabricators/assign_hook_fabricator.rb"
describe 'integration tests' do describe "integration tests" do
before do before { SiteSetting.assign_enabled = true }
SiteSetting.assign_enabled = true
end
it 'preloads data in topic list' do it "preloads data in topic list" do
admin = Fabricate(:admin) admin = Fabricate(:admin)
post = create_post post = create_post
list = TopicList.new("latest", admin, [post.topic]) list = TopicList.new("latest", admin, [post.topic])
@ -17,7 +15,7 @@ describe 'integration tests' do
# should not explode for now # should not explode for now
end end
describe 'for a private message' do describe "for a private message" do
let(:post) { Fabricate(:private_message_post) } let(:post) { Fabricate(:private_message_post) }
let(:pm) { post.topic } let(:pm) { post.topic }
let(:user) { pm.allowed_users.first } let(:user) { pm.allowed_users.first }
@ -25,7 +23,7 @@ describe 'integration tests' do
let(:channel) { "/private-messages/assigned" } let(:channel) { "/private-messages/assigned" }
fab!(:group) { Fabricate(:group, assignable_level: Group::ALIAS_LEVELS[:everyone]) } fab!(:group) { Fabricate(:group, assignable_level: Group::ALIAS_LEVELS[:everyone]) }
include_context 'A group that is allowed to assign' include_context "A group that is allowed to assign"
before do before do
add_to_assign_allowed_group(user) add_to_assign_allowed_group(user)
@ -35,9 +33,7 @@ describe 'integration tests' do
end end
def assert_publish_topic_state(topic, user: nil, group: nil) def assert_publish_topic_state(topic, user: nil, group: nil)
messages = MessageBus.track_publish do messages = MessageBus.track_publish { yield }
yield
end
message = messages.find { |m| m.channel == channel } message = messages.find { |m| m.channel == channel }
@ -46,7 +42,7 @@ describe 'integration tests' do
expect(message.group_ids).to eq([group.id]) if group expect(message.group_ids).to eq([group.id]) if group
end end
it 'publishes the right message on archive and move to inbox' do it "publishes the right message on archive and move to inbox" do
assigner = Assigner.new(pm, user) assigner = Assigner.new(pm, user)
assigner.assign(user) assigner.assign(user)
@ -59,7 +55,7 @@ describe 'integration tests' do
end end
end end
it 'publishes the right message on archive and move to inbox for groups' do it "publishes the right message on archive and move to inbox for groups" do
assigner = Assigner.new(pm, user) assigner = Assigner.new(pm, user)
assigner.assign(group) assigner.assign(group)
@ -106,7 +102,7 @@ describe 'integration tests' do
let(:user1) { Fabricate(:user) } let(:user1) { Fabricate(:user) }
let(:user2) { Fabricate(:user) } let(:user2) { Fabricate(:user) }
include_context 'A group that is allowed to assign' include_context "A group that is allowed to assign"
before do before do
add_to_assign_allowed_group(user1) add_to_assign_allowed_group(user1)
@ -142,15 +138,15 @@ describe 'integration tests' do
end end
end end
context 'already assigned' do context "already assigned" do
fab!(:post) { Fabricate(:post) } fab!(:post) { Fabricate(:post) }
fab!(:post_2) { Fabricate(:post, topic: post.topic) } fab!(:post_2) { Fabricate(:post, topic: post.topic) }
let(:topic) { post.topic } let(:topic) { post.topic }
fab!(:user) { Fabricate(:user) } fab!(:user) { Fabricate(:user) }
include_context 'A group that is allowed to assign' include_context "A group that is allowed to assign"
it 'does not allow to assign topic if post is already assigned' do it "does not allow to assign topic if post is already assigned" do
add_to_assign_allowed_group(user) add_to_assign_allowed_group(user)
assigner = Assigner.new(post, user) assigner = Assigner.new(post, user)
@ -168,14 +164,22 @@ describe 'integration tests' do
end end
end end
context 'move post' do context "move post" do
fab!(:old_topic) { Fabricate(:topic) } fab!(:old_topic) { Fabricate(:topic) }
fab!(:post) { Fabricate(:post, topic: old_topic) } fab!(:post) { Fabricate(:post, topic: old_topic) }
fab!(:user) { Fabricate(:user) } fab!(:user) { Fabricate(:user) }
fab!(:assignment) { Assignment.create!(target_id: post.id, target_type: "Post", topic_id: old_topic.id, assigned_by_user: user, assigned_to: user) } fab!(:assignment) do
Assignment.create!(
target_id: post.id,
target_type: "Post",
topic_id: old_topic.id,
assigned_by_user: user,
assigned_to: user,
)
end
let(:new_topic) { Fabricate(:topic) } let(:new_topic) { Fabricate(:topic) }
it 'assignment becomes topic assignment when new topic' do it "assignment becomes topic assignment when new topic" do
post.update!(topic: new_topic) post.update!(topic: new_topic)
DiscourseEvent.trigger(:post_moved, post, old_topic.id) DiscourseEvent.trigger(:post_moved, post, old_topic.id)
assignment.reload assignment.reload
@ -184,7 +188,7 @@ describe 'integration tests' do
expect(assignment.target_id).to eq(new_topic.id) expect(assignment.target_id).to eq(new_topic.id)
end end
it 'assigment is still post assignment when not first post' do it "assigment is still post assignment when not first post" do
post.update!(topic: new_topic, post_number: "3") post.update!(topic: new_topic, post_number: "3")
DiscourseEvent.trigger(:post_moved, post, old_topic.id) DiscourseEvent.trigger(:post_moved, post, old_topic.id)
assignment.reload assignment.reload

View File

@ -1,51 +1,65 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'rails_helper' require "rails_helper"
RSpec.describe Jobs::AssignNotification do RSpec.describe Jobs::AssignNotification do
describe '#execute' do describe "#execute" do
fab!(:user1) { Fabricate(:user, last_seen_at: 1.day.ago) } fab!(:user1) { Fabricate(:user, last_seen_at: 1.day.ago) }
fab!(:user2) { Fabricate(:user, last_seen_at: 1.day.ago) } fab!(:user2) { Fabricate(:user, last_seen_at: 1.day.ago) }
fab!(:topic) { Fabricate(:topic, title: 'Basic topic title') } fab!(:topic) { Fabricate(:topic, title: "Basic topic title") }
fab!(:post) { Fabricate(:post, topic: topic) } fab!(:post) { Fabricate(:post, topic: topic) }
fab!(:pm_post) { Fabricate(:private_message_post) } fab!(:pm_post) { Fabricate(:private_message_post) }
fab!(:pm) { pm_post.topic } fab!(:pm) { pm_post.topic }
fab!(:assign_allowed_group) { Group.find_by(name: 'staff') } fab!(:assign_allowed_group) { Group.find_by(name: "staff") }
def assert_publish_topic_state(topic, user) def assert_publish_topic_state(topic, user)
message = MessageBus.track_publish('/private-messages/assigned') do message = MessageBus.track_publish("/private-messages/assigned") { yield }.first
yield
end.first
expect(message.data[:topic_id]).to eq(topic.id) expect(message.data[:topic_id]).to eq(topic.id)
expect(message.user_ids).to eq([user.id]) expect(message.user_ids).to eq([user.id])
end end
before do before { assign_allowed_group.add(user1) }
assign_allowed_group.add(user1)
end
context 'User' do context "User" do
it "sends notification alert" do
it 'sends notification alert' do messages =
messages = MessageBus.track_publish("/notification-alert/#{user2.id}") do MessageBus.track_publish("/notification-alert/#{user2.id}") do
described_class.new.execute({ topic_id: topic.id, post_id: post.id, assigned_to_id: user2.id, assigned_to_type: 'User', assigned_by_id: user1.id, skip_small_action_post: false }) described_class.new.execute(
{
topic_id: topic.id,
post_id: post.id,
assigned_to_id: user2.id,
assigned_to_type: "User",
assigned_by_id: user1.id,
skip_small_action_post: false,
},
)
end end
expect(messages.length).to eq(1) expect(messages.length).to eq(1)
expect(messages.first.data[:excerpt]).to eq("assigned you the topic 'Basic topic title'") expect(messages.first.data[:excerpt]).to eq("assigned you the topic 'Basic topic title'")
end end
it 'should publish the right message when private message' do it "should publish the right message when private message" do
user = pm.allowed_users.first user = pm.allowed_users.first
assign_allowed_group.add(user) assign_allowed_group.add(user)
assert_publish_topic_state(pm, user) do assert_publish_topic_state(pm, user) do
described_class.new.execute({ topic_id: pm.id, post_id: pm_post.id, assigned_to_id: pm.allowed_users.first.id, assigned_to_type: 'User', assigned_by_id: user1.id, skip_small_action_post: false }) described_class.new.execute(
{
topic_id: pm.id,
post_id: pm_post.id,
assigned_to_id: pm.allowed_users.first.id,
assigned_to_type: "User",
assigned_by_id: user1.id,
skip_small_action_post: false,
},
)
end end
end end
it 'sends a high priority notification to the assignee' do it "sends a high priority notification to the assignee" do
Notification.expects(:create!).with( Notification.expects(:create!).with(
notification_type: Notification.types[:assigned] || Notification.types[:custom], notification_type: Notification.types[:assigned] || Notification.types[:custom],
user_id: user2.id, user_id: user2.id,
@ -53,20 +67,31 @@ RSpec.describe Jobs::AssignNotification do
post_number: 1, post_number: 1,
high_priority: true, high_priority: true,
data: { data: {
message: 'discourse_assign.assign_notification', message: "discourse_assign.assign_notification",
display_username: user1.username, display_username: user1.username,
topic_title: topic.title topic_title: topic.title,
}.to_json }.to_json,
)
described_class.new.execute(
{
topic_id: topic.id,
post_id: post.id,
assigned_to_id: user2.id,
assigned_to_type: "User",
assigned_by_id: user1.id,
skip_small_action_post: false,
},
) )
described_class.new.execute({ topic_id: topic.id, post_id: post.id, assigned_to_id: user2.id, assigned_to_type: 'User', assigned_by_id: user1.id, skip_small_action_post: false })
end end
end end
context 'Group' do context "Group" do
fab!(:user3) { Fabricate(:user, last_seen_at: 1.day.ago) } fab!(:user3) { Fabricate(:user, last_seen_at: 1.day.ago) }
fab!(:user4) { Fabricate(:user, suspended_till: 1.year.from_now) } fab!(:user4) { Fabricate(:user, suspended_till: 1.year.from_now) }
fab!(:group) { Fabricate(:group, name: 'Developers') } fab!(:group) { Fabricate(:group, name: "Developers") }
let(:assignment) { Assignment.create!(topic: topic, assigned_by_user: user1, assigned_to: group) } let(:assignment) do
Assignment.create!(topic: topic, assigned_by_user: user1, assigned_to: group)
end
before do before do
group.add(user2) group.add(user2)
@ -74,26 +99,60 @@ RSpec.describe Jobs::AssignNotification do
group.add(user4) group.add(user4)
end end
it 'sends notification alert to all group members' do it "sends notification alert to all group members" do
messages = MessageBus.track_publish("/notification-alert/#{user2.id}") do messages =
described_class.new.execute({ topic_id: topic.id, post_id: post.id, assigned_to_id: group.id, assigned_to_type: 'Group', assigned_by_id: user1.id, skip_small_action_post: false }) MessageBus.track_publish("/notification-alert/#{user2.id}") do
described_class.new.execute(
{
topic_id: topic.id,
post_id: post.id,
assigned_to_id: group.id,
assigned_to_type: "Group",
assigned_by_id: user1.id,
skip_small_action_post: false,
},
)
end end
expect(messages.length).to eq(1) expect(messages.length).to eq(1)
expect(messages.first.data[:excerpt]).to eq("assigned to Developers the topic 'Basic topic title'") expect(messages.first.data[:excerpt]).to eq(
"assigned to Developers the topic 'Basic topic title'",
)
messages = MessageBus.track_publish("/notification-alert/#{user3.id}") do messages =
described_class.new.execute({ topic_id: topic.id, post_id: post.id, assigned_to_id: group.id, assigned_to_type: 'Group', assigned_by_id: user1.id, skip_small_action_post: false }) MessageBus.track_publish("/notification-alert/#{user3.id}") do
described_class.new.execute(
{
topic_id: topic.id,
post_id: post.id,
assigned_to_id: group.id,
assigned_to_type: "Group",
assigned_by_id: user1.id,
skip_small_action_post: false,
},
)
end end
expect(messages.length).to eq(1) expect(messages.length).to eq(1)
expect(messages.first.data[:excerpt]).to eq("assigned to Developers the topic 'Basic topic title'") expect(messages.first.data[:excerpt]).to eq(
"assigned to Developers the topic 'Basic topic title'",
)
messages = MessageBus.track_publish("/notification-alert/#{user4.id}") do messages =
described_class.new.execute({ topic_id: topic.id, post_id: post.id, assigned_to_id: group.id, assigned_to_type: 'Group', assigned_by_id: user1.id, skip_small_action_post: false }) MessageBus.track_publish("/notification-alert/#{user4.id}") do
described_class.new.execute(
{
topic_id: topic.id,
post_id: post.id,
assigned_to_id: group.id,
assigned_to_type: "Group",
assigned_by_id: user1.id,
skip_small_action_post: false,
},
)
end end
expect(messages.length).to eq(0) expect(messages.length).to eq(0)
end end
it 'sends a high priority notification to all group members' do it "sends a high priority notification to all group members" do
[user2, user3, user4].each do |user| [user2, user3, user4].each do |user|
Notification.expects(:create!).with( Notification.expects(:create!).with(
notification_type: Notification.types[:assigned] || Notification.types[:custom], notification_type: Notification.types[:assigned] || Notification.types[:custom],
@ -102,14 +161,23 @@ RSpec.describe Jobs::AssignNotification do
post_number: 1, post_number: 1,
high_priority: true, high_priority: true,
data: { data: {
message: 'discourse_assign.assign_group_notification', message: "discourse_assign.assign_group_notification",
display_username: group.name, display_username: group.name,
topic_title: topic.title topic_title: topic.title,
}.to_json }.to_json,
) )
end end
described_class.new.execute({ topic_id: topic.id, post_id: post.id, assigned_to_id: group.id, assigned_to_type: 'Group', assigned_by_id: user1.id, skip_small_action_post: false }) described_class.new.execute(
{
topic_id: topic.id,
post_id: post.id,
assigned_to_id: group.id,
assigned_to_type: "Group",
assigned_by_id: user1.id,
skip_small_action_post: false,
},
)
end end
end end
end end

View File

@ -1,13 +1,11 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'rails_helper' require "rails_helper"
RSpec.describe Jobs::RemindUser do RSpec.describe Jobs::RemindUser do
describe '#execute' do describe "#execute" do
it "should raise the right error when user_id is invalid" do it "should raise the right error when user_id is invalid" do
expect do expect do described_class.new.execute({}) end.to raise_error(Discourse::InvalidParameters)
described_class.new.execute({})
end.to raise_error(Discourse::InvalidParameters)
end end
end end
end end

View File

@ -1,51 +1,70 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'rails_helper' require "rails_helper"
RSpec.describe Jobs::UnassignNotification do RSpec.describe Jobs::UnassignNotification do
describe '#execute' do describe "#execute" do
fab!(:user1) { Fabricate(:user) } fab!(:user1) { Fabricate(:user) }
fab!(:user2) { Fabricate(:user) } fab!(:user2) { Fabricate(:user) }
fab!(:topic) { Fabricate(:topic) } fab!(:topic) { Fabricate(:topic) }
fab!(:post) { Fabricate(:post, topic: topic) } fab!(:post) { Fabricate(:post, topic: topic) }
fab!(:pm_post) { Fabricate(:private_message_post) } fab!(:pm_post) { Fabricate(:private_message_post) }
fab!(:pm) { pm_post.topic } fab!(:pm) { pm_post.topic }
fab!(:assign_allowed_group) { Group.find_by(name: 'staff') } fab!(:assign_allowed_group) { Group.find_by(name: "staff") }
before do before { assign_allowed_group.add(user1) }
assign_allowed_group.add(user1)
end
def assert_publish_topic_state(topic, user) def assert_publish_topic_state(topic, user)
message = MessageBus.track_publish('/private-messages/assigned') do message = MessageBus.track_publish("/private-messages/assigned") { yield }.first
yield
end.first
expect(message.data[:topic_id]).to eq(topic.id) expect(message.data[:topic_id]).to eq(topic.id)
expect(message.user_ids).to eq([user.id]) expect(message.user_ids).to eq([user.id])
end end
context 'User' do context "User" do
it 'deletes notifications' do it "deletes notifications" do
Jobs::AssignNotification.new.execute({ topic_id: topic.id, post_id: post.id, assigned_to_id: user2.id, assigned_to_type: 'User', assigned_by_id: user1.id, skip_small_action_post: false }) Jobs::AssignNotification.new.execute(
{
topic_id: topic.id,
post_id: post.id,
assigned_to_id: user2.id,
assigned_to_type: "User",
assigned_by_id: user1.id,
skip_small_action_post: false,
},
)
expect { expect {
described_class.new.execute({ topic_id: topic.id, post_id: post.id, assigned_to_id: user2.id, assigned_to_type: 'User' }) described_class.new.execute(
{
topic_id: topic.id,
post_id: post.id,
assigned_to_id: user2.id,
assigned_to_type: "User",
},
)
}.to change { user2.notifications.count }.by(-1) }.to change { user2.notifications.count }.by(-1)
end end
it 'should publish the right message when private message' do it "should publish the right message when private message" do
user = pm.allowed_users.first user = pm.allowed_users.first
assign_allowed_group.add(user) assign_allowed_group.add(user)
assert_publish_topic_state(pm, user) do assert_publish_topic_state(pm, user) do
described_class.new.execute({ topic_id: pm.id, post_id: pm_post.id, assigned_to_id: pm.allowed_users.first.id, assigned_to_type: 'User' }) described_class.new.execute(
{
topic_id: pm.id,
post_id: pm_post.id,
assigned_to_id: pm.allowed_users.first.id,
assigned_to_type: "User",
},
)
end end
end end
end end
context 'Group' do context "Group" do
fab!(:assign_allowed_group) { Group.find_by(name: 'staff') } fab!(:assign_allowed_group) { Group.find_by(name: "staff") }
fab!(:user3) { Fabricate(:user) } fab!(:user3) { Fabricate(:user) }
fab!(:group) { Fabricate(:group) } fab!(:group) { Fabricate(:group) }
@ -54,11 +73,27 @@ RSpec.describe Jobs::UnassignNotification do
group.add(user3) group.add(user3)
end end
it 'deletes notifications' do it "deletes notifications" do
Jobs::AssignNotification.new.execute({ topic_id: topic.id, post_id: post.id, assigned_to_id: group.id, assigned_to_type: 'Group', assigned_by_id: user1.id, skip_small_action_post: false }) Jobs::AssignNotification.new.execute(
{
topic_id: topic.id,
post_id: post.id,
assigned_to_id: group.id,
assigned_to_type: "Group",
assigned_by_id: user1.id,
skip_small_action_post: false,
},
)
expect { expect {
described_class.new.execute({ topic_id: topic.id, post_id: post.id, assigned_to_id: group.id, assigned_to_type: 'Group' }) described_class.new.execute(
{
topic_id: topic.id,
post_id: post.id,
assigned_to_id: group.id,
assigned_to_type: "Group",
},
)
}.to change { Notification.count }.by(-2) }.to change { Notification.count }.by(-2)
end end
end end

View File

@ -1,9 +1,9 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'rails_helper' require "rails_helper"
RSpec.describe Jobs::EnqueueReminders do RSpec.describe Jobs::EnqueueReminders do
let(:assign_allowed_group) { Group.find_by(name: 'staff') } let(:assign_allowed_group) { Group.find_by(name: "staff") }
let(:user) { Fabricate(:user, groups: [assign_allowed_group]) } let(:user) { Fabricate(:user, groups: [assign_allowed_group]) }
before do before do
@ -11,26 +11,26 @@ RSpec.describe Jobs::EnqueueReminders do
SiteSetting.assign_enabled = true SiteSetting.assign_enabled = true
end end
describe '#execute' do describe "#execute" do
it 'does not enqueue reminders when there are no assigned tasks' do it "does not enqueue reminders when there are no assigned tasks" do
assert_reminders_enqueued(0) assert_reminders_enqueued(0)
end end
it 'does not enqueue reminders when no groups are allowed to assign' do it "does not enqueue reminders when no groups are allowed to assign" do
SiteSetting.assign_allowed_on_groups = '' SiteSetting.assign_allowed_on_groups = ""
assign_multiple_tasks_to(user) assign_multiple_tasks_to(user)
assert_reminders_enqueued(0) assert_reminders_enqueued(0)
end end
it 'enqueues a reminder when the user has more than one task' do it "enqueues a reminder when the user has more than one task" do
assign_multiple_tasks_to(user) assign_multiple_tasks_to(user)
assert_reminders_enqueued(1) assert_reminders_enqueued(1)
end end
it 'does not enqueue a reminder when the user only has one task' do it "does not enqueue a reminder when the user only has one task" do
assign_one_task_to(user) assign_one_task_to(user)
assert_reminders_enqueued(0) assert_reminders_enqueued(0)
@ -43,31 +43,32 @@ RSpec.describe Jobs::EnqueueReminders do
assert_reminders_enqueued(0) assert_reminders_enqueued(0)
end end
it 'enqueues a reminder if the user was reminded more than a month ago' do it "enqueues a reminder if the user was reminded more than a month ago" do
user.upsert_custom_fields(PendingAssignsReminder::REMINDED_AT => 31.days.ago) user.upsert_custom_fields(PendingAssignsReminder::REMINDED_AT => 31.days.ago)
assign_multiple_tasks_to(user) assign_multiple_tasks_to(user)
assert_reminders_enqueued(1) assert_reminders_enqueued(1)
end end
it 'does not enqueue reminders if the remind frequency is set to never' do it "does not enqueue reminders if the remind frequency is set to never" do
SiteSetting.remind_assigns_frequency = 0 SiteSetting.remind_assigns_frequency = 0
assign_multiple_tasks_to(user) assign_multiple_tasks_to(user)
assert_reminders_enqueued(0) assert_reminders_enqueued(0)
end end
it 'does not enqueue reminders if the topic was just assigned to the user' do it "does not enqueue reminders if the topic was just assigned to the user" do
just_assigned = DateTime.now just_assigned = DateTime.now
assign_multiple_tasks_to(user, assigned_on: just_assigned) assign_multiple_tasks_to(user, assigned_on: just_assigned)
assert_reminders_enqueued(0) assert_reminders_enqueued(0)
end end
it 'enqueues a reminder when the user overrides the global frequency' do it "enqueues a reminder when the user overrides the global frequency" do
SiteSetting.remind_assigns_frequency = 0 SiteSetting.remind_assigns_frequency = 0
user.custom_fields.merge!( user.custom_fields.merge!(
PendingAssignsReminder::REMINDERS_FREQUENCY => RemindAssignsFrequencySiteSettings::DAILY_MINUTES PendingAssignsReminder::REMINDERS_FREQUENCY =>
RemindAssignsFrequencySiteSettings::DAILY_MINUTES,
) )
user.save_custom_fields user.save_custom_fields
@ -91,9 +92,7 @@ RSpec.describe Jobs::EnqueueReminders do
end end
def assign_one_task_to(user, assigned_on: 3.months.ago, post: Fabricate(:post)) def assign_one_task_to(user, assigned_on: 3.months.ago, post: Fabricate(:post))
freeze_time(assigned_on) do freeze_time(assigned_on) { Assigner.new(post.topic, user).assign(user) }
Assigner.new(post.topic, user).assign(user)
end
end end
def assign_multiple_tasks_to(user, assigned_on: 3.months.ago) def assign_multiple_tasks_to(user, assigned_on: 3.months.ago)

View File

@ -1,11 +1,11 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'rails_helper' require "rails_helper"
RSpec.describe Assigner do RSpec.describe Assigner do
before { SiteSetting.assign_enabled = true } before { SiteSetting.assign_enabled = true }
let(:assign_allowed_group) { Group.find_by(name: 'staff') } let(:assign_allowed_group) { Group.find_by(name: "staff") }
let(:pm_post) { Fabricate(:private_message_post) } let(:pm_post) { Fabricate(:private_message_post) }
let(:pm) { pm_post.topic } let(:pm) { pm_post.topic }
@ -21,27 +21,23 @@ RSpec.describe Assigner do
let(:assigner_self) { described_class.new(topic, moderator) } let(:assigner_self) { described_class.new(topic, moderator) }
it "can assign and unassign correctly" do it "can assign and unassign correctly" do
expect_enqueued_with(job: :assign_notification) do expect_enqueued_with(job: :assign_notification) { assigner.assign(moderator) }
assigner.assign(moderator)
end
expect(TopicQuery.new( expect(TopicQuery.new(moderator, assigned: moderator.username).list_latest.topics).to eq(
moderator, assigned: moderator.username [topic],
).list_latest.topics).to eq([topic]) )
expect(TopicUser.find_by(user: moderator).notification_level) expect(TopicUser.find_by(user: moderator).notification_level).to eq(
.to eq(TopicUser.notification_levels[:watching]) TopicUser.notification_levels[:watching],
)
expect_enqueued_with(job: :unassign_notification) do expect_enqueued_with(job: :unassign_notification) { assigner.unassign }
assigner.unassign
end
expect(TopicQuery.new( expect(TopicQuery.new(moderator, assigned: moderator.username).list_latest.topics).to eq([])
moderator, assigned: moderator.username
).list_latest.topics).to eq([])
expect(TopicUser.find_by(user: moderator).notification_level) expect(TopicUser.find_by(user: moderator).notification_level).to eq(
.to eq(TopicUser.notification_levels[:tracking]) TopicUser.notification_levels[:tracking],
)
end end
it "can assign with note" do it "can assign with note" do
@ -51,63 +47,76 @@ RSpec.describe Assigner do
end end
it "assign with note adds moderator post with note" do it "assign with note adds moderator post with note" do
expect { assigner.assign(moderator, note: "tomtom best mom") }.to change { topic.posts.count }.by(1) expect { assigner.assign(moderator, note: "tomtom best mom") }.to change {
topic.posts.count
}.by(1)
expect(topic.posts.last.raw).to eq "tomtom best mom" expect(topic.posts.last.raw).to eq "tomtom best mom"
end end
it "publishes topic assignment after assign and unassign" do it "publishes topic assignment after assign and unassign" do
messages = MessageBus.track_publish('/staff/topic-assignment') do messages =
MessageBus.track_publish("/staff/topic-assignment") do
assigner = described_class.new(topic, moderator_2) assigner = described_class.new(topic, moderator_2)
assigner.assign(moderator, note: "tomtom best mom") assigner.assign(moderator, note: "tomtom best mom")
assigner.unassign assigner.unassign
end end
expect(messages[0].channel).to eq "/staff/topic-assignment" expect(messages[0].channel).to eq "/staff/topic-assignment"
expect(messages[0].data).to include({ expect(messages[0].data).to include(
{
type: "assigned", type: "assigned",
topic_id: topic.id, topic_id: topic.id,
post_id: false, post_id: false,
post_number: false, post_number: false,
assigned_type: "User", assigned_type: "User",
assigned_to: BasicUserSerializer.new(moderator, scope: Guardian.new, root: false).as_json, assigned_to: BasicUserSerializer.new(moderator, scope: Guardian.new, root: false).as_json,
assignment_note: "tomtom best mom" assignment_note: "tomtom best mom",
}) },
)
expect(messages[1].channel).to eq "/staff/topic-assignment" expect(messages[1].channel).to eq "/staff/topic-assignment"
expect(messages[1].data).to include({ expect(messages[1].data).to include(
{
type: "unassigned", type: "unassigned",
topic_id: topic.id, topic_id: topic.id,
post_id: false, post_id: false,
post_number: false, post_number: false,
assigned_type: "User", assigned_type: "User",
assignment_note: nil assignment_note: nil,
}) },
)
end end
it 'does not update notification level if already watching' do it "does not update notification level if already watching" do
TopicUser.change(moderator.id, topic.id, TopicUser.change(
notification_level: TopicUser.notification_levels[:watching] moderator.id,
topic.id,
notification_level: TopicUser.notification_levels[:watching],
) )
expect do expect do assigner_self.assign(moderator) end.to_not change {
assigner_self.assign(moderator) TopicUser.last.notifications_reason_id
end.to_not change { TopicUser.last.notifications_reason_id } }
end end
it 'does not update notification level if it is not set by the plugin' do it "does not update notification level if it is not set by the plugin" do
assigner.assign(moderator) assigner.assign(moderator)
expect(TopicUser.find_by(user: moderator).notification_level) expect(TopicUser.find_by(user: moderator).notification_level).to eq(
.to eq(TopicUser.notification_levels[:watching]) TopicUser.notification_levels[:watching],
)
TopicUser.change(moderator.id, topic.id, TopicUser.change(
notification_level: TopicUser.notification_levels[:muted] moderator.id,
topic.id,
notification_level: TopicUser.notification_levels[:muted],
) )
assigner.unassign assigner.unassign
expect(TopicUser.find_by(user: moderator, topic: topic).notification_level) expect(TopicUser.find_by(user: moderator, topic: topic).notification_level).to eq(
.to eq(TopicUser.notification_levels[:muted]) TopicUser.notification_levels[:muted],
)
end end
context "when assigns_by_staff_mention is set to true" do context "when assigns_by_staff_mention is set to true" do
@ -206,7 +215,7 @@ RSpec.describe Assigner do
expect(second_assign[:success]).to eq(true) expect(second_assign[:success]).to eq(true)
end end
it 'fails to assign when the assigned user and note is the same' do it "fails to assign when the assigned user and note is the same" do
assigner = described_class.new(topic, moderator_2) assigner = described_class.new(topic, moderator_2)
assigner.assign(moderator, note: "note me down") assigner.assign(moderator, note: "note me down")
@ -216,7 +225,7 @@ RSpec.describe Assigner do
expect(assign[:reason]).to eq(:already_assigned) expect(assign[:reason]).to eq(:already_assigned)
end end
it 'allows assign when the assigned user is same but note is different' do it "allows assign when the assigned user is same but note is different" do
assigner = described_class.new(topic, moderator_2) assigner = described_class.new(topic, moderator_2)
assigner.assign(moderator, note: "note me down") assigner.assign(moderator, note: "note me down")
@ -225,21 +234,21 @@ RSpec.describe Assigner do
expect(assign[:success]).to eq(true) expect(assign[:success]).to eq(true)
end end
it 'fails to assign when the assigned user cannot view the pm' do it "fails to assign when the assigned user cannot view the pm" do
assign = described_class.new(pm, moderator_2).assign(moderator) assign = described_class.new(pm, moderator_2).assign(moderator)
expect(assign[:success]).to eq(false) expect(assign[:success]).to eq(false)
expect(assign[:reason]).to eq(:forbidden_assignee_not_pm_participant) expect(assign[:reason]).to eq(:forbidden_assignee_not_pm_participant)
end end
it 'fails to assign when the assigned admin cannot view the pm' do it "fails to assign when the assigned admin cannot view the pm" do
assign = described_class.new(pm, moderator_2).assign(admin) assign = described_class.new(pm, moderator_2).assign(admin)
expect(assign[:success]).to eq(false) expect(assign[:success]).to eq(false)
expect(assign[:reason]).to eq(:forbidden_assignee_not_pm_participant) expect(assign[:reason]).to eq(:forbidden_assignee_not_pm_participant)
end end
it 'fails to assign when not all group members has access to pm' do it "fails to assign when not all group members has access to pm" do
assign = described_class.new(pm, moderator_2).assign(moderator.groups.first) assign = described_class.new(pm, moderator_2).assign(moderator.groups.first)
expect(assign[:success]).to eq(false) expect(assign[:success]).to eq(false)
@ -252,14 +261,14 @@ RSpec.describe Assigner do
expect(assign[:reason]).to eq(:forbidden_group_assignee_not_pm_participant) expect(assign[:reason]).to eq(:forbidden_group_assignee_not_pm_participant)
end end
it 'fails to assign when the assigned user cannot view the topic' do it "fails to assign when the assigned user cannot view the topic" do
assign = described_class.new(secure_topic, moderator_2).assign(moderator) assign = described_class.new(secure_topic, moderator_2).assign(moderator)
expect(assign[:success]).to eq(false) expect(assign[:success]).to eq(false)
expect(assign[:reason]).to eq(:forbidden_assignee_cant_see_topic) expect(assign[:reason]).to eq(:forbidden_assignee_cant_see_topic)
end end
it 'fails to assign when the not all group members can view the topic' do it "fails to assign when the not all group members can view the topic" do
assign = described_class.new(secure_topic, moderator_2).assign(moderator.groups.first) assign = described_class.new(secure_topic, moderator_2).assign(moderator.groups.first)
expect(assign[:success]).to eq(false) expect(assign[:success]).to eq(false)
@ -283,7 +292,7 @@ RSpec.describe Assigner do
expect(assign[:success]).to eq(true) expect(assign[:success]).to eq(true)
end end
it 'triggers error for incorrect type' do it "triggers error for incorrect type" do
expect do expect do
described_class.new(secure_category, moderator).assign(moderator) described_class.new(secure_category, moderator).assign(moderator)
end.to raise_error(Discourse::InvalidAccess) end.to raise_error(Discourse::InvalidAccess)
@ -293,9 +302,9 @@ RSpec.describe Assigner do
it "does not recreate assignment if no assignee change" do it "does not recreate assignment if no assignee change" do
assigner.assign(moderator) assigner.assign(moderator)
expect do expect do assigner.assign(moderator, note: "new notes!") end.to_not change {
assigner.assign(moderator, note: "new notes!") Assignment.last.id
end.to_not change { Assignment.last.id } }
end end
it "updates notes" do it "updates notes" do
@ -317,21 +326,25 @@ RSpec.describe Assigner do
it "publishes topic assignment with note" do it "publishes topic assignment with note" do
assigner.assign(moderator) assigner.assign(moderator)
messages = MessageBus.track_publish('/staff/topic-assignment') do messages =
MessageBus.track_publish("/staff/topic-assignment") do
assigner = described_class.new(topic, moderator_2) assigner = described_class.new(topic, moderator_2)
assigner.assign(moderator, note: "new notes!") assigner.assign(moderator, note: "new notes!")
end end
expect(messages[0].channel).to eq "/staff/topic-assignment" expect(messages[0].channel).to eq "/staff/topic-assignment"
expect(messages[0].data).to include({ expect(messages[0].data).to include(
{
type: "assigned", type: "assigned",
topic_id: topic.id, topic_id: topic.id,
post_id: false, post_id: false,
post_number: false, post_number: false,
assigned_type: "User", assigned_type: "User",
assigned_to: BasicUserSerializer.new(moderator, scope: Guardian.new, root: false).as_json, assigned_to:
assignment_note: "new notes!" BasicUserSerializer.new(moderator, scope: Guardian.new, root: false).as_json,
}) assignment_note: "new notes!",
},
)
end end
it "adds a note_change small action post" do it "adds a note_change small action post" do
@ -348,7 +361,9 @@ RSpec.describe Assigner do
context "assign_self_regex" do context "assign_self_regex" do
fab!(:me) { Fabricate(:admin) } fab!(:me) { Fabricate(:admin) }
fab!(:op) { Fabricate(:post) } fab!(:op) { Fabricate(:post) }
fab!(:reply) { Fabricate(:post, topic: op.topic, user: me, raw: "Will fix. Added to my list ;)") } fab!(:reply) do
Fabricate(:post, topic: op.topic, user: me, raw: "Will fix. Added to my list ;)")
end
before do before do
SiteSetting.assigns_by_staff_mention = true SiteSetting.assigns_by_staff_mention = true
@ -388,7 +403,14 @@ RSpec.describe Assigner do
fab!(:me) { Fabricate(:admin) } fab!(:me) { Fabricate(:admin) }
fab!(:other) { Fabricate(:admin) } fab!(:other) { Fabricate(:admin) }
fab!(:op) { Fabricate(:post) } fab!(:op) { Fabricate(:post) }
fab!(:reply) { Fabricate(:post, topic: op.topic, user: me, raw: "can you add this to your list, @#{other.username}") } fab!(:reply) do
Fabricate(
:post,
topic: op.topic,
user: me,
raw: "can you add this to your list, @#{other.username}",
)
end
before do before do
SiteSetting.assigns_by_staff_mention = true SiteSetting.assigns_by_staff_mention = true
@ -407,7 +429,7 @@ RSpec.describe Assigner do
let(:topic) { post.topic } let(:topic) { post.topic }
let(:moderator) { Fabricate(:moderator, groups: [assign_allowed_group]) } let(:moderator) { Fabricate(:moderator, groups: [assign_allowed_group]) }
context 'topic' do context "topic" do
let(:assigner) { described_class.new(topic, moderator) } let(:assigner) { described_class.new(topic, moderator) }
before do before do
@ -417,26 +439,34 @@ RSpec.describe Assigner do
it "unassigns on topic closed" do it "unassigns on topic closed" do
topic.update_status("closed", true, moderator) topic.update_status("closed", true, moderator)
expect(TopicQuery.new(moderator, assigned: moderator.username).list_latest.topics).to be_blank expect(
TopicQuery.new(moderator, assigned: moderator.username).list_latest.topics,
).to be_blank
end end
it "unassigns on topic autoclosed" do it "unassigns on topic autoclosed" do
topic.update_status("autoclosed", true, moderator) topic.update_status("autoclosed", true, moderator)
expect(TopicQuery.new(moderator, assigned: moderator.username).list_latest.topics).to be_blank expect(
TopicQuery.new(moderator, assigned: moderator.username).list_latest.topics,
).to be_blank
end end
it "does not unassign on topic open" do it "does not unassign on topic open" do
topic.update_status("closed", false, moderator) topic.update_status("closed", false, moderator)
expect(TopicQuery.new(moderator, assigned: moderator.username).list_latest.topics).to eq([topic]) expect(TopicQuery.new(moderator, assigned: moderator.username).list_latest.topics).to eq(
[topic],
)
end end
it "does not unassign on automatic topic open" do it "does not unassign on automatic topic open" do
topic.update_status("autoclosed", false, moderator) topic.update_status("autoclosed", false, moderator)
expect(TopicQuery.new(moderator, assigned: moderator.username).list_latest.topics).to eq([topic]) expect(TopicQuery.new(moderator, assigned: moderator.username).list_latest.topics).to eq(
[topic],
)
end end
end end
context 'post' do context "post" do
let(:post_2) { Fabricate(:post, topic: topic) } let(:post_2) { Fabricate(:post, topic: topic) }
let(:assigner) { described_class.new(post_2, moderator) } let(:assigner) { described_class.new(post_2, moderator) }
@ -447,7 +477,7 @@ RSpec.describe Assigner do
assigner.assign(moderator) assigner.assign(moderator)
end end
it 'deactivates post assignments when topic is closed' do it "deactivates post assignments when topic is closed" do
assigner.assign(moderator) assigner.assign(moderator)
expect(post_2.assignment.active).to be true expect(post_2.assignment.active).to be true
@ -456,7 +486,7 @@ RSpec.describe Assigner do
expect(post_2.assignment.reload.active).to be false expect(post_2.assignment.reload.active).to be false
end end
it 'deactivates post assignments when post is deleted and activate when recovered' do it "deactivates post assignments when post is deleted and activate when recovered" do
assigner.assign(moderator) assigner.assign(moderator)
expect(post_2.assignment.active).to be true expect(post_2.assignment.active).to be true
@ -468,7 +498,7 @@ RSpec.describe Assigner do
expect(post_2.assignment.reload.active).to be true expect(post_2.assignment.reload.active).to be true
end end
it 'deletes post small action for deleted post' do it "deletes post small action for deleted post" do
assigner.assign(moderator) assigner.assign(moderator)
small_action_post = PostCustomField.where(name: "action_code_post_id").first.post small_action_post = PostCustomField.where(name: "action_code_post_id").first.post
@ -483,7 +513,7 @@ RSpec.describe Assigner do
let(:topic) { post.topic } let(:topic) { post.topic }
let(:moderator) { Fabricate(:moderator, groups: [assign_allowed_group]) } let(:moderator) { Fabricate(:moderator, groups: [assign_allowed_group]) }
context 'topic' do context "topic" do
let(:assigner) { described_class.new(topic, moderator) } let(:assigner) { described_class.new(topic, moderator) }
before do before do
@ -494,14 +524,18 @@ RSpec.describe Assigner do
it "reassigns on topic open" do it "reassigns on topic open" do
topic.update_status("closed", true, moderator) topic.update_status("closed", true, moderator)
expect(TopicQuery.new(moderator, assigned: moderator.username).list_latest.topics).to be_blank expect(
TopicQuery.new(moderator, assigned: moderator.username).list_latest.topics,
).to be_blank
topic.update_status("closed", false, moderator) topic.update_status("closed", false, moderator)
expect(TopicQuery.new(moderator, assigned: moderator.username).list_latest.topics).to eq([topic]) expect(TopicQuery.new(moderator, assigned: moderator.username).list_latest.topics).to eq(
[topic],
)
end end
end end
context 'post' do context "post" do
let(:post_2) { Fabricate(:post, topic: topic) } let(:post_2) { Fabricate(:post, topic: topic) }
let(:assigner) { described_class.new(post_2, moderator) } let(:assigner) { described_class.new(post_2, moderator) }
@ -512,7 +546,7 @@ RSpec.describe Assigner do
assigner.assign(moderator) assigner.assign(moderator)
end end
it 'reassigns post on topic open' do it "reassigns post on topic open" do
assigner.assign(moderator) assigner.assign(moderator)
expect(post_2.assignment.active).to be true expect(post_2.assignment.active).to be true
@ -535,36 +569,41 @@ RSpec.describe Assigner do
it "send an email if set to 'always'" do it "send an email if set to 'always'" do
SiteSetting.assign_mailer = AssignMailer.levels[:always] SiteSetting.assign_mailer = AssignMailer.levels[:always]
expect { described_class.new(topic, moderator).assign(moderator) } expect { described_class.new(topic, moderator).assign(moderator) }.to change {
.to change { ActionMailer::Base.deliveries.size }.by(1) ActionMailer::Base.deliveries.size
}.by(1)
end end
it "doesn't send an email if assignee is a group" do it "doesn't send an email if assignee is a group" do
SiteSetting.assign_mailer = AssignMailer.levels[:always] SiteSetting.assign_mailer = AssignMailer.levels[:always]
expect { described_class.new(topic, moderator).assign(assign_allowed_group) } expect { described_class.new(topic, moderator).assign(assign_allowed_group) }.to change {
.to change { ActionMailer::Base.deliveries.size }.by(0) ActionMailer::Base.deliveries.size
}.by(0)
end end
it "doesn't send an email if the assigner and assignee are not different" do it "doesn't send an email if the assigner and assignee are not different" do
SiteSetting.assign_mailer = AssignMailer.levels[:different_users] SiteSetting.assign_mailer = AssignMailer.levels[:different_users]
expect { described_class.new(topic, moderator).assign(moderator_2) } expect { described_class.new(topic, moderator).assign(moderator_2) }.to change {
.to change { ActionMailer::Base.deliveries.size }.by(1) ActionMailer::Base.deliveries.size
}.by(1)
end end
it "doesn't send an email if the assigner and assignee are not different" do it "doesn't send an email if the assigner and assignee are not different" do
SiteSetting.assign_mailer = AssignMailer.levels[:different_users] SiteSetting.assign_mailer = AssignMailer.levels[:different_users]
expect { described_class.new(topic, moderator).assign(moderator) } expect { described_class.new(topic, moderator).assign(moderator) }.to change {
.to change { ActionMailer::Base.deliveries.size }.by(0) ActionMailer::Base.deliveries.size
}.by(0)
end end
it "doesn't send an email" do it "doesn't send an email" do
SiteSetting.assign_mailer = AssignMailer.levels[:never] SiteSetting.assign_mailer = AssignMailer.levels[:never]
expect { described_class.new(topic, moderator).assign(moderator_2) } expect { described_class.new(topic, moderator).assign(moderator_2) }.to change {
.to change { ActionMailer::Base.deliveries.size }.by(0) ActionMailer::Base.deliveries.size
}.by(0)
end end
end end
end end

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'rails_helper' require "rails_helper"
require_relative '../support/assign_allowed_group' require_relative "../support/assign_allowed_group"
def assert_reminder_not_created def assert_reminder_not_created
expect { subject.remind(user) }.to change { Post.count }.by(0) expect { subject.remind(user) }.to change { Post.count }.by(0)
@ -12,21 +12,21 @@ RSpec.describe PendingAssignsReminder do
let(:user) { Fabricate(:user) } let(:user) { Fabricate(:user) }
it 'does not create a reminder if the user has 0 assigned topics' do it "does not create a reminder if the user has 0 assigned topics" do
assert_reminder_not_created assert_reminder_not_created
end end
it 'does not create a reminder if the user only has one task' do it "does not create a reminder if the user only has one task" do
post = Fabricate(:post) post = Fabricate(:post)
Assigner.new(post.topic, user).assign(user) Assigner.new(post.topic, user).assign(user)
assert_reminder_not_created assert_reminder_not_created
end end
describe 'when the user has multiple tasks' do describe "when the user has multiple tasks" do
let(:system) { Discourse.system_user } let(:system) { Discourse.system_user }
include_context 'A group that is allowed to assign' include_context "A group that is allowed to assign"
before do before do
add_to_assign_allowed_group(user) add_to_assign_allowed_group(user)
@ -46,7 +46,7 @@ RSpec.describe PendingAssignsReminder do
@post4.topic.update(category: secure_category) @post4.topic.update(category: secure_category)
end end
it 'creates a reminder for a particular user and sets the timestamp of the last reminder' do it "creates a reminder for a particular user and sets the timestamp of the last reminder" do
freeze_time freeze_time
subject.remind(user) subject.remind(user)
@ -56,31 +56,29 @@ RSpec.describe PendingAssignsReminder do
expect(topic.user).to eq(system) expect(topic.user).to eq(system)
expect(topic.archetype).to eq(Archetype.private_message) expect(topic.archetype).to eq(Archetype.private_message)
expect(topic.topic_allowed_users.pluck(:user_id)).to contain_exactly( expect(topic.topic_allowed_users.pluck(:user_id)).to contain_exactly(system.id, user.id)
system.id, user.id
)
expect(topic.title).to eq(I18n.t( expect(topic.title).to eq(I18n.t("pending_assigns_reminder.title", pending_assignments: 3))
'pending_assigns_reminder.title',
pending_assignments: 3
))
expect(post.raw).to include(@post1.topic.fancy_title) expect(post.raw).to include(@post1.topic.fancy_title)
expect(post.raw).to include(@post2.topic.fancy_title) expect(post.raw).to include(@post2.topic.fancy_title)
expect(post.raw).to_not include(@post3.topic.fancy_title) expect(post.raw).to_not include(@post3.topic.fancy_title)
expect(post.raw).to_not include(@post4.topic.fancy_title) expect(post.raw).to_not include(@post4.topic.fancy_title)
expect( expect(user.reload.custom_fields[described_class::REMINDED_AT].to_datetime).to eq_time(
user.reload.custom_fields[described_class::REMINDED_AT].to_datetime DateTime.now,
).to eq_time(DateTime.now) )
end end
it 'deletes previous reminders when creating a new one' do it "deletes previous reminders when creating a new one" do
subject.remind(user) subject.remind(user)
subject.remind(user) subject.remind(user)
reminders_count = Topic.joins(:_custom_fields) reminders_count =
.where(topic_custom_fields: { name: described_class::CUSTOM_FIELD_NAME }).count Topic
.joins(:_custom_fields)
.where(topic_custom_fields: { name: described_class::CUSTOM_FIELD_NAME })
.count
expect(reminders_count).to eq(1) expect(reminders_count).to eq(1)
end end
@ -96,8 +94,11 @@ RSpec.describe PendingAssignsReminder do
subject.remind(another_user) subject.remind(another_user)
reminders_count = Topic.joins(:_custom_fields) reminders_count =
.where(topic_custom_fields: { name: described_class::CUSTOM_FIELD_NAME }).count Topic
.joins(:_custom_fields)
.where(topic_custom_fields: { name: described_class::CUSTOM_FIELD_NAME })
.count
expect(reminders_count).to eq(2) expect(reminders_count).to eq(2)
end end
@ -107,8 +108,11 @@ RSpec.describe PendingAssignsReminder do
Fabricate(:post, topic: Topic.last) Fabricate(:post, topic: Topic.last)
subject.remind(user) subject.remind(user)
reminders_count = Topic.joins(:_custom_fields) reminders_count =
.where(topic_custom_fields: { name: described_class::CUSTOM_FIELD_NAME }).count Topic
.joins(:_custom_fields)
.where(topic_custom_fields: { name: described_class::CUSTOM_FIELD_NAME })
.count
expect(reminders_count).to eq(2) expect(reminders_count).to eq(2)
end end
@ -124,10 +128,7 @@ RSpec.describe PendingAssignsReminder do
post = Post.last post = Post.last
topic = post.topic topic = post.topic
expect(topic.title).to eq(I18n.t( expect(topic.title).to eq(I18n.t("pending_assigns_reminder.title", pending_assignments: 4))
'pending_assigns_reminder.title',
pending_assignments: 4
))
@post5.topic.update_status("closed", true, Discourse.system_user) @post5.topic.update_status("closed", true, Discourse.system_user)
expect(@post5.topic.closed).to eq(true) expect(@post5.topic.closed).to eq(true)
@ -137,11 +138,7 @@ RSpec.describe PendingAssignsReminder do
post = Post.last post = Post.last
topic = post.topic topic = post.topic
expect(topic.title).to eq(I18n.t( expect(topic.title).to eq(I18n.t("pending_assigns_reminder.title", pending_assignments: 3))
'pending_assigns_reminder.title',
pending_assignments: 3
))
end end
end end
end end

View File

@ -1,8 +1,8 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'rails_helper' require "rails_helper"
require_relative '../support/assign_allowed_group' require_relative "../support/assign_allowed_group"
require 'random_assign_utils' require "random_assign_utils"
describe RandomAssignUtils do describe RandomAssignUtils do
before do before do
@ -12,33 +12,44 @@ describe RandomAssignUtils do
Rails.logger = @fake_logger = FakeLogger.new Rails.logger = @fake_logger = FakeLogger.new
end end
after do after { Rails.logger = @orig_logger }
Rails.logger = @orig_logger
end
FakeAutomation = Struct.new(:id) FakeAutomation = Struct.new(:id)
let(:post) { Fabricate(:post) } let(:post) { Fabricate(:post) }
let!(:automation) { FakeAutomation.new(1) } let!(:automation) { FakeAutomation.new(1) }
describe '.automation_script!' do describe ".automation_script!" do
context 'all users of group are on holidays' do context "all users of group are on holidays" do
fab!(:topic_1) { Fabricate(:topic) } fab!(:topic_1) { Fabricate(:topic) }
fab!(:group_1) { Fabricate(:group) } fab!(:group_1) { Fabricate(:group) }
fab!(:user_1) { Fabricate(:user) } fab!(:user_1) { Fabricate(:user) }
before do before do
group_1.add(user_1) group_1.add(user_1)
UserCustomField.create!(name: 'on_holiday', value: 't', user_id: user_1.id) UserCustomField.create!(name: "on_holiday", value: "t", user_id: user_1.id)
end end
it 'creates post on the topic' do it "creates post on the topic" do
described_class.automation_script!({}, { 'assignees_group' => { 'value' => group_1.id }, 'assigned_topic' => { 'value' => topic_1.id } }, automation) described_class.automation_script!(
expect(topic_1.posts.first.raw).to match("Attempted randomly assign a member of `@#{group_1.name}`, but no one was available.") {},
{
"assignees_group" => {
"value" => group_1.id,
},
"assigned_topic" => {
"value" => topic_1.id,
},
},
automation,
)
expect(topic_1.posts.first.raw).to match(
"Attempted randomly assign a member of `@#{group_1.name}`, but no one was available.",
)
end end
end end
context 'all users of group have been assigned recently' do context "all users of group have been assigned recently" do
fab!(:topic_1) { Fabricate(:topic) } fab!(:topic_1) { Fabricate(:topic) }
fab!(:group_1) { Fabricate(:group) } fab!(:group_1) { Fabricate(:group) }
fab!(:user_1) { Fabricate(:user) } fab!(:user_1) { Fabricate(:user) }
@ -48,127 +59,217 @@ describe RandomAssignUtils do
group_1.add(user_1) group_1.add(user_1)
end end
it 'creates post on the topic' do it "creates post on the topic" do
described_class.automation_script!({}, { 'assignees_group' => { 'value' => group_1.id }, 'assigned_topic' => { 'value' => topic_1.id } }, automation) described_class.automation_script!(
expect(topic_1.posts.first.raw).to match("Attempted randomly assign a member of `@#{group_1.name}`, but no one was available.") {},
{
"assignees_group" => {
"value" => group_1.id,
},
"assigned_topic" => {
"value" => topic_1.id,
},
},
automation,
)
expect(topic_1.posts.first.raw).to match(
"Attempted randomly assign a member of `@#{group_1.name}`, but no one was available.",
)
end end
end end
context 'user can be assigned' do context "user can be assigned" do
fab!(:group_1) { Fabricate(:group) } fab!(:group_1) { Fabricate(:group) }
fab!(:user_1) { Fabricate(:user) } fab!(:user_1) { Fabricate(:user) }
fab!(:topic_1) { Fabricate(:topic) } fab!(:topic_1) { Fabricate(:topic) }
before do before do
SiteSetting.assign_allowed_on_groups = [group_1.id.to_s].join('|') SiteSetting.assign_allowed_on_groups = [group_1.id.to_s].join("|")
group_1.add(user_1) group_1.add(user_1)
end end
context 'post_template is set' do context "post_template is set" do
it 'creates a post with the template and assign the user' do it "creates a post with the template and assign the user" do
described_class.automation_script!({}, { 'post_template' => { 'value' => 'this is a post template' }, 'assignees_group' => { 'value' => group_1.id }, 'assigned_topic' => { 'value' => topic_1.id } }, automation) described_class.automation_script!(
expect(topic_1.posts.first.raw).to match('this is a post template') {},
{
"post_template" => {
"value" => "this is a post template",
},
"assignees_group" => {
"value" => group_1.id,
},
"assigned_topic" => {
"value" => topic_1.id,
},
},
automation,
)
expect(topic_1.posts.first.raw).to match("this is a post template")
end end
end end
context 'post_template is not set' do context "post_template is not set" do
fab!(:post_1) { Fabricate(:post, topic: topic_1) } fab!(:post_1) { Fabricate(:post, topic: topic_1) }
it 'assigns the user to the topic' do it "assigns the user to the topic" do
described_class.automation_script!({}, { 'assignees_group' => { 'value' => group_1.id }, 'assigned_topic' => { 'value' => topic_1.id } }, automation) described_class.automation_script!(
{},
{
"assignees_group" => {
"value" => group_1.id,
},
"assigned_topic" => {
"value" => topic_1.id,
},
},
automation,
)
expect(topic_1.assignment.assigned_to_id).to eq(user_1.id) expect(topic_1.assignment.assigned_to_id).to eq(user_1.id)
end end
end end
end end
context 'all users in working hours' do context "all users in working hours" do
fab!(:topic_1) { Fabricate(:topic) } fab!(:topic_1) { Fabricate(:topic) }
fab!(:group_1) { Fabricate(:group) } fab!(:group_1) { Fabricate(:group) }
fab!(:user_1) { Fabricate(:user) } fab!(:user_1) { Fabricate(:user) }
before do before do
freeze_time('2022-10-01 02:00') freeze_time("2022-10-01 02:00")
UserOption.find_by(user_id: user_1.id).update(timezone: 'Europe/Paris') UserOption.find_by(user_id: user_1.id).update(timezone: "Europe/Paris")
group_1.add(user_1) group_1.add(user_1)
end end
it 'creates post on the topic' do it "creates post on the topic" do
described_class.automation_script!({}, { 'in_working_hours' => { 'value' => true }, 'assignees_group' => { 'value' => group_1.id }, 'assigned_topic' => { 'value' => topic_1.id } }, automation) described_class.automation_script!(
expect(topic_1.posts.first.raw).to match("Attempted randomly assign a member of `@#{group_1.name}`, but no one was available.") {},
{
"in_working_hours" => {
"value" => true,
},
"assignees_group" => {
"value" => group_1.id,
},
"assigned_topic" => {
"value" => topic_1.id,
},
},
automation,
)
expect(topic_1.posts.first.raw).to match(
"Attempted randomly assign a member of `@#{group_1.name}`, but no one was available.",
)
end end
end end
context 'assignees_group not provided' do context "assignees_group not provided" do
fab!(:topic_1) { Fabricate(:topic) } fab!(:topic_1) { Fabricate(:topic) }
it 'raises an error' do it "raises an error" do
expect { expect {
described_class.automation_script!({}, { 'assigned_topic' => { 'value' => topic_1.id } }, automation) described_class.automation_script!(
{},
{ "assigned_topic" => { "value" => topic_1.id } },
automation,
)
}.to raise_error(/`assignees_group` not provided/) }.to raise_error(/`assignees_group` not provided/)
end end
end end
context 'assignees_group not found' do context "assignees_group not found" do
fab!(:topic_1) { Fabricate(:topic) } fab!(:topic_1) { Fabricate(:topic) }
it 'raises an error' do it "raises an error" do
expect { expect {
described_class.automation_script!({}, { 'assigned_topic' => { 'value' => topic_1.id }, 'assignees_group' => { 'value' => -1 } }, automation) described_class.automation_script!(
{},
{
"assigned_topic" => {
"value" => topic_1.id,
},
"assignees_group" => {
"value" => -1,
},
},
automation,
)
}.to raise_error(/Group\(-1\) not found/) }.to raise_error(/Group\(-1\) not found/)
end end
end end
context 'assigned_topic not provided' do context "assigned_topic not provided" do
it 'raises an error' do it "raises an error" do
expect { expect { described_class.automation_script!({}, {}, automation) }.to raise_error(
described_class.automation_script!({}, {}, automation) /`assigned_topic` not provided/,
}.to raise_error(/`assigned_topic` not provided/) )
end end
end end
context 'assigned_topic is not found' do context "assigned_topic is not found" do
it 'raises an error' do it "raises an error" do
expect { expect {
described_class.automation_script!({}, { 'assigned_topic' => { 'value' => 1 } }, automation) described_class.automation_script!(
{},
{ "assigned_topic" => { "value" => 1 } },
automation,
)
}.to raise_error(/Topic\(1\) not found/) }.to raise_error(/Topic\(1\) not found/)
end end
end end
context 'minimum_time_between_assignments is set' do context "minimum_time_between_assignments is set" do
context 'the topic has been assigned recently' do context "the topic has been assigned recently" do
fab!(:topic_1) { Fabricate(:topic) } fab!(:topic_1) { Fabricate(:topic) }
before do before do
freeze_time freeze_time
TopicCustomField.create!(name: 'assigned_to_id', topic_id: topic_1.id, created_at: 20.hours.ago) TopicCustomField.create!(
name: "assigned_to_id",
topic_id: topic_1.id,
created_at: 20.hours.ago,
)
end end
it 'logs a warning' do it "logs a warning" do
described_class.automation_script!({}, { 'assigned_topic' => { 'value' => topic_1.id }, 'minimum_time_between_assignments' => { 'value' => 10 } }, automation) described_class.automation_script!(
expect(Rails.logger.infos.first).to match(/Topic\(#{topic_1.id}\) has already been assigned recently/) {},
{
"assigned_topic" => {
"value" => topic_1.id,
},
"minimum_time_between_assignments" => {
"value" => 10,
},
},
automation,
)
expect(Rails.logger.infos.first).to match(
/Topic\(#{topic_1.id}\) has already been assigned recently/,
)
end end
end end
end end
end end
describe '.recently_assigned_users_ids' do describe ".recently_assigned_users_ids" do
context 'no one has been assigned' do context "no one has been assigned" do
it 'returns an empty array' do it "returns an empty array" do
assignees_ids = described_class.recently_assigned_users_ids(post.topic_id, 2.months.ago) assignees_ids = described_class.recently_assigned_users_ids(post.topic_id, 2.months.ago)
expect(assignees_ids).to eq([]) expect(assignees_ids).to eq([])
end end
end end
context 'users have been assigned' do context "users have been assigned" do
let(:admin) { Fabricate(:admin) } let(:admin) { Fabricate(:admin) }
let(:assign_allowed_group) { Group.find_by(name: 'staff') } let(:assign_allowed_group) { Group.find_by(name: "staff") }
let(:user_1) { Fabricate(:user, groups: [assign_allowed_group]) } let(:user_1) { Fabricate(:user, groups: [assign_allowed_group]) }
let(:user_2) { Fabricate(:user, groups: [assign_allowed_group]) } let(:user_2) { Fabricate(:user, groups: [assign_allowed_group]) }
let(:user_3) { Fabricate(:user, groups: [assign_allowed_group]) } let(:user_3) { Fabricate(:user, groups: [assign_allowed_group]) }
let(:user_4) { Fabricate(:user, groups: [assign_allowed_group]) } let(:user_4) { Fabricate(:user, groups: [assign_allowed_group]) }
let(:post_2) { Fabricate(:post, topic: post.topic) } let(:post_2) { Fabricate(:post, topic: post.topic) }
it 'returns the recently assigned user ids' do it "returns the recently assigned user ids" do
freeze_time 1.months.ago do freeze_time 1.months.ago do
Assigner.new(post.topic, admin).assign(user_1) Assigner.new(post.topic, admin).assign(user_1)
Assigner.new(post.topic, admin).assign(user_2) Assigner.new(post.topic, admin).assign(user_2)

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'topic_view' require "topic_view"
describe TopicQuery do describe TopicQuery do
fab!(:user) { Fabricate(:user) } fab!(:user) { Fabricate(:user) }
@ -13,17 +13,13 @@ describe TopicQuery do
fab!(:group) { Fabricate(:group) } fab!(:group) { Fabricate(:group) }
describe '#list_group_topics_assigned' do describe "#list_group_topics_assigned" do
before do before do
SiteSetting.assign_enabled = true SiteSetting.assign_enabled = true
[user, admin, other_admin].each do |user| [user, admin, other_admin].each { |user| group.add(user) }
group.add(user)
end
[user_pm, admin_pm, other_admin_pm].each do |topic| [user_pm, admin_pm, other_admin_pm].each { |topic| Fabricate(:post, topic: topic) }
Fabricate(:post, topic: topic)
end
Fabricate(:topic_allowed_user, user: admin, topic: user_pm) Fabricate(:topic_allowed_user, user: admin, topic: user_pm)
Assigner.new(user_pm, Discourse.system_user).assign(admin) Assigner.new(user_pm, Discourse.system_user).assign(admin)
@ -32,9 +28,17 @@ describe TopicQuery do
end end
it "includes PMs from all users" do it "includes PMs from all users" do
expect(TopicQuery.new(user).list_group_topics_assigned(group).topics).to contain_exactly(user_pm) expect(TopicQuery.new(user).list_group_topics_assigned(group).topics).to contain_exactly(
expect(TopicQuery.new(admin).list_group_topics_assigned(group).topics).to contain_exactly(user_pm, admin_pm, other_admin_pm) user_pm,
expect(TopicQuery.new(other_admin).list_group_topics_assigned(group).topics).to contain_exactly(user_pm, admin_pm, other_admin_pm) )
expect(TopicQuery.new(admin).list_group_topics_assigned(group).topics).to contain_exactly(
user_pm,
admin_pm,
other_admin_pm,
)
expect(
TopicQuery.new(other_admin).list_group_topics_assigned(group).topics,
).to contain_exactly(user_pm, admin_pm, other_admin_pm)
end end
end end
end end

View File

@ -1,21 +1,18 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'rails_helper' require "rails_helper"
require_relative '../support/assign_allowed_group' require_relative "../support/assign_allowed_group"
RSpec.describe Group do RSpec.describe Group do
let(:group) { Fabricate(:group) } let(:group) { Fabricate(:group) }
before do before { SiteSetting.assign_enabled = true }
SiteSetting.assign_enabled = true
end
context 'Tracking changes that could affect the allow assign on groups site setting' do context "Tracking changes that could affect the allow assign on groups site setting" do
let(:removed_group_setting) { "3|4" }
let(:removed_group_setting) { '3|4' }
let(:group_attribute) { group.id } let(:group_attribute) { group.id }
it 'removes the group from the setting when the group gets destroyed' do it "removes the group from the setting when the group gets destroyed" do
SiteSetting.assign_allowed_on_groups = "#{group_attribute}|#{removed_group_setting}" SiteSetting.assign_allowed_on_groups = "#{group_attribute}|#{removed_group_setting}"
group.destroy! group.destroy!
@ -23,7 +20,7 @@ RSpec.describe Group do
expect(SiteSetting.assign_allowed_on_groups).to eq removed_group_setting expect(SiteSetting.assign_allowed_on_groups).to eq removed_group_setting
end end
it 'removes the group from the setting when this is the last one on the list' do it "removes the group from the setting when this is the last one on the list" do
SiteSetting.assign_allowed_on_groups = "#{removed_group_setting}|#{group_attribute}" SiteSetting.assign_allowed_on_groups = "#{removed_group_setting}|#{group_attribute}"
group.destroy! group.destroy!
@ -31,7 +28,7 @@ RSpec.describe Group do
expect(SiteSetting.assign_allowed_on_groups).to eq removed_group_setting expect(SiteSetting.assign_allowed_on_groups).to eq removed_group_setting
end end
it 'removes the group from the list when it is on the middle of the list' do it "removes the group from the list when it is on the middle of the list" do
allowed_groups = "3|#{group_attribute}|4" allowed_groups = "3|#{group_attribute}|4"
SiteSetting.assign_allowed_on_groups = allowed_groups SiteSetting.assign_allowed_on_groups = allowed_groups
@ -41,13 +38,13 @@ RSpec.describe Group do
end end
end end
context 'includes can_show_assigned_tab? method' do context "includes can_show_assigned_tab? method" do
let(:admin) { Fabricate(:admin) } let(:admin) { Fabricate(:admin) }
let(:user) { Fabricate(:user) } let(:user) { Fabricate(:user) }
let(:user1) { Fabricate(:user) } let(:user1) { Fabricate(:user) }
let(:user2) { Fabricate(:user) } let(:user2) { Fabricate(:user) }
include_context 'A group that is allowed to assign' include_context "A group that is allowed to assign"
before do before do
add_to_assign_allowed_group(user) add_to_assign_allowed_group(user)
@ -55,7 +52,7 @@ RSpec.describe Group do
add_to_assign_allowed_group(admin) add_to_assign_allowed_group(admin)
end end
it 'gives false in can_show_assigned_tab? when all users are not in assigned_allowed_group' do it "gives false in can_show_assigned_tab? when all users are not in assigned_allowed_group" do
group.add(user) group.add(user)
group.add(user1) group.add(user1)
group.add(user2) group.add(user2)
@ -63,7 +60,7 @@ RSpec.describe Group do
expect(group.can_show_assigned_tab?).to eq(false) expect(group.can_show_assigned_tab?).to eq(false)
end end
it 'gives true in can_show_assigned_tab? when all users are in assigned_allowed_group' do it "gives true in can_show_assigned_tab? when all users are in assigned_allowed_group" do
group.add(user) group.add(user)
group.add(user1) group.add(user1)

View File

@ -8,14 +8,24 @@ describe Reviewable do
fab!(:reviewable1) { Fabricate(:reviewable_flagged_post, target: post1) } fab!(:reviewable1) { Fabricate(:reviewable_flagged_post, target: post1) }
fab!(:reviewable2) { Fabricate(:reviewable_flagged_post, target: post2) } fab!(:reviewable2) { Fabricate(:reviewable_flagged_post, target: post2) }
before do before { SiteSetting.assign_enabled = true }
SiteSetting.assign_enabled = true
end
it "can filter by assigned_to" do it "can filter by assigned_to" do
Assignment.create!(target: post1, topic_id: post1.topic.id, assigned_by_user: user, assigned_to: user) Assignment.create!(
Assignment.create!(target: post2, topic_id: post2.topic.id, assigned_by_user: user, assigned_to: admin) target: post1,
topic_id: post1.topic.id,
assigned_by_user: user,
assigned_to: user,
)
Assignment.create!(
target: post2,
topic_id: post2.topic.id,
assigned_by_user: user,
assigned_to: admin,
)
expect(Reviewable.list_for(admin, additional_filters: { assigned_to: user.username })).to eq([reviewable1]) expect(Reviewable.list_for(admin, additional_filters: { assigned_to: user.username })).to eq(
[reviewable1],
)
end end
end end

View File

@ -8,19 +8,29 @@ describe Topic do
let(:group) { Fabricate(:group) } let(:group) { Fabricate(:group) }
let(:topic) { Fabricate(:topic) } let(:topic) { Fabricate(:topic) }
before do before { SiteSetting.assign_enabled = true }
SiteSetting.assign_enabled = true
end
describe "#assigned_to" do describe "#assigned_to" do
it "correctly points to a user" do it "correctly points to a user" do
Assignment.create!(target_id: topic.id, target_type: "Topic", topic_id: topic.id, assigned_by_user: user1, assigned_to: user2) Assignment.create!(
target_id: topic.id,
target_type: "Topic",
topic_id: topic.id,
assigned_by_user: user1,
assigned_to: user2,
)
expect(topic.reload.assigned_to).to eq(user2) expect(topic.reload.assigned_to).to eq(user2)
end end
it "correctly points to a group" do it "correctly points to a group" do
Assignment.create!(target_id: topic.id, target_type: "Topic", topic_id: topic.id, assigned_by_user: user1, assigned_to: group) Assignment.create!(
target_id: topic.id,
target_type: "Topic",
topic_id: topic.id,
assigned_by_user: user1,
assigned_to: group,
)
expect(topic.reload.assigned_to).to eq(group) expect(topic.reload.assigned_to).to eq(group)
end end

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'rails_helper' require "rails_helper"
RSpec.describe UserCustomField do RSpec.describe UserCustomField do
before { SiteSetting.assign_enabled = true } before { SiteSetting.assign_enabled = true }
@ -8,12 +8,12 @@ RSpec.describe UserCustomField do
let(:field_name) { PendingAssignsReminder::REMINDERS_FREQUENCY } let(:field_name) { PendingAssignsReminder::REMINDERS_FREQUENCY }
let(:new_field) { UserCustomField.new(name: field_name, user_id: 1) } let(:new_field) { UserCustomField.new(name: field_name, user_id: 1) }
it 'coerces the value to be an integer' do it "coerces the value to be an integer" do
new_field.value = 'DROP TABLE USERS;' new_field.value = "DROP TABLE USERS;"
new_field.save! new_field.save!
saved_field = new_field.reload saved_field = new_field.reload
expect(saved_field.value).to eq('0') expect(saved_field.value).to eq("0")
end end
end end

View File

@ -1,12 +1,12 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'rails_helper' require "rails_helper"
describe 'plugin' do describe "plugin" do
before { SiteSetting.assign_enabled = true } before { SiteSetting.assign_enabled = true }
describe 'events' do describe "events" do
describe 'on user_removed_from_group' do describe "on user_removed_from_group" do
before do before do
@topic = Fabricate(:post).topic @topic = Fabricate(:post).topic
@user = Fabricate(:user) @user = Fabricate(:user)
@ -14,7 +14,7 @@ describe 'plugin' do
@group_a.add(@user) @group_a.add(@user)
end end
it 'unassigns the user' do it "unassigns the user" do
SiteSetting.assign_allowed_on_groups = @group_a.id.to_s SiteSetting.assign_allowed_on_groups = @group_a.id.to_s
Assigner.new(@topic, Discourse.system_user).assign(@user) Assigner.new(@topic, Discourse.system_user).assign(@user)
@ -26,7 +26,7 @@ describe 'plugin' do
it "doesn't unassign the user if it still has access through another group" do it "doesn't unassign the user if it still has access through another group" do
@group_b = Fabricate(:group) @group_b = Fabricate(:group)
@group_b.add(@user) @group_b.add(@user)
SiteSetting.assign_allowed_on_groups = [@group_a.id.to_s, @group_b.id.to_s].join('|') SiteSetting.assign_allowed_on_groups = [@group_a.id.to_s, @group_b.id.to_s].join("|")
Assigner.new(@topic, Discourse.system_user).assign(@user) Assigner.new(@topic, Discourse.system_user).assign(@user)
@group_a.remove(@user) @group_a.remove(@user)

View File

@ -1,45 +1,55 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'rails_helper' require "rails_helper"
require_relative '../support/assign_allowed_group' require_relative "../support/assign_allowed_group"
RSpec.describe DiscourseAssign::AssignController do RSpec.describe DiscourseAssign::AssignController do
before { SiteSetting.assign_enabled = true } before { SiteSetting.assign_enabled = true }
fab!(:default_allowed_group) { Group.find_by(name: 'staff') } fab!(:default_allowed_group) { Group.find_by(name: "staff") }
let(:user) { Fabricate(:admin, groups: [default_allowed_group], name: 'Robin Ward', username: 'eviltrout') } let(:user) do
Fabricate(:admin, groups: [default_allowed_group], name: "Robin Ward", username: "eviltrout")
end
fab!(:post) { Fabricate(:post) } fab!(:post) { Fabricate(:post) }
fab!(:user2) { Fabricate(:active_user, name: 'David Tylor', username: 'david', groups: [default_allowed_group]) } fab!(:user2) do
Fabricate(:active_user, name: "David Tylor", username: "david", groups: [default_allowed_group])
end
let(:nonadmin) { Fabricate(:user, groups: [default_allowed_group]) } let(:nonadmin) { Fabricate(:user, groups: [default_allowed_group]) }
fab!(:normal_user) { Fabricate(:user) } fab!(:normal_user) { Fabricate(:user) }
fab!(:normal_admin) { Fabricate(:admin) } fab!(:normal_admin) { Fabricate(:admin) }
context 'only allow users from allowed groups' do context "only allow users from allowed groups" do
before { sign_in(user2) } before { sign_in(user2) }
it 'filters requests where current_user is not member of an allowed group' do it "filters requests where current_user is not member of an allowed group" do
SiteSetting.assign_allowed_on_groups = '' SiteSetting.assign_allowed_on_groups = ""
put '/assign/assign.json', params: { put "/assign/assign.json",
target_id: post.topic_id, target_type: 'Topic', username: user2.username params: {
target_id: post.topic_id,
target_type: "Topic",
username: user2.username,
} }
expect(response.status).to eq(403) expect(response.status).to eq(403)
end end
it 'filters requests where assigne group is not allowed' do it "filters requests where assigne group is not allowed" do
put '/assign/assign.json', params: { put "/assign/assign.json",
target_id: post.topic_id, target_type: 'Topic', group_name: default_allowed_group.name params: {
target_id: post.topic_id,
target_type: "Topic",
group_name: default_allowed_group.name,
} }
expect(response.status).to eq(400) expect(response.status).to eq(400)
end end
describe '#suggestions' do describe "#suggestions" do
before { sign_in(user) } before { sign_in(user) }
it 'includes users in allowed groups' do it "includes users in allowed groups" do
allowed_group = Group.find_by(name: 'everyone') allowed_group = Group.find_by(name: "everyone")
allowed_group.add(user2) allowed_group.add(user2)
defaults = "#{default_allowed_group.id}|#{allowed_group.id}" defaults = "#{default_allowed_group.id}|#{allowed_group.id}"
@ -47,25 +57,25 @@ RSpec.describe DiscourseAssign::AssignController do
SiteSetting.assign_allowed_on_groups = defaults SiteSetting.assign_allowed_on_groups = defaults
Assigner.new(post.topic, user).assign(user2) Assigner.new(post.topic, user).assign(user2)
get '/assign/suggestions.json' get "/assign/suggestions.json"
suggestions = JSON.parse(response.body)['suggestions'].map { |u| u['username'] } suggestions = JSON.parse(response.body)["suggestions"].map { |u| u["username"] }
expect(suggestions).to contain_exactly(user2.username, user.username) expect(suggestions).to contain_exactly(user2.username, user.username)
end end
it 'does not include users from disallowed groups' do it "does not include users from disallowed groups" do
allowed_group = Group.find_by(name: 'everyone') allowed_group = Group.find_by(name: "everyone")
allowed_group.add(user2) allowed_group.add(user2)
SiteSetting.assign_allowed_on_groups = default_allowed_group.id.to_s SiteSetting.assign_allowed_on_groups = default_allowed_group.id.to_s
Assigner.new(post.topic, user).assign(user2) Assigner.new(post.topic, user).assign(user2)
get '/assign/suggestions.json' get "/assign/suggestions.json"
suggestions = JSON.parse(response.body)['suggestions'].map { |u| u['username'] }.sort suggestions = JSON.parse(response.body)["suggestions"].map { |u| u["username"] }.sort
expect(suggestions).to eq(['david', 'eviltrout']) expect(suggestions).to eq(%w[david eviltrout])
end end
it 'does include only visible assign_allowed_on_groups' do it "does include only visible assign_allowed_on_groups" do
sign_in(nonadmin) # Need to use nonadmin to test. Admins can see all groups sign_in(nonadmin) # Need to use nonadmin to test. Admins can see all groups
visible_group = Fabricate(:group, visibility_level: Group.visibility_levels[:members]) visible_group = Fabricate(:group, visibility_level: Group.visibility_levels[:members])
@ -74,8 +84,8 @@ RSpec.describe DiscourseAssign::AssignController do
SiteSetting.assign_allowed_on_groups = "#{visible_group.id}|#{invisible_group.id}" SiteSetting.assign_allowed_on_groups = "#{visible_group.id}|#{invisible_group.id}"
get '/assign/suggestions.json' get "/assign/suggestions.json"
assign_allowed_on_groups = JSON.parse(response.body)['assign_allowed_on_groups'] assign_allowed_on_groups = JSON.parse(response.body)["assign_allowed_on_groups"]
expect(assign_allowed_on_groups).to contain_exactly(visible_group.name) expect(assign_allowed_on_groups).to contain_exactly(visible_group.name)
end end
@ -88,68 +98,86 @@ RSpec.describe DiscourseAssign::AssignController do
sign_in(user) sign_in(user)
end end
it 'excludes other users from the suggestions when they already reached the max assigns limit' do it "excludes other users from the suggestions when they already reached the max assigns limit" do
another_admin = Fabricate(:admin, groups: [default_allowed_group]) another_admin = Fabricate(:admin, groups: [default_allowed_group])
Assigner.new(post.topic, user).assign(another_admin) Assigner.new(post.topic, user).assign(another_admin)
get '/assign/suggestions.json' get "/assign/suggestions.json"
suggestions = JSON.parse(response.body)['suggestions'].map { |u| u['username'] } suggestions = JSON.parse(response.body)["suggestions"].map { |u| u["username"] }
expect(suggestions).to contain_exactly(user.username) expect(suggestions).to contain_exactly(user.username)
end end
end end
describe '#assign' do describe "#assign" do
include_context 'A group that is allowed to assign' include_context "A group that is allowed to assign"
before do before do
sign_in(user) sign_in(user)
add_to_assign_allowed_group(user2) add_to_assign_allowed_group(user2)
end end
it 'assigns topic to a user' do it "assigns topic to a user" do
put '/assign/assign.json', params: { put "/assign/assign.json",
target_id: post.topic_id, target_type: 'Topic', username: user2.username params: {
target_id: post.topic_id,
target_type: "Topic",
username: user2.username,
} }
expect(response.status).to eq(200) expect(response.status).to eq(200)
expect(post.topic.reload.assignment.assigned_to_id).to eq(user2.id) expect(post.topic.reload.assignment.assigned_to_id).to eq(user2.id)
end end
it 'assigns topic with note to a user' do it "assigns topic with note to a user" do
put '/assign/assign.json', params: { put "/assign/assign.json",
target_id: post.topic_id, target_type: 'Topic', username: user2.username, note: "do dis pls" params: {
target_id: post.topic_id,
target_type: "Topic",
username: user2.username,
note: "do dis pls",
} }
expect(post.topic.reload.assignment.note).to eq("do dis pls") expect(post.topic.reload.assignment.note).to eq("do dis pls")
end end
it 'assigns topic to a group' do it "assigns topic to a group" do
put '/assign/assign.json', params: { put "/assign/assign.json",
target_id: post.topic_id, target_type: 'Topic', group_name: assign_allowed_group.name params: {
target_id: post.topic_id,
target_type: "Topic",
group_name: assign_allowed_group.name,
} }
expect(response.status).to eq(200) expect(response.status).to eq(200)
expect(post.topic.reload.assignment.assigned_to).to eq(assign_allowed_group) expect(post.topic.reload.assignment.assigned_to).to eq(assign_allowed_group)
end end
it 'fails to assign topic to the user if its already assigned to the same user' do it "fails to assign topic to the user if its already assigned to the same user" do
put '/assign/assign.json', params: { put "/assign/assign.json",
target_id: post.topic_id, target_type: 'Topic', username: user2.username params: {
target_id: post.topic_id,
target_type: "Topic",
username: user2.username,
} }
expect(response.status).to eq(200) expect(response.status).to eq(200)
expect(post.topic.reload.assignment.assigned_to_id).to eq(user2.id) expect(post.topic.reload.assignment.assigned_to_id).to eq(user2.id)
put '/assign/assign.json', params: { put "/assign/assign.json",
target_id: post.topic_id, target_type: 'Topic', username: user2.username params: {
target_id: post.topic_id,
target_type: "Topic",
username: user2.username,
} }
expect(response.status).to eq(400) expect(response.status).to eq(400)
expect(JSON.parse(response.body)['error']).to eq(I18n.t('discourse_assign.already_assigned', username: user2.username)) expect(JSON.parse(response.body)["error"]).to eq(
I18n.t("discourse_assign.already_assigned", username: user2.username),
)
end end
it 'fails to assign topic to the user if they already reached the max assigns limit' do it "fails to assign topic to the user if they already reached the max assigns limit" do
another_user = Fabricate(:user) another_user = Fabricate(:user)
add_to_assign_allowed_group(another_user) add_to_assign_allowed_group(another_user)
another_post = Fabricate(:post) another_post = Fabricate(:post)
@ -157,45 +185,64 @@ RSpec.describe DiscourseAssign::AssignController do
SiteSetting.max_assigned_topics = max_assigns SiteSetting.max_assigned_topics = max_assigns
Assigner.new(post.topic, user).assign(another_user) Assigner.new(post.topic, user).assign(another_user)
put '/assign/assign.json', params: { put "/assign/assign.json",
target_id: another_post.topic_id, target_type: 'Topic', username: another_user.username params: {
target_id: another_post.topic_id,
target_type: "Topic",
username: another_user.username,
} }
expect(response.status).to eq(400) expect(response.status).to eq(400)
expect(JSON.parse(response.body)['error']).to eq( expect(JSON.parse(response.body)["error"]).to eq(
I18n.t('discourse_assign.too_many_assigns', username: another_user.username, max: max_assigns) I18n.t(
"discourse_assign.too_many_assigns",
username: another_user.username,
max: max_assigns,
),
) )
end end
it 'fails with a specific error message if the topic is a PM and the assignee can not see it' do it "fails with a specific error message if the topic is a PM and the assignee can not see it" do
pm = Fabricate(:private_message_post, user: user).topic pm = Fabricate(:private_message_post, user: user).topic
another_user = Fabricate(:user) another_user = Fabricate(:user)
add_to_assign_allowed_group(another_user) add_to_assign_allowed_group(another_user)
put '/assign/assign.json', params: { put "/assign/assign.json",
target_id: pm.id, target_type: 'Topic', username: another_user.username params: {
target_id: pm.id,
target_type: "Topic",
username: another_user.username,
} }
expect(response.parsed_body['error']).to eq( expect(response.parsed_body["error"]).to eq(
I18n.t('discourse_assign.forbidden_assignee_not_pm_participant', username: another_user.username) I18n.t(
"discourse_assign.forbidden_assignee_not_pm_participant",
username: another_user.username,
),
) )
end end
it 'fails with a specific error message if the topic is not a PM and the assignee can not see it' do it "fails with a specific error message if the topic is not a PM and the assignee can not see it" do
topic = Fabricate(:topic, category: Fabricate(:private_category, group: Fabricate(:group))) topic = Fabricate(:topic, category: Fabricate(:private_category, group: Fabricate(:group)))
another_user = Fabricate(:user) another_user = Fabricate(:user)
add_to_assign_allowed_group(another_user) add_to_assign_allowed_group(another_user)
put '/assign/assign.json', params: { put "/assign/assign.json",
target_id: topic.id, target_type: "Topic", username: another_user.username params: {
target_id: topic.id,
target_type: "Topic",
username: another_user.username,
} }
expect(response.parsed_body['error']).to eq( expect(response.parsed_body["error"]).to eq(
I18n.t('discourse_assign.forbidden_assignee_cant_see_topic', username: another_user.username) I18n.t(
"discourse_assign.forbidden_assignee_cant_see_topic",
username: another_user.username,
),
) )
end end
end end
describe '#assigned' do describe "#assigned" do
include_context 'A group that is allowed to assign' include_context "A group that is allowed to assign"
fab!(:post1) { Fabricate(:post) } fab!(:post1) { Fabricate(:post) }
fab!(:post2) { Fabricate(:post) } fab!(:post2) { Fabricate(:post) }
@ -216,40 +263,44 @@ RSpec.describe DiscourseAssign::AssignController do
sign_in(user) sign_in(user)
end end
it 'lists topics ordered by user' do it "lists topics ordered by user" do
get '/assign/assigned.json' get "/assign/assigned.json"
expect(JSON.parse(response.body)['topics'].map { |t| t['id'] }).to match_array([post2.topic_id, post1.topic_id, post3.topic_id]) expect(JSON.parse(response.body)["topics"].map { |t| t["id"] }).to match_array(
[post2.topic_id, post1.topic_id, post3.topic_id],
)
get '/assign/assigned.json', params: { limit: 2 } get "/assign/assigned.json", params: { limit: 2 }
expect(JSON.parse(response.body)['topics'].map { |t| t['id'] }).to match_array([post3.topic_id, post2.topic_id]) expect(JSON.parse(response.body)["topics"].map { |t| t["id"] }).to match_array(
[post3.topic_id, post2.topic_id],
)
get '/assign/assigned.json', params: { offset: 2 } get "/assign/assigned.json", params: { offset: 2 }
expect(JSON.parse(response.body)['topics'].map { |t| t['id'] }).to match_array([post1.topic_id]) expect(JSON.parse(response.body)["topics"].map { |t| t["id"] }).to match_array(
[post1.topic_id],
)
end end
context "with custom allowed groups" do context "with custom allowed groups" do
let(:custom_allowed_group) { Fabricate(:group, name: 'mygroup') } let(:custom_allowed_group) { Fabricate(:group, name: "mygroup") }
let(:other_user) { Fabricate(:user, groups: [custom_allowed_group]) } let(:other_user) { Fabricate(:user, groups: [custom_allowed_group]) }
before do before { SiteSetting.assign_allowed_on_groups += "|#{custom_allowed_group.id}" }
SiteSetting.assign_allowed_on_groups += "|#{custom_allowed_group.id}"
end
it 'works for admins' do it "works for admins" do
get '/assign/assigned.json' get "/assign/assigned.json"
expect(response.status).to eq(200) expect(response.status).to eq(200)
end end
it 'does not work for other groups' do it "does not work for other groups" do
sign_in(other_user) sign_in(other_user)
get '/assign/assigned.json' get "/assign/assigned.json"
expect(response.status).to eq(403) expect(response.status).to eq(403)
end end
end end
end end
describe '#group_members' do describe "#group_members" do
include_context 'A group that is allowed to assign' include_context "A group that is allowed to assign"
fab!(:post1) { Fabricate(:post) } fab!(:post1) { Fabricate(:post) }
fab!(:post2) { Fabricate(:post) } fab!(:post2) { Fabricate(:post) }
@ -264,12 +315,14 @@ RSpec.describe DiscourseAssign::AssignController do
Assigner.new(post3.topic, user).assign(user) Assigner.new(post3.topic, user).assign(user)
end end
it 'list members order by assignments_count' do it "list members order by assignments_count" do
sign_in(user) sign_in(user)
get "/assign/members/#{get_assigned_allowed_group_name}.json" get "/assign/members/#{get_assigned_allowed_group_name}.json"
expect(response.status).to eq(200) expect(response.status).to eq(200)
expect(JSON.parse(response.body)['members'].map { |m| m['id'] }).to match_array([user.id, user2.id]) expect(JSON.parse(response.body)["members"].map { |m| m["id"] }).to match_array(
[user.id, user2.id],
)
end end
it "doesn't include members with no assignments" do it "doesn't include members with no assignments" do
@ -278,23 +331,27 @@ RSpec.describe DiscourseAssign::AssignController do
get "/assign/members/#{get_assigned_allowed_group_name}.json" get "/assign/members/#{get_assigned_allowed_group_name}.json"
expect(response.status).to eq(200) expect(response.status).to eq(200)
expect(JSON.parse(response.body)['members'].map { |m| m['id'] }).to match_array([user.id, user2.id]) expect(JSON.parse(response.body)["members"].map { |m| m["id"] }).to match_array(
[user.id, user2.id],
)
end end
it "returns members as according to filter" do it "returns members as according to filter" do
sign_in(user) sign_in(user)
get "/assign/members/#{get_assigned_allowed_group_name}.json", params: { filter: 'a' } get "/assign/members/#{get_assigned_allowed_group_name}.json", params: { filter: "a" }
expect(response.status).to eq(200) expect(response.status).to eq(200)
expect(JSON.parse(response.body)['members'].map { |m| m['id'] }).to match_array([user.id, user2.id]) expect(JSON.parse(response.body)["members"].map { |m| m["id"] }).to match_array(
[user.id, user2.id],
)
get "/assign/members/#{get_assigned_allowed_group_name}.json", params: { filter: 'david' } get "/assign/members/#{get_assigned_allowed_group_name}.json", params: { filter: "david" }
expect(response.status).to eq(200) expect(response.status).to eq(200)
expect(JSON.parse(response.body)['members'].map { |m| m['id'] }).to match_array([user2.id]) expect(JSON.parse(response.body)["members"].map { |m| m["id"] }).to match_array([user2.id])
get "/assign/members/#{get_assigned_allowed_group_name}.json", params: { filter: 'Tylor' } get "/assign/members/#{get_assigned_allowed_group_name}.json", params: { filter: "Tylor" }
expect(response.status).to eq(200) expect(response.status).to eq(200)
expect(JSON.parse(response.body)['members'].map { |m| m['id'] }).to match_array([user2.id]) expect(JSON.parse(response.body)["members"].map { |m| m["id"] }).to match_array([user2.id])
end end
it "404 error to non-group-members" do it "404 error to non-group-members" do

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'rails_helper' require "rails_helper"
require_relative '../support/assign_allowed_group' require_relative "../support/assign_allowed_group"
describe ListController do describe ListController do
before { SiteSetting.assign_enabled = true } before { SiteSetting.assign_enabled = true }
@ -11,12 +11,12 @@ describe ListController do
let(:admin) { Fabricate(:admin) } let(:admin) { Fabricate(:admin) }
let(:post) { Fabricate(:post) } let(:post) { Fabricate(:post) }
describe 'only allow users from allowed groups' do describe "only allow users from allowed groups" do
include_context 'A group that is allowed to assign' include_context "A group that is allowed to assign"
it 'filters requests where current_user is not member of an allowed group' do it "filters requests where current_user is not member of an allowed group" do
sign_in(user) sign_in(user)
SiteSetting.assign_allowed_on_groups = '' SiteSetting.assign_allowed_on_groups = ""
get "/topics/group-topics-assigned/#{get_assigned_allowed_group_name}.json" get "/topics/group-topics-assigned/#{get_assigned_allowed_group_name}.json"
expect(response.status).to eq(403) expect(response.status).to eq(403)
@ -25,7 +25,7 @@ describe ListController do
expect(response.status).to eq(403) expect(response.status).to eq(403)
end end
it 'as an anon user' do it "as an anon user" do
get "/topics/group-topics-assigned/#{get_assigned_allowed_group_name}.json" get "/topics/group-topics-assigned/#{get_assigned_allowed_group_name}.json"
expect(response.status).to eq(403) expect(response.status).to eq(403)
@ -33,7 +33,7 @@ describe ListController do
expect(response.status).to eq(403) expect(response.status).to eq(403)
end end
it 'as an admin user' do it "as an admin user" do
sign_in(admin) sign_in(admin)
get "/topics/group-topics-assigned/#{get_assigned_allowed_group_name}.json" get "/topics/group-topics-assigned/#{get_assigned_allowed_group_name}.json"
expect(response.status).to eq(200) expect(response.status).to eq(200)
@ -43,8 +43,8 @@ describe ListController do
end end
end end
context '#group_topics_assigned' do context "#group_topics_assigned" do
include_context 'A group that is allowed to assign' include_context "A group that is allowed to assign"
fab!(:post1) { Fabricate(:post) } fab!(:post1) { Fabricate(:post) }
fab!(:post2) { Fabricate(:post) } fab!(:post2) { Fabricate(:post) }
@ -62,30 +62,30 @@ describe ListController do
sign_in(user) sign_in(user)
end end
it 'returns user-assigned-topics-list of users in the assigned_allowed_group and doesnt include deleted topic' do it "returns user-assigned-topics-list of users in the assigned_allowed_group and doesnt include deleted topic" do
get "/topics/group-topics-assigned/#{get_assigned_allowed_group_name}.json" get "/topics/group-topics-assigned/#{get_assigned_allowed_group_name}.json"
expect(JSON.parse(response.body)['topic_list']['topics'].map { |t| t['assigned_to_user']['id'] }).to match_array([user.id]) expect(
JSON.parse(response.body)["topic_list"]["topics"].map { |t| t["assigned_to_user"]["id"] },
).to match_array([user.id])
end end
it 'returns user-assigned-topics-list of users in the assigned_allowed_group and doesnt include inactive topics' do it "returns user-assigned-topics-list of users in the assigned_allowed_group and doesnt include inactive topics" do
Assignment.where(assigned_to: user, target: topic1).update_all(active: false) Assignment.where(assigned_to: user, target: topic1).update_all(active: false)
get "/topics/group-topics-assigned/#{get_assigned_allowed_group_name}.json" get "/topics/group-topics-assigned/#{get_assigned_allowed_group_name}.json"
expect(response.parsed_body['topic_list']['topics']).to be_empty expect(response.parsed_body["topic_list"]["topics"]).to be_empty
end end
it 'returns empty user-assigned-topics-list for users not in the assigned_allowed_group' do it "returns empty user-assigned-topics-list for users not in the assigned_allowed_group" do
ids = [] ids = []
get "/topics/group-topics-assigned/#{get_assigned_allowed_group_name}.json" get "/topics/group-topics-assigned/#{get_assigned_allowed_group_name}.json"
JSON.parse(response.body)['topic_list']['topics'].each do |t| JSON.parse(response.body)["topic_list"]["topics"].each do |t|
if t['assigned_to_user']['id'] == user2.id ids.push(t["assigned_to_user"]["id"]) if t["assigned_to_user"]["id"] == user2.id
ids.push(t['assigned_to_user']['id'])
end
end end
expect(ids).to be_empty expect(ids).to be_empty
end end
it 'doesnt returns deleted topics' do it "doesnt returns deleted topics" do
sign_in(admin) sign_in(admin)
Assigner.new(topic, user).assign(user) Assigner.new(topic, user).assign(user)
@ -97,18 +97,16 @@ describe ListController do
id = 0 id = 0
get "/topics/group-topics-assigned/#{get_assigned_allowed_group_name}.json" get "/topics/group-topics-assigned/#{get_assigned_allowed_group_name}.json"
JSON.parse(response.body)['topic_list']['topics'].each do |t| JSON.parse(response.body)["topic_list"]["topics"].each do |t|
if t['id'] == topic.id id = t.id if t["id"] == topic.id
id = t.id
end
end end
expect(id).to eq(0) expect(id).to eq(0)
end end
end end
context '#sorting messages_assigned and group_topics_assigned' do context "#sorting messages_assigned and group_topics_assigned" do
include_context 'A group that is allowed to assign' include_context "A group that is allowed to assign"
fab!(:post1) { Fabricate(:post) } fab!(:post1) { Fabricate(:post) }
fab!(:post2) { Fabricate(:post) } fab!(:post2) { Fabricate(:post) }
@ -128,7 +126,7 @@ describe ListController do
sign_in(user) sign_in(user)
end end
it 'group_topics_assigned returns sorted topicsList' do it "group_topics_assigned returns sorted topicsList" do
topic1.bumped_at = Time.now topic1.bumped_at = Time.now
topic2.bumped_at = 1.day.ago topic2.bumped_at = 1.day.ago
topic3.bumped_at = 3.day.ago topic3.bumped_at = 3.day.ago
@ -146,25 +144,37 @@ describe ListController do
topic3.save! topic3.save!
get "/topics/group-topics-assigned/#{get_assigned_allowed_group_name}.json?order=posts" get "/topics/group-topics-assigned/#{get_assigned_allowed_group_name}.json?order=posts"
expect(JSON.parse(response.body)['topic_list']['topics'].map { |t| t['id'] }).to match_array([topic2.id, topic1.id, topic3.id]) expect(JSON.parse(response.body)["topic_list"]["topics"].map { |t| t["id"] }).to match_array(
[topic2.id, topic1.id, topic3.id],
)
get "/topics/group-topics-assigned/#{get_assigned_allowed_group_name}.json?order=views" get "/topics/group-topics-assigned/#{get_assigned_allowed_group_name}.json?order=views"
expect(JSON.parse(response.body)['topic_list']['topics'].map { |t| t['id'] }).to match_array([topic3.id, topic1.id, topic2.id]) expect(JSON.parse(response.body)["topic_list"]["topics"].map { |t| t["id"] }).to match_array(
[topic3.id, topic1.id, topic2.id],
)
get "/topics/group-topics-assigned/#{get_assigned_allowed_group_name}.json?order=activity" get "/topics/group-topics-assigned/#{get_assigned_allowed_group_name}.json?order=activity"
expect(JSON.parse(response.body)['topic_list']['topics'].map { |t| t['id'] }).to match_array([topic3.id, topic2.id, topic1.id]) expect(JSON.parse(response.body)["topic_list"]["topics"].map { |t| t["id"] }).to match_array(
[topic3.id, topic2.id, topic1.id],
)
get "/topics/group-topics-assigned/#{get_assigned_allowed_group_name}.json?order=posts&ascending=true" get "/topics/group-topics-assigned/#{get_assigned_allowed_group_name}.json?order=posts&ascending=true"
expect(JSON.parse(response.body)['topic_list']['topics'].map { |t| t['id'] }).to match_array([topic3.id, topic1.id, topic2.id]) expect(JSON.parse(response.body)["topic_list"]["topics"].map { |t| t["id"] }).to match_array(
[topic3.id, topic1.id, topic2.id],
)
get "/topics/group-topics-assigned/#{get_assigned_allowed_group_name}.json?order=views&ascending=true" get "/topics/group-topics-assigned/#{get_assigned_allowed_group_name}.json?order=views&ascending=true"
expect(JSON.parse(response.body)['topic_list']['topics'].map { |t| t['id'] }).to match_array([topic2.id, topic1.id, topic3.id]) expect(JSON.parse(response.body)["topic_list"]["topics"].map { |t| t["id"] }).to match_array(
[topic2.id, topic1.id, topic3.id],
)
get "/topics/group-topics-assigned/#{get_assigned_allowed_group_name}.json?order=activity&ascending=true" get "/topics/group-topics-assigned/#{get_assigned_allowed_group_name}.json?order=activity&ascending=true"
expect(JSON.parse(response.body)['topic_list']['topics'].map { |t| t['id'] }).to match_array([ topic1.id, topic2.id, topic3.id]) expect(JSON.parse(response.body)["topic_list"]["topics"].map { |t| t["id"] }).to match_array(
[topic1.id, topic2.id, topic3.id],
)
end end
it 'messages_assigned returns sorted topicsList' do it "messages_assigned returns sorted topicsList" do
topic1.bumped_at = Time.now topic1.bumped_at = Time.now
topic3.bumped_at = 3.day.ago topic3.bumped_at = 3.day.ago
@ -178,27 +188,39 @@ describe ListController do
topic3.reload topic3.reload
get "/topics/messages-assigned/#{user.username}.json?order=posts" get "/topics/messages-assigned/#{user.username}.json?order=posts"
expect(JSON.parse(response.body)['topic_list']['topics'].map { |t| t['id'] }).to match_array([topic1.id, topic3.id]) expect(JSON.parse(response.body)["topic_list"]["topics"].map { |t| t["id"] }).to match_array(
[topic1.id, topic3.id],
)
get "/topics/messages-assigned/#{user.username}.json?order=views" get "/topics/messages-assigned/#{user.username}.json?order=views"
expect(JSON.parse(response.body)['topic_list']['topics'].map { |t| t['id'] }).to match_array([topic3.id, topic1.id]) expect(JSON.parse(response.body)["topic_list"]["topics"].map { |t| t["id"] }).to match_array(
[topic3.id, topic1.id],
)
get "/topics/messages-assigned/#{user.username}.json?order=activity" get "/topics/messages-assigned/#{user.username}.json?order=activity"
expect(JSON.parse(response.body)['topic_list']['topics'].map { |t| t['id'] }).to match_array([topic3.id, topic1.id]) expect(JSON.parse(response.body)["topic_list"]["topics"].map { |t| t["id"] }).to match_array(
[topic3.id, topic1.id],
)
get "/topics/messages-assigned/#{user.username}.json?order=posts&ascending=true" get "/topics/messages-assigned/#{user.username}.json?order=posts&ascending=true"
expect(JSON.parse(response.body)['topic_list']['topics'].map { |t| t['id'] }).to match_array([topic3.id, topic1.id]) expect(JSON.parse(response.body)["topic_list"]["topics"].map { |t| t["id"] }).to match_array(
[topic3.id, topic1.id],
)
get "/topics/messages-assigned/#{user.username}.json?order=views&ascending=true" get "/topics/messages-assigned/#{user.username}.json?order=views&ascending=true"
expect(JSON.parse(response.body)['topic_list']['topics'].map { |t| t['id'] }).to match_array([topic1.id, topic3.id]) expect(JSON.parse(response.body)["topic_list"]["topics"].map { |t| t["id"] }).to match_array(
[topic1.id, topic3.id],
)
get "/topics/messages-assigned/#{user.username}.json?order=activity&ascending=true" get "/topics/messages-assigned/#{user.username}.json?order=activity&ascending=true"
expect(JSON.parse(response.body)['topic_list']['topics'].map { |t| t['id'] }).to match_array([topic1.id, topic3.id]) expect(JSON.parse(response.body)["topic_list"]["topics"].map { |t| t["id"] }).to match_array(
[topic1.id, topic3.id],
)
end end
end end
context 'filtering of topics as per parameter' do context "filtering of topics as per parameter" do
include_context 'A group that is allowed to assign' include_context "A group that is allowed to assign"
fab!(:post1) { Fabricate(:post) } fab!(:post1) { Fabricate(:post) }
fab!(:post2) { Fabricate(:post) } fab!(:post2) { Fabricate(:post) }
@ -222,44 +244,66 @@ describe ListController do
after { SearchIndexer.disable } after { SearchIndexer.disable }
it 'returns topics as per filter for #group_topics_assigned' do it "returns topics as per filter for #group_topics_assigned" do
topic1.title = 'QUnit testing is love' topic1.title = "QUnit testing is love"
topic2.title = 'RSpec testing is too fun' topic2.title = "RSpec testing is too fun"
topic3.title = 'Testing is main part of programming' topic3.title = "Testing is main part of programming"
topic1.save! topic1.save!
topic2.save! topic2.save!
topic3.save! topic3.save!
get "/topics/group-topics-assigned/#{get_assigned_allowed_group_name}.json", params: { search: 'Testing' } get "/topics/group-topics-assigned/#{get_assigned_allowed_group_name}.json",
expect(JSON.parse(response.body)['topic_list']['topics'].map { |t| t['id'] }).to match_array([topic1.id, topic2.id, topic3.id]) params: {
search: "Testing",
}
expect(JSON.parse(response.body)["topic_list"]["topics"].map { |t| t["id"] }).to match_array(
[topic1.id, topic2.id, topic3.id],
)
get "/topics/group-topics-assigned/#{get_assigned_allowed_group_name}.json", params: { search: 'RSpec' } get "/topics/group-topics-assigned/#{get_assigned_allowed_group_name}.json",
expect(JSON.parse(response.body)['topic_list']['topics'].map { |t| t['id'] }).to match_array([topic2.id]) params: {
search: "RSpec",
}
expect(JSON.parse(response.body)["topic_list"]["topics"].map { |t| t["id"] }).to match_array(
[topic2.id],
)
get "/topics/group-topics-assigned/#{get_assigned_allowed_group_name}.json", params: { search: 'love' } get "/topics/group-topics-assigned/#{get_assigned_allowed_group_name}.json",
expect(JSON.parse(response.body)['topic_list']['topics'].map { |t| t['id'] }).to match_array([topic1.id]) params: {
search: "love",
}
expect(JSON.parse(response.body)["topic_list"]["topics"].map { |t| t["id"] }).to match_array(
[topic1.id],
)
end end
it 'returns topics as per filter for #group_topics_assigned' do it "returns topics as per filter for #group_topics_assigned" do
topic1.title = 'QUnit testing is love' topic1.title = "QUnit testing is love"
topic2.title = 'RSpec testing is too fun' topic2.title = "RSpec testing is too fun"
topic3.title = 'Testing is main part of programming' topic3.title = "Testing is main part of programming"
topic1.save! topic1.save!
topic2.save! topic2.save!
topic3.save! topic3.save!
get "/topics/messages-assigned/#{user.username}.json", params: { search: 'Testing' } get "/topics/messages-assigned/#{user.username}.json", params: { search: "Testing" }
expect(JSON.parse(response.body)['topic_list']['topics'].map { |t| t['id'] }).to match_array([topic1.id, topic3.id]) expect(JSON.parse(response.body)["topic_list"]["topics"].map { |t| t["id"] }).to match_array(
[topic1.id, topic3.id],
)
get "/topics/group-topics-assigned/#{get_assigned_allowed_group_name}.json", params: { search: 'love' } get "/topics/group-topics-assigned/#{get_assigned_allowed_group_name}.json",
expect(JSON.parse(response.body)['topic_list']['topics'].map { |t| t['id'] }).to match_array([topic1.id]) params: {
search: "love",
}
expect(JSON.parse(response.body)["topic_list"]["topics"].map { |t| t["id"] }).to match_array(
[topic1.id],
)
end end
end end
context '#messages_assigned' do context "#messages_assigned" do
include_context 'A group that is allowed to assign' include_context "A group that is allowed to assign"
fab!(:post1) { Fabricate(:post) } fab!(:post1) { Fabricate(:post) }
fab!(:post2) { Fabricate(:post) } fab!(:post2) { Fabricate(:post) }
@ -273,14 +317,16 @@ describe ListController do
sign_in(user) sign_in(user)
end end
it 'returns user-assigned-topics-list of given user' do it "returns user-assigned-topics-list of given user" do
get "/topics/messages-assigned/#{user.username_lower}.json" get "/topics/messages-assigned/#{user.username_lower}.json"
expect(JSON.parse(response.body)['topic_list']['topics'].map { |t| t['assigned_to_user']['id'] }).to match_array([user.id]) expect(
JSON.parse(response.body)["topic_list"]["topics"].map { |t| t["assigned_to_user"]["id"] },
).to match_array([user.id])
end end
it 'returns empty user-assigned-topics-list for given user not in the assigned_allowed_group' do it "returns empty user-assigned-topics-list for given user not in the assigned_allowed_group" do
get "/topics/messages-assigned/#{user2.username_lower}.json" get "/topics/messages-assigned/#{user2.username_lower}.json"
expect(JSON.parse(response.body)['topic_list']['topics']).to be_empty expect(JSON.parse(response.body)["topic_list"]["topics"]).to be_empty
end end
end end
end end

View File

@ -27,11 +27,8 @@ describe FlaggedTopicSerializer do
context "when there is a user assignment" do context "when there is a user assignment" do
let(:topic) do let(:topic) do
topic = Fabricate(:topic, topic =
topic_allowed_users: [ Fabricate(:topic, topic_allowed_users: [Fabricate.build(:topic_allowed_user, user: user)])
Fabricate.build(:topic_allowed_user, user: user)
]
)
topic.posts << Fabricate(:post) topic.posts << Fabricate(:post)
@ -42,23 +39,27 @@ describe FlaggedTopicSerializer do
it "includes the assigned_to_user attribute" do it "includes the assigned_to_user attribute" do
json = FlaggedTopicSerializer.new(topic, scope: guardian).as_json json = FlaggedTopicSerializer.new(topic, scope: guardian).as_json
expect(json[:flagged_topic][:assigned_to_user]).to match({ expect(json[:flagged_topic][:assigned_to_user]).to match(
{
username: user.username, username: user.username,
name: user.name, name: user.name,
assign_icon: "user-plus", assign_icon: "user-plus",
avatar_template: /letter_avatar_proxy.*/, avatar_template: /letter_avatar_proxy.*/,
assign_path: "/u/#{user.username}/activity/assigned", assign_path: "/u/#{user.username}/activity/assigned",
}) },
)
expect(json[:flagged_topic]).to_not have_key(:assigned_to_group) expect(json[:flagged_topic]).to_not have_key(:assigned_to_group)
end end
end end
context "when there is a group assignment" do context "when there is a group assignment" do
let(:topic) do let(:topic) do
topic = Fabricate(:topic, topic =
Fabricate(
:topic,
topic_allowed_groups: [ topic_allowed_groups: [
Fabricate.build(:topic_allowed_group, group: assign_allowed_group) Fabricate.build(:topic_allowed_group, group: assign_allowed_group),
] ],
) )
topic.posts << Fabricate(:post) topic.posts << Fabricate(:post)
@ -70,7 +71,8 @@ describe FlaggedTopicSerializer do
it "includes the assigned_to_group attribute" do it "includes the assigned_to_group attribute" do
json = FlaggedTopicSerializer.new(topic, scope: guardian).as_json json = FlaggedTopicSerializer.new(topic, scope: guardian).as_json
expect(json[:flagged_topic][:assigned_to_group]).to match({ expect(json[:flagged_topic][:assigned_to_group]).to match(
{
name: assign_allowed_group.name, name: assign_allowed_group.name,
flair_bg_color: assign_allowed_group.flair_bg_color, flair_bg_color: assign_allowed_group.flair_bg_color,
flair_color: assign_allowed_group.flair_color, flair_color: assign_allowed_group.flair_color,
@ -78,7 +80,8 @@ describe FlaggedTopicSerializer do
flair_upload_id: assign_allowed_group.flair_upload_id, flair_upload_id: assign_allowed_group.flair_upload_id,
assign_icon: "group-plus", assign_icon: "group-plus",
assign_path: "/g/#{assign_allowed_group.name}/assigned/everyone", assign_path: "/g/#{assign_allowed_group.name}/assigned/everyone",
}) },
)
expect(json[:flagged_topic]).to_not have_key(:assigned_to_user) expect(json[:flagged_topic]).to_not have_key(:assigned_to_user)
end end
end end

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'rails_helper' require "rails_helper"
RSpec.describe GroupShowSerializer do RSpec.describe GroupShowSerializer do
fab!(:user) { Fabricate(:user) } fab!(:user) { Fabricate(:user) }

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'rails_helper' require "rails_helper"
require_relative '../support/assign_allowed_group' require_relative "../support/assign_allowed_group"
describe PostSerializer do describe PostSerializer do
fab!(:user) { Fabricate(:user) } fab!(:user) { Fabricate(:user) }
@ -9,7 +9,7 @@ describe PostSerializer do
fab!(:post) { Fabricate(:post, topic: topic) } fab!(:post) { Fabricate(:post, topic: topic) }
let(:guardian) { Guardian.new(user) } let(:guardian) { Guardian.new(user) }
include_context 'A group that is allowed to assign' include_context "A group that is allowed to assign"
before do before do
SiteSetting.assign_enabled = true SiteSetting.assign_enabled = true

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'rails_helper' require "rails_helper"
RSpec.describe SuggestedTopicSerializer do RSpec.describe SuggestedTopicSerializer do
fab!(:user) { Fabricate(:user) } fab!(:user) { Fabricate(:user) }

View File

@ -1,26 +1,26 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'rails_helper' require "rails_helper"
require_relative '../support/assign_allowed_group' require_relative "../support/assign_allowed_group"
RSpec.describe TopicListSerializer do RSpec.describe TopicListSerializer do
fab!(:user) { Fabricate(:user) } fab!(:user) { Fabricate(:user) }
let(:private_message_topic) do let(:private_message_topic) do
topic = Fabricate(:private_message_topic, topic =
topic_allowed_users: [ Fabricate(
Fabricate.build(:topic_allowed_user, user: user) :private_message_topic,
] topic_allowed_users: [Fabricate.build(:topic_allowed_user, user: user)],
) )
topic.posts << Fabricate(:post) topic.posts << Fabricate(:post)
topic topic
end end
let(:assigned_topic) do let(:assigned_topic) do
topic = Fabricate(:private_message_topic, topic =
topic_allowed_users: [ Fabricate(
Fabricate.build(:topic_allowed_user, user: user) :private_message_topic,
] topic_allowed_users: [Fabricate.build(:topic_allowed_user, user: user)],
) )
topic.posts << Fabricate(:post) topic.posts << Fabricate(:post)
@ -32,77 +32,67 @@ RSpec.describe TopicListSerializer do
let(:guardian) { Guardian.new(user) } let(:guardian) { Guardian.new(user) }
let(:serializer) { TopicListSerializer.new(topic_list, scope: guardian) } let(:serializer) { TopicListSerializer.new(topic_list, scope: guardian) }
include_context 'A group that is allowed to assign' include_context "A group that is allowed to assign"
before do before do
SiteSetting.assign_enabled = true SiteSetting.assign_enabled = true
add_to_assign_allowed_group(user) add_to_assign_allowed_group(user)
end end
describe '#assigned_messages_count' do describe "#assigned_messages_count" do
let(:topic_list) do let(:topic_list) do
TopicQuery.new(user, assigned: user.username).list_private_messages_assigned(user) TopicQuery.new(user, assigned: user.username).list_private_messages_assigned(user)
end end
before do before { assigned_topic }
assigned_topic
it "should include right attribute" do
expect(serializer.as_json[:topic_list][:assigned_messages_count]).to eq(1)
end end
it 'should include right attribute' do describe "when not viewing assigned list" do
expect(serializer.as_json[:topic_list][:assigned_messages_count]) let(:topic_list) { TopicQuery.new(user).list_private_messages_assigned(user) }
.to eq(1)
end
describe 'when not viewing assigned list' do describe "as an admin user" do
let(:topic_list) do
TopicQuery.new(user).list_private_messages_assigned(user)
end
describe 'as an admin user' do
let(:guardian) { Guardian.new(Fabricate(:admin)) } let(:guardian) { Guardian.new(Fabricate(:admin)) }
it 'should not include the attribute' do it "should not include the attribute" do
expect(serializer.as_json[:topic_list][:assigned_messages_count]) expect(serializer.as_json[:topic_list][:assigned_messages_count]).to eq(nil)
.to eq(nil)
end end
end end
describe 'as an anon user' do describe "as an anon user" do
let(:guardian) { Guardian.new } let(:guardian) { Guardian.new }
it 'should not include the attribute' do it "should not include the attribute" do
expect(serializer.as_json[:topic_list][:assigned_messages_count]) expect(serializer.as_json[:topic_list][:assigned_messages_count]).to eq(nil)
.to eq(nil)
end end
end end
end end
describe 'viewing another user' do describe "viewing another user" do
describe 'as an anon user' do describe "as an anon user" do
let(:guardian) { Guardian.new } let(:guardian) { Guardian.new }
it 'should not include the attribute' do it "should not include the attribute" do
expect(serializer.as_json[:topic_list][:assigned_messages_count]) expect(serializer.as_json[:topic_list][:assigned_messages_count]).to eq(nil)
.to eq(nil)
end end
end end
describe 'as a staff' do describe "as a staff" do
let(:admin) { Fabricate(:admin, groups: [Group.find_by(name: 'staff')]) } let(:admin) { Fabricate(:admin, groups: [Group.find_by(name: "staff")]) }
let(:guardian) { Guardian.new(admin) } let(:guardian) { Guardian.new(admin) }
it 'should include the right attribute' do it "should include the right attribute" do
expect(serializer.as_json[:topic_list][:assigned_messages_count]) expect(serializer.as_json[:topic_list][:assigned_messages_count]).to eq(1)
.to eq(1)
end end
end end
describe 'as a normal user' do describe "as a normal user" do
let(:guardian) { Guardian.new(Fabricate(:user)) } let(:guardian) { Guardian.new(Fabricate(:user)) }
it 'should not include the attribute' do it "should not include the attribute" do
expect(serializer.as_json[:topic_list][:assigned_messages_count]) expect(serializer.as_json[:topic_list][:assigned_messages_count]).to eq(nil)
.to eq(nil)
end end
end end
end end

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'rails_helper' require "rails_helper"
require_relative '../support/assign_allowed_group' require_relative "../support/assign_allowed_group"
RSpec.describe TopicViewSerializer do RSpec.describe TopicViewSerializer do
fab!(:user) { Fabricate(:user) } fab!(:user) { Fabricate(:user) }
@ -9,7 +9,7 @@ RSpec.describe TopicViewSerializer do
fab!(:post) { Fabricate(:post, topic: topic) } fab!(:post) { Fabricate(:post, topic: topic) }
let(:guardian) { Guardian.new(user) } let(:guardian) { Guardian.new(user) }
include_context 'A group that is allowed to assign' include_context "A group that is allowed to assign"
before do before do
SiteSetting.assign_enabled = true SiteSetting.assign_enabled = true
@ -26,7 +26,9 @@ RSpec.describe TopicViewSerializer do
it "includes assigned group in serializer" do it "includes assigned group in serializer" do
Assigner.new(topic, user).assign(assign_allowed_group) Assigner.new(topic, user).assign(assign_allowed_group)
serializer = TopicViewSerializer.new(TopicView.new(topic), scope: guardian) serializer = TopicViewSerializer.new(TopicView.new(topic), scope: guardian)
expect(serializer.as_json[:topic_view][:assigned_to_group][:name]).to eq(assign_allowed_group.name) expect(serializer.as_json[:topic_view][:assigned_to_group][:name]).to eq(
assign_allowed_group.name,
)
expect(serializer.as_json[:topic_view][:assigned_to_user]).to be nil expect(serializer.as_json[:topic_view][:assigned_to_user]).to be nil
end end
@ -39,6 +41,8 @@ RSpec.describe TopicViewSerializer do
it "includes indirectly_assigned_to notes in serializer" do it "includes indirectly_assigned_to notes in serializer" do
Assigner.new(post, user).assign(user, note: "note me down") Assigner.new(post, user).assign(user, note: "note me down")
serializer = TopicViewSerializer.new(TopicView.new(topic), scope: guardian) serializer = TopicViewSerializer.new(TopicView.new(topic), scope: guardian)
expect(serializer.as_json[:topic_view][:indirectly_assigned_to][post.id][:assignment_note]).to eq("note me down") expect(
serializer.as_json[:topic_view][:indirectly_assigned_to][post.id][:assignment_note],
).to eq("note me down")
end end
end end

View File

@ -1,10 +1,10 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'rails_helper' require "rails_helper"
require_relative '../support/assign_allowed_group' require_relative "../support/assign_allowed_group"
describe UserBookmarkBaseSerializer do describe UserBookmarkBaseSerializer do
include_context 'A group that is allowed to assign' include_context "A group that is allowed to assign"
before do before do
SiteSetting.assign_enabled = true SiteSetting.assign_enabled = true

View File

@ -1,12 +1,12 @@
# frozen_string_literal: true # frozen_string_literal: true
shared_context 'A group that is allowed to assign' do shared_context "A group that is allowed to assign" do
fab!(:assign_allowed_group) { Fabricate(:group, assignable_level: Group::ALIAS_LEVELS[:everyone]) } fab!(:assign_allowed_group) do
Fabricate(:group, assignable_level: Group::ALIAS_LEVELS[:everyone])
before do
SiteSetting.assign_allowed_on_groups += "|#{assign_allowed_group.id}"
end end
before { SiteSetting.assign_allowed_on_groups += "|#{assign_allowed_group.id}" }
def add_to_assign_allowed_group(user) def add_to_assign_allowed_group(user)
assign_allowed_group.add(user) assign_allowed_group.add(user)
end end