]> source.dussan.org Git - redmine.git/commitdiff
Speeds up Project.allowed_to_condition for users who belong to hundreds of projects.
authorJean-Philippe Lang <jp_lang@yahoo.fr>
Tue, 3 Jan 2017 20:53:57 +0000 (20:53 +0000)
committerJean-Philippe Lang <jp_lang@yahoo.fr>
Tue, 3 Jan 2017 20:53:57 +0000 (20:53 +0000)
git-svn-id: http://svn.redmine.org/redmine/trunk@16124 e93f8b46-1217-0410-a6f0-8f06a7374b81

app/models/project.rb
app/models/user.rb

index 3582a2c678e217bac9e912c5ec753249ea2a7a2b..e5eb12c58bf0e67e8f538db9a428f6b6cc54dd79 100644 (file)
@@ -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?
index 628b0cfb07a40ff9b3822fcbff0b019cb62a2f28..9192032f03dbc26e7e45f1709dc135c85417f974 100644 (file)
@@ -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