summaryrefslogtreecommitdiff
path: root/app/controllers/concerns/issuable_collections.rb
blob: 5060ce69d9c097b1915d28f99566c50b1191b2d6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# frozen_string_literal: true

module IssuableCollections
  extend ActiveSupport::Concern
  include PaginatedCollection
  include SearchRateLimitable
  include SortingHelper
  include SortingPreference
  include Gitlab::Utils::StrongMemoize

  included do
    helper_method :finder
  end

  private

  def show_alert_if_search_is_disabled
    if current_user || params[:search].blank? || !html_request? || Feature.disabled?(:disable_anonymous_search, type: :ops)
      return
    end

    flash.now[:notice] = _('You must sign in to search for specific terms.')
  end

  # rubocop:disable Gitlab/ModuleWithInstanceVariables
  def set_issuables_index
    show_alert_if_search_is_disabled

    @issuables = issuables_collection
    set_pagination

    return if redirect_out_of_range(@issuables, @total_pages)

    if params[:label_name].present? && @project
      labels_params = { project_id: @project.id, title: params[:label_name] }
      @labels = LabelsFinder.new(current_user, labels_params).execute
    end

    @users = []
    if params[:assignee_id].present?
      assignee = User.find_by_id(params[:assignee_id])
      @users.push(assignee) if assignee
    end

    if params[:author_id].present?
      author = User.find_by_id(params[:author_id])
      @users.push(author) if author
    end
  end

  def set_pagination
    row_count = finder.row_count

    @issuables          = @issuables.page(params[:page])
    @issuables          = per_page_for_relative_position if params[:sort] == 'relative_position'
    @issuables          = @issuables.without_count if row_count == -1
    @issuable_meta_data = Gitlab::IssuableMetadata.new(current_user, @issuables).data
    @total_pages        = page_count_for_relation(@issuables, row_count)
  end
  # rubocop:enable Gitlab/ModuleWithInstanceVariables

  # rubocop: disable CodeReuse/ActiveRecord
  def issuables_collection
    finder.execute.preload(preload_for_collection)
  end
  # rubocop: enable CodeReuse/ActiveRecord

  def page_count_for_relation(relation, row_count)
    limit = relation.limit_value.to_f

    return 1 if limit == 0
    return (params[:page] || 1).to_i + 1 if row_count == -1

    (row_count.to_f / limit).ceil
  end

  # manual / relative_position sorting allows for 100 items on the page
  def per_page_for_relative_position
    @issuables.per(100) # rubocop:disable Gitlab/ModuleWithInstanceVariables
  end

  def issuable_finder_for(finder_class)
    finder_class.new(current_user, finder_options)
  end

  # rubocop:disable Gitlab/ModuleWithInstanceVariables
  def finder_options
    strong_memoize(:finder_options) do
      params[:state] = default_state if params[:state].blank?

      options = {
        scope: params[:scope],
        state: params[:state],
        confidential: Gitlab::Utils.to_boolean(params[:confidential]),
        sort: set_sort_order
      }

      # Used by view to highlight active option
      @sort = options[:sort]

      # When a user looks for an exact iid, we do not filter by search but only by iid
      if params[:search] =~ /^#(?<iid>\d+)\z/
        options[:iids] = Regexp.last_match[:iid]
        params[:search] = nil
      end

      if @project
        options[:project_id] = @project.id
        options[:attempt_project_search_optimizations] = true
      elsif @group
        options[:group_id] = @group.id
        options[:include_subgroups] = true
        options[:attempt_group_search_optimizations] = true
      end

      params.permit(finder_type.valid_params).merge(options)
    end
  end
  # rubocop:enable Gitlab/ModuleWithInstanceVariables

  def default_state
    'opened'
  end

  def legacy_sort_cookie_name
    'issuable_sort'
  end

  def default_sort_order
    case params[:state]
    when 'opened', 'all'    then sort_value_created_date
    when 'merged', 'closed' then sort_value_recently_updated
    else sort_value_created_date
    end
  end

  def finder
    @finder ||= issuable_finder_for(finder_type)
  end

  def collection_type
    @collection_type ||= if finder_type <= IssuesFinder
                           'Issue'
                         elsif finder_type <= MergeRequestsFinder
                           'MergeRequest'
                         end
  end

  # rubocop:disable Gitlab/ModuleWithInstanceVariables
  def preload_for_collection
    common_attributes = [:author, :assignees, :labels, :milestone]
    @preload_for_collection ||= case collection_type
                                when 'Issue'
                                  common_attributes + [:project, project: :namespace]
                                when 'MergeRequest'
                                  common_attributes + [
                                    :target_project, :latest_merge_request_diff, :approvals, :approved_by_users, :reviewers,
                                    source_project: :route, head_pipeline: :project, target_project: :namespace
                                  ]
                                end
  end
  # rubocop:enable Gitlab/ModuleWithInstanceVariables
end

IssuableCollections.prepend_mod_with('IssuableCollections')