2017-07-04 03:26:46 +08:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2020-07-10 17:05:55 +08:00
|
|
|
require 'net/imap'
|
|
|
|
|
2013-04-17 15:08:21 +08:00
|
|
|
class Group < ActiveRecord::Base
|
2020-05-26 03:13:50 +08:00
|
|
|
# TODO(2021-05-26): remove
|
2020-04-23 02:27:01 +08:00
|
|
|
self.ignored_columns = %w{
|
|
|
|
automatic_membership_retroactive
|
2020-05-26 03:13:50 +08:00
|
|
|
flair_url
|
2020-04-23 02:27:01 +08:00
|
|
|
}
|
|
|
|
|
2014-04-28 16:31:51 +08:00
|
|
|
include HasCustomFields
|
2016-12-22 13:46:22 +08:00
|
|
|
include AnonCacheInvalidator
|
2018-10-05 16:53:59 +08:00
|
|
|
include HasDestroyedWebHook
|
2021-02-19 04:24:44 +08:00
|
|
|
include GlobalPath
|
2014-04-25 21:14:05 +08:00
|
|
|
|
2017-09-29 23:04:05 +08:00
|
|
|
cattr_accessor :preloaded_custom_field_names
|
|
|
|
self.preloaded_custom_field_names = Set.new
|
2017-08-08 21:45:27 +08:00
|
|
|
|
2014-09-01 04:10:38 +08:00
|
|
|
has_many :category_groups, dependent: :destroy
|
2013-05-09 09:33:56 +08:00
|
|
|
has_many :group_users, dependent: :destroy
|
2019-03-27 19:30:59 +08:00
|
|
|
has_many :group_requests, dependent: :destroy
|
2015-12-01 13:52:43 +08:00
|
|
|
has_many :group_mentions, dependent: :destroy
|
2013-04-29 14:33:24 +08:00
|
|
|
|
2015-12-23 08:09:17 +08:00
|
|
|
has_many :group_archived_messages, dependent: :destroy
|
|
|
|
|
2013-04-29 14:33:24 +08:00
|
|
|
has_many :categories, through: :category_groups
|
|
|
|
has_many :users, through: :group_users
|
2019-03-27 19:30:59 +08:00
|
|
|
has_many :requesters, through: :group_requests, source: :user
|
2016-12-11 23:36:15 +08:00
|
|
|
has_many :group_histories, dependent: :destroy
|
2019-04-18 05:12:32 +08:00
|
|
|
has_many :category_reviews, class_name: 'Category', foreign_key: :reviewable_by_group_id, dependent: :nullify
|
|
|
|
has_many :reviewables, foreign_key: :reviewable_by_group_id, dependent: :nullify
|
2020-08-07 00:27:27 +08:00
|
|
|
has_many :group_category_notification_defaults, dependent: :destroy
|
|
|
|
has_many :group_tag_notification_defaults, dependent: :destroy
|
2013-04-29 14:33:24 +08:00
|
|
|
|
2020-05-25 13:38:47 +08:00
|
|
|
belongs_to :flair_upload, class_name: 'Upload'
|
|
|
|
|
2016-06-16 01:49:57 +08:00
|
|
|
has_and_belongs_to_many :web_hooks
|
|
|
|
|
2015-12-07 19:39:28 +08:00
|
|
|
before_save :downcase_incoming_email
|
2016-12-05 16:18:24 +08:00
|
|
|
before_save :cook_bio
|
2015-12-07 19:39:28 +08:00
|
|
|
|
2013-05-09 09:33:56 +08:00
|
|
|
after_save :destroy_deletions
|
2015-04-10 10:17:28 +08:00
|
|
|
after_save :update_primary_group
|
|
|
|
after_save :update_title
|
2013-05-09 09:33:56 +08:00
|
|
|
|
2017-01-18 13:39:34 +08:00
|
|
|
after_save :enqueue_update_mentions_job,
|
2017-08-31 12:06:56 +08:00
|
|
|
if: Proc.new { |g| g.name_before_last_save && g.saved_change_to_name? }
|
2017-01-18 13:39:34 +08:00
|
|
|
|
2015-09-28 14:43:38 +08:00
|
|
|
after_save :expire_cache
|
|
|
|
after_destroy :expire_cache
|
|
|
|
|
2018-09-20 11:24:01 +08:00
|
|
|
after_commit :automatic_group_membership, on: [:create, :update]
|
2018-03-27 14:23:35 +08:00
|
|
|
after_commit :trigger_group_created_event, on: :create
|
2018-05-21 17:29:19 +08:00
|
|
|
after_commit :trigger_group_updated_event, on: :update
|
2018-03-27 14:23:35 +08:00
|
|
|
after_commit :trigger_group_destroyed_event, on: :destroy
|
2020-08-07 00:27:27 +08:00
|
|
|
after_commit :set_default_notifications, on: [:create, :update]
|
2018-03-27 14:23:35 +08:00
|
|
|
|
2015-09-28 14:43:38 +08:00
|
|
|
def expire_cache
|
|
|
|
ApplicationSerializer.expire_cache_fragment!("group_names")
|
2018-11-27 05:49:57 +08:00
|
|
|
SvgSprite.expire_cache
|
2020-07-10 17:05:55 +08:00
|
|
|
Discourse.cache.delete("group_imap_mailboxes_#{self.id}")
|
2015-09-28 14:43:38 +08:00
|
|
|
end
|
|
|
|
|
2019-04-18 05:12:32 +08:00
|
|
|
def remove_review_groups
|
|
|
|
Category.where(review_group_id: self.id).update_all(review_group_id: nil)
|
|
|
|
end
|
|
|
|
|
2018-04-03 00:44:04 +08:00
|
|
|
validate :name_format_validator
|
2018-04-02 18:17:06 +08:00
|
|
|
validates :name, presence: true
|
2015-11-27 13:35:16 +08:00
|
|
|
validate :automatic_membership_email_domains_format_validator
|
2015-12-07 19:39:28 +08:00
|
|
|
validate :incoming_email_validator
|
2017-07-27 14:39:47 +08:00
|
|
|
validate :can_allow_membership_requests, if: :allow_membership_requests
|
2018-04-06 17:11:00 +08:00
|
|
|
validate :validate_grant_trust_level, if: :will_save_change_to_grant_trust_level?
|
2013-05-09 15:37:34 +08:00
|
|
|
|
2013-05-06 12:49:56 +08:00
|
|
|
AUTO_GROUPS = {
|
2013-07-14 09:24:16 +08:00
|
|
|
everyone: 0,
|
2013-05-06 12:49:56 +08:00
|
|
|
admins: 1,
|
|
|
|
moderators: 2,
|
|
|
|
staff: 3,
|
2014-06-17 16:13:07 +08:00
|
|
|
trust_level_0: 10,
|
2013-05-06 12:49:56 +08:00
|
|
|
trust_level_1: 11,
|
|
|
|
trust_level_2: 12,
|
|
|
|
trust_level_3: 13,
|
2014-07-10 10:17:13 +08:00
|
|
|
trust_level_4: 14
|
2013-05-06 12:49:56 +08:00
|
|
|
}
|
|
|
|
|
2014-07-10 10:17:13 +08:00
|
|
|
AUTO_GROUP_IDS = Hash[*AUTO_GROUPS.to_a.flatten.reverse]
|
2016-03-24 04:20:24 +08:00
|
|
|
STAFF_GROUPS = [:admins, :moderators, :staff]
|
2014-06-17 08:46:30 +08:00
|
|
|
|
2014-01-08 00:47:01 +08:00
|
|
|
ALIAS_LEVELS = {
|
|
|
|
nobody: 0,
|
|
|
|
only_admins: 1,
|
|
|
|
mods_and_admins: 2,
|
|
|
|
members_mods_and_admins: 3,
|
2019-07-09 05:12:09 +08:00
|
|
|
owners_mods_and_admins: 4,
|
2014-01-08 00:47:01 +08:00
|
|
|
everyone: 99
|
|
|
|
}
|
|
|
|
|
2020-05-26 13:40:09 +08:00
|
|
|
VALID_DOMAIN_REGEX = /\A[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,24}(:[0-9]{1,5})?(\/.*)?\Z/i
|
|
|
|
|
2017-07-04 03:26:46 +08:00
|
|
|
def self.visibility_levels
|
|
|
|
@visibility_levels = Enum.new(
|
|
|
|
public: 0,
|
2019-07-09 03:09:50 +08:00
|
|
|
logged_on_users: 1,
|
|
|
|
members: 2,
|
|
|
|
staff: 3,
|
|
|
|
owners: 4
|
2017-07-04 03:26:46 +08:00
|
|
|
)
|
|
|
|
end
|
|
|
|
|
2017-08-29 00:32:08 +08:00
|
|
|
validates :mentionable_level, inclusion: { in: ALIAS_LEVELS.values }
|
|
|
|
validates :messageable_level, inclusion: { in: ALIAS_LEVELS.values }
|
2014-01-08 00:47:01 +08:00
|
|
|
|
2020-08-14 10:01:31 +08:00
|
|
|
scope :with_imap_configured, -> { where.not(imap_mailbox_name: '') }
|
|
|
|
|
2019-02-15 07:24:29 +08:00
|
|
|
scope :visible_groups, Proc.new { |user, order, opts|
|
2019-04-30 14:58:18 +08:00
|
|
|
groups = self.order(order || "name ASC")
|
2019-02-15 07:24:29 +08:00
|
|
|
|
|
|
|
if !opts || !opts[:include_everyone]
|
|
|
|
groups = groups.where("groups.id > 0")
|
|
|
|
end
|
2017-02-03 16:51:32 +08:00
|
|
|
|
2020-01-15 18:21:58 +08:00
|
|
|
if !user&.admin
|
2020-09-11 10:50:13 +08:00
|
|
|
is_staff = !!user&.staff?
|
|
|
|
|
|
|
|
if user.blank?
|
|
|
|
sql = "groups.visibility_level = :public"
|
|
|
|
elsif is_staff
|
|
|
|
sql = "groups.visibility_level IN (:public, :logged_on_users, :members, :staff)"
|
|
|
|
else
|
|
|
|
sql = <<~SQL
|
|
|
|
groups.id IN (
|
|
|
|
SELECT id
|
|
|
|
FROM groups
|
|
|
|
WHERE visibility_level IN (:public, :logged_on_users)
|
|
|
|
|
|
|
|
UNION ALL
|
|
|
|
|
|
|
|
SELECT g.id
|
|
|
|
FROM groups g
|
|
|
|
JOIN group_users gu ON gu.group_id = g.id AND gu.user_id = :user_id
|
|
|
|
WHERE g.visibility_level = :members
|
|
|
|
|
|
|
|
UNION ALL
|
|
|
|
|
|
|
|
SELECT g.id
|
|
|
|
FROM groups g
|
|
|
|
JOIN group_users gu ON gu.group_id = g.id AND gu.user_id = :user_id AND gu.owner
|
|
|
|
WHERE g.visibility_level IN (:staff, :owners)
|
|
|
|
)
|
|
|
|
SQL
|
|
|
|
end
|
|
|
|
|
|
|
|
params = Group.visibility_levels.to_h.merge(user_id: user&.id, is_staff: is_staff)
|
2020-01-15 18:21:58 +08:00
|
|
|
groups = groups.where(sql, params)
|
2017-02-03 16:51:32 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
groups
|
|
|
|
}
|
|
|
|
|
2019-08-14 21:30:04 +08:00
|
|
|
scope :members_visible_groups, Proc.new { |user, order, opts|
|
|
|
|
groups = self.order(order || "name ASC")
|
|
|
|
|
|
|
|
if !opts || !opts[:include_everyone]
|
|
|
|
groups = groups.where("groups.id > 0")
|
|
|
|
end
|
|
|
|
|
2020-01-15 18:21:58 +08:00
|
|
|
if !user&.admin
|
2020-09-11 10:50:13 +08:00
|
|
|
is_staff = !!user&.staff?
|
|
|
|
|
|
|
|
if user.blank?
|
|
|
|
sql = "groups.members_visibility_level = :public"
|
|
|
|
elsif is_staff
|
|
|
|
sql = "groups.members_visibility_level IN (:public, :logged_on_users, :members, :staff)"
|
|
|
|
else
|
|
|
|
sql = <<~SQL
|
|
|
|
groups.id IN (
|
|
|
|
SELECT id
|
|
|
|
FROM groups
|
|
|
|
WHERE members_visibility_level IN (:public, :logged_on_users)
|
|
|
|
|
|
|
|
UNION ALL
|
|
|
|
|
|
|
|
SELECT g.id
|
|
|
|
FROM groups g
|
|
|
|
JOIN group_users gu ON gu.group_id = g.id AND gu.user_id = :user_id
|
|
|
|
WHERE g.members_visibility_level = :members
|
|
|
|
|
|
|
|
UNION ALL
|
|
|
|
|
|
|
|
SELECT g.id
|
|
|
|
FROM groups g
|
|
|
|
JOIN group_users gu ON gu.group_id = g.id AND gu.user_id = :user_id AND gu.owner
|
|
|
|
WHERE g.members_visibility_level IN (:staff, :owners)
|
|
|
|
)
|
|
|
|
SQL
|
|
|
|
end
|
|
|
|
|
|
|
|
params = Group.visibility_levels.to_h.merge(user_id: user&.id, is_staff: is_staff)
|
2020-01-15 18:21:58 +08:00
|
|
|
groups = groups.where(sql, params)
|
2019-08-14 21:30:04 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
groups
|
|
|
|
}
|
|
|
|
|
2019-12-12 19:13:40 +08:00
|
|
|
scope :mentionable, lambda { |user, include_public: true|
|
|
|
|
where(self.mentionable_sql_clause(include_public: include_public),
|
2018-11-22 16:01:03 +08:00
|
|
|
levels: alias_levels(user),
|
|
|
|
user_id: user&.id
|
|
|
|
)
|
2017-08-29 00:32:08 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
scope :messageable, lambda { |user|
|
|
|
|
where("messageable_level in (:levels) OR
|
|
|
|
(
|
|
|
|
messageable_level = #{ALIAS_LEVELS[:members_mods_and_admins]} AND id in (
|
|
|
|
SELECT group_id FROM group_users WHERE user_id = :user_id)
|
2019-07-09 05:12:09 +08:00
|
|
|
) OR (
|
|
|
|
messageable_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)
|
2017-08-29 00:32:08 +08:00
|
|
|
)", levels: alias_levels(user), user_id: user && user.id)
|
|
|
|
}
|
|
|
|
|
2019-12-12 19:13:40 +08:00
|
|
|
def self.mentionable_sql_clause(include_public: true)
|
|
|
|
clause = +<<~SQL
|
|
|
|
mentionable_level in (:levels)
|
|
|
|
OR (
|
|
|
|
mentionable_level = #{ALIAS_LEVELS[:members_mods_and_admins]}
|
|
|
|
AND id in (
|
|
|
|
SELECT group_id FROM group_users WHERE user_id = :user_id)
|
|
|
|
) OR (
|
|
|
|
mentionable_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)
|
|
|
|
)
|
|
|
|
SQL
|
|
|
|
|
|
|
|
if include_public
|
|
|
|
clause << "OR visibility_level = #{Group.visibility_levels[:public]}"
|
|
|
|
end
|
|
|
|
|
|
|
|
clause
|
2018-11-22 16:01:03 +08:00
|
|
|
end
|
|
|
|
|
2017-08-29 00:32:08 +08:00
|
|
|
def self.alias_levels(user)
|
2020-01-15 18:21:58 +08:00
|
|
|
if user&.admin?
|
|
|
|
[
|
|
|
|
ALIAS_LEVELS[:everyone],
|
|
|
|
ALIAS_LEVELS[:only_admins],
|
|
|
|
ALIAS_LEVELS[:mods_and_admins],
|
|
|
|
ALIAS_LEVELS[:members_mods_and_admins],
|
|
|
|
ALIAS_LEVELS[:owners_mods_and_admins],
|
|
|
|
]
|
|
|
|
elsif user&.moderator?
|
|
|
|
[
|
|
|
|
ALIAS_LEVELS[:everyone],
|
|
|
|
ALIAS_LEVELS[:mods_and_admins],
|
|
|
|
ALIAS_LEVELS[:members_mods_and_admins],
|
|
|
|
ALIAS_LEVELS[:owners_mods_and_admins],
|
|
|
|
]
|
|
|
|
else
|
|
|
|
[ALIAS_LEVELS[:everyone]]
|
2015-11-30 14:03:47 +08:00
|
|
|
end
|
2017-08-29 00:32:08 +08:00
|
|
|
end
|
2015-11-30 14:03:47 +08:00
|
|
|
|
2019-06-06 10:05:33 +08:00
|
|
|
def self.register_plugin_editable_group_custom_field(custom_field_name, plugin)
|
2020-05-15 21:04:38 +08:00
|
|
|
Discourse.deprecate("Editable group custom fields should be registered using the plugin API", since: "v2.4.0.beta4", drop_from: "v2.5.0")
|
|
|
|
DiscoursePluginRegistry.register_editable_group_custom_field(custom_field_name, plugin)
|
2019-06-06 10:05:33 +08:00
|
|
|
end
|
|
|
|
|
2015-12-07 19:39:28 +08:00
|
|
|
def downcase_incoming_email
|
|
|
|
self.incoming_email = (incoming_email || "").strip.downcase.presence
|
|
|
|
end
|
|
|
|
|
2016-12-05 16:18:24 +08:00
|
|
|
def cook_bio
|
2019-06-12 14:18:38 +08:00
|
|
|
if self.bio_raw.present?
|
2016-12-05 16:18:24 +08:00
|
|
|
self.bio_cooked = PrettyText.cook(self.bio_raw)
|
2019-06-12 14:18:38 +08:00
|
|
|
else
|
|
|
|
self.bio_cooked = nil
|
2016-12-05 16:18:24 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-12-07 19:39:28 +08:00
|
|
|
def incoming_email_validator
|
|
|
|
return if self.automatic || self.incoming_email.blank?
|
2016-07-28 23:57:30 +08:00
|
|
|
|
2016-02-25 02:47:58 +08:00
|
|
|
incoming_email.split("|").each do |email|
|
2016-07-28 23:57:30 +08:00
|
|
|
escaped = Rack::Utils.escape_html(email)
|
2016-03-09 03:52:04 +08:00
|
|
|
if !Email.is_valid?(email)
|
2017-05-05 19:13:49 +08:00
|
|
|
self.errors.add(:base, I18n.t('groups.errors.invalid_incoming_email', email: escaped))
|
2016-03-09 03:52:04 +08:00
|
|
|
elsif group = Group.where.not(id: self.id).find_by_email(email)
|
2016-07-28 23:57:30 +08:00
|
|
|
self.errors.add(:base, I18n.t('groups.errors.email_already_used_in_group', email: escaped, group_name: Rack::Utils.escape_html(group.name)))
|
2016-03-09 03:52:04 +08:00
|
|
|
elsif category = Category.find_by_email(email)
|
2016-07-28 23:57:30 +08:00
|
|
|
self.errors.add(:base, I18n.t('groups.errors.email_already_used_in_category', email: escaped, category_name: Rack::Utils.escape_html(category.name)))
|
2016-02-25 02:47:58 +08:00
|
|
|
end
|
2015-12-07 19:39:28 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-11-01 04:47:47 +08:00
|
|
|
def posts_for(guardian, opts = nil)
|
|
|
|
opts ||= {}
|
2018-11-12 10:26:41 +08:00
|
|
|
result = Post.joins(:topic, user: :groups, topic: :category)
|
|
|
|
.preload(:topic, user: :groups, topic: :category)
|
2015-12-08 06:19:33 +08:00
|
|
|
.references(:posts, :topics, :category)
|
2018-11-12 08:44:20 +08:00
|
|
|
.where(groups: { id: id })
|
2014-03-14 02:06:22 +08:00
|
|
|
.where('topics.archetype <> ?', Archetype.private_message)
|
2018-03-01 03:39:11 +08:00
|
|
|
.where('topics.visible')
|
2020-12-11 03:55:53 +08:00
|
|
|
.where(post_type: [Post.types[:regular], Post.types[:moderator_action]])
|
2014-02-07 23:44:03 +08:00
|
|
|
|
2017-11-01 04:47:47 +08:00
|
|
|
if opts[:category_id].present?
|
|
|
|
result = result.where('topics.category_id = ?', opts[:category_id].to_i)
|
|
|
|
end
|
|
|
|
|
2015-02-13 00:52:59 +08:00
|
|
|
result = guardian.filter_allowed_categories(result)
|
2017-11-01 04:47:47 +08:00
|
|
|
result = result.where('posts.id < ?', opts[:before_post_id].to_i) if opts[:before_post_id]
|
2015-12-01 13:52:43 +08:00
|
|
|
result.order('posts.created_at desc')
|
|
|
|
end
|
|
|
|
|
2017-11-01 04:47:47 +08:00
|
|
|
def messages_for(guardian, opts = nil)
|
|
|
|
opts ||= {}
|
|
|
|
|
2015-12-08 06:19:33 +08:00
|
|
|
result = Post.includes(:user, :topic, topic: :category)
|
|
|
|
.references(:posts, :topics, :category)
|
|
|
|
.where('topics.archetype = ?', Archetype.private_message)
|
|
|
|
.where(post_type: Post.types[:regular])
|
|
|
|
.where('topics.id IN (SELECT topic_id FROM topic_allowed_groups WHERE group_id = ?)', self.id)
|
|
|
|
|
2017-11-01 04:47:47 +08:00
|
|
|
if opts[:category_id].present?
|
|
|
|
result = result.where('topics.category_id = ?', opts[:category_id].to_i)
|
|
|
|
end
|
|
|
|
|
2015-12-08 06:19:33 +08:00
|
|
|
result = guardian.filter_allowed_categories(result)
|
2017-11-01 04:47:47 +08:00
|
|
|
result = result.where('posts.id < ?', opts[:before_post_id].to_i) if opts[:before_post_id]
|
2015-12-08 06:19:33 +08:00
|
|
|
result.order('posts.created_at desc')
|
|
|
|
end
|
|
|
|
|
2017-11-01 04:47:47 +08:00
|
|
|
def mentioned_posts_for(guardian, opts = nil)
|
|
|
|
opts ||= {}
|
2015-12-01 13:52:43 +08:00
|
|
|
result = Post.joins(:group_mentions)
|
2015-12-08 06:19:33 +08:00
|
|
|
.includes(:user, :topic, topic: :category)
|
2015-12-01 13:52:43 +08:00
|
|
|
.references(:posts, :topics, :category)
|
|
|
|
.where('topics.archetype <> ?', Archetype.private_message)
|
|
|
|
.where(post_type: Post.types[:regular])
|
2015-12-02 13:16:19 +08:00
|
|
|
.where('group_mentions.group_id = ?', self.id)
|
2015-12-01 13:52:43 +08:00
|
|
|
|
2017-11-01 04:47:47 +08:00
|
|
|
if opts[:category_id].present?
|
|
|
|
result = result.where('topics.category_id = ?', opts[:category_id].to_i)
|
|
|
|
end
|
|
|
|
|
2015-12-01 13:52:43 +08:00
|
|
|
result = guardian.filter_allowed_categories(result)
|
2017-11-01 04:47:47 +08:00
|
|
|
result = result.where('posts.id < ?', opts[:before_post_id].to_i) if opts[:before_post_id]
|
2014-02-13 03:00:45 +08:00
|
|
|
result.order('posts.created_at desc')
|
2014-02-07 23:44:03 +08:00
|
|
|
end
|
|
|
|
|
2013-05-06 12:49:56 +08:00
|
|
|
def self.trust_group_ids
|
|
|
|
(10..19).to_a
|
|
|
|
end
|
|
|
|
|
2019-08-16 16:23:51 +08:00
|
|
|
def set_message_default_notification_levels!(topic, ignore_existing: false)
|
|
|
|
group_users.pluck(:user_id, :notification_level).each do |user_id, notification_level|
|
|
|
|
next if user_id == -1
|
|
|
|
next if user_id == topic.user_id
|
|
|
|
next if ignore_existing && TopicUser.where(user_id: user_id, topic_id: topic.id).exists?
|
|
|
|
|
|
|
|
action =
|
|
|
|
case notification_level
|
|
|
|
when TopicUser.notification_levels[:tracking] then "track!"
|
|
|
|
when TopicUser.notification_levels[:regular] then "regular!"
|
|
|
|
when TopicUser.notification_levels[:muted] then "mute!"
|
|
|
|
when TopicUser.notification_levels[:watching] then "watch!"
|
|
|
|
else "track!"
|
|
|
|
end
|
|
|
|
|
|
|
|
topic.notifier.public_send(action, user_id)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-08-07 00:27:27 +08:00
|
|
|
def self.set_category_and_tag_default_notification_levels!(user, group_name)
|
|
|
|
if group = lookup_group(group_name)
|
|
|
|
GroupUser.set_category_notifications(group, user)
|
|
|
|
GroupUser.set_tag_notifications(group, user)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-05-06 12:49:56 +08:00
|
|
|
def self.refresh_automatic_group!(name)
|
2015-12-08 06:19:33 +08:00
|
|
|
return unless id = AUTO_GROUPS[name]
|
2013-05-06 12:49:56 +08:00
|
|
|
|
2013-05-09 15:37:34 +08:00
|
|
|
unless group = self.lookup_group(name)
|
|
|
|
group = Group.new(name: name.to_s, automatic: true)
|
2017-12-14 10:53:21 +08:00
|
|
|
|
|
|
|
if AUTO_GROUPS[:moderators] == id
|
|
|
|
group.default_notification_level = 2
|
|
|
|
group.messageable_level = ALIAS_LEVELS[:everyone]
|
|
|
|
end
|
|
|
|
|
2013-05-06 12:49:56 +08:00
|
|
|
group.id = id
|
|
|
|
group.save!
|
|
|
|
end
|
|
|
|
|
2013-06-23 12:44:16 +08:00
|
|
|
# don't allow shoddy localization to break this
|
2019-04-23 18:22:47 +08:00
|
|
|
localized_name = User.normalize_username(I18n.t("groups.default_names.#{name}", locale: SiteSetting.default_locale))
|
2016-10-24 16:53:31 +08:00
|
|
|
validator = UsernameValidator.new(localized_name)
|
2017-01-18 12:20:23 +08:00
|
|
|
|
2019-02-20 05:31:03 +08:00
|
|
|
if validator.valid_format? && !User.username_exists?(localized_name)
|
2017-07-14 13:15:33 +08:00
|
|
|
group.name = localized_name
|
|
|
|
end
|
2013-06-23 12:44:16 +08:00
|
|
|
|
2014-09-05 06:15:37 +08:00
|
|
|
# the everyone group is special, it can include non-users so there is no
|
|
|
|
# way to have the membership in a table
|
2017-12-19 10:13:58 +08:00
|
|
|
case name
|
|
|
|
when :everyone
|
2019-03-06 14:44:08 +08:00
|
|
|
group.visibility_level = Group.visibility_levels[:staff]
|
2014-09-05 06:15:37 +08:00
|
|
|
group.save!
|
|
|
|
return group
|
2017-12-19 10:13:58 +08:00
|
|
|
when :moderators
|
|
|
|
group.update!(messageable_level: ALIAS_LEVELS[:everyone])
|
2014-09-05 06:15:37 +08:00
|
|
|
end
|
|
|
|
|
2019-07-09 03:09:50 +08:00
|
|
|
group.update!(visibility_level: Group.visibility_levels[:logged_on_users]) if group.visibility_level == Group.visibility_levels[:public]
|
|
|
|
|
2014-08-12 01:30:51 +08:00
|
|
|
# Remove people from groups they don't belong in.
|
2017-04-25 04:48:32 +08:00
|
|
|
remove_subquery =
|
|
|
|
case name
|
|
|
|
when :admins
|
2019-06-17 13:10:47 +08:00
|
|
|
"SELECT id FROM users WHERE id <= 0 OR NOT admin OR staged"
|
2017-04-25 04:48:32 +08:00
|
|
|
when :moderators
|
2019-06-17 13:10:47 +08:00
|
|
|
"SELECT id FROM users WHERE id <= 0 OR NOT moderator OR staged"
|
2017-04-25 04:48:32 +08:00
|
|
|
when :staff
|
2019-06-17 13:10:47 +08:00
|
|
|
"SELECT id FROM users WHERE id <= 0 OR (NOT admin AND NOT moderator) OR staged"
|
2017-04-25 04:48:32 +08:00
|
|
|
when :trust_level_0, :trust_level_1, :trust_level_2, :trust_level_3, :trust_level_4
|
2019-06-17 13:10:47 +08:00
|
|
|
"SELECT id FROM users WHERE id <= 0 OR trust_level < #{id - 10} OR staged"
|
2017-04-25 04:48:32 +08:00
|
|
|
end
|
|
|
|
|
2018-06-19 14:13:14 +08:00
|
|
|
DB.exec <<-SQL
|
2017-04-25 04:48:32 +08:00
|
|
|
DELETE FROM group_users
|
|
|
|
USING (#{remove_subquery}) X
|
|
|
|
WHERE group_id = #{group.id}
|
|
|
|
AND user_id = X.id
|
|
|
|
SQL
|
2014-08-12 01:30:51 +08:00
|
|
|
|
|
|
|
# Add people to groups
|
2017-04-25 04:48:32 +08:00
|
|
|
insert_subquery =
|
|
|
|
case name
|
|
|
|
when :admins
|
2019-06-17 13:10:47 +08:00
|
|
|
"SELECT id FROM users WHERE id > 0 AND admin AND NOT staged"
|
2017-04-25 04:48:32 +08:00
|
|
|
when :moderators
|
2019-06-17 13:10:47 +08:00
|
|
|
"SELECT id FROM users WHERE id > 0 AND moderator AND NOT staged"
|
2017-04-25 04:48:32 +08:00
|
|
|
when :staff
|
2019-06-17 13:10:47 +08:00
|
|
|
"SELECT id FROM users WHERE id > 0 AND (moderator OR admin) AND NOT staged"
|
2017-04-25 04:48:32 +08:00
|
|
|
when :trust_level_1, :trust_level_2, :trust_level_3, :trust_level_4
|
2019-06-17 13:10:47 +08:00
|
|
|
"SELECT id FROM users WHERE id > 0 AND trust_level >= #{id - 10} AND NOT staged"
|
2017-04-25 04:48:32 +08:00
|
|
|
when :trust_level_0
|
2019-06-17 13:10:47 +08:00
|
|
|
"SELECT id FROM users WHERE id > 0 AND NOT staged"
|
2017-04-25 04:48:32 +08:00
|
|
|
end
|
|
|
|
|
2018-06-19 14:13:14 +08:00
|
|
|
DB.exec <<-SQL
|
2017-04-25 04:48:32 +08:00
|
|
|
INSERT INTO group_users (group_id, user_id, created_at, updated_at)
|
|
|
|
SELECT #{group.id}, X.id, now(), now()
|
|
|
|
FROM group_users
|
|
|
|
RIGHT JOIN (#{insert_subquery}) X ON X.id = user_id AND group_id = #{group.id}
|
|
|
|
WHERE user_id IS NULL
|
|
|
|
SQL
|
2013-05-06 12:49:56 +08:00
|
|
|
|
|
|
|
group.save!
|
2013-05-08 13:20:38 +08:00
|
|
|
|
|
|
|
# we want to ensure consistency
|
|
|
|
Group.reset_counters(group.id, :group_users)
|
2013-05-09 15:37:34 +08:00
|
|
|
|
|
|
|
group
|
2013-05-06 12:49:56 +08:00
|
|
|
end
|
|
|
|
|
2016-04-04 23:03:18 +08:00
|
|
|
def self.ensure_consistency!
|
2016-04-05 05:41:49 +08:00
|
|
|
reset_all_counters!
|
|
|
|
refresh_automatic_groups!
|
2017-09-29 00:34:19 +08:00
|
|
|
refresh_has_messages!
|
2016-04-04 23:03:18 +08:00
|
|
|
end
|
|
|
|
|
2016-04-05 05:41:49 +08:00
|
|
|
def self.reset_all_counters!
|
2018-06-19 14:13:14 +08:00
|
|
|
DB.exec <<-SQL
|
2017-04-25 04:48:32 +08:00
|
|
|
WITH X AS (
|
|
|
|
SELECT group_id
|
|
|
|
, COUNT(user_id) users
|
|
|
|
FROM group_users
|
|
|
|
GROUP BY group_id
|
|
|
|
)
|
|
|
|
UPDATE groups
|
|
|
|
SET user_count = X.users
|
|
|
|
FROM X
|
|
|
|
WHERE id = X.group_id
|
|
|
|
AND user_count <> X.users
|
|
|
|
SQL
|
2016-04-04 23:03:18 +08:00
|
|
|
end
|
|
|
|
|
2013-05-06 12:49:56 +08:00
|
|
|
def self.refresh_automatic_groups!(*args)
|
2017-04-25 04:48:32 +08:00
|
|
|
args = AUTO_GROUPS.keys if args.empty?
|
|
|
|
args.each { |group| refresh_automatic_group!(group) }
|
2013-05-06 12:49:56 +08:00
|
|
|
end
|
|
|
|
|
2017-09-29 00:34:19 +08:00
|
|
|
def self.refresh_has_messages!
|
2018-06-19 14:13:14 +08:00
|
|
|
DB.exec <<-SQL
|
2017-09-29 00:34:19 +08:00
|
|
|
UPDATE groups g SET has_messages = false
|
|
|
|
WHERE NOT EXISTS (SELECT tg.id
|
|
|
|
FROM topic_allowed_groups tg
|
|
|
|
INNER JOIN topics t ON t.id = tg.topic_id
|
|
|
|
WHERE tg.group_id = g.id
|
|
|
|
AND t.deleted_at IS NULL)
|
|
|
|
AND g.has_messages = true
|
|
|
|
SQL
|
|
|
|
end
|
|
|
|
|
2013-11-18 09:53:14 +08:00
|
|
|
def self.ensure_automatic_groups!
|
2015-04-18 19:53:53 +08:00
|
|
|
AUTO_GROUPS.each_key do |name|
|
2013-11-18 09:53:14 +08:00
|
|
|
refresh_automatic_group!(name) unless lookup_group(name)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-05-06 12:49:56 +08:00
|
|
|
def self.[](name)
|
2013-05-17 07:03:30 +08:00
|
|
|
lookup_group(name) || refresh_automatic_group!(name)
|
2013-05-09 15:37:34 +08:00
|
|
|
end
|
2013-05-06 12:49:56 +08:00
|
|
|
|
2017-11-02 10:20:14 +08:00
|
|
|
def self.search_groups(name, groups: nil)
|
2017-11-03 21:39:55 +08:00
|
|
|
(groups || Group).where(
|
|
|
|
"name ILIKE :term_like OR full_name ILIKE :term_like", term_like: "%#{name}%"
|
|
|
|
)
|
2013-12-23 22:46:00 +08:00
|
|
|
end
|
|
|
|
|
2013-05-09 15:37:34 +08:00
|
|
|
def self.lookup_group(name)
|
2013-11-23 02:18:45 +08:00
|
|
|
if id = AUTO_GROUPS[name]
|
2014-05-06 21:41:59 +08:00
|
|
|
Group.find_by(id: id)
|
2013-07-23 08:10:36 +08:00
|
|
|
else
|
2014-05-06 21:41:59 +08:00
|
|
|
unless group = Group.find_by(name: name)
|
2013-11-23 02:18:45 +08:00
|
|
|
raise ArgumentError, "unknown group"
|
2013-07-23 08:10:36 +08:00
|
|
|
end
|
|
|
|
group
|
|
|
|
end
|
2013-05-06 12:49:56 +08:00
|
|
|
end
|
|
|
|
|
2017-07-21 14:12:24 +08:00
|
|
|
def self.lookup_groups(group_ids: [], group_names: [])
|
|
|
|
if group_ids.present?
|
2020-07-13 20:06:49 +08:00
|
|
|
group_ids = group_ids.split(",") if group_ids.is_a?(String)
|
2017-07-21 14:12:24 +08:00
|
|
|
group_ids.map!(&:to_i)
|
|
|
|
groups = Group.where(id: group_ids) if group_ids.present?
|
2014-05-09 16:22:15 +08:00
|
|
|
end
|
|
|
|
|
2017-07-21 14:12:24 +08:00
|
|
|
if group_names.present?
|
2014-05-09 16:22:15 +08:00
|
|
|
group_names = group_names.split(",")
|
2017-07-21 14:12:24 +08:00
|
|
|
groups = (groups || Group).where(name: group_names) if group_names.present?
|
2014-05-09 16:22:15 +08:00
|
|
|
end
|
|
|
|
|
2017-07-21 14:12:24 +08:00
|
|
|
groups || []
|
2014-05-09 16:22:15 +08:00
|
|
|
end
|
|
|
|
|
2014-06-17 08:46:30 +08:00
|
|
|
def self.desired_trust_level_groups(trust_level)
|
|
|
|
trust_group_ids.keep_if do |id|
|
2014-06-17 16:13:07 +08:00
|
|
|
id == AUTO_GROUPS[:trust_level_0] || (trust_level + 10) >= id
|
2014-06-17 08:46:30 +08:00
|
|
|
end
|
|
|
|
end
|
2013-05-06 12:49:56 +08:00
|
|
|
|
|
|
|
def self.user_trust_level_change!(user_id, trust_level)
|
2014-06-17 08:46:30 +08:00
|
|
|
desired = desired_trust_level_groups(trust_level)
|
|
|
|
undesired = trust_group_ids - desired
|
2013-05-06 12:49:56 +08:00
|
|
|
|
2014-06-17 08:46:30 +08:00
|
|
|
GroupUser.where(group_id: undesired, user_id: user_id).delete_all
|
2013-05-06 12:49:56 +08:00
|
|
|
|
2014-06-17 08:46:30 +08:00
|
|
|
desired.each do |id|
|
|
|
|
if group = find_by(id: id)
|
|
|
|
unless GroupUser.where(group_id: id, user_id: user_id).exists?
|
|
|
|
group.group_users.create!(user_id: user_id)
|
|
|
|
end
|
|
|
|
else
|
|
|
|
name = AUTO_GROUP_IDS[trust_level]
|
|
|
|
refresh_automatic_group!(name)
|
|
|
|
end
|
2013-05-06 12:49:56 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-03-27 05:04:55 +08:00
|
|
|
# given something that might be a group name, id, or record, return the group id
|
|
|
|
def self.group_id_from_param(group_param)
|
|
|
|
return group_param.id if group_param.is_a?(Group)
|
|
|
|
return group_param if group_param.is_a?(Integer)
|
|
|
|
|
|
|
|
# subtle, using Group[] ensures the group exists in the DB
|
|
|
|
Group[group_param.to_sym].id
|
|
|
|
end
|
|
|
|
|
2013-04-17 15:08:21 +08:00
|
|
|
def self.builtin
|
|
|
|
Enum.new(:moderators, :admins, :trust_level_1, :trust_level_2)
|
|
|
|
end
|
2013-04-29 14:33:24 +08:00
|
|
|
|
2013-05-09 09:33:56 +08:00
|
|
|
def usernames=(val)
|
|
|
|
current = usernames.split(",")
|
|
|
|
expected = val.split(",")
|
|
|
|
|
|
|
|
additions = expected - current
|
|
|
|
deletions = current - expected
|
|
|
|
|
|
|
|
map = Hash[*User.where(username: additions + deletions)
|
|
|
|
.select('id,username')
|
|
|
|
.map { |u| [u.username, u.id] }.flatten]
|
|
|
|
|
|
|
|
deletions = Set.new(deletions.map { |d| map[d] })
|
|
|
|
|
|
|
|
@deletions = []
|
2014-03-27 15:00:23 +08:00
|
|
|
group_users.each do |gu|
|
2013-05-09 09:33:56 +08:00
|
|
|
@deletions << gu if deletions.include?(gu.user_id)
|
|
|
|
end
|
|
|
|
|
|
|
|
additions.each do |a|
|
|
|
|
group_users.build(user_id: map[a])
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
def usernames
|
2013-05-18 03:11:37 +08:00
|
|
|
users.pluck(:username).join(",")
|
2013-05-09 09:33:56 +08:00
|
|
|
end
|
|
|
|
|
2017-08-16 10:38:30 +08:00
|
|
|
PUBLISH_CATEGORIES_LIMIT = 10
|
|
|
|
|
2019-09-11 00:58:08 +08:00
|
|
|
def add(user, notify: false, automatic: false)
|
2020-05-26 21:28:03 +08:00
|
|
|
return self if self.users.include?(user)
|
|
|
|
|
|
|
|
self.users.push(user)
|
2017-06-02 16:38:14 +08:00
|
|
|
|
2019-08-06 18:29:46 +08:00
|
|
|
if notify
|
|
|
|
Notification.create!(
|
|
|
|
notification_type: Notification.types[:membership_request_accepted],
|
|
|
|
user_id: user.id,
|
2020-05-26 21:28:03 +08:00
|
|
|
data: { group_id: id, group_name: name }.to_json
|
2019-08-06 18:29:46 +08:00
|
|
|
)
|
|
|
|
end
|
|
|
|
|
2017-08-16 10:38:30 +08:00
|
|
|
if self.categories.count < PUBLISH_CATEGORIES_LIMIT
|
|
|
|
MessageBus.publish('/categories', {
|
|
|
|
categories: ActiveModel::ArraySerializer.new(self.categories).as_json
|
|
|
|
}, user_ids: [user.id])
|
|
|
|
else
|
|
|
|
Discourse.request_refresh!(user_ids: [user.id])
|
|
|
|
end
|
2017-06-02 16:38:14 +08:00
|
|
|
|
2019-09-11 00:58:08 +08:00
|
|
|
DiscourseEvent.trigger(:user_added_to_group, user, self, automatic: automatic)
|
|
|
|
|
2016-12-11 23:36:15 +08:00
|
|
|
self
|
2013-04-29 14:33:24 +08:00
|
|
|
end
|
2014-08-18 19:04:08 +08:00
|
|
|
|
2014-11-21 01:29:56 +08:00
|
|
|
def remove(user)
|
2020-03-11 05:25:00 +08:00
|
|
|
result = self.group_users.where(user: user).each(&:destroy)
|
|
|
|
return false if result.blank?
|
2015-02-09 13:03:09 +08:00
|
|
|
user.update_columns(primary_group_id: nil) if user.primary_group_id == self.id
|
2019-09-11 00:58:08 +08:00
|
|
|
DiscourseEvent.trigger(:user_removed_from_group, user, self)
|
2020-03-11 05:25:00 +08:00
|
|
|
true
|
2014-11-21 01:29:56 +08:00
|
|
|
end
|
|
|
|
|
2015-11-09 21:52:04 +08:00
|
|
|
def add_owner(user)
|
2016-11-29 16:25:02 +08:00
|
|
|
if group_user = self.group_users.find_by(user: user)
|
2017-07-27 14:39:47 +08:00
|
|
|
group_user.update!(owner: true) if !group_user.owner
|
2016-11-29 16:25:02 +08:00
|
|
|
else
|
2017-07-27 14:39:47 +08:00
|
|
|
self.group_users.create!(user: user, owner: true)
|
2016-11-29 16:25:02 +08:00
|
|
|
end
|
2015-01-09 07:35:52 +08:00
|
|
|
end
|
|
|
|
|
2015-12-08 00:01:08 +08:00
|
|
|
def self.find_by_email(email)
|
2016-03-09 03:52:04 +08:00
|
|
|
self.where("string_to_array(incoming_email, '|') @> ARRAY[?]", Email.downcase(email)).first
|
2015-12-08 00:01:08 +08:00
|
|
|
end
|
|
|
|
|
2016-02-19 03:03:00 +08:00
|
|
|
def bulk_add(user_ids)
|
2017-07-27 14:39:47 +08:00
|
|
|
return unless user_ids.present?
|
|
|
|
|
|
|
|
Group.transaction do
|
2017-07-26 16:10:13 +08:00
|
|
|
sql = <<~SQL
|
|
|
|
INSERT INTO group_users
|
|
|
|
(group_id, user_id, created_at, updated_at)
|
|
|
|
SELECT
|
|
|
|
#{self.id},
|
|
|
|
u.id,
|
|
|
|
CURRENT_TIMESTAMP,
|
|
|
|
CURRENT_TIMESTAMP
|
|
|
|
FROM users AS u
|
|
|
|
WHERE u.id IN (:user_ids)
|
|
|
|
AND NOT EXISTS (
|
|
|
|
SELECT 1 FROM group_users AS gu
|
|
|
|
WHERE gu.user_id = u.id AND
|
|
|
|
gu.group_id = :group_id
|
|
|
|
)
|
|
|
|
SQL
|
|
|
|
|
2018-06-19 14:13:14 +08:00
|
|
|
DB.exec(sql, group_id: self.id, user_ids: user_ids)
|
2016-02-19 03:03:00 +08:00
|
|
|
|
2017-07-26 16:22:21 +08:00
|
|
|
user_attributes = {}
|
2017-07-26 16:18:56 +08:00
|
|
|
|
2016-02-19 03:03:00 +08:00
|
|
|
if self.primary_group?
|
2017-07-26 16:22:21 +08:00
|
|
|
user_attributes[:primary_group_id] = self.id
|
2016-02-19 03:03:00 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
if self.title.present?
|
2017-07-26 16:22:21 +08:00
|
|
|
user_attributes[:title] = self.title
|
2017-07-26 16:18:56 +08:00
|
|
|
end
|
|
|
|
|
2017-07-26 16:22:21 +08:00
|
|
|
if user_attributes.present?
|
|
|
|
User.where(id: user_ids).update_all(user_attributes)
|
2016-02-19 03:03:00 +08:00
|
|
|
end
|
2018-02-23 16:39:49 +08:00
|
|
|
|
|
|
|
# update group user count
|
2018-06-19 14:13:14 +08:00
|
|
|
DB.exec <<~SQL
|
2018-02-23 16:39:49 +08:00
|
|
|
UPDATE groups g
|
|
|
|
SET user_count =
|
|
|
|
(SELECT COUNT(gu.user_id)
|
|
|
|
FROM group_users gu
|
|
|
|
WHERE gu.group_id = g.id)
|
|
|
|
WHERE g.id = #{self.id};
|
|
|
|
SQL
|
2017-07-27 14:39:47 +08:00
|
|
|
end
|
2017-03-03 02:16:01 +08:00
|
|
|
|
2017-07-27 14:39:47 +08:00
|
|
|
if self.grant_trust_level.present?
|
|
|
|
Jobs.enqueue(:bulk_grant_trust_level,
|
|
|
|
user_ids: user_ids,
|
|
|
|
trust_level: self.grant_trust_level
|
|
|
|
)
|
2016-02-19 03:03:00 +08:00
|
|
|
end
|
2017-07-27 14:39:47 +08:00
|
|
|
|
|
|
|
self
|
2016-02-19 03:03:00 +08:00
|
|
|
end
|
|
|
|
|
2016-03-24 04:20:24 +08:00
|
|
|
def staff?
|
|
|
|
STAFF_GROUPS.include?(self.name.to_sym)
|
|
|
|
end
|
|
|
|
|
2018-03-20 15:50:46 +08:00
|
|
|
def self.member_of(groups, user)
|
2020-01-15 18:21:58 +08:00
|
|
|
groups
|
|
|
|
.joins("LEFT JOIN group_users gu ON gu.group_id = groups.id ")
|
|
|
|
.where("gu.user_id = ?", user.id)
|
2018-03-20 15:50:46 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
def self.owner_of(groups, user)
|
|
|
|
self.member_of(groups, user).where("gu.owner")
|
|
|
|
end
|
|
|
|
|
2018-05-21 17:29:19 +08:00
|
|
|
%i{
|
|
|
|
group_created
|
|
|
|
group_updated
|
|
|
|
group_destroyed
|
|
|
|
}.each do |event|
|
|
|
|
define_method("trigger_#{event}_event") do
|
|
|
|
DiscourseEvent.trigger(event, self)
|
|
|
|
true
|
|
|
|
end
|
2018-03-27 14:23:35 +08:00
|
|
|
end
|
|
|
|
|
2020-05-25 13:38:47 +08:00
|
|
|
def flair_type
|
|
|
|
return :icon if flair_icon.present?
|
|
|
|
return :image if flair_upload_id.present?
|
|
|
|
end
|
|
|
|
|
|
|
|
def flair_url
|
2021-02-19 04:24:44 +08:00
|
|
|
case flair_type
|
|
|
|
when :icon
|
|
|
|
flair_icon
|
|
|
|
when :image
|
|
|
|
upload_cdn_path(flair_upload.url)
|
|
|
|
else
|
|
|
|
nil
|
|
|
|
end
|
2020-05-25 13:38:47 +08:00
|
|
|
end
|
|
|
|
|
2020-08-14 05:20:23 +08:00
|
|
|
[:muted, :regular, :tracking, :watching, :watching_first_post].each do |level|
|
2020-08-07 00:27:27 +08:00
|
|
|
define_method("#{level}_category_ids=") do |category_ids|
|
|
|
|
@category_notifications ||= {}
|
|
|
|
@category_notifications[level] = category_ids
|
|
|
|
end
|
|
|
|
|
|
|
|
define_method("#{level}_tags=") do |tag_names|
|
|
|
|
@tag_notifications ||= {}
|
|
|
|
@tag_notifications[level] = tag_names
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def set_default_notifications
|
|
|
|
if @category_notifications
|
|
|
|
@category_notifications.each do |level, category_ids|
|
|
|
|
GroupCategoryNotificationDefault.batch_set(self, level, category_ids)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if @tag_notifications
|
|
|
|
@tag_notifications.each do |level, tag_names|
|
|
|
|
GroupTagNotificationDefault.batch_set(self, level, tag_names)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-07-10 17:05:55 +08:00
|
|
|
def imap_mailboxes
|
|
|
|
return [] if self.imap_server.blank? ||
|
|
|
|
self.email_username.blank? ||
|
2020-08-04 12:19:57 +08:00
|
|
|
self.email_password.blank? ||
|
|
|
|
!SiteSetting.enable_imap
|
2020-07-10 17:05:55 +08:00
|
|
|
|
|
|
|
Discourse.cache.fetch("group_imap_mailboxes_#{self.id}", expires_in: 30.minutes) do
|
|
|
|
Rails.logger.info("[IMAP] Refreshing mailboxes list for group #{self.name}")
|
|
|
|
mailboxes = []
|
|
|
|
|
|
|
|
begin
|
2020-08-04 12:19:57 +08:00
|
|
|
imap_provider = Imap::Providers::Detector.init_with_detected_provider(
|
|
|
|
self.imap_config
|
|
|
|
)
|
|
|
|
imap_provider.connect!
|
|
|
|
mailboxes = imap_provider.list_mailboxes
|
|
|
|
imap_provider.disconnect!
|
2020-07-10 17:05:55 +08:00
|
|
|
|
|
|
|
update_columns(imap_last_error: nil)
|
|
|
|
rescue => ex
|
2020-08-04 12:19:57 +08:00
|
|
|
Rails.logger.warn("[IMAP] Mailbox refresh failed for group #{self.name} with error: #{ex}")
|
2020-07-10 17:05:55 +08:00
|
|
|
update_columns(imap_last_error: ex.message)
|
|
|
|
end
|
|
|
|
|
|
|
|
mailboxes
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-08-04 12:19:57 +08:00
|
|
|
def imap_config
|
|
|
|
{
|
|
|
|
server: self.imap_server,
|
|
|
|
port: self.imap_port,
|
|
|
|
ssl: self.imap_ssl,
|
|
|
|
username: self.email_username,
|
|
|
|
password: self.email_password
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2020-10-28 05:01:58 +08:00
|
|
|
def imap_enabled?
|
|
|
|
return false if !SiteSetting.enable_imap
|
|
|
|
imap_config.values.compact.length == imap_config.keys.length
|
|
|
|
end
|
|
|
|
|
2020-07-10 17:05:55 +08:00
|
|
|
def email_username_regex
|
|
|
|
user, domain = email_username.split('@')
|
|
|
|
if user.present? && domain.present?
|
|
|
|
/^#{Regexp.escape(user)}(\+[^@]*)?@#{Regexp.escape(domain)}$/i
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-08-04 23:02:01 +08:00
|
|
|
def notify_added_to_group(user, owner: false)
|
|
|
|
SystemMessage.create_from_system_user(
|
|
|
|
user,
|
2020-09-02 21:24:01 +08:00
|
|
|
owner ? :user_added_to_group_as_owner : :user_added_to_group_as_member,
|
2020-08-04 23:02:01 +08:00
|
|
|
group_name: self.full_name.presence || self.name,
|
2020-09-02 21:24:01 +08:00
|
|
|
group_path: "/g/#{self.name}"
|
2020-08-04 23:02:01 +08:00
|
|
|
)
|
|
|
|
end
|
|
|
|
|
2021-01-22 05:59:34 +08:00
|
|
|
def message_count
|
|
|
|
return 0 unless self.has_messages
|
|
|
|
TopicAllowedGroup.where(group_id: self.id).joins(:topic).count
|
|
|
|
end
|
|
|
|
|
2013-05-09 09:33:56 +08:00
|
|
|
protected
|
|
|
|
|
2014-08-18 19:04:08 +08:00
|
|
|
def name_format_validator
|
2018-08-15 14:59:56 +08:00
|
|
|
return if !name_changed?
|
|
|
|
|
|
|
|
# avoid strip! here, it works now
|
|
|
|
# but may not continue to work long term, especially
|
|
|
|
# once we start returning frozen strings
|
2019-04-23 18:22:47 +08:00
|
|
|
if self.name != (stripped = self.name.unicode_normalize.strip)
|
2018-08-15 14:59:56 +08:00
|
|
|
self.name = stripped
|
|
|
|
end
|
2018-04-02 18:17:06 +08:00
|
|
|
|
2018-04-03 00:44:04 +08:00
|
|
|
UsernameValidator.perform_validation(self, 'name') || begin
|
2019-04-23 18:22:47 +08:00
|
|
|
normalized_name = User.normalize_username(self.name)
|
2018-05-07 14:02:11 +08:00
|
|
|
|
2019-04-23 18:22:47 +08:00
|
|
|
if self.will_save_change_to_name? && User.normalize_username(self.name_was) != normalized_name && User.username_exists?(self.name)
|
2019-02-20 05:31:03 +08:00
|
|
|
errors.add(:name, I18n.t("activerecord.errors.messages.taken"))
|
2018-04-03 00:44:04 +08:00
|
|
|
end
|
2014-08-18 19:04:08 +08:00
|
|
|
end
|
2018-06-07 13:28:18 +08:00
|
|
|
end
|
2013-05-09 15:37:34 +08:00
|
|
|
|
2015-11-27 13:35:16 +08:00
|
|
|
def automatic_membership_email_domains_format_validator
|
|
|
|
return if self.automatic_membership_email_domains.blank?
|
|
|
|
|
2020-04-23 00:37:39 +08:00
|
|
|
domains = Group.get_valid_email_domains(self.automatic_membership_email_domains) do |domain|
|
2020-05-26 13:40:09 +08:00
|
|
|
self.errors.add :base, (I18n.t('groups.errors.invalid_domain', domain: domain))
|
2015-11-27 13:35:16 +08:00
|
|
|
end
|
2020-04-23 00:37:39 +08:00
|
|
|
|
2015-11-27 13:35:16 +08:00
|
|
|
self.automatic_membership_email_domains = domains.join("|")
|
2018-06-07 13:28:18 +08:00
|
|
|
end
|
2015-11-27 13:35:16 +08:00
|
|
|
|
2014-08-18 19:04:08 +08:00
|
|
|
# hack around AR
|
|
|
|
def destroy_deletions
|
|
|
|
if @deletions
|
|
|
|
@deletions.each do |gu|
|
|
|
|
gu.destroy
|
|
|
|
User.where('id = ? AND primary_group_id = ?', gu.user_id, gu.group_id).update_all 'primary_group_id = NULL'
|
2013-05-09 09:33:56 +08:00
|
|
|
end
|
|
|
|
end
|
2014-08-18 19:04:08 +08:00
|
|
|
@deletions = nil
|
2018-06-07 13:28:18 +08:00
|
|
|
end
|
2013-05-09 09:33:56 +08:00
|
|
|
|
2015-01-24 01:25:43 +08:00
|
|
|
def automatic_group_membership
|
2020-04-23 00:37:39 +08:00
|
|
|
if self.automatic_membership_email_domains.present?
|
2015-01-24 01:25:43 +08:00
|
|
|
Jobs.enqueue(:automatic_group_membership, group_id: self.id)
|
|
|
|
end
|
2018-06-07 13:28:18 +08:00
|
|
|
end
|
2015-01-24 01:25:43 +08:00
|
|
|
|
2015-04-10 10:17:28 +08:00
|
|
|
def update_title
|
|
|
|
return if new_record? && !self.title.present?
|
|
|
|
|
2017-08-31 12:06:56 +08:00
|
|
|
if self.saved_change_to_title?
|
2018-06-19 14:13:14 +08:00
|
|
|
sql = <<~SQL
|
|
|
|
UPDATE users
|
|
|
|
SET title = :title
|
|
|
|
WHERE (title = :title_was OR title = '' OR title IS NULL)
|
|
|
|
AND COALESCE(title,'') <> COALESCE(:title,'')
|
|
|
|
AND id IN (SELECT user_id FROM group_users WHERE group_id = :id)
|
|
|
|
SQL
|
|
|
|
|
|
|
|
DB.exec(sql, title: title, title_was: title_before_last_save, id: id)
|
2015-04-10 10:17:28 +08:00
|
|
|
end
|
2018-06-07 13:28:18 +08:00
|
|
|
end
|
2015-04-10 10:17:28 +08:00
|
|
|
|
|
|
|
def update_primary_group
|
|
|
|
return if new_record? && !self.primary_group?
|
|
|
|
|
2017-08-31 12:06:56 +08:00
|
|
|
if self.saved_change_to_primary_group?
|
2017-07-04 03:26:46 +08:00
|
|
|
sql = <<~SQL
|
2018-06-19 14:13:14 +08:00
|
|
|
UPDATE users
|
|
|
|
/*set*/
|
|
|
|
/*where*/
|
|
|
|
SQL
|
|
|
|
|
|
|
|
builder = DB.build(sql)
|
|
|
|
builder.where(<<~SQL, id: id)
|
|
|
|
id IN (
|
|
|
|
SELECT user_id
|
|
|
|
FROM group_users
|
|
|
|
WHERE group_id = :id
|
|
|
|
)
|
|
|
|
SQL
|
2015-04-10 10:17:28 +08:00
|
|
|
|
|
|
|
if primary_group
|
|
|
|
builder.set("primary_group_id = :id")
|
2018-06-07 13:28:18 +08:00
|
|
|
else
|
2015-04-10 10:17:28 +08:00
|
|
|
builder.set("primary_group_id = NULL")
|
|
|
|
builder.where("primary_group_id = :id")
|
|
|
|
end
|
2018-06-07 13:28:18 +08:00
|
|
|
|
|
|
|
builder.exec
|
2015-04-10 10:17:28 +08:00
|
|
|
end
|
2018-06-07 13:28:18 +08:00
|
|
|
end
|
2017-01-18 13:39:34 +08:00
|
|
|
|
2020-04-23 00:37:39 +08:00
|
|
|
def self.automatic_membership_users(domains, group_id = nil)
|
|
|
|
pattern = "@(#{domains.gsub('.', '\.')})$"
|
|
|
|
|
|
|
|
users = User.joins(:user_emails).where("user_emails.email ~* ?", pattern).activated.where(staged: false)
|
|
|
|
users = users.where("users.id NOT IN (SELECT user_id FROM group_users WHERE group_users.group_id = ?)", group_id) if group_id.present?
|
|
|
|
|
|
|
|
users
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.get_valid_email_domains(value)
|
|
|
|
valid_domains = []
|
|
|
|
|
|
|
|
value.split("|").each do |domain|
|
|
|
|
domain.sub!(/^https?:\/\//, '')
|
|
|
|
domain.sub!(/\/.*$/, '')
|
|
|
|
|
2020-05-26 13:40:09 +08:00
|
|
|
if domain =~ Group::VALID_DOMAIN_REGEX
|
2020-04-23 00:37:39 +08:00
|
|
|
valid_domains << domain
|
|
|
|
else
|
2020-05-26 13:40:09 +08:00
|
|
|
yield domain if block_given?
|
2020-04-23 00:37:39 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
valid_domains
|
|
|
|
end
|
|
|
|
|
2017-01-18 13:39:34 +08:00
|
|
|
private
|
|
|
|
|
2018-04-06 17:11:00 +08:00
|
|
|
def validate_grant_trust_level
|
|
|
|
unless TrustLevel.valid?(self.grant_trust_level)
|
|
|
|
self.errors.add(:base, I18n.t(
|
|
|
|
'groups.errors.grant_trust_level_not_valid',
|
|
|
|
trust_level: self.grant_trust_level
|
|
|
|
))
|
|
|
|
end
|
2018-06-07 13:28:18 +08:00
|
|
|
end
|
2018-04-06 17:11:00 +08:00
|
|
|
|
2017-06-02 22:15:57 +08:00
|
|
|
def can_allow_membership_requests
|
2017-07-27 14:39:47 +08:00
|
|
|
valid = true
|
|
|
|
|
|
|
|
valid =
|
|
|
|
if self.persisted?
|
2017-06-02 22:15:57 +08:00
|
|
|
self.group_users.where(owner: true).exists?
|
2018-06-07 13:28:18 +08:00
|
|
|
else
|
2017-06-02 22:15:57 +08:00
|
|
|
self.group_users.any?(&:owner)
|
|
|
|
end
|
|
|
|
|
2017-01-18 13:39:34 +08:00
|
|
|
if !valid
|
2017-08-31 12:06:56 +08:00
|
|
|
self.errors.add(:base, I18n.t('groups.errors.cant_allow_membership_requests'))
|
2017-01-18 13:39:34 +08:00
|
|
|
end
|
2018-06-07 13:28:18 +08:00
|
|
|
end
|
|
|
|
|
2017-01-18 13:39:34 +08:00
|
|
|
def enqueue_update_mentions_job
|
|
|
|
Jobs.enqueue(:update_group_mentions,
|
2017-08-31 12:06:56 +08:00
|
|
|
previous_name: self.name_before_last_save,
|
2017-01-18 13:39:34 +08:00
|
|
|
group_id: self.id
|
2018-06-07 13:28:18 +08:00
|
|
|
)
|
|
|
|
end
|
2013-04-17 15:08:21 +08:00
|
|
|
end
|
2013-05-24 10:48:32 +08:00
|
|
|
|
|
|
|
# == Schema Information
|
|
|
|
#
|
|
|
|
# Table name: groups
|
|
|
|
#
|
2015-02-04 12:09:00 +08:00
|
|
|
# id :integer not null, primary key
|
2019-01-12 03:29:56 +08:00
|
|
|
# name :string not null
|
2015-02-04 12:09:00 +08:00
|
|
|
# created_at :datetime not null
|
|
|
|
# updated_at :datetime not null
|
|
|
|
# automatic :boolean default(FALSE), not null
|
|
|
|
# user_count :integer default(0), not null
|
|
|
|
# automatic_membership_email_domains :text
|
2015-09-18 08:41:10 +08:00
|
|
|
# primary_group :boolean default(FALSE), not null
|
2019-01-12 03:29:56 +08:00
|
|
|
# title :string
|
2015-09-02 04:52:05 +08:00
|
|
|
# grant_trust_level :integer
|
2016-01-11 14:30:56 +08:00
|
|
|
# incoming_email :string
|
|
|
|
# has_messages :boolean default(FALSE), not null
|
2016-10-31 17:32:11 +08:00
|
|
|
# flair_bg_color :string
|
|
|
|
# flair_color :string
|
2016-12-07 11:05:18 +08:00
|
|
|
# bio_raw :text
|
|
|
|
# bio_cooked :text
|
2016-12-12 22:59:40 +08:00
|
|
|
# allow_membership_requests :boolean default(FALSE), not null
|
2016-12-13 16:16:26 +08:00
|
|
|
# full_name :string
|
2017-04-21 03:47:25 +08:00
|
|
|
# default_notification_level :integer default(3), not null
|
2017-04-27 02:47:36 +08:00
|
|
|
# visibility_level :integer default(0), not null
|
2017-07-28 10:37:10 +08:00
|
|
|
# public_exit :boolean default(FALSE), not null
|
2017-08-16 22:38:11 +08:00
|
|
|
# public_admission :boolean default(FALSE), not null
|
|
|
|
# membership_request_template :text
|
2017-12-05 23:29:14 +08:00
|
|
|
# messageable_level :integer default(0)
|
|
|
|
# mentionable_level :integer default(0)
|
2021-01-29 07:59:10 +08:00
|
|
|
# publish_read_state :boolean default(FALSE), not null
|
|
|
|
# members_visibility_level :integer default(0), not null
|
|
|
|
# flair_icon :string
|
|
|
|
# flair_upload_id :integer
|
2020-07-10 17:05:55 +08:00
|
|
|
# smtp_server :string
|
|
|
|
# smtp_port :integer
|
|
|
|
# smtp_ssl :boolean
|
|
|
|
# imap_server :string
|
|
|
|
# imap_port :integer
|
|
|
|
# imap_ssl :boolean
|
|
|
|
# imap_mailbox_name :string default(""), not null
|
|
|
|
# imap_uid_validity :integer default(0), not null
|
|
|
|
# imap_last_uid :integer default(0), not null
|
|
|
|
# email_username :string
|
|
|
|
# email_password :string
|
|
|
|
# imap_last_error :text
|
|
|
|
# imap_old_emails :integer
|
|
|
|
# imap_new_emails :integer
|
2021-01-29 07:59:10 +08:00
|
|
|
# allow_unknown_sender_topic_replies :boolean default(FALSE)
|
2013-05-24 10:48:32 +08:00
|
|
|
#
|
|
|
|
# Indexes
|
|
|
|
#
|
2016-01-11 14:30:56 +08:00
|
|
|
# index_groups_on_incoming_email (incoming_email) UNIQUE
|
|
|
|
# index_groups_on_name (name) UNIQUE
|
2013-05-24 10:48:32 +08:00
|
|
|
#
|