issuable_finder.rb 7.17 KB
Newer Older
1
# IssuableFinder
2
3
4
5
6
7
8
9
10
11
12
#
# Used to filter Issues and MergeRequests collections by set of params
#
# Arguments:
#   klass - actual class like Issue or MergeRequest
#   current_user - which user use
#   params:
#     scope: 'created-by-me' or 'assigned-to-me' or 'all'
#     state: 'open' or 'closed' or 'all'
#     group_id: integer
#     project_id: integer
13
#     milestone_title: string
14
15
16
17
18
#     assignee_id: integer
#     search: string
#     label_name: string
#     sort: string
#
19
20
21
require_relative 'projects_finder'

class IssuableFinder
22
  NONE = '0'
23

24
  attr_accessor :current_user, :params
25

26
  def initialize(current_user, params)
27
28
    @current_user = current_user
    @params = params
29
  end
30

31
  def execute
32
33
    items = init_collection
    items = by_scope(items)
34
35
    items = by_state(items)
    items = by_group(items)
Marin Jankovski's avatar
Marin Jankovski committed
36
    items = by_project(items)
37
38
39
    items = by_search(items)
    items = by_milestone(items)
    items = by_assignee(items)
40
    items = by_author(items)
41
    items = by_label(items)
42
    items = by_due_date(items)
43
    sort(items)
44
45
  end

46
47
48
  def group
    return @group if defined?(@group)

49
    @group =
50
51
      if params[:group_id].present?
        Group.find(params[:group_id])
52
      else
53
54
55
56
        nil
      end
  end

57
58
59
60
  def project?
    params[:project_id].present?
  end

61
62
63
  def project
    return @project if defined?(@project)

64
65
    if project?
      @project = Project.find(params[:project_id])
66

http://jneen.net/'s avatar
http://jneen.net/ committed
67
      unless Ability.allowed?(current_user, :read_project, @project)
68
        @project = nil
69
      end
70
71
72
73
74
75
76
77
78
79
    else
      @project = nil
    end

    @project
  end

  def projects
    return @projects if defined?(@projects)

80
    if project?
81
      @projects = project
82
    elsif current_user && params[:authorized_only].presence && !current_user_related?
83
      @projects = current_user.authorized_projects.reorder(nil)
84
85
    elsif group
      @projects = GroupProjectsFinder.new(group).execute(current_user).reorder(nil)
86
    else
87
      @projects = ProjectsFinder.new.execute(current_user).reorder(nil)
88
    end
89
90
91
92
93
94
95
96
97
98
  end

  def search
    params[:search].presence
  end

  def milestones?
    params[:milestone_title].present?
  end

Douwe Maan's avatar
Douwe Maan committed
99
  def filter_by_no_milestone?
100
101
102
    milestones? && params[:milestone_title] == Milestone::None.title
  end

103
104
105
106
  def milestones
    return @milestones if defined?(@milestones)

    @milestones =
107
      if milestones?
108
        scope = Milestone.where(project_id: projects)
109
110

        scope.where(title: params[:milestone_title])
111
      else
112
        Milestone.none
113
114
115
      end
  end

116
117
118
119
  def labels?
    params[:label_name].present?
  end

Douwe Maan's avatar
Douwe Maan committed
120
  def filter_by_no_label?
121
    labels? && params[:label_name].include?(Label::None.title)
122
123
  end

Tap's avatar
Tap committed
124
125
126
127
128
129
130
  def labels
    return @labels if defined?(@labels)

    if labels? && !filter_by_no_label?
      @labels = Label.where(title: label_names)

      if projects
131
        @labels = LabelsFinder.new(current_user, project_ids: projects, title: label_names).execute
Tap's avatar
Tap committed
132
133
134
135
136
137
      end
    else
      @labels = Label.none
    end
  end

138
139
140
141
142
143
144
  def assignee?
    params[:assignee_id].present?
  end

  def assignee
    return @assignee if defined?(@assignee)

145
    @assignee =
146
147
148
149
150
151
152
153
154
155
156
157
158
159
      if assignee? && params[:assignee_id] != NONE
        User.find(params[:assignee_id])
      else
        nil
      end
  end

  def author?
    params[:author_id].present?
  end

  def author
    return @author if defined?(@author)

160
    @author =
161
162
163
164
165
166
167
      if author? && params[:author_id] != NONE
        User.find(params[:author_id])
      else
        nil
      end
  end

168
169
  private

170
  def init_collection
171
    klass.all
172
173
174
  end

  def by_scope(items)
Douwe Maan's avatar
Douwe Maan committed
175
176
    case params[:scope]
    when 'created-by-me', 'authored'
177
      items.where(author_id: current_user.id)
Douwe Maan's avatar
Douwe Maan committed
178
    when 'assigned-to-me'
179
      items.where(assignee_id: current_user.id)
180
    else
Douwe Maan's avatar
Douwe Maan committed
181
      items
182
183
184
185
    end
  end

  def by_state(items)
186
187
188
189
    params[:state] ||= 'all'

    if items.respond_to?(params[:state])
      items.public_send(params[:state])
190
    else
191
      items
192
193
194
195
    end
  end

  def by_group(items)
196
    # Selection by group is already covered by `by_project` and `projects`
197
198
199
200
    items
  end

  def by_project(items)
201
    items =
202
203
204
205
      if project?
        items.of_projects(projects).references_project
      elsif projects
        items.merge(projects.reorder(nil)).join_project
206
207
208
      else
        items.none
      end
209
210
211
212
213

    items
  end

  def by_search(items)
barthc's avatar
barthc committed
214
215
216
217
218
219
220
221
    if search
      items =
        if search =~ iid_pattern
          items.where(iid: $~[:iid])
        else
          items.full_search(search)
        end
    end
222
223
224
225
226

    items
  end

  def sort(items)
227
228
    # Ensure we always have an explicit sort order (instead of inheriting
    # multiple orders when combining ActiveRecord::Relation objects).
229
    params[:sort] ? items.sort(params[:sort], excluded_labels: label_names) : items.reorder(id: :desc)
230
231
232
  end

  def by_assignee(items)
233
234
    if assignee?
      items = items.where(assignee_id: assignee.try(:id))
235
236
237
238
239
    end

    items
  end

240
  def by_author(items)
241
242
    if author?
      items = items.where(author_id: author.try(:id))
243
244
245
246
247
    end

    items
  end

tiagonbotelho's avatar
tiagonbotelho committed
248
  def filter_by_upcoming_milestone?
249
    params[:milestone_title] == Milestone::Upcoming.name
tiagonbotelho's avatar
tiagonbotelho committed
250
251
  end

252
253
  def by_milestone(items)
    if milestones?
Douwe Maan's avatar
Douwe Maan committed
254
      if filter_by_no_milestone?
255
        items = items.left_joins_milestones.where(milestone_id: [-1, nil])
tiagonbotelho's avatar
tiagonbotelho committed
256
      elsif filter_by_upcoming_milestone?
257
        upcoming_ids = Milestone.upcoming_ids_by_projects(projects)
258
        items = items.left_joins_milestones.where(milestone_id: upcoming_ids)
259
      else
260
        items = items.with_milestone(params[:milestone_title])
261
262
263
264
265
266
267
268
269
270

        if projects
          items = items.where(milestones: { project_id: projects })
        end
      end
    end

    items
  end

271
  def by_label(items)
272
    if labels?
Douwe Maan's avatar
Douwe Maan committed
273
      if filter_by_no_label?
274
        items = items.without_label
275
      else
276
        items = items.with_label(label_names, params[:sort])
277

278
        if projects
279
          label_ids = LabelsFinder.new(current_user, project_ids: projects).execute.select(:id)
280
          items = items.where(labels: { id: label_ids })
281
        end
282
      end
283
284
    end

285
    items
286
  end
287

288
289
290
  def by_due_date(items)
    if due_date?
      if filter_by_no_due_date?
291
292
        items = items.without_due_date
      elsif filter_by_overdue?
Rémy Coutable's avatar
Rémy Coutable committed
293
        items = items.due_before(Date.today)
294
      elsif filter_by_due_this_week?
Rémy Coutable's avatar
Rémy Coutable committed
295
        items = items.due_between(Date.today.beginning_of_week, Date.today.end_of_week)
296
      elsif filter_by_due_this_month?
Rémy Coutable's avatar
Rémy Coutable committed
297
        items = items.due_between(Date.today.beginning_of_month, Date.today.end_of_month)
298
299
      end
    end
Rémy Coutable's avatar
Rémy Coutable committed
300

301
302
    items
  end
303

304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
  def filter_by_no_due_date?
    due_date? && params[:due_date] == Issue::NoDueDate.name
  end

  def filter_by_overdue?
    due_date? && params[:due_date] == Issue::Overdue.name
  end

  def filter_by_due_this_week?
    due_date? && params[:due_date] == Issue::DueThisWeek.name
  end

  def filter_by_due_this_month?
    due_date? && params[:due_date] == Issue::DueThisMonth.name
  end

  def due_date?
    params[:due_date].present? && klass.column_names.include?('due_date')
  end

Tap's avatar
Tap committed
324
  def label_names
Thijs Wouters's avatar
Thijs Wouters committed
325
326
327
328
329
    if labels?
      params[:label_name].is_a?(String) ? params[:label_name].split(',') : params[:label_name]
    else
      []
    end
Tap's avatar
Tap committed
330
331
  end

332
333
334
  def current_user_related?
    params[:scope] == 'created-by-me' || params[:scope] == 'authored' || params[:scope] == 'assigned-to-me'
  end
335
end