merge_request_presenter.rb 5.74 KB
Newer Older
Fatih Acet's avatar
Fatih Acet committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
class MergeRequestPresenter < Gitlab::View::Presenter::Delegated
  include ActionView::Helpers::UrlHelper
  include GitlabRoutingHelper
  include MarkupHelper
  include TreeHelper

  presents :merge_request

  def ci_status
    if pipeline
      status = pipeline.status
      status = "success_with_warnings" if pipeline.success? && pipeline.has_warnings?

      status || "preparing"
    else
      ci_service = source_project.try(:ci_service)
      ci_service&.commit_status(diff_head_sha, source_branch)
    end
  end

  def cancel_merge_when_pipeline_succeeds_path
    if can_cancel_merge_when_pipeline_succeeds?(current_user)
23
      cancel_merge_when_pipeline_succeeds_project_merge_request_path(project, merge_request)
Fatih Acet's avatar
Fatih Acet committed
24 25 26 27 28
    end
  end

  def create_issue_to_resolve_discussions_path
    if can?(current_user, :create_issue, project) && project.issues_enabled?
29
      new_project_issue_path(project, merge_request_to_resolve_discussions_of: iid)
Fatih Acet's avatar
Fatih Acet committed
30 31 32 33
    end
  end

  def remove_wip_path
34
    if work_in_progress? && can?(current_user, :update_merge_request, merge_request.project)
35
      remove_wip_project_merge_request_path(project, merge_request)
Fatih Acet's avatar
Fatih Acet committed
36 37 38 39 40
    end
  end

  def merge_path
    if can_be_merged_by?(current_user)
41
      merge_project_merge_request_path(project, merge_request)
Fatih Acet's avatar
Fatih Acet committed
42 43 44 45 46 47 48 49 50 51 52
    end
  end

  def revert_in_fork_path
    if user_can_fork_project? && can_be_reverted?(current_user)
      continue_params = {
        to: merge_request_path(merge_request),
        notice: "#{edit_in_new_fork_notice} Try to cherry-pick this commit again.",
        notice_now: edit_in_new_fork_notice_now
      }

53
      project_forks_path(merge_request.project,
Fatih Acet's avatar
Fatih Acet committed
54 55 56 57 58 59 60 61 62 63 64 65 66
                                   namespace_key: current_user.namespace.id,
                                   continue: continue_params)
    end
  end

  def cherry_pick_in_fork_path
    if user_can_fork_project? && can_be_cherry_picked?
      continue_params = {
        to: merge_request_path(merge_request),
        notice: "#{edit_in_new_fork_notice} Try to revert this commit again.",
        notice_now: edit_in_new_fork_notice_now
      }

67
      project_forks_path(project,
Fatih Acet's avatar
Fatih Acet committed
68 69 70 71 72 73
                                   namespace_key: current_user.namespace.id,
                                   continue: continue_params)
    end
  end

  def conflict_resolution_path
74
    if conflicts.can_be_resolved_in_ui? && conflicts.can_be_resolved_by?(current_user)
75
      conflicts_project_merge_request_path(project, merge_request)
Fatih Acet's avatar
Fatih Acet committed
76 77 78
    end
  end

79 80 81 82 83 84
  def rebase_path
    if !rebase_in_progress? && should_be_rebased? && user_can_push_to_source_branch?
      rebase_project_merge_request_path(project, merge_request)
    end
  end

85
  def target_branch_tree_path
Fatih Acet's avatar
Fatih Acet committed
86
    if target_branch_exists?
87
      project_tree_path(project, target_branch)
Fatih Acet's avatar
Fatih Acet committed
88 89 90
    end
  end

91 92 93 94 95 96
  def target_branch_commits_path
    if target_branch_exists?
      project_commits_path(project, target_branch)
    end
  end

Fatih Acet's avatar
Fatih Acet committed
97 98
  def source_branch_path
    if source_branch_exists?
99
      project_branch_path(source_project, source_branch)
Fatih Acet's avatar
Fatih Acet committed
100 101 102 103 104 105 106 107 108
    end
  end

  def source_branch_with_namespace_link
    namespace = source_project_namespace
    branch = source_branch

    if source_branch_exists?
      namespace = link_to(namespace, project_path(source_project))
109
      branch = link_to(branch, project_tree_path(source_project, source_branch))
Fatih Acet's avatar
Fatih Acet committed
110 111 112 113 114 115 116 117 118 119
    end

    if for_fork?
      namespace + ":" + branch
    else
      branch
    end
  end

  def closing_issues_links
120 121 122 123 124 125 126
    markdown(
      issues_sentence(project, closing_issues),
      pipeline: :gfm,
      author: author,
      project: project,
      issuable_state_filter_enabled: true
    )
Fatih Acet's avatar
Fatih Acet committed
127 128 129 130
  end

  def mentioned_issues_links
    mentioned_issues = issues_mentioned_but_not_closing(current_user)
131 132 133 134 135 136 137
    markdown(
      issues_sentence(project, mentioned_issues),
      pipeline: :gfm,
      author: author,
      project: project,
      issuable_state_filter_enabled: true
    )
Fatih Acet's avatar
Fatih Acet committed
138 139 140 141 142 143 144 145
  end

  def assign_to_closing_issues_link
    issues = MergeRequests::AssignIssuesService.new(project,
                                                    current_user,
                                                    merge_request: merge_request,
                                                    closes_issues: closing_issues
                                                   ).assignable_issues
146
    path = assign_related_issues_project_merge_request_path(project, merge_request)
Fatih Acet's avatar
Fatih Acet committed
147 148 149 150 151 152 153 154 155 156 157 158 159 160
    if issues.present?
      pluralize_this_issue = issues.count > 1 ? "these issues" : "this issue"
      link_to "Assign yourself to #{pluralize_this_issue}", path, method: :post
    end
  end

  def can_revert_on_current_merge_request?
    user_can_collaborate_with_project? && can_be_reverted?(current_user)
  end

  def can_cherry_pick_on_current_merge_request?
    user_can_collaborate_with_project? && can_be_cherry_picked?
  end

161 162 163 164
  def can_push_to_source_branch?
    source_branch_exists? && user_can_push_to_source_branch?
  end

Fatih Acet's avatar
Fatih Acet committed
165 166
  private

167 168 169 170
  def conflicts
    @conflicts ||= MergeRequests::Conflicts::ListService.new(merge_request)
  end

Fatih Acet's avatar
Fatih Acet committed
171 172 173 174 175
  def closing_issues
    @closing_issues ||= closes_issues(current_user)
  end

  def pipeline
176
    @pipeline ||= actual_head_pipeline
Fatih Acet's avatar
Fatih Acet committed
177 178 179 180 181 182 183 184 185 186
  end

  def issues_sentence(project, issues)
    # Sorting based on the `#123` or `group/project#123` reference will sort
    # local issues first.
    issues.map do |issue|
      issue.to_reference(project)
    end.sort.to_sentence
  end

187 188 189 190 191 192 193 194
  def user_can_push_to_source_branch?
    return false unless source_branch_exists?

    ::Gitlab::UserAccess
      .new(current_user, project: source_project)
      .can_push_to_branch?(source_branch)
  end

Fatih Acet's avatar
Fatih Acet committed
195 196 197 198 199 200 201 202 203
  def user_can_collaborate_with_project?
    can?(current_user, :push_code, project) ||
      (current_user && current_user.already_forked?(project))
  end

  def user_can_fork_project?
    can?(current_user, :fork_project, project)
  end
end