summaryrefslogtreecommitdiff
path: root/app/controllers/concerns/issuable_collections.rb
blob: 6d6e0cc6c7f65334faf1d04413ef2aad39ac4c81 (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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# frozen_string_literal: true

module IssuableCollections
  extend ActiveSupport::Concern
  include CookiesHelper
  include SortingHelper
  include Gitlab::IssuableMetadata
  include Gitlab::Utils::StrongMemoize

  included do
    helper_method :finder
  end

  private

  # rubocop:disable Gitlab/ModuleWithInstanceVariables
  def set_issuables_index
    @issuables = issuables_collection

    set_pagination
    return if redirect_out_of_range(@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
    return if pagination_disabled?

    @issuables          = @issuables.page(params[:page])
    @issuable_meta_data = issuable_meta_data(@issuables, collection_type)
    @total_pages        = issuable_page_count
  end
  # rubocop:enable Gitlab/ModuleWithInstanceVariables

  def pagination_disabled?
    false
  end

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

  def redirect_out_of_range(total_pages)
    return false if total_pages.nil? || total_pages.zero?

    out_of_range = @issuables.current_page > total_pages # rubocop:disable Gitlab/ModuleWithInstanceVariables

    if out_of_range
      redirect_to(url_for(safe_params.merge(page: total_pages, only_path: true)))
    end

    out_of_range
  end

  def issuable_page_count
    page_count_for_relation(@issuables, finder.row_count) # rubocop:disable Gitlab/ModuleWithInstanceVariables
  end

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

    return 1 if limit.zero?

    (row_count.to_f / limit).ceil
  end

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

  # rubocop:disable Gitlab/ModuleWithInstanceVariables
  def finder_options
    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]

    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
  # rubocop:enable Gitlab/ModuleWithInstanceVariables

  def default_state
    'opened'
  end

  def set_sort_order
    set_sort_order_from_user_preference || set_sort_order_from_cookie || default_sort_order
  end

  def set_sort_order_from_user_preference
    return unless current_user
    return unless issuable_sorting_field

    user_preference = current_user.user_preference

    sort_param = params[:sort]
    sort_param ||= user_preference[issuable_sorting_field]

    return sort_param if Gitlab::Database.read_only?

    if user_preference[issuable_sorting_field] != sort_param
      user_preference.update(issuable_sorting_field => sort_param)
    end

    sort_param
  end

  # Implement issuable_sorting_field method on controllers
  # to choose which column to store the sorting parameter.
  def issuable_sorting_field
    nil
  end

  def set_sort_order_from_cookie
    sort_param = params[:sort] if params[:sort].present?
    # fallback to legacy cookie value for backward compatibility
    sort_param ||= cookies['issuable_sort']
    sort_param ||= cookies[remember_sorting_key]

    sort_value = update_cookie_value(sort_param)
    set_secure_cookie(remember_sorting_key, sort_value)
    sort_value
  end

  def remember_sorting_key
    @remember_sorting_key ||= "#{collection_type.downcase}_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

  # Update old values to the actual ones.
  def update_cookie_value(value)
    case value
    when 'id_asc'             then sort_value_oldest_created
    when 'id_desc'            then sort_value_recently_created
    when 'downvotes_asc'      then sort_value_popularity
    when 'downvotes_desc'     then sort_value_popularity
    else value
    end
  end

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

  def collection_type
    @collection_type ||= case finder_type.name
                         when 'IssuesFinder'
                           'Issue'
                         when 'MergeRequestsFinder'
                           'MergeRequest'
                         end
  end

  def preload_for_collection
    @preload_for_collection ||= case collection_type
                                when 'Issue'
                                  [:project, :author, :assignees, :labels, :milestone, project: :namespace]
                                when 'MergeRequest'
                                  [
                                    :target_project, :author, :assignee, :labels, :milestone,
                                    source_project: :route, head_pipeline: :project, target_project: :namespace, latest_merge_request_diff: :merge_request_diff_commits
                                  ]
                                end
  end
end