Read about our upcoming Code of Conduct on this issue

issues_controller.rb 8.08 KB
Newer Older
1
2
# frozen_string_literal: true

3
class Projects::IssuesController < Projects::ApplicationController
Douwe Maan's avatar
Douwe Maan committed
4
  include RendersNotes
5
  include ToggleSubscriptionAction
6
  include IssuableActions
7
  include ToggleAwardEmoji
8
  include IssuableCollections
9
  include IssuesCalendar
10
  include SpammableActions
11
  include RecordUserLastActivity
12

13
  def issue_except_actions
Heinrich Lee Yu's avatar
Heinrich Lee Yu committed
14
    %i[index calendar new create bulk_update import_csv]
15
16
  end

17
  def set_issuables_index_only_actions
18
19
20
    %i[index calendar]
  end

21
22
  prepend_before_action(only: [:index]) { authenticate_sessionless_user!(:rss) }
  prepend_before_action(only: [:calendar]) { authenticate_sessionless_user!(:ics) }
23
  prepend_before_action :authenticate_user!, only: [:new]
24
  prepend_before_action :store_uri, only: [:new, :show]
25

26
  before_action :whitelist_query_limiting, only: [:create, :create_merge_request, :move, :bulk_update]
27
  before_action :check_issues_available!
28
  before_action :issue, unless: ->(c) { c.issue_except_actions.include?(c.action_name.to_sym) }
29

30
  before_action :set_issuables_index, if: ->(c) { c.set_issuables_index_only_actions.include?(c.action_name.to_sym) }
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
31
32

  # Allow write(create) issue
33
  before_action :authorize_create_issue!, only: [:new, :create]
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
34
35

  # Allow modify issue
36
  before_action :authorize_update_issuable!, only: [:edit, :update, :move, :reorder]
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
37

38
  # Allow create a new branch and empty WIP merge request from current issue
39
  before_action :authorize_create_merge_request_from!, only: [:create_merge_request]
40

41
  before_action :authorize_import_issues!, only: [:import_csv]
42
  before_action :authorize_download_code!, only: [:related_branches]
43

44
  respond_to :html
gitlabhq's avatar
gitlabhq committed
45
46

  def index
47
    @issues = @issuables
48

gitlabhq's avatar
gitlabhq committed
49
    respond_to do |format|
50
      format.html
51
      format.atom { render layout: 'xml.atom' }
52
53
      format.json do
        render json: {
54
          html: view_to_html_string("projects/issues/_issues"),
Phil Hughes's avatar
Phil Hughes committed
55
          labels: @labels.as_json(methods: :text_color)
56
57
        }
      end
gitlabhq's avatar
gitlabhq committed
58
59
60
    end
  end

61
  def calendar
62
    render_issues_calendar(@issuables)
63
64
  end

gitlabhq's avatar
gitlabhq committed
65
  def new
66
    params[:issue] ||= ActionController::Parameters.new(
67
      assignee_ids: ""
68
    )
69
    build_params = issue_params.merge(
Bob Van Landuyt's avatar
Bob Van Landuyt committed
70
      merge_request_to_resolve_discussions_of: params[:merge_request_to_resolve_discussions_of],
71
      discussion_to_resolve: params[:discussion_to_resolve]
72
    )
73
    service = Issues::BuildService.new(project, current_user, build_params)
74

75
    @issue = @noteable = service.execute
Bob Van Landuyt's avatar
Bob Van Landuyt committed
76
    @merge_request_to_resolve_discussions_of = service.merge_request_to_resolve_discussions_of
77
    @discussion_to_resolve = service.discussions_to_resolve.first if params[:discussion_to_resolve]
78

gitlabhq's avatar
gitlabhq committed
79
80
81
    respond_with(@issue)
  end

82
83
84
85
  def edit
    respond_with(@issue)
  end

gitlabhq's avatar
gitlabhq committed
86
  def create
87
    create_params = issue_params.merge(spammable_params).merge(
Bob Van Landuyt's avatar
Bob Van Landuyt committed
88
      merge_request_to_resolve_discussions_of: params[:merge_request_to_resolve_discussions_of],
89
90
91
      discussion_to_resolve: params[:discussion_to_resolve]
    )

92
93
94
    service = Issues::CreateService.new(project, current_user, create_params)
    @issue = service.execute

95
    if service.discussions_to_resolve.count(&:resolved?) > 0
96
      flash[:notice] = if service.discussion_to_resolve_id
97
                         _("Resolved 1 discussion.")
98
                       else
99
                         _("Resolved all discussions.")
100
                       end
101
    end
gitlabhq's avatar
gitlabhq committed
102

103
    respond_to do |format|
104
      format.html do
105
        recaptcha_check_with_fallback { render :new }
106
      end
107
      format.js do
108
109
        @link = @issue.attachment.url.to_js
      end
110
    end
gitlabhq's avatar
gitlabhq committed
111
112
  end

113
114
115
  def move
    params.require(:move_to_project_id)

116
117
    if params[:move_to_project_id].to_i > 0
      new_project = Project.find(params[:move_to_project_id])
118
119
      return render_404 unless issue.can_move?(current_user, new_project)

120
      @issue = Issues::UpdateService.new(project, current_user, target_project: new_project).execute(issue)
121
    end
gitlabhq's avatar
gitlabhq committed
122
123

    respond_to do |format|
124
      format.json do
125
        render_issue_json
126
      end
gitlabhq's avatar
gitlabhq committed
127
    end
128
129

  rescue ActiveRecord::StaleObjectError
130
    render_conflict_response
gitlabhq's avatar
gitlabhq committed
131
132
  end

133
134
135
136
137
138
139
140
141
142
  def reorder
    service = Issues::ReorderService.new(project, current_user, reorder_params)

    if service.execute(issue)
      head :ok
    else
      head :unprocessable_entity
    end
  end

143
  def related_branches
144
    @related_branches = Issues::RelatedBranchesService.new(project, current_user).execute(issue)
145
146
147
148
149
150
151
152
153
154

    respond_to do |format|
      format.json do
        render json: {
          html: view_to_html_string('projects/issues/_related_branches')
        }
      end
    end
  end

155
156
157
  def can_create_branch
    can_create = current_user &&
      can?(current_user, :push_code, @project) &&
Heinrich Lee Yu's avatar
Heinrich Lee Yu committed
158
      @issue.can_be_worked_on?
159
160
161

    respond_to do |format|
      format.json do
Heinrich Lee Yu's avatar
Heinrich Lee Yu committed
162
        render json: { can_create_branch: can_create, suggested_branch_name: @issue.suggested_branch_name }
163
164
165
166
      end
    end
  end

167
  def create_merge_request
168
    create_params = params.slice(:branch_name, :ref).merge(issue_iid: issue.iid)
169
    create_params[:target_project_id] = params[:target_project_id] if helpers.create_confidential_merge_request_enabled?
170
    result = ::MergeRequests::CreateFromIssueService.new(project, current_user, create_params).execute
171
172
173
174
175
176
177
178

    if result[:status] == :success
      render json: MergeRequestCreateSerializer.new.represent(result[:merge_request])
    else
      render json: result[:messsage], status: :unprocessable_entity
    end
  end

Heinrich Lee Yu's avatar
Heinrich Lee Yu committed
179
  def import_csv
180
181
    if uploader = UploadService.new(project, params[:file]).execute
      ImportIssuesCsvWorker.perform_async(current_user.id, project.id, uploader.upload.id)
Heinrich Lee Yu's avatar
Heinrich Lee Yu committed
182
183
184
185
186
187
188

      flash[:notice] = _("Your issues are being imported. Once finished, you'll get a confirmation email.")
    else
      flash[:alert] = _("File upload error.")
    end

    redirect_to project_issues_path(project)
Heinrich Lee Yu's avatar
Heinrich Lee Yu committed
189
190
  end

Nihad Abbasov's avatar
Nihad Abbasov committed
191
  protected
gitlabhq's avatar
gitlabhq committed
192

George Koltsov's avatar
George Koltsov committed
193
  def sorting_field
194
195
196
    Issue::SORTING_PREFERENCE_FIELD
  end

197
  # rubocop: disable CodeReuse/ActiveRecord
gitlabhq's avatar
gitlabhq committed
198
  def issue
199
    return @issue if defined?(@issue)
200

201
    # The Sortable default scope causes performance issues when used with find_by
202
    @issuable = @noteable = @issue ||= @project.issues.includes(author: :status).where(iid: params[:id]).reorder(nil).take!
203
    @note = @project.notes.new(noteable: @issuable)
204
205
206
207

    return render_404 unless can?(current_user, :read_issue, @issue)

    @issue
gitlabhq's avatar
gitlabhq committed
208
  end
209
  # rubocop: enable CodeReuse/ActiveRecord
210
  alias_method :subscribable_resource, :issue
211
  alias_method :issuable, :issue
212
  alias_method :awardable, :issue
213
  alias_method :spammable, :issue
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
214

215
216
217
218
  def spammable_path
    project_issue_path(@project, @issue)
  end

219
  def authorize_create_merge_request!
220
    render_404 unless can?(current_user, :push_code, @project) && @issue.can_be_worked_on?
221
222
  end

223
224
225
226
227
228
229
230
  def render_issue_json
    if @issue.valid?
      render json: serializer.represent(@issue)
    else
      render json: { errors: @issue.errors.full_messages }, status: :unprocessable_entity
    end
  end

231
  def issue_params
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
    params.require(:issue).permit(*issue_params_attributes)
  end

  def issue_params_attributes
    %i[
      title
      assignee_id
      position
      description
      confidential
      milestone_id
      due_date
      state_event
      task_num
      lock_version
247
      discussion_locked
248
    ] + [{ label_ids: [], assignee_ids: [], update_task: [:index, :checked, :line_number, :line_source] }]
249
  end
250

251
252
253
254
  def reorder_params
    params.permit(:move_before_id, :move_after_id, :group_full_path)
  end

255
  def store_uri
256
257
258
    if request.get? && !request.xhr?
      store_location_for :user, request.fullpath
    end
259
  end
Douwe Maan's avatar
Douwe Maan committed
260
261
262
263

  def serializer
    IssueSerializer.new(current_user: current_user, project: issue.project)
  end
264
265
266
267
268

  def update_service
    update_params = issue_params.merge(spammable_params)
    Issues::UpdateService.new(project, current_user, update_params)
  end
269

270
271
  def finder_type
    IssuesFinder
272
  end
273
274
275
276
277
278
279
280
281

  def whitelist_query_limiting
    # Also see the following issues:
    #
    # 1. https://gitlab.com/gitlab-org/gitlab-ce/issues/42423
    # 2. https://gitlab.com/gitlab-org/gitlab-ce/issues/42424
    # 3. https://gitlab.com/gitlab-org/gitlab-ce/issues/42426
    Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42422')
  end
gitlabhq's avatar
gitlabhq committed
282
end