issuable_actions.rb 5.08 KB
Newer Older
1 2 3 4
module IssuableActions
  extend ActiveSupport::Concern

  included do
5
    before_action :labels, only: [:show, :new, :edit]
6
    before_action :authorize_destroy_issuable!, only: :destroy
7
    before_action :authorize_admin_issuable!, only: :bulk_update
8 9
  end

10 11 12 13 14 15 16 17 18 19 20 21 22
  def permitted_keys
    [
      :issuable_ids,
      :assignee_id,
      :milestone_id,
      :state_event,
      :subscription_event,
      label_ids: [],
      add_label_ids: [],
      remove_label_ids: []
    ]
  end

23 24
  def show
    respond_to do |format|
25
      format.html
26 27 28 29 30 31 32
      format.json do
        render json: serializer.represent(issuable, serializer: params[:serializer])
      end
    end
  end

  def update
33
    @issuable = update_service.execute(issuable) # rubocop:disable Gitlab/ModuleWithInstanceVariables
34 35
    respond_to do |format|
      format.html do
36
        recaptcha_check_if_spammable { render :edit }
37 38 39
      end

      format.json do
40
        recaptcha_check_if_spammable(false) { render_entity_json }
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
      end
    end

  rescue ActiveRecord::StaleObjectError
    render_conflict_response
  end

  def realtime_changes
    Gitlab::PollingInterval.set_header(response, interval: 3_000)

    response = {
      title: view_context.markdown_field(issuable, :title),
      title_text: issuable.title,
      description: view_context.markdown_field(issuable, :description),
      description_text: issuable.description,
      task_status: issuable.task_status
    }

    if issuable.edited?
60
      response[:updated_at] = issuable.last_edited_at.to_time.iso8601
61 62 63 64 65 66 67
      response[:updated_by_name] = issuable.last_edited_by.name
      response[:updated_by_path] = user_path(issuable.last_edited_by)
    end

    render json: response
  end

68
  def destroy
Valery Sizov's avatar
Valery Sizov committed
69
    Issuable::DestroyService.new(issuable.project, current_user).execute(issuable)
70

71
    name = issuable.human_class_name
72
    flash[:notice] = "The #{name} was successfully deleted."
73
    index_path = polymorphic_path([parent, issuable.class])
74 75 76 77 78

    respond_to do |format|
      format.html { redirect_to index_path }
      format.json do
        render json: {
79
          web_url: index_path
80 81 82
        }
      end
    end
83 84
  end

85 86 87 88 89 90 91
  def bulk_update
    result = Issuable::BulkUpdateService.new(project, current_user, bulk_update_params).execute(resource_name)
    quantity = result[:count]

    render json: { notice: "#{quantity} #{resource_name.pluralize(quantity)} updated" }
  end

92
  def discussions
Felipe Artur's avatar
Felipe Artur committed
93
    notes = issuable.discussion_notes
94 95 96 97 98 99 100 101 102
      .inc_relations_for_view
      .includes(:noteable)
      .fresh

    notes = prepare_notes_for_rendering(notes)
    notes = notes.reject { |n| n.cross_reference_not_visible_for?(current_user) }

    discussions = Discussion.build_collection(notes, issuable)

103
    render json: discussion_serializer.represent(discussions, context: self)
104 105
  end

106 107
  private

108 109 110 111
  def discussion_serializer
    DiscussionSerializer.new(project: project, noteable: issuable, current_user: current_user, note_entity: ProjectNoteEntity)
  end

112
  def recaptcha_check_if_spammable(should_redirect = true, &block)
Lin Jen-Shin's avatar
Lin Jen-Shin committed
113
    return yield unless issuable.is_a? Spammable
114 115 116 117

    recaptcha_check_with_fallback(should_redirect, &block)
  end

118 119 120
  def render_conflict_response
    respond_to do |format|
      format.html do
121
        @conflict = true # rubocop:disable Gitlab/ModuleWithInstanceVariables
122 123 124 125 126 127 128 129
        render :edit
      end

      format.json do
        render json: {
          errors: [
            "Someone edited this #{issuable.human_class_name} at the same time you did. Please refresh your browser and make sure your changes will not unintentionally remove theirs."
          ]
Lin Jen-Shin's avatar
Lin Jen-Shin committed
130
        }, status: :conflict
131 132 133 134
      end
    end
  end

135
  def labels
Lin Jen-Shin's avatar
Lin Jen-Shin committed
136
    @labels ||= LabelsFinder.new(current_user, project_id: @project.id).execute # rubocop:disable Gitlab/ModuleWithInstanceVariables
137 138
  end

139
  def authorize_destroy_issuable!
140
    unless can?(current_user, :"destroy_#{issuable.to_ability_name}", issuable)
141 142 143
      return access_denied!
    end
  end
144 145

  def authorize_admin_issuable!
146
    unless can?(current_user, :"admin_#{resource_name}", @project) # rubocop:disable Gitlab/ModuleWithInstanceVariables
147 148 149 150
      return access_denied!
    end
  end

151 152 153 154
  def authorize_update_issuable!
    render_404 unless can?(current_user, :"update_#{resource_name}", issuable)
  end

155
  def bulk_update_params
156
    permitted_keys_array = permitted_keys.dup
157 158

    if resource_name == 'issue'
159
      permitted_keys_array << { assignee_ids: [] }
160
    else
161
      permitted_keys_array.unshift(:assignee_id)
162 163
    end

164
    params.require(:update).permit(permitted_keys_array)
165 166 167 168 169
  end

  def resource_name
    @resource_name ||= controller_name.singularize
  end
170

171
  # rubocop:disable Gitlab/ModuleWithInstanceVariables
172 173 174 175 176 177 178
  def render_entity_json
    if @issuable.valid?
      render json: serializer.represent(@issuable)
    else
      render json: { errors: @issuable.errors.full_messages }, status: :unprocessable_entity
    end
  end
179
  # rubocop:enable Gitlab/ModuleWithInstanceVariables
180 181 182 183 184 185 186 187

  def serializer
    raise NotImplementedError
  end

  def update_service
    raise NotImplementedError
  end
188 189

  def parent
190
    @project || @group # rubocop:disable Gitlab/ModuleWithInstanceVariables
191
  end
192
end