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

@ -5,4 +5,4 @@ source "https://rubygems.org"
group :development do
gem "rubocop-discourse", git: "https://github.com/discourse/rubocop-discourse/", branch: "stree"
gem "syntax_tree"
end
end

View File

@ -7,9 +7,10 @@ module DiscourseAssign
def suggestions
users = [current_user]
users += User
.where('users.id <> ?', current_user.id)
.joins(<<~SQL
users +=
User
.where("users.id <> ?", current_user.id)
.joins(<<~SQL)
JOIN(
SELECT assigned_to_id user_id, MAX(created_at) last_assigned
FROM assignments
@ -18,16 +19,22 @@ module DiscourseAssign
HAVING COUNT(*) < #{SiteSetting.max_assigned_topics}
) as X ON X.user_id = users.id
SQL
)
.assign_allowed
.order('X.last_assigned DESC')
.limit(6)
.assign_allowed
.order("X.last_assigned DESC")
.limit(6)
render json: {
assign_allowed_on_groups: Group.visible_groups(current_user).assign_allowed_groups.pluck(:name),
assign_allowed_for_groups: Group.visible_groups(current_user).assignable(current_user).pluck(:name),
suggestions: ActiveModel::ArraySerializer.new(users, scope: guardian, each_serializer: BasicUserSerializer),
}
assign_allowed_on_groups:
Group.visible_groups(current_user).assign_allowed_groups.pluck(:name),
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
def unassign
@ -46,11 +53,18 @@ module DiscourseAssign
def assign
target_id = params.require(:target_id)
target_type = params.require(:target_type)
username = params.permit(:username)['username']
group_name = params.permit(:group_name)['group_name']
note = params.permit(:note)['note'].presence
username = params.permit(:username)["username"]
group_name = params.permit(:group_name)["group_name"]
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 if !Assignment.valid_type?(target_type)
@ -72,23 +86,31 @@ module DiscourseAssign
offset = (params[:offset] || 0).to_i
limit = (params[:limit] || 100).to_i
topics = Topic
.includes(:tags)
.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")
.order("a.assigned_to_id, topics.bumped_at desc")
.offset(offset)
.limit(limit)
topics =
Topic
.includes(:tags)
.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",
)
.order("a.assigned_to_id, topics.bumped_at desc")
.offset(offset)
.limit(limit)
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
.where("users.id IN (?)", topic_assignments.values.uniq)
.joins("join user_emails on user_emails.user_id = users.id AND user_emails.primary")
.select(UserLookup.lookup_columns)
.to_a
users =
User
.where("users.id IN (?)", topic_assignments.values.uniq)
.joins("join user_emails on user_emails.user_id = users.id AND user_emails.primary")
.select(UserLookup.lookup_columns)
.to_a
User.preload_custom_fields(users, User.allowed_user_custom_fields(guardian))
@ -114,41 +136,41 @@ module DiscourseAssign
guardian.ensure_can_see_group_members!(group)
members = User
.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 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("a.assigned_to_id IS NOT NULL AND a.active")
.order('COUNT(users.id) DESC')
.group('users.id')
.select('users.*, COUNT(users.id) as "assignments_count"')
.limit(limit)
.offset(offset)
members =
User
.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 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("a.assigned_to_id IS NOT NULL AND a.active")
.order("COUNT(users.id) DESC")
.group("users.id")
.select('users.*, COUNT(users.id) as "assignments_count"')
.limit(limit)
.offset(offset)
if params[:filter]
members = members.where(<<~SQL, pattern: "%#{params[:filter]}%")
members = members.where(<<~SQL, pattern: "%#{params[:filter]}%") if params[:filter]
users.name ILIKE :pattern OR users.username_lower ILIKE :pattern
SQL
end
group_assignments = Topic
.joins("JOIN assignments a ON a.topic_id = topics.id")
.where(<<~SQL, group_id: group.id)
group_assignments =
Topic
.joins("JOIN assignments a ON a.topic_id = topics.id")
.where(<<~SQL, group_id: group.id)
a.assigned_to_id = :group_id AND a.assigned_to_type = 'Group' AND a.active
SQL
.pluck(:topic_id)
.pluck(:topic_id)
assignments = TopicQuery
.new(current_user)
.group_topics_assigned_results(group)
.pluck('topics.id')
assignments =
TopicQuery.new(current_user).group_topics_assigned_results(group).pluck("topics.id")
render json: {
members: serialize_data(members, GroupUserAssignedSerializer),
assignment_count: (assignments | group_assignments).count,
group_assignment_count: group_assignments.count
}
members: serialize_data(members, GroupUserAssignedSerializer),
assignment_count: (assignments | group_assignments).count,
group_assignment_count: group_assignments.count,
}
end
private
@ -156,26 +178,59 @@ module DiscourseAssign
def translate_failure(reason, assign_to)
case reason
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
{ 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
{ 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
{ 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
{ 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
{ 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
{ 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
{ 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
{ 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
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

View File

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

View File

@ -1,18 +1,20 @@
# frozen_string_literal: true
require_dependency 'enum_site_setting'
require_dependency "enum_site_setting"
class AssignMailerSiteSettings < EnumSiteSetting
def self.valid_value?(val)
values.any? { |v| v[:value].to_s == val.to_s }
end
def self.values
@values ||= [
{ 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.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] },
]
end

View File

@ -1,25 +1,30 @@
# frozen_string_literal: true
class Assignment < ActiveRecord::Base
VALID_TYPES = %w(topic post).freeze
VALID_TYPES = %w[topic post].freeze
belongs_to :topic
belongs_to :assigned_to, polymorphic: true
belongs_to :assigned_by_user, class_name: "User"
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)
VALID_TYPES.include?(type.downcase)
end
def assigned_to_user?
assigned_to_type == 'User'
assigned_to_type == "User"
end
def assigned_to_group?
assigned_to_type == 'Group'
assigned_to_type == "Group"
end
end

View File

@ -1,12 +1,10 @@
# frozen_string_literal: true
require_dependency 'enum_site_setting'
require_dependency "enum_site_setting"
class RemindAssignsFrequencySiteSettings < EnumSiteSetting
def self.valid_value?(val)
val.to_i.to_s == val.to_s &&
values.any? { |v| v[:value] == val.to_i }
val.to_i.to_s == val.to_s && values.any? { |v| v[:value] == val.to_i }
end
DAILY_MINUTES = 24 * 60 * 1
@ -16,25 +14,11 @@ class RemindAssignsFrequencySiteSettings < EnumSiteSetting
def self.values
@values ||= [
{
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.quarterly',
value: QUARTERLY_MINUTES
}
{ 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.quarterly", value: QUARTERLY_MINUTES },
]
end
@ -45,7 +29,7 @@ class RemindAssignsFrequencySiteSettings < EnumSiteSetting
def self.frequency_for(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])
end

View File

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

View File

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

View File

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

View File

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

View File

@ -2,15 +2,19 @@
class SetAssignAllowedOnGroupsDefault < ActiveRecord::Migration[5.2]
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.
if current_values.nil?
min_version = 201_907_171_337_43
migrated_site_setting = DB.query_single(
"SELECT schema_migrations.version FROM schema_migrations WHERE schema_migrations.version = '#{min_version}'"
).first
default = migrated_site_setting.present? ? Group::AUTO_GROUPS[:staff] : 'staff'
migrated_site_setting =
DB.query_single(
"SELECT schema_migrations.version FROM schema_migrations WHERE schema_migrations.version = '#{min_version}'",
).first
default = migrated_site_setting.present? ? Group::AUTO_GROUPS[:staff] : "staff"
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())"

View File

@ -2,6 +2,6 @@
class AddIndexToAssignedToIdAndType < ActiveRecord::Migration[6.1]
def change
add_index :assignments, [:assigned_to_id, :assigned_to_type]
add_index :assignments, %i[assigned_to_id assigned_to_type]
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_type, :string, null: false
add_index :assignments, [: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[target_id target_type], unique: true
add_index :assignments,
%i[assigned_to_id assigned_to_type target_id target_type],
unique: true,
name: "unique_target_and_assigned"
end
def down

View File

@ -41,7 +41,6 @@ class CorrectlyMoveAssignmentsFromCustomFieldsToATable < ActiveRecord::Migration
ORDER BY assigned_by.created_at DESC
ON CONFLICT DO NOTHING
SQL
else
execute <<~SQL
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_type) if args[:assigned_to_type].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])
post = Post.find(args[:post_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.each do |user|
@ -29,12 +38,18 @@ module Jobs
username: assigned_by.username,
notification_type: Notification.types[:assigned] || Notification.types[:custom],
excerpt:
I18n.t(
(assigned_to_user ? "discourse_assign.topic_assigned_excerpt" : "discourse_assign.topic_group_assigned_excerpt"),
title: topic.title,
group: assigned_to.name,
locale: user.effective_locale
)
I18n.t(
(
if assigned_to_user
"discourse_assign.topic_assigned_excerpt"
else
"discourse_assign.topic_group_assigned_excerpt"
end
),
title: topic.title,
group: assigned_to.name,
locale: user.effective_locale,
),
)
next if args[:skip_small_action_post]
@ -45,10 +60,17 @@ module Jobs
post_number: post.post_number,
high_priority: true,
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,
topic_title: topic.title
}.to_json
topic_title: topic.title,
}.to_json,
)
end
end

View File

@ -2,7 +2,7 @@
module Jobs
class RemindUser < ::Jobs::Base
sidekiq_options queue: 'low'
sidekiq_options queue: "low"
def execute(args)
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?
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|
Assigner.publish_topic_tracking_state(topic, user.id)
Notification.where(
notification_type: Notification.types[:assigned] || Notification.types[:custom],
user_id: user.id,
topic_id: topic.id,
).where("data like '%discourse_assign.assign_notification%' OR data like '%discourse_assign.assign_group_notification%'").destroy_all
Notification
.where(
notification_type: Notification.types[:assigned] || Notification.types[:custom],
user_id: user.id,
topic_id: topic.id,
)
.where(
"data like '%discourse_assign.assign_notification%' OR data like '%discourse_assign.assign_group_notification%'",
)
.destroy_all
end
end
end

View File

@ -12,18 +12,22 @@ module Jobs
private
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
def allowed_group_ids
Group.assign_allowed_groups.pluck(:id).join(',')
Group.assign_allowed_groups.pluck(:id).join(",")
end
def user_ids
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
FROM assignments
@ -50,7 +54,6 @@ module Jobs
GROUP BY assignments.assigned_to_id
HAVING COUNT(assignments.assigned_to_id) > 1
SQL
)
end
end
end

View File

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

View File

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

View File

@ -1,9 +1,9 @@
# frozen_string_literal: true
class PendingAssignsReminder
REMINDED_AT = 'last_reminded_at'
REMINDERS_FREQUENCY = 'remind_assigns_frequency'
CUSTOM_FIELD_NAME = 'assigns_reminder'
REMINDED_AT = "last_reminded_at"
REMINDERS_FREQUENCY = "remind_assigns_frequency"
CUSTOM_FIELD_NAME = "assigns_reminder"
REMINDER_THRESHOLD = 2
def remind(user)
@ -14,7 +14,7 @@ class PendingAssignsReminder
oldest_topics = assigned_topics(user, order: :asc).where.not(id: newest_topics.map(&:id))
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!(
Discourse.system_user,
@ -23,7 +23,9 @@ class PendingAssignsReminder
archetype: Archetype.private_message,
subtype: TopicSubtype.system_message,
target_usernames: user.username,
custom_fields: { CUSTOM_FIELD_NAME => true }
custom_fields: {
CUSTOM_FIELD_NAME => true,
},
)
update_last_reminded(user)
@ -32,38 +34,46 @@ class PendingAssignsReminder
private
def delete_previous_reminders(user)
posts = Post
.joins(topic: { topic_allowed_users: :user })
.where(topic: {
posts_count: 1,
user_id: Discourse.system_user,
archetype: Archetype.private_message,
subtype: TopicSubtype.system_message,
topic_allowed_users: {
users: { id: user.id }
}
})
.joins(topic: :_custom_fields)
.where(topic_custom_fields: {
name: CUSTOM_FIELD_NAME
})
posts =
Post
.joins(topic: { topic_allowed_users: :user })
.where(
topic: {
posts_count: 1,
user_id: Discourse.system_user,
archetype: Archetype.private_message,
subtype: TopicSubtype.system_message,
topic_allowed_users: {
users: {
id: user.id,
},
},
},
)
.joins(topic: :_custom_fields)
.where(topic_custom_fields: { name: CUSTOM_FIELD_NAME })
posts.find_each do |post|
PostDestroyer.new(Discourse.system_user, post).destroy
end
posts.find_each { |post| PostDestroyer.new(Discourse.system_user, post).destroy }
end
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
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
.joins(:assignment)
.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)
.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,
)
.merge(secure)
.order("assignments.created_at #{order}")
.limit(3)
@ -74,38 +84,47 @@ class PendingAssignsReminder
oldest_list = build_list_for(:oldest, last_three_topics)
I18n.t(
'pending_assigns_reminder.body',
"pending_assigns_reminder.body",
pending_assignments: assigned_topics_count,
assignments_link: "#{Discourse.base_url}/u/#{user.username_lower}/activity/assigned",
newest_assignments: newest_list,
oldest_assignments: oldest_list,
frequency: frequency_in_words(user)
frequency: frequency_in_words(user),
)
end
def build_list_for(key, topics)
return '' if topics.empty?
initial_list = { 'topic_0' => '', 'topic_1' => '', 'topic_2' => '' }
items = 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
end
return "" if topics.empty?
initial_list = { "topic_0" => "", "topic_1" => "", "topic_2" => "" }
items =
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
end
I18n.t("pending_assigns_reminder.#{key}", items.symbolize_keys!)
end
def time_in_words_for(topic)
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
def frequency_in_words(user)
frequency = if user.custom_fields&.has_key?(REMINDERS_FREQUENCY)
user.custom_fields[REMINDERS_FREQUENCY]
else
SiteSetting.remind_assigns_frequency
end
frequency =
if user.custom_fields&.has_key?(REMINDERS_FREQUENCY)
user.custom_fields[REMINDERS_FREQUENCY]
else
SiteSetting.remind_assigns_frequency
end
::RemindAssignsFrequencySiteSettings.frequency_for(frequency)
end

View File

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

View File

@ -37,6 +37,10 @@ class ::TopicAssigner
end
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

474
plugin.rb
View File

@ -9,34 +9,46 @@
enabled_site_setting :assign_enabled
register_asset 'stylesheets/assigns.scss'
register_asset 'stylesheets/mobile/assigns.scss', :mobile
register_asset "stylesheets/assigns.scss"
register_asset "stylesheets/mobile/assigns.scss", :mobile
register_svg_icon "user-plus"
register_svg_icon "user-times"
%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/helpers.rb', __FILE__)
load File.expand_path("../lib/discourse_assign/engine.rb", __FILE__)
load File.expand_path("../lib/discourse_assign/helpers.rb", __FILE__)
Discourse::Application.routes.append do
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/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 "topics/private-messages-assigned/:username" => "list#private_messages_assigned",
:as => "topics_private_messages_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/:route_type" => "groups#index"
end
after_initialize do
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/assign_notification.rb', __FILE__)
require File.expand_path('../jobs/regular/unassign_notification.rb', __FILE__)
require 'topic_assigner'
require 'assigner'
require 'pending_assigns_reminder'
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/assign_notification.rb", __FILE__)
require File.expand_path("../jobs/regular/unassign_notification.rb", __FILE__)
require "topic_assigner"
require "assigner"
require "pending_assigns_reminder"
register_group_param(:assignable_level)
register_groups_callback_for_users_search_controller_action(:assignable_groups) do |groups, user|
@ -53,16 +65,21 @@ after_initialize do
end
class ::Group
scope :assignable, ->(user) {
where("assignable_level in (:levels) OR
scope :assignable,
->(user) {
where(
"assignable_level in (:levels) OR
(
assignable_level = #{ALIAS_LEVELS[:members_mods_and_admins]} AND id in (
SELECT group_id FROM group_users WHERE user_id = :user_id)
) OR (
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)
)", levels: alias_levels(user), user_id: user && user.id)
}
)",
levels: alias_levels(user),
user_id: user && user.id,
)
}
end
end
@ -70,17 +87,13 @@ after_initialize do
register_editable_user_custom_field frequency_field
User.register_custom_field_type frequency_field, :integer
DiscoursePluginRegistry.serialized_current_user_fields << frequency_field
add_to_serializer(:user, :reminders_frequency) do
RemindAssignsFrequencySiteSettings.values
end
add_to_serializer(:user, :reminders_frequency) { RemindAssignsFrequencySiteSettings.values }
add_to_serializer(:group_show, :assignment_count) do
Topic
.joins(<<~SQL)
Topic.joins(<<~SQL).where(<<~SQL, group_id: object.id).where("topics.deleted_at IS NULL").count
JOIN assignments a
ON topics.id = a.topic_id AND a.assigned_to_id IS NOT NULL
SQL
.where(<<~SQL, group_id: object.id)
a.active AND
((
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
))
SQL
.where("topics.deleted_at IS NULL")
.count
end
add_to_serializer(:group_show, 'include_assignment_count?') do
scope.can_assign?
end
add_to_serializer(:group_show, "include_assignment_count?") { scope.can_assign? }
add_to_serializer(:group_show, :assignable_level) do
object.assignable_level
end
add_to_serializer(:group_show, :assignable_level) { object.assignable_level }
add_to_serializer(:group_show, :can_show_assigned_tab?) do
object.can_show_assigned_tab?
end
add_to_serializer(:group_show, :can_show_assigned_tab?) { object.can_show_assigned_tab? }
add_model_callback(UserCustomField, :before_save) do
self.value = self.value.to_i if self.name == frequency_field
end
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)
end
@ -121,21 +126,24 @@ after_initialize do
@can_assign ||=
begin
return true if admin?
allowed_groups = SiteSetting.assign_allowed_on_groups.split('|').compact
allowed_groups.present? && groups.where(id: allowed_groups).exists? ?
:true : :false
allowed_groups = SiteSetting.assign_allowed_on_groups.split("|").compact
allowed_groups.present? && groups.where(id: allowed_groups).exists? ? :true : :false
end
@can_assign == :true
end
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
add_to_class(:group, :can_show_assigned_tab?) do
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 1 FROM 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_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
# are not added into the admin group.
where("users.id IN (
where(
"users.id IN (
SELECT
user_id
FROM group_users
@ -169,39 +178,46 @@ after_initialize do
SELECT id
FROM users
WHERE users.admin
)", allowed_groups)
)",
allowed_groups,
)
end
add_model_callback(Group, :before_update) do
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
add_model_callback(Group, :before_destroy) do
new_setting = SiteSetting.assign_allowed_on_groups.gsub(/#{id}[|]?/, '')
new_setting = new_setting.chomp('|') if new_setting.ends_with?('|')
new_setting = SiteSetting.assign_allowed_on_groups.gsub(/#{id}[|]?/, "")
new_setting = new_setting.chomp("|") if new_setting.ends_with?("|")
SiteSetting.assign_allowed_on_groups = new_setting
end
on(:assign_topic) do |topic, user, assigning_user, force|
if force || !Assignment.exists?(target: topic)
Assigner.new(topic, assigning_user).assign(user)
end
Assigner.new(topic, assigning_user).assign(user) if force || !Assignment.exists?(target: topic)
end
on(:unassign_topic) do |topic, unassigning_user|
Assigner.new(topic, unassigning_user).unassign
end
on(:unassign_topic) { |topic, unassigning_user| Assigner.new(topic, unassigning_user).unassign }
Site.preloaded_category_custom_fields << "enable_unassigned_filter"
BookmarkQuery.on_preload do |bookmarks, bookmark_query|
if SiteSetting.assign_enabled?
topics = Bookmark.select_type(bookmarks, "Topic").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 =
Bookmark
.select_type(bookmarks, "Topic")
.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|
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_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)
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)
topics.each do |topic|
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 = {}
assignments&.each do |assignment|
next if assignment.target_type == "Topic"
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 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
assignments
&.each do |assignment|
next if assignment.target_type == "Topic"
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(
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 =
if direct_assignment&.assigned_to_user?
@ -260,23 +294,34 @@ after_initialize do
if allowed_access && results.posts.length > 0
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|
topic_assignments = assignments[post.topic.id]
direct_assignment = topic_assignments&.find { |assignment| assignment.target_type == "Topic" }
indirect_assignments = topic_assignments&.select { |assignment| assignment.target_type == "Post" }
direct_assignment =
topic_assignments&.find { |assignment| assignment.target_type == "Topic" }
indirect_assignments =
topic_assignments&.select { |assignment| assignment.target_type == "Post" }
if direct_assignment
assigned_to = direct_assignment.assigned_to
post.topic.preload_assigned_to(assigned_to)
end
if indirect_assignments.present?
indirect_assignment_map = indirect_assignments.reduce({}) do |acc, assignment|
if assignment.target
acc[assignment.target_id] = { assigned_to: assignment.assigned_to, post_number: assignment.target.post_number }
indirect_assignment_map =
indirect_assignments.reduce({}) do |acc, assignment|
if assignment.target
acc[assignment.target_id] = {
assigned_to: assignment.assigned_to,
post_number: assignment.target.post_number,
}
end
acc
end
acc
end
post.topic.preload_indirectly_assigned_to(indirect_assignment_map)
end
end
@ -285,7 +330,7 @@ after_initialize do
end
# TopicQuery
require_dependency 'topic_query'
require_dependency "topic_query"
TopicQuery.add_custom_filter(:assigned) do |results, topic_query|
name = topic_query.options[:assigned]
next results if name.blank?
@ -293,32 +338,42 @@ after_initialize do
next results if !topic_query.guardian.can_assign? && !SiteSetting.assigns_public
if name == "nobody"
next results
.joins("LEFT JOIN assignments a ON a.topic_id = topics.id AND active")
.where("a.assigned_to_id IS NULL")
next(
results.joins("LEFT JOIN assignments a ON a.topic_id = topics.id AND active").where(
"a.assigned_to_id IS NULL",
)
)
end
if name == "*"
next results
.joins("JOIN assignments a ON a.topic_id = topics.id AND active")
.where("a.assigned_to_id IS NOT NULL")
next(
results.joins("JOIN assignments a ON a.topic_id = topics.id AND active").where(
"a.assigned_to_id IS NOT NULL",
)
)
end
user_id = topic_query.guardian.user.id if name == "me"
user_id ||= User.where(username_lower: name.downcase).pluck_first(:id)
if user_id
next results
.joins("JOIN assignments a ON a.topic_id = topics.id AND active")
.where("a.assigned_to_id = ? AND a.assigned_to_type = 'User'", user_id)
next(
results.joins("JOIN assignments a ON a.topic_id = topics.id AND active").where(
"a.assigned_to_id = ? AND a.assigned_to_type = 'User'",
user_id,
)
)
end
group_id = Group.where(name: name.downcase).pluck_first(:id)
if group_id
next results
.joins("JOIN assignments a ON a.topic_id = topics.id AND active")
.where("a.assigned_to_id = ? AND a.assigned_to_type = 'Group'", group_id)
next(
results.joins("JOIN assignments a ON a.topic_id = topics.id AND active").where(
"a.assigned_to_id = ? AND a.assigned_to_type = 'Group'",
group_id,
)
)
end
next results
@ -334,11 +389,9 @@ after_initialize do
(assigned_to_id = :user_id AND assigned_to_type = 'User' AND active)
SQL
if @options[:filter] != :direct
topic_ids_sql << <<~SQL
topic_ids_sql << <<~SQL if @options[:filter] != :direct
OR (assigned_to_id IN (group_users.group_id) AND assigned_to_type = 'Group' AND active)
SQL
end
sql = "topics.id IN (#{topic_ids_sql})"
@ -357,13 +410,11 @@ after_initialize do
)
SQL
if @options[:filter] != :direct
topic_ids_sql << <<~SQL
topic_ids_sql << <<~SQL if @options[:filter] != :direct
OR (
assigned_to_id IN (SELECT user_id from group_users where group_id = :group_id) AND assigned_to_type = 'User' AND active
)
SQL
end
sql = "topics.id IN (#{topic_ids_sql})"
@ -395,7 +446,7 @@ after_initialize do
end
# ListController
require_dependency 'list_controller'
require_dependency "list_controller"
class ::ListController
generate_message_route(:private_messages_assigned)
end
@ -441,15 +492,21 @@ after_initialize do
add_to_class(:topic, :indirectly_assigned_to) do
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|
acc[assignment.target_id] = { assigned_to: assignment.assigned_to, post_number: assignment.target.post_number, assignment_note: assignment.note } if assignment.target
acc
end
@indirectly_assigned_to =
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
end
end
add_to_class(:topic, :preload_assigned_to) do |assigned_to|
@assigned_to = assigned_to
end
add_to_class(:topic, :preload_assigned_to) { |assigned_to| @assigned_to = assigned_to }
add_to_class(:topic, :preload_indirectly_assigned_to) do |indirectly_assigned_to|
@indirectly_assigned_to = indirectly_assigned_to
@ -457,17 +514,17 @@ after_initialize do
# TopicList serializer
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)
.count
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)
if assigned_user = options.dig(:assigned)
scope.can_assign? ||
assigned_user.downcase == scope.current_user&.username_lower
scope.can_assign? || assigned_user.downcase == scope.current_user&.username_lower
end
end
@ -489,16 +546,18 @@ after_initialize do
end
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
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
add_to_serializer(:topic_view, :assignment_note, false) do
object.topic.assignment.note
end
add_to_serializer(:topic_view, :assignment_note, false) { object.topic.assignment.note }
add_to_serializer(:topic_view, :include_assignment_note?, false) do
(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)
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)
end
@ -567,7 +626,7 @@ after_initialize do
BasicGroupSerializer.new(object.assigned_to, scope: scope, root: false).as_json
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)
end
@ -583,30 +642,25 @@ after_initialize do
TopicsBulkAction.register_operation("assign") do
if @user.can_assign?
assign_user = User.find_by_username(@operation[:username])
topics.each do |topic|
Assigner.new(topic, @user).assign(assign_user)
end
topics.each { |topic| Assigner.new(topic, @user).assign(assign_user) }
end
end
TopicsBulkAction.register_operation("unassign") do
if @user.can_assign?
topics.each do |topic|
if guardian.can_assign?
Assigner.new(topic, @user).unassign
end
end
topics.each { |topic| Assigner.new(topic, @user).unassign if guardian.can_assign? }
end
end
register_permitted_bulk_action_parameter :username
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
add_to_class(:user_bookmark_base_serializer, :can_have_assignment?) do
["Post", "Topic"].include?(bookmarkable_type)
%w[Post Topic].include?(bookmarkable_type)
end
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
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?
(SiteSetting.assigns_public || scope.can_assign?) && assigned_to&.is_a?(User)
end
@ -624,56 +678,48 @@ after_initialize do
BasicGroupSerializer.new(assigned_to, scope: scope, root: false).as_json
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?
(SiteSetting.assigns_public || scope.can_assign?) && assigned_to&.is_a?(Group)
end
add_to_serializer(:basic_user, :assign_icon) do
'user-plus'
end
add_to_serializer(:basic_user, :assign_icon) { "user-plus" }
add_to_serializer(:basic_user, :assign_path) do
return if !object.is_a?(User)
SiteSetting.assigns_user_url_path.gsub("{username}", object.username)
end
add_to_serializer(:basic_group, :assign_icon) do
'group-plus'
end
add_to_serializer(:basic_group, :assign_icon) { "group-plus" }
add_to_serializer(:basic_group, :assign_path) do
"/g/#{object.name}/assigned/everyone"
end
add_to_serializer(:basic_group, :assign_path) { "/g/#{object.name}/assigned/everyone" }
# PostSerializer
add_to_serializer(:post, :assigned_to_user) do
BasicUserSerializer.new(object.assignment.assigned_to, scope: scope, root: false).as_json
end
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
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
end
add_to_serializer(:post, :assigned_to_group, false) do
BasicGroupSerializer.new(object.assignment.assigned_to, scope: scope, root: false).as_json
end
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
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
end
add_to_serializer(:post, :assignment_note, false) do
object.assignment.note
end
add_to_serializer(:post, :assignment_note, false) { object.assignment.note }
add_to_serializer(:post, :include_assignment_note?, false) do
(SiteSetting.assigns_public || scope.can_assign?) && object.assignment.present?
end
# CurrentUser serializer
add_to_serializer(:current_user, :can_assign) do
object.can_assign?
end
add_to_serializer(:current_user, :can_assign) { object.can_assign? }
# FlaggedTopic serializer
add_to_serializer(:flagged_topic, :assigned_to_user) do
@ -697,59 +743,47 @@ after_initialize do
[
:assigned_to,
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 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 users u ON u.id = a.assigned_to_id
SQL
)
.where(target_type: Post.name)
.where('u.username = ?', value)
end
]
end,
],
)
# TopicTrackingState
add_class_method(:topic_tracking_state, :publish_assigned_private_message) do |topic, assignee|
return unless topic.private_message?
opts =
if assignee.is_a?(User)
{ user_ids: [assignee.id] }
else
{ group_ids: [assignee.id] }
end
opts = (assignee.is_a?(User) ? { user_ids: [assignee.id] } : { group_ids: [assignee.id] })
MessageBus.publish(
"/private-messages/assigned",
{ topic_id: topic.id },
opts
)
MessageBus.publish("/private-messages/assigned", { topic_id: topic.id }, opts)
end
# Event listeners
on(:post_created) do |post|
::Assigner.auto_assign(post, force: true)
end
on(:post_created) { |post| ::Assigner.auto_assign(post, force: true) }
on(:post_edited) do |post, topic_changed|
::Assigner.auto_assign(post, force: true)
end
on(:post_edited) { |post, topic_changed| ::Assigner.auto_assign(post, force: true) }
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.unassign(silent: true, deactivate: true)
topic.posts.joins(:assignment).find_each do |post|
assigner = ::Assigner.new(post, Discourse.system_user)
assigner.unassign(silent: true, deactivate: true)
end
topic
.posts
.joins(:assignment)
.find_each do |post|
assigner = ::Assigner.new(post, Discourse.system_user)
assigner.unassign(silent: true, deactivate: true)
end
MessageBus.publish("/topic/#{topic.id}", reload_topic: true, refresh_stream: true)
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: "Post")
@ -766,14 +800,21 @@ after_initialize do
end
# 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|
next if ![Post.types[:small_action], Post.types[:whisper]].include?(post_custom_field.post.post_type)
post_custom_field.post.destroy
end
PostCustomField
.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
end
end
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)
MessageBus.publish("/topic/#{post.topic_id}", reload_topic: true, refresh_stream: true)
end
@ -782,22 +823,28 @@ after_initialize do
on(:move_to_inbox) do |info|
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 !info[:group]
Assignment.where(topic_id: topic.id, active: false).find_each do |assignment|
next unless assignment.target
assignment.update!(active: true)
Jobs.enqueue(:assign_notification,
topic_id: topic.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_type: assignment.assigned_to_type,
assigned_by_id: assignment.assigned_by_user_id,
skip_small_action_post: true)
end
Assignment
.where(topic_id: topic.id, active: false)
.find_each do |assignment|
next unless assignment.target
assignment.update!(active: true)
Jobs.enqueue(
:assign_notification,
topic_id: topic.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_type: assignment.assigned_to_type,
assigned_by_id: assignment.assigned_by_user_id,
skip_small_action_post: true,
)
end
end
on(:archive_message) do |info|
@ -809,33 +856,36 @@ after_initialize do
next if !SiteSetting.unassign_on_group_archive
next if !info[:group]
Assignment.where(topic_id: topic.id, active: true).find_each do |assignment|
assignment.update!(active: false)
Jobs.enqueue(:unassign_notification,
topic_id: topic.id,
assigned_to_id: assignment.assigned_to.id,
assigned_to_type: assignment.assigned_to_type)
end
Assignment
.where(topic_id: topic.id, active: true)
.find_each do |assignment|
assignment.update!(active: false)
Jobs.enqueue(
:unassign_notification,
topic_id: topic.id,
assigned_to_id: assignment.assigned_to.id,
assigned_to_type: assignment.assigned_to_type,
)
end
end
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)
groups = GroupUser.where(user: user).pluck(:group_id)
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|
Assigner.new(topic, Discourse.system_user).unassign
end
topics.each { |topic| Assigner.new(topic, Discourse.system_user).unassign }
end
end
end
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
if post.is_first_post?
assignment.update!(topic_id: post.topic_id, target_type: "Topic", target_id: post.topic_id)
@ -846,32 +896,24 @@ after_initialize do
class ::WebHook
def self.enqueue_assign_hooks(event, payload)
if active_web_hooks('assign').exists?
WebHook.enqueue_hooks(:assign, event,
payload: payload
)
end
WebHook.enqueue_hooks(:assign, event, payload: payload) if active_web_hooks("assign").exists?
end
end
register_search_advanced_filter(/in:assigned/) do |posts|
if @guardian.can_assign?
posts.where(<<~SQL)
posts.where(<<~SQL) if @guardian.can_assign?
topics.id IN (
SELECT a.topic_id FROM assignments a WHERE a.active
)
SQL
end
end
register_search_advanced_filter(/in:unassigned/) do |posts|
if @guardian.can_assign?
posts.where(<<~SQL)
posts.where(<<~SQL) if @guardian.can_assign?
topics.id NOT IN (
SELECT a.topic_id FROM assignments a WHERE a.active
)
SQL
end
end
register_search_advanced_filter(/assigned:(.+)$/) do |posts, match|
@ -893,9 +935,9 @@ after_initialize do
end
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 :assigned_topic, component: :text, required: true
field :minimum_time_between_assignments, component: :text

View File

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

View File

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

View File

@ -1,29 +1,29 @@
# frozen_string_literal: true
require 'rails_helper'
require_relative '../support/assign_allowed_group'
require "rails_helper"
require_relative "../support/assign_allowed_group"
describe TopicsBulkAction do
fab!(:post) { Fabricate(:post) }
fab!(:post1) { Fabricate(:post) }
fab!(:post2) { Fabricate(:post) }
before do
SiteSetting.assign_enabled = true
end
before { SiteSetting.assign_enabled = true }
let(:user) { 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
add_to_assign_allowed_group(user)
end
before { add_to_assign_allowed_group(user) }
describe "assign_topics" 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
@ -33,7 +33,11 @@ describe TopicsBulkAction do
end
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
@ -41,7 +45,11 @@ describe TopicsBulkAction do
end
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
@ -51,9 +59,13 @@ describe TopicsBulkAction do
describe "unassign_topics" 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
@ -63,9 +75,13 @@ describe TopicsBulkAction do
end
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
@ -73,6 +89,5 @@ describe TopicsBulkAction do
expect(assigned_topics).to contain_exactly(post.topic, post1.topic, post2.topic)
end
end
end

View File

@ -1,9 +1,7 @@
# frozen_string_literal: true
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|
web_hook.web_hook_event_types = [transients[:assign_hook]]
end
after_build { |web_hook, transients| web_hook.web_hook_event_types = [transients[:assign_hook]] }
end

View File

@ -1,15 +1,13 @@
# frozen_string_literal: true
require 'rails_helper'
require_relative '../support/assign_allowed_group'
require_relative '../fabricators/assign_hook_fabricator.rb'
require "rails_helper"
require_relative "../support/assign_allowed_group"
require_relative "../fabricators/assign_hook_fabricator.rb"
describe 'integration tests' do
before do
SiteSetting.assign_enabled = true
end
describe "integration tests" do
before { SiteSetting.assign_enabled = true }
it 'preloads data in topic list' do
it "preloads data in topic list" do
admin = Fabricate(:admin)
post = create_post
list = TopicList.new("latest", admin, [post.topic])
@ -17,7 +15,7 @@ describe 'integration tests' do
# should not explode for now
end
describe 'for a private message' do
describe "for a private message" do
let(:post) { Fabricate(:private_message_post) }
let(:pm) { post.topic }
let(:user) { pm.allowed_users.first }
@ -25,7 +23,7 @@ describe 'integration tests' do
let(:channel) { "/private-messages/assigned" }
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
add_to_assign_allowed_group(user)
@ -35,9 +33,7 @@ describe 'integration tests' do
end
def assert_publish_topic_state(topic, user: nil, group: nil)
messages = MessageBus.track_publish do
yield
end
messages = MessageBus.track_publish { yield }
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
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.assign(user)
@ -59,7 +55,7 @@ describe 'integration tests' do
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.assign(group)
@ -106,7 +102,7 @@ describe 'integration tests' do
let(:user1) { 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
add_to_assign_allowed_group(user1)
@ -142,15 +138,15 @@ describe 'integration tests' do
end
end
context 'already assigned' do
context "already assigned" do
fab!(:post) { Fabricate(:post) }
fab!(:post_2) { Fabricate(:post, topic: post.topic) }
let(:topic) { post.topic }
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)
assigner = Assigner.new(post, user)
@ -168,14 +164,22 @@ describe 'integration tests' do
end
end
context 'move post' do
context "move post" do
fab!(:old_topic) { Fabricate(:topic) }
fab!(:post) { Fabricate(:post, topic: old_topic) }
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) }
it 'assignment becomes topic assignment when new topic' do
it "assignment becomes topic assignment when new topic" do
post.update!(topic: new_topic)
DiscourseEvent.trigger(:post_moved, post, old_topic.id)
assignment.reload
@ -184,7 +188,7 @@ describe 'integration tests' do
expect(assignment.target_id).to eq(new_topic.id)
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")
DiscourseEvent.trigger(:post_moved, post, old_topic.id)
assignment.reload

View File

@ -1,51 +1,65 @@
# frozen_string_literal: true
require 'rails_helper'
require "rails_helper"
RSpec.describe Jobs::AssignNotification do
describe '#execute' do
describe "#execute" do
fab!(:user1) { 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!(:pm_post) { Fabricate(:private_message_post) }
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)
message = MessageBus.track_publish('/private-messages/assigned') do
yield
end.first
message = MessageBus.track_publish("/private-messages/assigned") { yield }.first
expect(message.data[:topic_id]).to eq(topic.id)
expect(message.user_ids).to eq([user.id])
end
before do
assign_allowed_group.add(user1)
end
before { assign_allowed_group.add(user1) }
context 'User' do
it 'sends notification alert' do
messages = 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 })
end
context "User" do
it "sends notification alert" do
messages =
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,
},
)
end
expect(messages.length).to eq(1)
expect(messages.first.data[:excerpt]).to eq("assigned you the topic 'Basic topic title'")
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
assign_allowed_group.add(user)
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
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_type: Notification.types[:assigned] || Notification.types[:custom],
user_id: user2.id,
@ -53,20 +67,31 @@ RSpec.describe Jobs::AssignNotification do
post_number: 1,
high_priority: true,
data: {
message: 'discourse_assign.assign_notification',
message: "discourse_assign.assign_notification",
display_username: user1.username,
topic_title: topic.title
}.to_json
topic_title: topic.title,
}.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
context 'Group' do
context "Group" do
fab!(:user3) { Fabricate(:user, last_seen_at: 1.day.ago) }
fab!(:user4) { Fabricate(:user, suspended_till: 1.year.from_now) }
fab!(:group) { Fabricate(:group, name: 'Developers') }
let(:assignment) { Assignment.create!(topic: topic, assigned_by_user: user1, assigned_to: group) }
fab!(:group) { Fabricate(:group, name: "Developers") }
let(:assignment) do
Assignment.create!(topic: topic, assigned_by_user: user1, assigned_to: group)
end
before do
group.add(user2)
@ -74,26 +99,60 @@ RSpec.describe Jobs::AssignNotification do
group.add(user4)
end
it 'sends notification alert to all group members' do
messages = 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
it "sends notification alert to all group members" do
messages =
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
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
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
messages =
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
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
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
messages =
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
expect(messages.length).to eq(0)
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|
Notification.expects(:create!).with(
notification_type: Notification.types[:assigned] || Notification.types[:custom],
@ -102,14 +161,23 @@ RSpec.describe Jobs::AssignNotification do
post_number: 1,
high_priority: true,
data: {
message: 'discourse_assign.assign_group_notification',
message: "discourse_assign.assign_group_notification",
display_username: group.name,
topic_title: topic.title
}.to_json
topic_title: topic.title,
}.to_json,
)
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

View File

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

View File

@ -1,51 +1,70 @@
# frozen_string_literal: true
require 'rails_helper'
require "rails_helper"
RSpec.describe Jobs::UnassignNotification do
describe '#execute' do
describe "#execute" do
fab!(:user1) { Fabricate(:user) }
fab!(:user2) { Fabricate(:user) }
fab!(:topic) { Fabricate(:topic) }
fab!(:post) { Fabricate(:post, topic: topic) }
fab!(:pm_post) { Fabricate(:private_message_post) }
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
assign_allowed_group.add(user1)
end
before { assign_allowed_group.add(user1) }
def assert_publish_topic_state(topic, user)
message = MessageBus.track_publish('/private-messages/assigned') do
yield
end.first
message = MessageBus.track_publish("/private-messages/assigned") { yield }.first
expect(message.data[:topic_id]).to eq(topic.id)
expect(message.user_ids).to eq([user.id])
end
context 'User' 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 })
context "User" 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,
},
)
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)
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
assign_allowed_group.add(user)
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
context 'Group' do
fab!(:assign_allowed_group) { Group.find_by(name: 'staff') }
context "Group" do
fab!(:assign_allowed_group) { Group.find_by(name: "staff") }
fab!(:user3) { Fabricate(:user) }
fab!(:group) { Fabricate(:group) }
@ -54,11 +73,27 @@ RSpec.describe Jobs::UnassignNotification do
group.add(user3)
end
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 })
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,
},
)
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)
end
end

View File

@ -1,9 +1,9 @@
# frozen_string_literal: true
require 'rails_helper'
require "rails_helper"
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]) }
before do
@ -11,26 +11,26 @@ RSpec.describe Jobs::EnqueueReminders do
SiteSetting.assign_enabled = true
end
describe '#execute' do
it 'does not enqueue reminders when there are no assigned tasks' do
describe "#execute" do
it "does not enqueue reminders when there are no assigned tasks" do
assert_reminders_enqueued(0)
end
it 'does not enqueue reminders when no groups are allowed to assign' do
SiteSetting.assign_allowed_on_groups = ''
it "does not enqueue reminders when no groups are allowed to assign" do
SiteSetting.assign_allowed_on_groups = ""
assign_multiple_tasks_to(user)
assert_reminders_enqueued(0)
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)
assert_reminders_enqueued(1)
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)
assert_reminders_enqueued(0)
@ -43,31 +43,32 @@ RSpec.describe Jobs::EnqueueReminders do
assert_reminders_enqueued(0)
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)
assign_multiple_tasks_to(user)
assert_reminders_enqueued(1)
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
assign_multiple_tasks_to(user)
assert_reminders_enqueued(0)
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
assign_multiple_tasks_to(user, assigned_on: just_assigned)
assert_reminders_enqueued(0)
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
user.custom_fields.merge!(
PendingAssignsReminder::REMINDERS_FREQUENCY => RemindAssignsFrequencySiteSettings::DAILY_MINUTES
PendingAssignsReminder::REMINDERS_FREQUENCY =>
RemindAssignsFrequencySiteSettings::DAILY_MINUTES,
)
user.save_custom_fields
@ -91,9 +92,7 @@ RSpec.describe Jobs::EnqueueReminders do
end
def assign_one_task_to(user, assigned_on: 3.months.ago, post: Fabricate(:post))
freeze_time(assigned_on) do
Assigner.new(post.topic, user).assign(user)
end
freeze_time(assigned_on) { Assigner.new(post.topic, user).assign(user) }
end
def assign_multiple_tasks_to(user, assigned_on: 3.months.ago)

View File

@ -1,11 +1,11 @@
# frozen_string_literal: true
require 'rails_helper'
require "rails_helper"
RSpec.describe Assigner do
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) { pm_post.topic }
@ -21,27 +21,23 @@ RSpec.describe Assigner do
let(:assigner_self) { described_class.new(topic, moderator) }
it "can assign and unassign correctly" do
expect_enqueued_with(job: :assign_notification) do
assigner.assign(moderator)
end
expect_enqueued_with(job: :assign_notification) { assigner.assign(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],
)
expect(TopicUser.find_by(user: moderator).notification_level)
.to eq(TopicUser.notification_levels[:watching])
expect(TopicUser.find_by(user: moderator).notification_level).to eq(
TopicUser.notification_levels[:watching],
)
expect_enqueued_with(job: :unassign_notification) do
assigner.unassign
end
expect_enqueued_with(job: :unassign_notification) { assigner.unassign }
expect(TopicQuery.new(
moderator, assigned: moderator.username
).list_latest.topics).to eq([])
expect(TopicQuery.new(moderator, assigned: moderator.username).list_latest.topics).to eq([])
expect(TopicUser.find_by(user: moderator).notification_level)
.to eq(TopicUser.notification_levels[:tracking])
expect(TopicUser.find_by(user: moderator).notification_level).to eq(
TopicUser.notification_levels[:tracking],
)
end
it "can assign with note" do
@ -51,63 +47,76 @@ RSpec.describe Assigner do
end
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"
end
it "publishes topic assignment after assign and unassign" do
messages = MessageBus.track_publish('/staff/topic-assignment') do
assigner = described_class.new(topic, moderator_2)
assigner.assign(moderator, note: "tomtom best mom")
assigner.unassign
end
messages =
MessageBus.track_publish("/staff/topic-assignment") do
assigner = described_class.new(topic, moderator_2)
assigner.assign(moderator, note: "tomtom best mom")
assigner.unassign
end
expect(messages[0].channel).to eq "/staff/topic-assignment"
expect(messages[0].data).to include({
type: "assigned",
topic_id: topic.id,
post_id: false,
post_number: false,
assigned_type: "User",
assigned_to: BasicUserSerializer.new(moderator, scope: Guardian.new, root: false).as_json,
assignment_note: "tomtom best mom"
})
expect(messages[1].channel).to eq "/staff/topic-assignment"
expect(messages[1].data).to include({
type: "unassigned",
topic_id: topic.id,
post_id: false,
post_number: false,
assigned_type: "User",
assignment_note: nil
})
end
it 'does not update notification level if already watching' do
TopicUser.change(moderator.id, topic.id,
notification_level: TopicUser.notification_levels[:watching]
expect(messages[0].data).to include(
{
type: "assigned",
topic_id: topic.id,
post_id: false,
post_number: false,
assigned_type: "User",
assigned_to: BasicUserSerializer.new(moderator, scope: Guardian.new, root: false).as_json,
assignment_note: "tomtom best mom",
},
)
expect do
assigner_self.assign(moderator)
end.to_not change { TopicUser.last.notifications_reason_id }
expect(messages[1].channel).to eq "/staff/topic-assignment"
expect(messages[1].data).to include(
{
type: "unassigned",
topic_id: topic.id,
post_id: false,
post_number: false,
assigned_type: "User",
assignment_note: nil,
},
)
end
it 'does not update notification level if it is not set by the plugin' do
it "does not update notification level if already watching" do
TopicUser.change(
moderator.id,
topic.id,
notification_level: TopicUser.notification_levels[:watching],
)
expect do assigner_self.assign(moderator) end.to_not change {
TopicUser.last.notifications_reason_id
}
end
it "does not update notification level if it is not set by the plugin" do
assigner.assign(moderator)
expect(TopicUser.find_by(user: moderator).notification_level)
.to eq(TopicUser.notification_levels[:watching])
expect(TopicUser.find_by(user: moderator).notification_level).to eq(
TopicUser.notification_levels[:watching],
)
TopicUser.change(moderator.id, topic.id,
notification_level: TopicUser.notification_levels[:muted]
TopicUser.change(
moderator.id,
topic.id,
notification_level: TopicUser.notification_levels[:muted],
)
assigner.unassign
expect(TopicUser.find_by(user: moderator, topic: topic).notification_level)
.to eq(TopicUser.notification_levels[:muted])
expect(TopicUser.find_by(user: moderator, topic: topic).notification_level).to eq(
TopicUser.notification_levels[:muted],
)
end
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)
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.assign(moderator, note: "note me down")
@ -216,7 +225,7 @@ RSpec.describe Assigner do
expect(assign[:reason]).to eq(:already_assigned)
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.assign(moderator, note: "note me down")
@ -225,21 +234,21 @@ RSpec.describe Assigner do
expect(assign[:success]).to eq(true)
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)
expect(assign[:success]).to eq(false)
expect(assign[:reason]).to eq(:forbidden_assignee_not_pm_participant)
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)
expect(assign[:success]).to eq(false)
expect(assign[:reason]).to eq(:forbidden_assignee_not_pm_participant)
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)
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)
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)
expect(assign[:success]).to eq(false)
expect(assign[:reason]).to eq(:forbidden_assignee_cant_see_topic)
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)
expect(assign[:success]).to eq(false)
@ -283,7 +292,7 @@ RSpec.describe Assigner do
expect(assign[:success]).to eq(true)
end
it 'triggers error for incorrect type' do
it "triggers error for incorrect type" do
expect do
described_class.new(secure_category, moderator).assign(moderator)
end.to raise_error(Discourse::InvalidAccess)
@ -293,9 +302,9 @@ RSpec.describe Assigner do
it "does not recreate assignment if no assignee change" do
assigner.assign(moderator)
expect do
assigner.assign(moderator, note: "new notes!")
end.to_not change { Assignment.last.id }
expect do assigner.assign(moderator, note: "new notes!") end.to_not change {
Assignment.last.id
}
end
it "updates notes" do
@ -317,21 +326,25 @@ RSpec.describe Assigner do
it "publishes topic assignment with note" do
assigner.assign(moderator)
messages = MessageBus.track_publish('/staff/topic-assignment') do
assigner = described_class.new(topic, moderator_2)
assigner.assign(moderator, note: "new notes!")
end
messages =
MessageBus.track_publish("/staff/topic-assignment") do
assigner = described_class.new(topic, moderator_2)
assigner.assign(moderator, note: "new notes!")
end
expect(messages[0].channel).to eq "/staff/topic-assignment"
expect(messages[0].data).to include({
type: "assigned",
topic_id: topic.id,
post_id: false,
post_number: false,
assigned_type: "User",
assigned_to: BasicUserSerializer.new(moderator, scope: Guardian.new, root: false).as_json,
assignment_note: "new notes!"
})
expect(messages[0].data).to include(
{
type: "assigned",
topic_id: topic.id,
post_id: false,
post_number: false,
assigned_type: "User",
assigned_to:
BasicUserSerializer.new(moderator, scope: Guardian.new, root: false).as_json,
assignment_note: "new notes!",
},
)
end
it "adds a note_change small action post" do
@ -348,7 +361,9 @@ RSpec.describe Assigner do
context "assign_self_regex" do
fab!(:me) { Fabricate(:admin) }
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
SiteSetting.assigns_by_staff_mention = true
@ -388,7 +403,14 @@ RSpec.describe Assigner do
fab!(:me) { Fabricate(:admin) }
fab!(:other) { Fabricate(:admin) }
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
SiteSetting.assigns_by_staff_mention = true
@ -407,7 +429,7 @@ RSpec.describe Assigner do
let(:topic) { post.topic }
let(:moderator) { Fabricate(:moderator, groups: [assign_allowed_group]) }
context 'topic' do
context "topic" do
let(:assigner) { described_class.new(topic, moderator) }
before do
@ -417,26 +439,34 @@ RSpec.describe Assigner do
it "unassigns on topic closed" do
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
it "unassigns on topic autoclosed" do
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
it "does not unassign on topic open" do
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
it "does not unassign on automatic topic open" do
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
context 'post' do
context "post" do
let(:post_2) { Fabricate(:post, topic: topic) }
let(:assigner) { described_class.new(post_2, moderator) }
@ -447,7 +477,7 @@ RSpec.describe Assigner do
assigner.assign(moderator)
end
it 'deactivates post assignments when topic is closed' do
it "deactivates post assignments when topic is closed" do
assigner.assign(moderator)
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
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)
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
end
it 'deletes post small action for deleted post' do
it "deletes post small action for deleted post" do
assigner.assign(moderator)
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(:moderator) { Fabricate(:moderator, groups: [assign_allowed_group]) }
context 'topic' do
context "topic" do
let(:assigner) { described_class.new(topic, moderator) }
before do
@ -494,14 +524,18 @@ RSpec.describe Assigner do
it "reassigns on topic open" do
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)
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
context 'post' do
context "post" do
let(:post_2) { Fabricate(:post, topic: topic) }
let(:assigner) { described_class.new(post_2, moderator) }
@ -512,7 +546,7 @@ RSpec.describe Assigner do
assigner.assign(moderator)
end
it 'reassigns post on topic open' do
it "reassigns post on topic open" do
assigner.assign(moderator)
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
SiteSetting.assign_mailer = AssignMailer.levels[:always]
expect { described_class.new(topic, moderator).assign(moderator) }
.to change { ActionMailer::Base.deliveries.size }.by(1)
expect { described_class.new(topic, moderator).assign(moderator) }.to change {
ActionMailer::Base.deliveries.size
}.by(1)
end
it "doesn't send an email if assignee is a group" do
SiteSetting.assign_mailer = AssignMailer.levels[:always]
expect { described_class.new(topic, moderator).assign(assign_allowed_group) }
.to change { ActionMailer::Base.deliveries.size }.by(0)
expect { described_class.new(topic, moderator).assign(assign_allowed_group) }.to change {
ActionMailer::Base.deliveries.size
}.by(0)
end
it "doesn't send an email if the assigner and assignee are not different" do
SiteSetting.assign_mailer = AssignMailer.levels[:different_users]
expect { described_class.new(topic, moderator).assign(moderator_2) }
.to change { ActionMailer::Base.deliveries.size }.by(1)
expect { described_class.new(topic, moderator).assign(moderator_2) }.to change {
ActionMailer::Base.deliveries.size
}.by(1)
end
it "doesn't send an email if the assigner and assignee are not different" do
SiteSetting.assign_mailer = AssignMailer.levels[:different_users]
expect { described_class.new(topic, moderator).assign(moderator) }
.to change { ActionMailer::Base.deliveries.size }.by(0)
expect { described_class.new(topic, moderator).assign(moderator) }.to change {
ActionMailer::Base.deliveries.size
}.by(0)
end
it "doesn't send an email" do
SiteSetting.assign_mailer = AssignMailer.levels[:never]
expect { described_class.new(topic, moderator).assign(moderator_2) }
.to change { ActionMailer::Base.deliveries.size }.by(0)
expect { described_class.new(topic, moderator).assign(moderator_2) }.to change {
ActionMailer::Base.deliveries.size
}.by(0)
end
end
end

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'rails_helper'
require_relative '../support/assign_allowed_group'
require "rails_helper"
require_relative "../support/assign_allowed_group"
def assert_reminder_not_created
expect { subject.remind(user) }.to change { Post.count }.by(0)
@ -12,21 +12,21 @@ RSpec.describe PendingAssignsReminder do
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
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)
Assigner.new(post.topic, user).assign(user)
assert_reminder_not_created
end
describe 'when the user has multiple tasks' do
describe "when the user has multiple tasks" do
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
add_to_assign_allowed_group(user)
@ -46,7 +46,7 @@ RSpec.describe PendingAssignsReminder do
@post4.topic.update(category: secure_category)
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
subject.remind(user)
@ -56,31 +56,29 @@ RSpec.describe PendingAssignsReminder do
expect(topic.user).to eq(system)
expect(topic.archetype).to eq(Archetype.private_message)
expect(topic.topic_allowed_users.pluck(:user_id)).to contain_exactly(
system.id, user.id
)
expect(topic.topic_allowed_users.pluck(:user_id)).to contain_exactly(system.id, user.id)
expect(topic.title).to eq(I18n.t(
'pending_assigns_reminder.title',
pending_assignments: 3
))
expect(topic.title).to eq(I18n.t("pending_assigns_reminder.title", pending_assignments: 3))
expect(post.raw).to include(@post1.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(@post4.topic.fancy_title)
expect(
user.reload.custom_fields[described_class::REMINDED_AT].to_datetime
).to eq_time(DateTime.now)
expect(user.reload.custom_fields[described_class::REMINDED_AT].to_datetime).to eq_time(
DateTime.now,
)
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)
reminders_count = Topic.joins(:_custom_fields)
.where(topic_custom_fields: { name: described_class::CUSTOM_FIELD_NAME }).count
reminders_count =
Topic
.joins(:_custom_fields)
.where(topic_custom_fields: { name: described_class::CUSTOM_FIELD_NAME })
.count
expect(reminders_count).to eq(1)
end
@ -96,8 +94,11 @@ RSpec.describe PendingAssignsReminder do
subject.remind(another_user)
reminders_count = Topic.joins(:_custom_fields)
.where(topic_custom_fields: { name: described_class::CUSTOM_FIELD_NAME }).count
reminders_count =
Topic
.joins(:_custom_fields)
.where(topic_custom_fields: { name: described_class::CUSTOM_FIELD_NAME })
.count
expect(reminders_count).to eq(2)
end
@ -107,8 +108,11 @@ RSpec.describe PendingAssignsReminder do
Fabricate(:post, topic: Topic.last)
subject.remind(user)
reminders_count = Topic.joins(:_custom_fields)
.where(topic_custom_fields: { name: described_class::CUSTOM_FIELD_NAME }).count
reminders_count =
Topic
.joins(:_custom_fields)
.where(topic_custom_fields: { name: described_class::CUSTOM_FIELD_NAME })
.count
expect(reminders_count).to eq(2)
end
@ -124,10 +128,7 @@ RSpec.describe PendingAssignsReminder do
post = Post.last
topic = post.topic
expect(topic.title).to eq(I18n.t(
'pending_assigns_reminder.title',
pending_assignments: 4
))
expect(topic.title).to eq(I18n.t("pending_assigns_reminder.title", pending_assignments: 4))
@post5.topic.update_status("closed", true, Discourse.system_user)
expect(@post5.topic.closed).to eq(true)
@ -137,11 +138,7 @@ RSpec.describe PendingAssignsReminder do
post = Post.last
topic = post.topic
expect(topic.title).to eq(I18n.t(
'pending_assigns_reminder.title',
pending_assignments: 3
))
expect(topic.title).to eq(I18n.t("pending_assigns_reminder.title", pending_assignments: 3))
end
end
end

View File

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

View File

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

View File

@ -1,21 +1,18 @@
# frozen_string_literal: true
require 'rails_helper'
require_relative '../support/assign_allowed_group'
require "rails_helper"
require_relative "../support/assign_allowed_group"
RSpec.describe Group do
let(:group) { Fabricate(:group) }
before do
SiteSetting.assign_enabled = true
end
before { SiteSetting.assign_enabled = true }
context 'Tracking changes that could affect the allow assign on groups site setting' do
let(:removed_group_setting) { '3|4' }
context "Tracking changes that could affect the allow assign on groups site setting" do
let(:removed_group_setting) { "3|4" }
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}"
group.destroy!
@ -23,7 +20,7 @@ RSpec.describe Group do
expect(SiteSetting.assign_allowed_on_groups).to eq removed_group_setting
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}"
group.destroy!
@ -31,7 +28,7 @@ RSpec.describe Group do
expect(SiteSetting.assign_allowed_on_groups).to eq removed_group_setting
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"
SiteSetting.assign_allowed_on_groups = allowed_groups
@ -41,13 +38,13 @@ RSpec.describe Group do
end
end
context 'includes can_show_assigned_tab? method' do
context "includes can_show_assigned_tab? method" do
let(:admin) { Fabricate(:admin) }
let(:user) { Fabricate(:user) }
let(:user1) { 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
add_to_assign_allowed_group(user)
@ -55,7 +52,7 @@ RSpec.describe Group do
add_to_assign_allowed_group(admin)
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(user1)
group.add(user2)
@ -63,7 +60,7 @@ RSpec.describe Group do
expect(group.can_show_assigned_tab?).to eq(false)
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(user1)

View File

@ -8,14 +8,24 @@ describe Reviewable do
fab!(:reviewable1) { Fabricate(:reviewable_flagged_post, target: post1) }
fab!(:reviewable2) { Fabricate(:reviewable_flagged_post, target: post2) }
before do
SiteSetting.assign_enabled = true
end
before { SiteSetting.assign_enabled = true }
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!(target: post2, topic_id: post2.topic.id, assigned_by_user: user, assigned_to: admin)
Assignment.create!(
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

View File

@ -8,19 +8,29 @@ describe Topic do
let(:group) { Fabricate(:group) }
let(:topic) { Fabricate(:topic) }
before do
SiteSetting.assign_enabled = true
end
before { SiteSetting.assign_enabled = true }
describe "#assigned_to" 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)
end
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)
end

View File

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

View File

@ -1,12 +1,12 @@
# frozen_string_literal: true
require 'rails_helper'
require "rails_helper"
describe 'plugin' do
describe "plugin" do
before { SiteSetting.assign_enabled = true }
describe 'events' do
describe 'on user_removed_from_group' do
describe "events" do
describe "on user_removed_from_group" do
before do
@topic = Fabricate(:post).topic
@user = Fabricate(:user)
@ -14,7 +14,7 @@ describe 'plugin' do
@group_a.add(@user)
end
it 'unassigns the user' do
it "unassigns the user" do
SiteSetting.assign_allowed_on_groups = @group_a.id.to_s
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
@group_b = Fabricate(:group)
@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)
@group_a.remove(@user)

View File

@ -1,45 +1,55 @@
# frozen_string_literal: true
require 'rails_helper'
require_relative '../support/assign_allowed_group'
require "rails_helper"
require_relative "../support/assign_allowed_group"
RSpec.describe DiscourseAssign::AssignController do
before { SiteSetting.assign_enabled = true }
fab!(:default_allowed_group) { Group.find_by(name: 'staff') }
let(:user) { Fabricate(:admin, groups: [default_allowed_group], name: 'Robin Ward', username: 'eviltrout') }
fab!(:default_allowed_group) { Group.find_by(name: "staff") }
let(:user) do
Fabricate(:admin, groups: [default_allowed_group], name: "Robin Ward", username: "eviltrout")
end
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]) }
fab!(:normal_user) { Fabricate(:user) }
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) }
it 'filters requests where current_user is not member of an allowed group' do
SiteSetting.assign_allowed_on_groups = ''
it "filters requests where current_user is not member of an allowed group" do
SiteSetting.assign_allowed_on_groups = ""
put '/assign/assign.json', params: {
target_id: post.topic_id, target_type: 'Topic', username: user2.username
}
put "/assign/assign.json",
params: {
target_id: post.topic_id,
target_type: "Topic",
username: user2.username,
}
expect(response.status).to eq(403)
end
it 'filters requests where assigne group is not allowed' do
put '/assign/assign.json', params: {
target_id: post.topic_id, target_type: 'Topic', group_name: default_allowed_group.name
}
it "filters requests where assigne group is not allowed" do
put "/assign/assign.json",
params: {
target_id: post.topic_id,
target_type: "Topic",
group_name: default_allowed_group.name,
}
expect(response.status).to eq(400)
end
describe '#suggestions' do
describe "#suggestions" do
before { sign_in(user) }
it 'includes users in allowed groups' do
allowed_group = Group.find_by(name: 'everyone')
it "includes users in allowed groups" do
allowed_group = Group.find_by(name: "everyone")
allowed_group.add(user2)
defaults = "#{default_allowed_group.id}|#{allowed_group.id}"
@ -47,25 +57,25 @@ RSpec.describe DiscourseAssign::AssignController do
SiteSetting.assign_allowed_on_groups = defaults
Assigner.new(post.topic, user).assign(user2)
get '/assign/suggestions.json'
suggestions = JSON.parse(response.body)['suggestions'].map { |u| u['username'] }
get "/assign/suggestions.json"
suggestions = JSON.parse(response.body)["suggestions"].map { |u| u["username"] }
expect(suggestions).to contain_exactly(user2.username, user.username)
end
it 'does not include users from disallowed groups' do
allowed_group = Group.find_by(name: 'everyone')
it "does not include users from disallowed groups" do
allowed_group = Group.find_by(name: "everyone")
allowed_group.add(user2)
SiteSetting.assign_allowed_on_groups = default_allowed_group.id.to_s
Assigner.new(post.topic, user).assign(user2)
get '/assign/suggestions.json'
suggestions = JSON.parse(response.body)['suggestions'].map { |u| u['username'] }.sort
get "/assign/suggestions.json"
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
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
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}"
get '/assign/suggestions.json'
assign_allowed_on_groups = JSON.parse(response.body)['assign_allowed_on_groups']
get "/assign/suggestions.json"
assign_allowed_on_groups = JSON.parse(response.body)["assign_allowed_on_groups"]
expect(assign_allowed_on_groups).to contain_exactly(visible_group.name)
end
@ -88,68 +98,86 @@ RSpec.describe DiscourseAssign::AssignController do
sign_in(user)
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])
Assigner.new(post.topic, user).assign(another_admin)
get '/assign/suggestions.json'
suggestions = JSON.parse(response.body)['suggestions'].map { |u| u['username'] }
get "/assign/suggestions.json"
suggestions = JSON.parse(response.body)["suggestions"].map { |u| u["username"] }
expect(suggestions).to contain_exactly(user.username)
end
end
describe '#assign' do
include_context 'A group that is allowed to assign'
describe "#assign" do
include_context "A group that is allowed to assign"
before do
sign_in(user)
add_to_assign_allowed_group(user2)
end
it 'assigns topic to a user' do
put '/assign/assign.json', params: {
target_id: post.topic_id, target_type: 'Topic', username: user2.username
}
it "assigns topic to a user" do
put "/assign/assign.json",
params: {
target_id: post.topic_id,
target_type: "Topic",
username: user2.username,
}
expect(response.status).to eq(200)
expect(post.topic.reload.assignment.assigned_to_id).to eq(user2.id)
end
it 'assigns topic with note to a user' do
put '/assign/assign.json', params: {
target_id: post.topic_id, target_type: 'Topic', username: user2.username, note: "do dis pls"
}
it "assigns topic with note to a user" do
put "/assign/assign.json",
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")
end
it 'assigns topic to a group' do
put '/assign/assign.json', params: {
target_id: post.topic_id, target_type: 'Topic', group_name: assign_allowed_group.name
}
it "assigns topic to a group" do
put "/assign/assign.json",
params: {
target_id: post.topic_id,
target_type: "Topic",
group_name: assign_allowed_group.name,
}
expect(response.status).to eq(200)
expect(post.topic.reload.assignment.assigned_to).to eq(assign_allowed_group)
end
it 'fails to assign topic to the user if its already assigned to the same user' do
put '/assign/assign.json', params: {
target_id: post.topic_id, target_type: 'Topic', username: user2.username
}
it "fails to assign topic to the user if its already assigned to the same user" do
put "/assign/assign.json",
params: {
target_id: post.topic_id,
target_type: "Topic",
username: user2.username,
}
expect(response.status).to eq(200)
expect(post.topic.reload.assignment.assigned_to_id).to eq(user2.id)
put '/assign/assign.json', params: {
target_id: post.topic_id, target_type: 'Topic', username: user2.username
}
put "/assign/assign.json",
params: {
target_id: post.topic_id,
target_type: "Topic",
username: user2.username,
}
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
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)
add_to_assign_allowed_group(another_user)
another_post = Fabricate(:post)
@ -157,45 +185,64 @@ RSpec.describe DiscourseAssign::AssignController do
SiteSetting.max_assigned_topics = max_assigns
Assigner.new(post.topic, user).assign(another_user)
put '/assign/assign.json', params: {
target_id: another_post.topic_id, target_type: 'Topic', username: another_user.username
}
put "/assign/assign.json",
params: {
target_id: another_post.topic_id,
target_type: "Topic",
username: another_user.username,
}
expect(response.status).to eq(400)
expect(JSON.parse(response.body)['error']).to eq(
I18n.t('discourse_assign.too_many_assigns', username: another_user.username, max: max_assigns)
expect(JSON.parse(response.body)["error"]).to eq(
I18n.t(
"discourse_assign.too_many_assigns",
username: another_user.username,
max: max_assigns,
),
)
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
another_user = Fabricate(:user)
add_to_assign_allowed_group(another_user)
put '/assign/assign.json', params: {
target_id: pm.id, target_type: 'Topic', username: another_user.username
}
put "/assign/assign.json",
params: {
target_id: pm.id,
target_type: "Topic",
username: another_user.username,
}
expect(response.parsed_body['error']).to eq(
I18n.t('discourse_assign.forbidden_assignee_not_pm_participant', username: another_user.username)
expect(response.parsed_body["error"]).to eq(
I18n.t(
"discourse_assign.forbidden_assignee_not_pm_participant",
username: another_user.username,
),
)
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)))
another_user = Fabricate(:user)
add_to_assign_allowed_group(another_user)
put '/assign/assign.json', params: {
target_id: topic.id, target_type: "Topic", username: another_user.username
}
put "/assign/assign.json",
params: {
target_id: topic.id,
target_type: "Topic",
username: another_user.username,
}
expect(response.parsed_body['error']).to eq(
I18n.t('discourse_assign.forbidden_assignee_cant_see_topic', username: another_user.username)
expect(response.parsed_body["error"]).to eq(
I18n.t(
"discourse_assign.forbidden_assignee_cant_see_topic",
username: another_user.username,
),
)
end
end
describe '#assigned' do
include_context 'A group that is allowed to assign'
describe "#assigned" do
include_context "A group that is allowed to assign"
fab!(:post1) { Fabricate(:post) }
fab!(:post2) { Fabricate(:post) }
@ -216,40 +263,44 @@ RSpec.describe DiscourseAssign::AssignController do
sign_in(user)
end
it 'lists topics ordered by user' do
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])
it "lists topics ordered by user" do
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],
)
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])
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],
)
get '/assign/assigned.json', params: { offset: 2 }
expect(JSON.parse(response.body)['topics'].map { |t| t['id'] }).to match_array([post1.topic_id])
get "/assign/assigned.json", params: { offset: 2 }
expect(JSON.parse(response.body)["topics"].map { |t| t["id"] }).to match_array(
[post1.topic_id],
)
end
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]) }
before do
SiteSetting.assign_allowed_on_groups += "|#{custom_allowed_group.id}"
end
before { SiteSetting.assign_allowed_on_groups += "|#{custom_allowed_group.id}" }
it 'works for admins' do
get '/assign/assigned.json'
it "works for admins" do
get "/assign/assigned.json"
expect(response.status).to eq(200)
end
it 'does not work for other groups' do
it "does not work for other groups" do
sign_in(other_user)
get '/assign/assigned.json'
get "/assign/assigned.json"
expect(response.status).to eq(403)
end
end
end
describe '#group_members' do
include_context 'A group that is allowed to assign'
describe "#group_members" do
include_context "A group that is allowed to assign"
fab!(:post1) { Fabricate(:post) }
fab!(:post2) { Fabricate(:post) }
@ -264,12 +315,14 @@ RSpec.describe DiscourseAssign::AssignController do
Assigner.new(post3.topic, user).assign(user)
end
it 'list members order by assignments_count' do
it "list members order by assignments_count" do
sign_in(user)
get "/assign/members/#{get_assigned_allowed_group_name}.json"
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
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"
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
it "returns members as according to filter" do
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(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(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(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
it "404 error to non-group-members" do

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'rails_helper'
require_relative '../support/assign_allowed_group'
require "rails_helper"
require_relative "../support/assign_allowed_group"
describe PostSerializer do
fab!(:user) { Fabricate(:user) }
@ -9,7 +9,7 @@ describe PostSerializer do
fab!(:post) { Fabricate(:post, topic: topic) }
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
SiteSetting.assign_enabled = true

View File

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

View File

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

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'rails_helper'
require_relative '../support/assign_allowed_group'
require "rails_helper"
require_relative "../support/assign_allowed_group"
RSpec.describe TopicViewSerializer do
fab!(:user) { Fabricate(:user) }
@ -9,7 +9,7 @@ RSpec.describe TopicViewSerializer do
fab!(:post) { Fabricate(:post, topic: topic) }
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
SiteSetting.assign_enabled = true
@ -26,7 +26,9 @@ RSpec.describe TopicViewSerializer do
it "includes assigned group in serializer" do
Assigner.new(topic, user).assign(assign_allowed_group)
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
end
@ -39,6 +41,8 @@ RSpec.describe TopicViewSerializer do
it "includes indirectly_assigned_to notes in serializer" do
Assigner.new(post, user).assign(user, note: "note me down")
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

View File

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

View File

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