2017-07-03 15:26:46 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
require_dependency 'enum'
|
|
|
|
|
2013-04-17 03:08:21 -04:00
|
|
|
class Group < ActiveRecord::Base
|
2014-04-28 04:31:51 -04:00
|
|
|
include HasCustomFields
|
2016-12-22 00:46:22 -05:00
|
|
|
include AnonCacheInvalidator
|
2014-04-25 09:14:05 -04:00
|
|
|
|
2014-08-31 16:10:38 -04:00
|
|
|
has_many :category_groups, dependent: :destroy
|
2013-05-08 21:33:56 -04:00
|
|
|
has_many :group_users, dependent: :destroy
|
2015-12-01 00:52:43 -05:00
|
|
|
has_many :group_mentions, dependent: :destroy
|
2013-04-29 02:33:24 -04:00
|
|
|
|
2015-12-22 19:09:17 -05:00
|
|
|
has_many :group_archived_messages, dependent: :destroy
|
|
|
|
|
2013-04-29 02:33:24 -04:00
|
|
|
has_many :categories, through: :category_groups
|
|
|
|
has_many :users, through: :group_users
|
2016-12-11 10:36:15 -05:00
|
|
|
has_many :group_histories, dependent: :destroy
|
2013-04-29 02:33:24 -04:00
|
|
|
|
2016-06-15 13:49:57 -04:00
|
|
|
has_and_belongs_to_many :web_hooks
|
|
|
|
|
2015-12-07 06:39:28 -05:00
|
|
|
before_save :downcase_incoming_email
|
2016-12-05 03:18:24 -05:00
|
|
|
before_save :cook_bio
|
2015-12-07 06:39:28 -05:00
|
|
|
|
2013-05-08 21:33:56 -04:00
|
|
|
after_save :destroy_deletions
|
2015-01-23 12:25:43 -05:00
|
|
|
after_save :automatic_group_membership
|
2015-04-09 22:17:28 -04:00
|
|
|
after_save :update_primary_group
|
|
|
|
after_save :update_title
|
2013-05-08 21:33:56 -04:00
|
|
|
|
2017-01-18 00:39:34 -05:00
|
|
|
after_save :enqueue_update_mentions_job,
|
|
|
|
if: Proc.new { |g| g.name_was && g.name_changed? }
|
|
|
|
|
2015-09-28 02:43:38 -04:00
|
|
|
after_save :expire_cache
|
|
|
|
after_destroy :expire_cache
|
|
|
|
|
|
|
|
def expire_cache
|
|
|
|
ApplicationSerializer.expire_cache_fragment!("group_names")
|
|
|
|
end
|
|
|
|
|
2013-05-09 03:37:34 -04:00
|
|
|
validate :name_format_validator
|
2014-12-16 07:07:15 -05:00
|
|
|
validates_uniqueness_of :name, case_sensitive: false
|
2015-11-27 00:35:16 -05:00
|
|
|
validate :automatic_membership_email_domains_format_validator
|
2015-12-07 06:39:28 -05:00
|
|
|
validate :incoming_email_validator
|
2017-06-02 10:15:57 -04:00
|
|
|
validate :can_allow_membership_requests
|
2016-08-26 17:15:37 -04:00
|
|
|
validates :flair_url, url: true, if: Proc.new { |g| g.flair_url && g.flair_url[0,3] != 'fa-' }
|
2013-05-09 03:37:34 -04:00
|
|
|
|
2013-05-06 00:49:56 -04:00
|
|
|
AUTO_GROUPS = {
|
2013-07-13 21:24:16 -04:00
|
|
|
:everyone => 0,
|
2013-05-06 00:49:56 -04:00
|
|
|
:admins => 1,
|
|
|
|
:moderators => 2,
|
|
|
|
:staff => 3,
|
2014-06-17 04:13:07 -04:00
|
|
|
:trust_level_0 => 10,
|
2013-05-06 00:49:56 -04:00
|
|
|
:trust_level_1 => 11,
|
|
|
|
:trust_level_2 => 12,
|
|
|
|
:trust_level_3 => 13,
|
2014-07-09 22:17:13 -04:00
|
|
|
:trust_level_4 => 14
|
2013-05-06 00:49:56 -04:00
|
|
|
}
|
|
|
|
|
2014-07-09 22:17:13 -04:00
|
|
|
AUTO_GROUP_IDS = Hash[*AUTO_GROUPS.to_a.flatten.reverse]
|
2016-03-23 16:20:24 -04:00
|
|
|
STAFF_GROUPS = [:admins, :moderators, :staff]
|
2014-06-16 20:46:30 -04:00
|
|
|
|
2014-01-07 11:47:01 -05:00
|
|
|
ALIAS_LEVELS = {
|
|
|
|
:nobody => 0,
|
|
|
|
:only_admins => 1,
|
|
|
|
:mods_and_admins => 2,
|
|
|
|
:members_mods_and_admins => 3,
|
|
|
|
:everyone => 99
|
|
|
|
}
|
|
|
|
|
2017-07-03 15:26:46 -04:00
|
|
|
def self.visibility_levels
|
|
|
|
@visibility_levels = Enum.new(
|
|
|
|
public: 0,
|
|
|
|
members: 1,
|
|
|
|
staff: 2,
|
|
|
|
owners: 3
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
2014-08-18 00:40:54 -04:00
|
|
|
validates :alias_level, inclusion: { in: ALIAS_LEVELS.values}
|
2014-01-07 11:47:01 -05:00
|
|
|
|
2017-02-03 03:51:32 -05:00
|
|
|
scope :visible_groups, ->(user) {
|
|
|
|
groups = Group.order(name: :asc).where("groups.id > 0")
|
|
|
|
|
2017-07-03 15:26:46 -04:00
|
|
|
unless user&.admin
|
|
|
|
sql = <<~SQL
|
|
|
|
groups.id IN (
|
|
|
|
SELECT g.id FROM groups g WHERE g.visibility_level = :public
|
|
|
|
|
|
|
|
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
|
|
|
|
LEFT JOIN group_users gu ON gu.group_id = g.id AND
|
|
|
|
gu.user_id = :user_id AND
|
|
|
|
gu.owner
|
|
|
|
WHERE g.visibility_level = :staff AND (gu.id IS NOT NULL OR :is_staff)
|
|
|
|
|
|
|
|
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 = :owners
|
|
|
|
|
|
|
|
)
|
|
|
|
SQL
|
|
|
|
|
|
|
|
groups = groups.where(
|
|
|
|
sql,
|
|
|
|
Group.visibility_levels.to_h.merge(user_id: user&.id, is_staff: !!user&.staff?)
|
|
|
|
)
|
2017-02-03 03:51:32 -05:00
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
groups
|
|
|
|
}
|
|
|
|
|
2015-11-30 01:03:47 -05:00
|
|
|
scope :mentionable, lambda {|user|
|
|
|
|
|
|
|
|
levels = [ALIAS_LEVELS[:everyone]]
|
|
|
|
|
|
|
|
if user && user.admin?
|
|
|
|
levels = [ALIAS_LEVELS[:everyone],
|
|
|
|
ALIAS_LEVELS[:only_admins],
|
|
|
|
ALIAS_LEVELS[:mods_and_admins],
|
|
|
|
ALIAS_LEVELS[:members_mods_and_admins]]
|
|
|
|
elsif user && user.moderator?
|
|
|
|
levels = [ALIAS_LEVELS[:everyone],
|
|
|
|
ALIAS_LEVELS[:mods_and_admins],
|
|
|
|
ALIAS_LEVELS[:members_mods_and_admins]]
|
|
|
|
end
|
|
|
|
|
|
|
|
where("alias_level in (:levels) OR
|
|
|
|
(
|
|
|
|
alias_level = #{ALIAS_LEVELS[:members_mods_and_admins]} AND id in (
|
|
|
|
SELECT group_id FROM group_users WHERE user_id = :user_id)
|
|
|
|
)", levels: levels, user_id: user && user.id )
|
|
|
|
}
|
|
|
|
|
2015-12-07 06:39:28 -05:00
|
|
|
def downcase_incoming_email
|
|
|
|
self.incoming_email = (incoming_email || "").strip.downcase.presence
|
|
|
|
end
|
|
|
|
|
2016-12-05 03:18:24 -05:00
|
|
|
def cook_bio
|
|
|
|
if !self.bio_raw.blank?
|
|
|
|
self.bio_cooked = PrettyText.cook(self.bio_raw)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-12-07 06:39:28 -05:00
|
|
|
def incoming_email_validator
|
|
|
|
return if self.automatic || self.incoming_email.blank?
|
2016-07-28 11:57:30 -04:00
|
|
|
|
2016-02-24 13:47:58 -05:00
|
|
|
incoming_email.split("|").each do |email|
|
2016-07-28 11:57:30 -04:00
|
|
|
escaped = Rack::Utils.escape_html(email)
|
2016-03-08 14:52:04 -05:00
|
|
|
if !Email.is_valid?(email)
|
2017-05-05 07:13:49 -04:00
|
|
|
self.errors.add(:base, I18n.t('groups.errors.invalid_incoming_email', email: escaped))
|
2016-03-08 14:52:04 -05:00
|
|
|
elsif group = Group.where.not(id: self.id).find_by_email(email)
|
2016-07-28 11:57:30 -04: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-08 14:52:04 -05:00
|
|
|
elsif category = Category.find_by_email(email)
|
2016-07-28 11:57:30 -04: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-24 13:47:58 -05:00
|
|
|
end
|
2015-12-07 06:39:28 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-02-12 14:00:45 -05:00
|
|
|
def posts_for(guardian, before_post_id=nil)
|
2015-12-07 17:19:33 -05:00
|
|
|
user_ids = group_users.map { |gu| gu.user_id }
|
|
|
|
result = Post.includes(:user, :topic, topic: :category)
|
|
|
|
.references(:posts, :topics, :category)
|
|
|
|
.where(user_id: user_ids)
|
2014-03-13 14:06:22 -04:00
|
|
|
.where('topics.archetype <> ?', Archetype.private_message)
|
|
|
|
.where(post_type: Post.types[:regular])
|
2014-02-07 10:44:03 -05:00
|
|
|
|
2015-02-12 11:52:59 -05:00
|
|
|
result = guardian.filter_allowed_categories(result)
|
2014-02-07 10:44:03 -05:00
|
|
|
result = result.where('posts.id < ?', before_post_id) if before_post_id
|
2015-12-01 00:52:43 -05:00
|
|
|
result.order('posts.created_at desc')
|
|
|
|
end
|
|
|
|
|
2015-12-07 17:19:33 -05:00
|
|
|
def messages_for(guardian, before_post_id=nil)
|
|
|
|
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)
|
|
|
|
|
|
|
|
result = guardian.filter_allowed_categories(result)
|
|
|
|
result = result.where('posts.id < ?', before_post_id) if before_post_id
|
|
|
|
result.order('posts.created_at desc')
|
|
|
|
end
|
|
|
|
|
2015-12-01 00:52:43 -05:00
|
|
|
def mentioned_posts_for(guardian, before_post_id=nil)
|
|
|
|
result = Post.joins(:group_mentions)
|
2015-12-07 17:19:33 -05:00
|
|
|
.includes(:user, :topic, topic: :category)
|
2015-12-01 00:52:43 -05:00
|
|
|
.references(:posts, :topics, :category)
|
|
|
|
.where('topics.archetype <> ?', Archetype.private_message)
|
|
|
|
.where(post_type: Post.types[:regular])
|
2015-12-02 00:16:19 -05:00
|
|
|
.where('group_mentions.group_id = ?', self.id)
|
2015-12-01 00:52:43 -05:00
|
|
|
|
|
|
|
result = guardian.filter_allowed_categories(result)
|
|
|
|
result = result.where('posts.id < ?', before_post_id) if before_post_id
|
2014-02-12 14:00:45 -05:00
|
|
|
result.order('posts.created_at desc')
|
2014-02-07 10:44:03 -05:00
|
|
|
end
|
|
|
|
|
2013-05-06 00:49:56 -04:00
|
|
|
def self.trust_group_ids
|
|
|
|
(10..19).to_a
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.refresh_automatic_group!(name)
|
2015-12-07 17:19:33 -05:00
|
|
|
return unless id = AUTO_GROUPS[name]
|
2013-05-06 00:49:56 -04:00
|
|
|
|
2013-05-09 03:37:34 -04:00
|
|
|
unless group = self.lookup_group(name)
|
|
|
|
group = Group.new(name: name.to_s, automatic: true)
|
2017-04-20 17:41:35 -04:00
|
|
|
group.default_notification_level = 2 if AUTO_GROUPS[:moderators] == id
|
2013-05-06 00:49:56 -04:00
|
|
|
group.id = id
|
|
|
|
group.save!
|
|
|
|
end
|
|
|
|
|
2013-06-23 00:44:16 -04:00
|
|
|
# don't allow shoddy localization to break this
|
2017-04-24 16:48:32 -04:00
|
|
|
localized_name = I18n.t("groups.default_names.#{name}").downcase
|
2016-10-24 04:53:31 -04:00
|
|
|
validator = UsernameValidator.new(localized_name)
|
2017-01-17 23:20:23 -05:00
|
|
|
|
|
|
|
group.name =
|
2017-04-24 16:48:32 -04:00
|
|
|
if !Group.where("LOWER(name) = ?", localized_name).exists? && validator.valid_format?
|
2017-01-17 23:20:23 -05:00
|
|
|
localized_name
|
|
|
|
else
|
|
|
|
name
|
|
|
|
end
|
2013-06-23 00:44:16 -04:00
|
|
|
|
2014-09-04 18:15:37 -04:00
|
|
|
# the everyone group is special, it can include non-users so there is no
|
|
|
|
# way to have the membership in a table
|
|
|
|
if name == :everyone
|
2017-07-03 15:26:46 -04:00
|
|
|
group.visibility_level = Group.visibility_levels[:owners]
|
2014-09-04 18:15:37 -04:00
|
|
|
group.save!
|
|
|
|
return group
|
|
|
|
end
|
|
|
|
|
2014-08-11 13:30:51 -04:00
|
|
|
# Remove people from groups they don't belong in.
|
2017-04-24 16:48:32 -04:00
|
|
|
remove_subquery = case name
|
|
|
|
when :admins
|
|
|
|
"SELECT id FROM users WHERE NOT admin"
|
|
|
|
when :moderators
|
|
|
|
"SELECT id FROM users WHERE NOT moderator"
|
|
|
|
when :staff
|
|
|
|
"SELECT id FROM users WHERE NOT admin AND NOT moderator"
|
|
|
|
when :trust_level_0, :trust_level_1, :trust_level_2, :trust_level_3, :trust_level_4
|
|
|
|
"SELECT id FROM users WHERE trust_level < #{id - 10}"
|
|
|
|
end
|
|
|
|
|
|
|
|
exec_sql <<-SQL
|
|
|
|
DELETE FROM group_users
|
|
|
|
USING (#{remove_subquery}) X
|
|
|
|
WHERE group_id = #{group.id}
|
|
|
|
AND user_id = X.id
|
|
|
|
SQL
|
2014-08-11 13:30:51 -04:00
|
|
|
|
|
|
|
# Add people to groups
|
2017-04-24 16:48:32 -04:00
|
|
|
insert_subquery = case name
|
|
|
|
when :admins
|
|
|
|
"SELECT id FROM users WHERE admin"
|
|
|
|
when :moderators
|
|
|
|
"SELECT id FROM users WHERE moderator"
|
|
|
|
when :staff
|
|
|
|
"SELECT id FROM users WHERE moderator OR admin"
|
|
|
|
when :trust_level_1, :trust_level_2, :trust_level_3, :trust_level_4
|
|
|
|
"SELECT id FROM users WHERE trust_level >= #{id - 10}"
|
|
|
|
when :trust_level_0
|
|
|
|
"SELECT id FROM users"
|
|
|
|
end
|
|
|
|
|
|
|
|
exec_sql <<-SQL
|
|
|
|
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 00:49:56 -04:00
|
|
|
|
|
|
|
group.save!
|
2013-05-08 01:20:38 -04:00
|
|
|
|
|
|
|
# we want to ensure consistency
|
|
|
|
Group.reset_counters(group.id, :group_users)
|
2013-05-09 03:37:34 -04:00
|
|
|
|
|
|
|
group
|
2013-05-06 00:49:56 -04:00
|
|
|
end
|
|
|
|
|
2016-04-04 11:03:18 -04:00
|
|
|
def self.ensure_consistency!
|
2016-04-04 17:41:49 -04:00
|
|
|
reset_all_counters!
|
|
|
|
refresh_automatic_groups!
|
2016-04-04 11:03:18 -04:00
|
|
|
end
|
|
|
|
|
2016-04-04 17:41:49 -04:00
|
|
|
def self.reset_all_counters!
|
2017-04-24 16:48:32 -04:00
|
|
|
exec_sql <<-SQL
|
|
|
|
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 11:03:18 -04:00
|
|
|
end
|
|
|
|
|
2013-05-06 00:49:56 -04:00
|
|
|
def self.refresh_automatic_groups!(*args)
|
2017-04-24 16:48:32 -04:00
|
|
|
args = AUTO_GROUPS.keys if args.empty?
|
|
|
|
args.each { |group| refresh_automatic_group!(group) }
|
2013-05-06 00:49:56 -04:00
|
|
|
end
|
|
|
|
|
2013-11-17 20:53:14 -05:00
|
|
|
def self.ensure_automatic_groups!
|
2015-04-18 07:53:53 -04:00
|
|
|
AUTO_GROUPS.each_key do |name|
|
2013-11-17 20:53:14 -05:00
|
|
|
refresh_automatic_group!(name) unless lookup_group(name)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-05-06 00:49:56 -04:00
|
|
|
def self.[](name)
|
2013-05-16 19:03:30 -04:00
|
|
|
lookup_group(name) || refresh_automatic_group!(name)
|
2013-05-09 03:37:34 -04:00
|
|
|
end
|
2013-05-06 00:49:56 -04:00
|
|
|
|
2015-11-30 01:03:47 -05:00
|
|
|
def self.search_group(name)
|
2017-07-03 15:26:46 -04:00
|
|
|
Group.where(visibility_level: visibility_levels[:public]).where(
|
2017-06-29 03:36:51 -04:00
|
|
|
"name ILIKE :term_like OR full_name ILIKE :term_like", term_like: "#{name}%"
|
|
|
|
)
|
2013-12-23 09:46:00 -05:00
|
|
|
end
|
|
|
|
|
2013-05-09 03:37:34 -04:00
|
|
|
def self.lookup_group(name)
|
2013-11-22 13:18:45 -05:00
|
|
|
if id = AUTO_GROUPS[name]
|
2014-05-06 09:41:59 -04:00
|
|
|
Group.find_by(id: id)
|
2013-07-22 20:10:36 -04:00
|
|
|
else
|
2014-05-06 09:41:59 -04:00
|
|
|
unless group = Group.find_by(name: name)
|
2013-11-22 13:18:45 -05:00
|
|
|
raise ArgumentError, "unknown group"
|
2013-07-22 20:10:36 -04:00
|
|
|
end
|
|
|
|
group
|
|
|
|
end
|
2013-05-06 00:49:56 -04:00
|
|
|
end
|
|
|
|
|
2014-05-09 04:22:15 -04:00
|
|
|
def self.lookup_group_ids(opts)
|
|
|
|
if group_ids = opts[:group_ids]
|
|
|
|
group_ids = group_ids.split(",").map(&:to_i)
|
|
|
|
group_ids = Group.where(id: group_ids).pluck(:id)
|
|
|
|
end
|
|
|
|
|
|
|
|
group_ids ||= []
|
|
|
|
|
|
|
|
if group_names = opts[:group_names]
|
|
|
|
group_names = group_names.split(",")
|
|
|
|
if group_names.present?
|
|
|
|
group_ids += Group.where(name: group_names).pluck(:id)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
group_ids
|
|
|
|
end
|
|
|
|
|
2014-06-16 20:46:30 -04:00
|
|
|
def self.desired_trust_level_groups(trust_level)
|
|
|
|
trust_group_ids.keep_if do |id|
|
2014-06-17 04:13:07 -04:00
|
|
|
id == AUTO_GROUPS[:trust_level_0] || (trust_level + 10) >= id
|
2014-06-16 20:46:30 -04:00
|
|
|
end
|
|
|
|
end
|
2013-05-06 00:49:56 -04:00
|
|
|
|
|
|
|
def self.user_trust_level_change!(user_id, trust_level)
|
2014-06-16 20:46:30 -04:00
|
|
|
desired = desired_trust_level_groups(trust_level)
|
|
|
|
undesired = trust_group_ids - desired
|
2013-05-06 00:49:56 -04:00
|
|
|
|
2014-06-16 20:46:30 -04:00
|
|
|
GroupUser.where(group_id: undesired, user_id: user_id).delete_all
|
2013-05-06 00:49:56 -04:00
|
|
|
|
2014-06-16 20:46:30 -04: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 00:49:56 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-04-17 03:08:21 -04:00
|
|
|
def self.builtin
|
|
|
|
Enum.new(:moderators, :admins, :trust_level_1, :trust_level_2)
|
|
|
|
end
|
2013-04-29 02:33:24 -04:00
|
|
|
|
2013-05-08 21:33:56 -04: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 03:00:23 -04:00
|
|
|
group_users.each do |gu|
|
2013-05-08 21:33:56 -04: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-17 15:11:37 -04:00
|
|
|
users.pluck(:username).join(",")
|
2013-05-08 21:33:56 -04:00
|
|
|
end
|
|
|
|
|
2013-04-29 02:33:24 -04:00
|
|
|
def add(user)
|
2016-12-11 10:36:15 -05:00
|
|
|
self.users.push(user) unless self.users.include?(user)
|
2017-06-02 04:38:14 -04:00
|
|
|
|
|
|
|
MessageBus.publish('/categories', {
|
|
|
|
categories: ActiveModel::ArraySerializer.new(self.categories).as_json
|
|
|
|
}, user_ids: [user.id])
|
|
|
|
|
2016-12-11 10:36:15 -05:00
|
|
|
self
|
2013-04-29 02:33:24 -04:00
|
|
|
end
|
2014-08-18 07:04:08 -04:00
|
|
|
|
2014-11-20 12:29:56 -05:00
|
|
|
def remove(user)
|
|
|
|
self.group_users.where(user: user).each(&:destroy)
|
2015-02-09 00:03:09 -05:00
|
|
|
user.update_columns(primary_group_id: nil) if user.primary_group_id == self.id
|
2014-11-20 12:29:56 -05:00
|
|
|
end
|
|
|
|
|
2015-11-09 08:52:04 -05:00
|
|
|
def add_owner(user)
|
2016-11-29 03:25:02 -05:00
|
|
|
if group_user = self.group_users.find_by(user: user)
|
|
|
|
group_user.update_attributes!(owner: true) if !group_user.owner
|
|
|
|
else
|
|
|
|
GroupUser.create!(user: user, group: self, owner: true)
|
|
|
|
end
|
2015-01-08 18:35:52 -05:00
|
|
|
end
|
|
|
|
|
2015-12-07 11:01:08 -05:00
|
|
|
def self.find_by_email(email)
|
2016-03-08 14:52:04 -05:00
|
|
|
self.where("string_to_array(incoming_email, '|') @> ARRAY[?]", Email.downcase(email)).first
|
2015-12-07 11:01:08 -05:00
|
|
|
end
|
|
|
|
|
2016-02-18 14:03:00 -05:00
|
|
|
def bulk_add(user_ids)
|
|
|
|
if user_ids.present?
|
|
|
|
Group.exec_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.join(', ')})
|
|
|
|
AND NOT EXISTS(SELECT 1 FROM group_users AS gu
|
|
|
|
WHERE gu.user_id = u.id AND
|
|
|
|
gu.group_id = #{self.id})")
|
|
|
|
|
|
|
|
if self.primary_group?
|
|
|
|
User.where(id: user_ids).update_all(primary_group_id: self.id)
|
|
|
|
end
|
|
|
|
|
|
|
|
if self.title.present?
|
|
|
|
User.where(id: user_ids).update_all(title: self.title)
|
|
|
|
end
|
2017-03-02 13:16:01 -05:00
|
|
|
|
|
|
|
if self.grant_trust_level.present?
|
|
|
|
Jobs.enqueue(:bulk_grant_trust_level, user_ids: user_ids, trust_level: self.grant_trust_level)
|
|
|
|
end
|
2016-02-18 14:03:00 -05:00
|
|
|
end
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
2016-03-23 16:20:24 -04:00
|
|
|
def staff?
|
|
|
|
STAFF_GROUPS.include?(self.name.to_sym)
|
|
|
|
end
|
|
|
|
|
2013-05-08 21:33:56 -04:00
|
|
|
protected
|
|
|
|
|
2014-08-18 07:04:08 -04:00
|
|
|
def name_format_validator
|
|
|
|
UsernameValidator.perform_validation(self, 'name')
|
|
|
|
end
|
2013-05-09 03:37:34 -04:00
|
|
|
|
2015-11-27 00:35:16 -05:00
|
|
|
def automatic_membership_email_domains_format_validator
|
|
|
|
return if self.automatic_membership_email_domains.blank?
|
|
|
|
|
|
|
|
domains = self.automatic_membership_email_domains.split("|")
|
|
|
|
domains.each do |domain|
|
|
|
|
domain.sub!(/^https?:\/\//, '')
|
|
|
|
domain.sub!(/\/.*$/, '')
|
2016-04-08 12:11:58 -04:00
|
|
|
self.errors.add :base, (I18n.t('groups.errors.invalid_domain', domain: domain)) unless domain =~ /\A[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,24}(:[0-9]{1,5})?(\/.*)?\Z/i
|
2015-11-27 00:35:16 -05:00
|
|
|
end
|
|
|
|
self.automatic_membership_email_domains = domains.join("|")
|
|
|
|
end
|
|
|
|
|
2014-08-18 07:04:08 -04: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'
|
|
|
|
end
|
2013-05-08 21:33:56 -04:00
|
|
|
end
|
2014-08-18 07:04:08 -04:00
|
|
|
@deletions = nil
|
2013-05-08 21:33:56 -04:00
|
|
|
end
|
|
|
|
|
2015-01-23 12:25:43 -05:00
|
|
|
def automatic_group_membership
|
|
|
|
if self.automatic_membership_retroactive
|
|
|
|
Jobs.enqueue(:automatic_group_membership, group_id: self.id)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-04-09 22:17:28 -04:00
|
|
|
def update_title
|
|
|
|
return if new_record? && !self.title.present?
|
|
|
|
|
|
|
|
if self.title_changed?
|
2017-04-24 16:48:32 -04:00
|
|
|
sql = <<-SQL.squish
|
|
|
|
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
|
|
|
|
|
|
|
|
self.class.exec_sql(sql, title: title, title_was: title_was, id: id)
|
2015-04-09 22:17:28 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def update_primary_group
|
|
|
|
return if new_record? && !self.primary_group?
|
|
|
|
|
|
|
|
if self.primary_group_changed?
|
2017-07-03 15:26:46 -04:00
|
|
|
sql = <<~SQL
|
|
|
|
UPDATE users
|
|
|
|
/*set*/
|
|
|
|
/*where*/
|
|
|
|
SQL
|
2015-04-09 22:17:28 -04:00
|
|
|
|
|
|
|
builder = SqlBuilder.new(sql)
|
|
|
|
builder.where("
|
|
|
|
id IN (
|
|
|
|
SELECT user_id
|
|
|
|
FROM group_users
|
|
|
|
WHERE group_id = :id
|
|
|
|
)", id: id)
|
|
|
|
|
|
|
|
if primary_group
|
|
|
|
builder.set("primary_group_id = :id")
|
|
|
|
else
|
|
|
|
builder.set("primary_group_id = NULL")
|
|
|
|
builder.where("primary_group_id = :id")
|
|
|
|
end
|
|
|
|
|
|
|
|
builder.exec
|
|
|
|
end
|
|
|
|
end
|
2017-01-18 00:39:34 -05:00
|
|
|
|
|
|
|
private
|
|
|
|
|
2017-06-02 10:15:57 -04:00
|
|
|
def can_allow_membership_requests
|
|
|
|
if self.allow_membership_requests && !self.group_users.where(owner: true).exists?
|
|
|
|
self.errors.add(:base, I18n.t('groups.errors.cant_allow_membership_requests'))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-01-18 00:39:34 -05:00
|
|
|
def enqueue_update_mentions_job
|
|
|
|
Jobs.enqueue(:update_group_mentions,
|
|
|
|
previous_name: self.name_was,
|
|
|
|
group_id: self.id
|
|
|
|
)
|
|
|
|
end
|
2013-04-17 03:08:21 -04:00
|
|
|
end
|
2013-05-23 22:48:32 -04:00
|
|
|
|
|
|
|
# == Schema Information
|
|
|
|
#
|
|
|
|
# Table name: groups
|
|
|
|
#
|
2015-02-03 23:09:00 -05:00
|
|
|
# id :integer not null, primary key
|
2016-02-22 18:33:53 -05:00
|
|
|
# name :string not null
|
2015-02-03 23:09:00 -05:00
|
|
|
# created_at :datetime not null
|
|
|
|
# updated_at :datetime not null
|
|
|
|
# automatic :boolean default(FALSE), not null
|
|
|
|
# user_count :integer default(0), not null
|
|
|
|
# alias_level :integer default(0)
|
|
|
|
# automatic_membership_email_domains :text
|
|
|
|
# automatic_membership_retroactive :boolean default(FALSE)
|
2015-09-17 20:41:10 -04:00
|
|
|
# primary_group :boolean default(FALSE), not null
|
2016-02-22 18:33:53 -05:00
|
|
|
# title :string
|
2015-09-01 16:52:05 -04:00
|
|
|
# grant_trust_level :integer
|
2016-01-11 01:30:56 -05:00
|
|
|
# incoming_email :string
|
|
|
|
# has_messages :boolean default(FALSE), not null
|
2016-10-31 05:32:11 -04:00
|
|
|
# flair_url :string
|
|
|
|
# flair_bg_color :string
|
|
|
|
# flair_color :string
|
2016-12-06 22:05:18 -05:00
|
|
|
# bio_raw :text
|
|
|
|
# bio_cooked :text
|
|
|
|
# public :boolean default(FALSE), not null
|
2016-12-12 09:59:40 -05:00
|
|
|
# allow_membership_requests :boolean default(FALSE), not null
|
2016-12-13 03:16:26 -05:00
|
|
|
# full_name :string
|
2017-04-20 15:47:25 -04:00
|
|
|
# default_notification_level :integer default(3), not null
|
2017-07-03 15:26:46 -04:00
|
|
|
# visibility_level :integer default(0)
|
2013-05-23 22:48:32 -04:00
|
|
|
#
|
|
|
|
# Indexes
|
|
|
|
#
|
2016-01-11 01:30:56 -05:00
|
|
|
# index_groups_on_incoming_email (incoming_email) UNIQUE
|
|
|
|
# index_groups_on_name (name) UNIQUE
|
2013-05-23 22:48:32 -04:00
|
|
|
#
|