Use participants from database table instead of parsing all comments

Signed-off-by: NDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
上级 b2cc24ba
......@@ -55,7 +55,7 @@ class Projects::IssuesController < Projects::ApplicationController
end
def show
@participants = @issue.participants(current_user, @project)
@participants = @issue.participants
@note = @project.notes.new(noteable: @issue)
@notes = @issue.notes.inc_author.fresh
@noteable = @issue
......
......@@ -246,7 +246,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
def define_show_vars
@participants = @merge_request.participants(current_user, @project)
@participants = @merge_request.participants
# Build a note object for comment form
@note = @project.notes.new(noteable: @merge_request)
......
......@@ -8,7 +8,6 @@ class Commit
include StaticModel
attr_mentionable :safe_message
participant :author, :committer, :notes, :mentioned_users
attr_accessor :project
......
......@@ -17,7 +17,6 @@ module Issuable
has_many :label_links, as: :target, dependent: :destroy
has_many :labels, through: :label_links
has_many :subscriptions, dependent: :destroy, as: :subscribable
# has_many :participants, dependent: :destroy, as: :target
validates :author, presence: true
validates :title, presence: true, length: { within: 0..255 }
......@@ -47,7 +46,6 @@ module Issuable
prefix: true
attr_mentionable :title, :description
participant :author, :assignee, :notes, :mentioned_users
end
module ClassMethods
......@@ -127,7 +125,7 @@ module Issuable
return subscription.subscribed
end
participants(user).include?(user)
participants.include?(user)
end
def toggle_subscription(user)
......
......@@ -9,10 +9,6 @@
#
# class Issue < ActiveRecord::Base
# include Participable
#
# # ...
#
# participant :author, :assignee, :mentioned_users, :notes
# end
#
# issue = Issue.last
......@@ -23,53 +19,14 @@
# # since Note implements Participable as well.
#
module Participable
extend ActiveSupport::Concern
module ClassMethods
def participant(*attrs)
participant_attrs.concat(attrs.map(&:to_s))
end
def participant_attrs
@participant_attrs ||= []
end
end
# Be aware that this method makes a lot of sql queries.
# Save result into variable if you are going to reuse it inside same request
def participants(current_user = self.author, project = self.project)
participants = self.class.participant_attrs.flat_map do |attr|
meth = method(attr)
value =
if meth.arity == 1 || meth.arity == -1
meth.call(current_user)
else
meth.call
end
participants_for(value, current_user, project)
end.compact.uniq
if project
participants.select! do |user|
user.can?(:read_project, project)
def participants
@participants ||=
begin
user_ids = Participant.
where(target_id: id.to_s, target_type: self.class.name).
pluck(:user_id)
User.where(id: user_ids)
end
end
participants
end
private
def participants_for(value, current_user = nil, project = nil)
case value
when User
[value]
when Enumerable, ActiveRecord::Relation
value.flat_map { |v| participants_for(v, current_user, project) }
when Participable
value.participants(current_user, project)
end
end
end
......@@ -23,12 +23,10 @@ require 'file_size_validator'
class Note < ActiveRecord::Base
include Mentionable
include Gitlab::CurrentSettings
include Participable
default_value_for :system, false
attr_mentionable :note
participant :author, :mentioned_users
belongs_to :project
belongs_to :noteable, polymorphic: true, touch: true
......
......@@ -2,7 +2,8 @@ class Participant < ActiveRecord::Base
belongs_to :user
belongs_to :target, polymorphic: true
validates :target, presence: true
validates :target_id, presence: true
validates :target_type, presence: true
validates :user_id,
uniqueness: { scope: [:target_id, :target_type] },
presence: true
......
......@@ -49,8 +49,6 @@ class Snippet < ActiveRecord::Base
scope :expired, -> { where(["expires_at IS NOT NULL AND expires_at < ?", Time.current]) }
scope :non_expired, -> { where(["expires_at IS NULL OR expires_at > ?", Time.current]) }
participant :author, :notes
def self.reference_prefix
'$'
end
......
......@@ -7,6 +7,7 @@ module Notes
if note.save
notification_service.new_note(note)
update_participants(note)
# Skip system notes, like status changes and cross-references.
unless note.system
......@@ -34,5 +35,25 @@ module Notes
note.project.execute_hooks(note_data, :note_hooks)
note.project.execute_services(note_data, :note_hooks)
end
def update_participants(note)
users = [note.author, note.mentioned_users].flatten
noteable = note.noteable
# Add users as participants only if they have access to project
users.select! do |user|
user.can?(:read_project, note.project)
end
users.each do |user|
if Participant.where(user_id: user, target_id: noteable.id.to_s, target_type: noteable.class.name).blank?
participant = Participant.new
participant.target_id = noteable.id
participant.target_type = noteable.class.name
participant.user = user
participant.save
end
end
end
end
end
......@@ -132,9 +132,9 @@ class NotificationService
recipients = []
# Add all users participating in the thread (author, assignee, comment authors)
participants =
participants =
if target.respond_to?(:participants)
target.participants(note.author)
target.participants
else
note.mentioned_users
end
......@@ -344,7 +344,7 @@ class NotificationService
def reject_unsubscribed_users(recipients, target)
return recipients unless target.respond_to? :subscriptions
recipients.reject do |user|
subscription = target.subscriptions.find_by_user_id(user.id)
subscription && !subscription.subscribed
......@@ -362,7 +362,7 @@ class NotificationService
recipients
end
end
def new_resource_email(target, project, method)
recipients = build_recipients(target, project)
recipients.delete(target.author)
......
......@@ -13,7 +13,7 @@ module Projects
end
def participants_in(type, id)
target =
target =
case type
when "Issue"
project.issues.find_by_iid(id)
......@@ -22,21 +22,21 @@ module Projects
when "Commit"
project.commit(id)
end
return [] unless target
users = target.participants(current_user)
users = target.participants
sorted(users)
end
def sorted(users)
users.uniq.to_a.compact.sort_by(&:username).map do |user|
users.uniq.to_a.compact.sort_by(&:username).map do |user|
{ username: user.username, name: user.name }
end
end
def groups
current_user.authorized_groups.sort_by(&:path).map do |group|
current_user.authorized_groups.sort_by(&:path).map do |group|
count = group.users.count
{ username: group.path, name: group.name, count: count }
end
......
......@@ -21,7 +21,7 @@
- if member
%span.note-role.label
= member.human_access
- if note.system
= link_to user_path(note.author) do
= image_tag avatar_icon(note.author_email), class: 'avatar s16', alt: ''
......
class CreateParticipants < ActiveRecord::Migration
def change
create_table :participants do |t|
t.integer :target_id, null: false
t.string :target_id, null: false
t.string :target_type, null: false
t.integer :user_id, null: false
......
......@@ -331,7 +331,7 @@ ActiveRecord::Schema.define(version: 20150625153454) do
add_index "oauth_applications", ["uid"], name: "index_oauth_applications_on_uid", unique: true, using: :btree
create_table "participants", force: true do |t|
t.integer "target_id", null: false
t.string "target_id", null: false
t.string "target_type", null: false
t.integer "user_id", null: false
t.datetime "created_at"
......@@ -504,12 +504,12 @@ ActiveRecord::Schema.define(version: 20150625153454) do
t.string "bitbucket_access_token"
t.string "bitbucket_access_token_secret"
t.string "location"
t.string "public_email", default: "", null: false
t.string "encrypted_otp_secret"
t.string "encrypted_otp_secret_iv"
t.string "encrypted_otp_secret_salt"
t.boolean "otp_required_for_login", default: false, null: false
t.text "otp_backup_codes"
t.string "public_email", default: "", null: false
t.integer "dashboard", default: 0
end
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册