summaryrefslogtreecommitdiff
path: root/app/finders
diff options
context:
space:
mode:
Diffstat (limited to 'app/finders')
-rw-r--r--app/finders/contributed_projects_finder.rb37
-rw-r--r--app/finders/groups_finder.rb68
-rw-r--r--app/finders/issuable_finder.rb120
-rw-r--r--app/finders/joined_groups_finder.rb49
-rw-r--r--app/finders/milestones_finder.rb12
-rw-r--r--app/finders/notes_finder.rb4
-rw-r--r--app/finders/personal_projects_finder.rb41
-rw-r--r--app/finders/projects_finder.rb121
-rw-r--r--app/finders/trending_projects_finder.rb11
9 files changed, 317 insertions, 146 deletions
diff --git a/app/finders/contributed_projects_finder.rb b/app/finders/contributed_projects_finder.rb
new file mode 100644
index 00000000000..0209649b017
--- /dev/null
+++ b/app/finders/contributed_projects_finder.rb
@@ -0,0 +1,37 @@
+class ContributedProjectsFinder
+ def initialize(user)
+ @user = user
+ end
+
+ # Finds the projects "@user" contributed to, limited to either public projects
+ # or projects visible to the given user.
+ #
+ # current_user - When given the list of the projects is limited to those only
+ # visible by this user.
+ #
+ # Returns an ActiveRecord::Relation.
+ def execute(current_user = nil)
+ if current_user
+ relation = projects_visible_to_user(current_user)
+ else
+ relation = public_projects
+ end
+
+ relation.includes(:namespace).order_id_desc
+ end
+
+ private
+
+ def projects_visible_to_user(current_user)
+ authorized = @user.contributed_projects.visible_to_user(current_user)
+
+ union = Gitlab::SQL::Union.
+ new([authorized.select(:id), public_projects.select(:id)])
+
+ Project.where("projects.id IN (#{union.to_sql})")
+ end
+
+ def public_projects
+ @user.contributed_projects.public_only
+ end
+end
diff --git a/app/finders/groups_finder.rb b/app/finders/groups_finder.rb
index d3597ef0901..91cb0f228f0 100644
--- a/app/finders/groups_finder.rb
+++ b/app/finders/groups_finder.rb
@@ -1,38 +1,44 @@
class GroupsFinder
- def execute(current_user, options = {})
- all_groups(current_user)
+ # Finds the groups available to the given user.
+ #
+ # current_user - The user to find the groups for.
+ #
+ # Returns an ActiveRecord::Relation.
+ def execute(current_user = nil)
+ if current_user
+ relation = groups_visible_to_user(current_user)
+ else
+ relation = public_groups
+ end
+
+ relation.order_id_desc
end
private
- def all_groups(current_user)
- if current_user
- if current_user.authorized_groups.any?
- # User has access to groups
- #
- # Return only:
- # groups with public projects
- # groups with internal projects
- # groups with joined projects
- #
- group_ids = Project.public_and_internal_only.pluck(:namespace_id) +
- current_user.authorized_groups.pluck(:id)
- Group.where(id: group_ids)
- else
- # User has no group membership
- #
- # Return only:
- # groups with public projects
- # groups with internal projects
- #
- Group.where(id: Project.public_and_internal_only.pluck(:namespace_id))
- end
- else
- # Not authenticated
- #
- # Return only:
- # groups with public projects
- Group.where(id: Project.public_only.pluck(:namespace_id))
- end
+ # This method returns the groups "current_user" can see.
+ def groups_visible_to_user(current_user)
+ base = groups_for_projects(public_and_internal_projects)
+
+ union = Gitlab::SQL::Union.
+ new([base.select(:id), current_user.authorized_groups.select(:id)])
+
+ Group.where("namespaces.id IN (#{union.to_sql})")
+ end
+
+ def public_groups
+ groups_for_projects(public_projects)
+ end
+
+ def groups_for_projects(projects)
+ Group.public_and_given_groups(projects.select(:namespace_id))
+ end
+
+ def public_projects
+ Project.unscoped.public_only
+ end
+
+ def public_and_internal_projects
+ Project.unscoped.public_and_internal_only
end
end
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index ab89aa2c53a..3d5e8b6fbe7 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -39,7 +39,7 @@ class IssuableFinder
items = by_assignee(items)
items = by_author(items)
items = by_label(items)
- items = sort(items)
+ sort(items)
end
def group
@@ -53,15 +53,36 @@ class IssuableFinder
end
end
+ def project?
+ params[:project_id].present?
+ end
+
def project
return @project if defined?(@project)
- @project =
- if params[:project_id].present?
- Project.find(params[:project_id])
- else
- nil
+ if project?
+ @project = Project.find(params[:project_id])
+
+ unless Ability.abilities.allowed?(current_user, :read_project, @project)
+ @project = nil
end
+ else
+ @project = nil
+ end
+
+ @project
+ end
+
+ def projects
+ return @projects if defined?(@projects)
+
+ if project?
+ @projects = project
+ elsif current_user && params[:authorized_only].presence && !current_user_related?
+ @projects = current_user.authorized_projects
+ else
+ @projects = ProjectsFinder.new.execute(current_user)
+ end
end
def search
@@ -72,17 +93,31 @@ class IssuableFinder
params[:milestone_title].present?
end
+ def filter_by_no_milestone?
+ milestones? && params[:milestone_title] == Milestone::None.title
+ end
+
def milestones
return @milestones if defined?(@milestones)
@milestones =
- if milestones? && params[:milestone_title] != Milestone::None.title
- Milestone.where(title: params[:milestone_title])
+ if milestones?
+ scope = Milestone.where(project_id: projects)
+
+ scope.where(title: params[:milestone_title])
else
nil
end
end
+ def labels?
+ params[:label_name].present?
+ end
+
+ def filter_by_no_label?
+ labels? && params[:label_name] == Label::None.title
+ end
+
def assignee?
params[:assignee_id].present?
end
@@ -116,19 +151,7 @@ class IssuableFinder
private
def init_collection
- table_name = klass.table_name
-
- if project
- if Ability.abilities.allowed?(current_user, :read_project, project)
- project.send(table_name)
- else
- []
- end
- elsif current_user && params[:authorized_only].presence && !current_user_related?
- klass.of_projects(current_user.authorized_projects).references(:project)
- else
- klass.of_projects(ProjectsFinder.new.execute(current_user)).references(:project)
- end
+ klass.all
end
def by_scope(items)
@@ -166,7 +189,14 @@ class IssuableFinder
end
def by_project(items)
- items = items.of_projects(project.id) if project
+ items =
+ if project?
+ items.of_projects(projects).references_project
+ elsif projects
+ items.merge(projects.reorder(nil)).join_project
+ else
+ items.none
+ end
items
end
@@ -178,15 +208,9 @@ class IssuableFinder
end
def sort(items)
- items.sort(params[:sort])
- end
-
- def by_milestone(items)
- if milestones?
- items = items.where(milestone_id: milestones.try(:pluck, :id))
- end
-
- items
+ # Ensure we always have an explicit sort order (instead of inheriting
+ # multiple orders when combining ActiveRecord::Relation objects).
+ params[:sort] ? items.sort(params[:sort]) : items.reorder(id: :desc)
end
def by_assignee(items)
@@ -205,15 +229,37 @@ class IssuableFinder
items
end
+ def by_milestone(items)
+ if milestones?
+ if filter_by_no_milestone?
+ items = items.where(milestone_id: [-1, nil])
+ else
+ items = items.joins(:milestone).where(milestones: { title: params[:milestone_title] })
+
+ if projects
+ items = items.where(milestones: { project_id: projects })
+ end
+ end
+ end
+
+ items
+ end
+
def by_label(items)
- if params[:label_name].present?
- label_names = params[:label_name].split(",")
+ if labels?
+ if filter_by_no_label?
+ items = items.
+ joins("LEFT OUTER JOIN label_links ON label_links.target_type = '#{klass.name}' AND label_links.target_id = #{klass.table_name}.id").
+ where(label_links: { id: nil })
+ else
+ label_names = params[:label_name].split(",")
- item_ids = LabelLink.joins(:label).
- where('labels.title in (?)', label_names).
- where(target_type: klass.name).pluck(:target_id)
+ items = items.joins(:labels).where(labels: { title: label_names })
- items = items.where(id: item_ids)
+ if projects
+ items = items.where(labels: { project_id: projects })
+ end
+ end
end
items
diff --git a/app/finders/joined_groups_finder.rb b/app/finders/joined_groups_finder.rb
new file mode 100644
index 00000000000..e7523136fea
--- /dev/null
+++ b/app/finders/joined_groups_finder.rb
@@ -0,0 +1,49 @@
+# Class for finding the groups a user is a member of.
+class JoinedGroupsFinder
+ def initialize(user = nil)
+ @user = user
+ end
+
+ # Finds the groups of the source user, optionally limited to those visible to
+ # the current user.
+ #
+ # current_user - If given the groups of "@user" will only include the groups
+ # "current_user" can also see.
+ #
+ # Returns an ActiveRecord::Relation.
+ def execute(current_user = nil)
+ if current_user
+ relation = groups_visible_to_user(current_user)
+ else
+ relation = public_groups
+ end
+
+ relation.order_id_desc
+ end
+
+ private
+
+ # Returns the groups the user in "current_user" can see.
+ #
+ # This list includes all public/internal projects as well as the projects of
+ # "@user" that "current_user" also has access to.
+ def groups_visible_to_user(current_user)
+ base = @user.authorized_groups.visible_to_user(current_user)
+ extra = public_and_internal_groups
+ union = Gitlab::SQL::Union.new([base.select(:id), extra.select(:id)])
+
+ Group.where("namespaces.id IN (#{union.to_sql})")
+ end
+
+ def public_groups
+ groups_for_projects(@user.authorized_projects.public_only)
+ end
+
+ def public_and_internal_groups
+ groups_for_projects(@user.authorized_projects.public_and_internal_only)
+ end
+
+ def groups_for_projects(projects)
+ @user.groups.public_and_given_groups(projects.select(:namespace_id))
+ end
+end
diff --git a/app/finders/milestones_finder.rb b/app/finders/milestones_finder.rb
new file mode 100644
index 00000000000..630c73c2a94
--- /dev/null
+++ b/app/finders/milestones_finder.rb
@@ -0,0 +1,12 @@
+class MilestonesFinder
+ def execute(projects, params)
+ milestones = Milestone.of_projects(projects)
+ milestones = milestones.reorder("due_date ASC")
+
+ case params[:state]
+ when 'closed' then milestones.closed
+ when 'all' then milestones
+ else milestones.active
+ end
+ end
+end
diff --git a/app/finders/notes_finder.rb b/app/finders/notes_finder.rb
index ab252821b52..fa4c635f55c 100644
--- a/app/finders/notes_finder.rb
+++ b/app/finders/notes_finder.rb
@@ -12,9 +12,9 @@ class NotesFinder
when "commit"
project.notes.for_commit_id(target_id).not_inline
when "issue"
- project.issues.find(target_id).notes.inc_author
+ project.issues.find(target_id).notes.nonawards.inc_author
when "merge_request"
- project.merge_requests.find(target_id).mr_and_commit_notes.inc_author
+ project.merge_requests.find(target_id).mr_and_commit_notes.nonawards.inc_author
when "snippet", "project_snippet"
project.snippets.find(target_id).notes
else
diff --git a/app/finders/personal_projects_finder.rb b/app/finders/personal_projects_finder.rb
new file mode 100644
index 00000000000..a61ffa22990
--- /dev/null
+++ b/app/finders/personal_projects_finder.rb
@@ -0,0 +1,41 @@
+class PersonalProjectsFinder
+ def initialize(user)
+ @user = user
+ end
+
+ # Finds the projects belonging to the user in "@user", limited to either
+ # public projects or projects visible to the given user.
+ #
+ # current_user - When given the list of projects is limited to those only
+ # visible by this user.
+ #
+ # Returns an ActiveRecord::Relation.
+ def execute(current_user = nil)
+ if current_user
+ relation = projects_visible_to_user(current_user)
+ else
+ relation = public_projects
+ end
+
+ relation.includes(:namespace).order_id_desc
+ end
+
+ private
+
+ def projects_visible_to_user(current_user)
+ authorized = @user.personal_projects.visible_to_user(current_user)
+
+ union = Gitlab::SQL::Union.
+ new([authorized.select(:id), public_and_internal_projects.select(:id)])
+
+ Project.where("projects.id IN (#{union.to_sql})")
+ end
+
+ def public_projects
+ @user.personal_projects.public_only
+ end
+
+ def public_and_internal_projects
+ @user.personal_projects.public_and_internal_only
+ end
+end
diff --git a/app/finders/projects_finder.rb b/app/finders/projects_finder.rb
index c81bb51583a..3b4e0362e04 100644
--- a/app/finders/projects_finder.rb
+++ b/app/finders/projects_finder.rb
@@ -1,11 +1,39 @@
class ProjectsFinder
- def execute(current_user, options = {})
+ # Returns all projects, optionally including group projects a user has access
+ # to.
+ #
+ # ## Examples
+ #
+ # Retrieving all public projects:
+ #
+ # ProjectsFinder.new.execute
+ #
+ # Retrieving all public/internal projects and those the given user has access
+ # to:
+ #
+ # ProjectsFinder.new.execute(some_user)
+ #
+ # Retrieving all public/internal projects as well as the group's projects the
+ # user has access to:
+ #
+ # ProjectsFinder.new.execute(some_user, group: some_group)
+ #
+ # Returns an ActiveRecord::Relation.
+ def execute(current_user = nil, options = {})
group = options[:group]
if group
- group_projects(current_user, group)
+ segments = group_projects(current_user, group)
else
- all_projects(current_user)
+ segments = all_projects(current_user)
+ end
+
+ if segments.length > 1
+ union = Gitlab::SQL::Union.new(segments.map { |s| s.select(:id) })
+
+ Project.where("projects.id IN (#{union.to_sql})")
+ else
+ segments.first
end
end
@@ -13,77 +41,36 @@ class ProjectsFinder
def group_projects(current_user, group)
if current_user
- if group.users.include?(current_user)
- # User is group member
- #
- # Return ALL group projects
- group.projects
- else
- projects_members = ProjectMember.in_projects(group.projects).
- with_user(current_user)
-
- if projects_members.any?
- # User is a project member
- #
- # Return only:
- # public projects
- # internal projects
- # joined projects
- #
- group.projects.where(
- "projects.id IN (?) OR projects.visibility_level IN (?)",
- projects_members.pluck(:source_id),
- Project.public_and_internal_levels
- )
- else
- # User has no access to group or group projects
- #
- # Return only:
- # public projects
- # internal projects
- #
- group.projects.public_and_internal_only
- end
- end
+ [
+ group_projects_for_user(current_user, group),
+ group.projects.public_and_internal_only
+ ]
else
- # Not authenticated
- #
- # Return only:
- # public projects
- group.projects.public_only
+ [group.projects.public_only]
end
end
def all_projects(current_user)
if current_user
- if current_user.authorized_projects.any?
- # User has access to private projects
- #
- # Return only:
- # public projects
- # internal projects
- # joined projects
- #
- Project.where(
- "projects.id IN (?) OR projects.visibility_level IN (?)",
- current_user.authorized_projects.pluck(:id),
- Project.public_and_internal_levels
- )
- else
- # User has no access to private projects
- #
- # Return only:
- # public projects
- # internal projects
- #
- Project.public_and_internal_only
- end
+ [current_user.authorized_projects, public_and_internal_projects]
else
- # Not authenticated
- #
- # Return only:
- # public projects
- Project.public_only
+ [Project.public_only]
end
end
+
+ def group_projects_for_user(current_user, group)
+ if group.users.include?(current_user)
+ group.projects
+ else
+ group.projects.visible_to_user(current_user)
+ end
+ end
+
+ def public_projects
+ Project.unscoped.public_only
+ end
+
+ def public_and_internal_projects
+ Project.unscoped.public_and_internal_only
+ end
end
diff --git a/app/finders/trending_projects_finder.rb b/app/finders/trending_projects_finder.rb
index 9ea342cb26d..81a12403801 100644
--- a/app/finders/trending_projects_finder.rb
+++ b/app/finders/trending_projects_finder.rb
@@ -1,13 +1,6 @@
class TrendingProjectsFinder
- def execute(current_user, start_date = nil)
- start_date ||= Date.today - 1.month
-
- projects = projects_for(current_user)
-
- # Determine trending projects based on comments count
- # for period of time - ex. month
- projects.joins(:notes).where('notes.created_at > ?', start_date).
- group("projects.id").reorder("count(notes.id) DESC")
+ def execute(current_user, start_date = 1.month.ago)
+ projects_for(current_user).trending(start_date)
end
private