From 98f8a17851d55bc9cbdadfc40329c626178b5c0d Mon Sep 17 00:00:00 2001 From: Jean-Philippe Lang Date: Tue, 3 Jan 2017 20:53:57 +0000 Subject: [PATCH] Speeds up Project.allowed_to_condition for users who belong to hundreds of projects. git-svn-id: http://svn.redmine.org/redmine/trunk@16124 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/models/project.rb | 6 ++--- app/models/user.rb | 55 +++++++++++++++++++++++++++++-------------- 2 files changed, 40 insertions(+), 21 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index 3582a2c67..e5eb12c58 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -204,9 +204,9 @@ class Project < ActiveRecord::Base statement_by_role[role] = s end end - user.projects_by_role.each do |role, projects| - if role.allowed_to?(permission) && projects.any? - statement_by_role[role] = "#{Project.table_name}.id IN (#{projects.collect(&:id).join(',')})" + user.project_ids_by_role.each do |role, project_ids| + if role.allowed_to?(permission) && project_ids.any? + statement_by_role[role] = "#{Project.table_name}.id IN (#{project_ids.join(',')})" end end if statement_by_role.empty? diff --git a/app/models/user.rb b/app/models/user.rb index 628b0cfb0..9192032f0 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -163,6 +163,7 @@ class User < Principal def reload(*args) @name = nil @projects_by_role = nil + @project_ids_by_role = nil @membership_by_project_id = nil @notified_projects_ids = nil @notified_projects_ids_changed = false @@ -564,33 +565,51 @@ class User < Principal end # Returns a hash of user's projects grouped by roles + # TODO: No longer used, should be deprecated def projects_by_role return @projects_by_role if @projects_by_role - hash = Hash.new([]) + result = Hash.new([]) + project_ids_by_role.each do |role, ids| + result[role] = Project.where(:id => ids).to_a + end + @projects_by_role = result + end + + # Returns a hash of project ids grouped by roles. + # Includes the projects that the user is a member of and the projects + # that grant custom permissions to the builtin groups. + def project_ids_by_role + return @project_ids_by_role if @project_ids_by_role group_class = anonymous? ? GroupAnonymous : GroupNonMember - members = Member.joins(:project, :principal). + group_id = group_class.pluck(:id).first + + members = Member.joins(:project, :member_roles). where("#{Project.table_name}.status <> 9"). - where("#{Member.table_name}.user_id = ? OR (#{Project.table_name}.is_public = ? AND #{Principal.table_name}.type = ?)", self.id, true, group_class.name). - preload(:project, :roles). - to_a - - members.reject! {|member| member.user_id != id && project_ids.include?(member.project_id)} - members.each do |member| - if member.project - member.roles.each do |role| - hash[role] = [] unless hash.key?(role) - hash[role] << member.project - end - end - end + where("#{Member.table_name}.user_id = ? OR (#{Project.table_name}.is_public = ? AND #{Member.table_name}.user_id = ?)", self.id, true, group_id). + pluck(:user_id, :role_id, :project_id) + + hash = {} + members.each do |user_id, role_id, project_id| + # Ignore the roles of the builtin group if the user is a member of the project + next if user_id != id && project_ids.include?(project_id) - hash.each do |role, projects| - projects.uniq! + hash[role_id] ||= [] + hash[role_id] << project_id end - @projects_by_role = hash + result = Hash.new([]) + if hash.present? + roles = Role.where(:id => hash.keys).to_a + hash.each do |role_id, proj_ids| + role = roles.detect {|r| r.id == role_id} + if role + result[role] = proj_ids.uniq + end + end + end + @project_ids_by_role = result end # Returns the ids of visible projects -- 2.39.5