issuable_collections.rb 5.67 KB
Newer Older
1 2
# frozen_string_literal: true

3 4
module IssuableCollections
  extend ActiveSupport::Concern
5
  include CookiesHelper
6
  include SortingHelper
7
  include Gitlab::IssuableMetadata
8
  include Gitlab::Utils::StrongMemoize
9 10

  included do
11
    helper_method :finder
12 13 14 15
  end

  private

16
  # rubocop:disable Gitlab/ModuleWithInstanceVariables
17
  def set_issuables_index
18
    @issuables = issuables_collection
19

20
    set_pagination
21
    return if redirect_out_of_range(@total_pages)
22

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

    @users = []
29 30 31 32
    if params[:assignee_id].present?
      assignee = User.find_by_id(params[:assignee_id])
      @users.push(assignee) if assignee
    end
33

34 35 36 37
    if params[:author_id].present?
      author = User.find_by_id(params[:author_id])
      @users.push(author) if author
    end
38
  end
39 40 41 42 43 44 45 46

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

49 50 51 52
  def pagination_disabled?
    false
  end

53
  # rubocop: disable CodeReuse/ActiveRecord
54 55
  def issuables_collection
    finder.execute.preload(preload_for_collection)
56
  end
57
  # rubocop: enable CodeReuse/ActiveRecord
58

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

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

    if out_of_range
65
      redirect_to(url_for(safe_params.merge(page: total_pages, only_path: true)))
66 67 68 69 70
    end

    out_of_range
  end

71
  def issuable_page_count
72
    page_count_for_relation(@issuables, finder.row_count) # rubocop:disable Gitlab/ModuleWithInstanceVariables
73 74 75 76 77 78 79 80 81 82
  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

83
  def issuable_finder_for(finder_class)
84
    finder_class.new(current_user, finder_options)
85 86
  end

87
  # rubocop:disable Gitlab/ModuleWithInstanceVariables
88 89
  def finder_options
    params[:state] = default_state if params[:state].blank?
90

91 92 93
    options = {
      scope: params[:scope],
      state: params[:state],
94
      confidential: Gitlab::Utils.to_boolean(params[:confidential]),
95
      sort: set_sort_order
96
    }
97

98 99
    # Used by view to highlight active option
    @sort = options[:sort]
100 101

    if @project
102
      options[:project_id] = @project.id
103
    elsif @group
104 105
      options[:group_id] = @group.id
      options[:include_subgroups] = true
106
      options[:attempt_group_search_optimizations] = true
107 108
    end

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

113 114
  def default_state
    'opened'
115 116
  end

117 118 119 120 121 122 123 124 125 126 127 128 129
  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]

130 131
    return sort_param if Gitlab::Database.read_only?

132
    if user_preference[issuable_sorting_field] != sort_param
133
      user_preference.update(issuable_sorting_field => sort_param)
134 135 136 137 138
    end

    sort_param
  end

139
  # Implement issuable_sorting_field method on controllers
140 141 142 143 144
  # to choose which column to store the sorting parameter.
  def issuable_sorting_field
    nil
  end

145
  def set_sort_order_from_cookie
146
    sort_param = params[:sort] if params[:sort].present?
147
    # fallback to legacy cookie value for backward compatibility
148 149 150 151 152
    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)
153
    sort_value
154
  end
155

156 157
  def remember_sorting_key
    @remember_sorting_key ||= "#{collection_type.downcase}_sort"
158 159 160 161
  end

  def default_sort_order
    case params[:state]
162
    when 'opened', 'all'    then sort_value_created_date
163
    when 'merged', 'closed' then sort_value_recently_updated
164 165 166 167 168 169 170 171 172 173 174 175
    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
176 177
    end
  end
178 179

  def finder
180
    @finder ||= issuable_finder_for(finder_type)
181 182 183
  end

  def collection_type
184 185
    @collection_type ||= case finder_type.name
                         when 'IssuesFinder'
186
                           'Issue'
187
                         when 'MergeRequestsFinder'
188 189 190 191 192 193 194 195 196 197
                           'MergeRequest'
                         end
  end

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