2018-09-14 05:42:05 +00:00
# frozen_string_literal: true
2016-03-21 13:12:52 +00:00
module IssuableActions
extend ActiveSupport :: Concern
2018-10-23 09:49:45 +00:00
include Gitlab :: Utils :: StrongMemoize
2016-03-21 13:12:52 +00:00
included do
before_action :authorize_destroy_issuable! , only : :destroy
2019-08-29 04:57:54 +00:00
before_action :check_destroy_confirmation! , only : :destroy
2016-09-06 12:49:49 +00:00
before_action :authorize_admin_issuable! , only : :bulk_update
2019-04-02 10:48:20 +00:00
before_action only : :show do
push_frontend_feature_flag ( :scoped_labels , default_enabled : true )
end
2020-05-07 03:09:46 +00:00
before_action do
push_frontend_feature_flag ( :not_issuable_queries , @project , default_enabled : true )
end
2016-03-21 13:12:52 +00:00
end
2017-11-01 17:35:14 +00:00
def show
respond_to do | format |
2018-11-27 16:53:16 +00:00
format . html do
@issuable_sidebar = serializer . represent ( issuable , serializer : 'sidebar' ) # rubocop:disable Gitlab/ModuleWithInstanceVariables
2019-09-16 09:06:25 +00:00
render 'show'
2018-11-27 16:53:16 +00:00
end
2017-11-01 17:35:14 +00:00
format . json do
render json : serializer . represent ( issuable , serializer : params [ :serializer ] )
end
end
end
def update
2017-11-22 07:50:36 +00:00
@issuable = update_service . execute ( issuable ) # rubocop:disable Gitlab/ModuleWithInstanceVariables
2017-11-01 17:35:14 +00:00
respond_to do | format |
format . html do
2017-12-08 18:19:51 +00:00
recaptcha_check_if_spammable { render :edit }
2017-11-01 17:35:14 +00:00
end
format . json do
2017-12-08 18:19:51 +00:00
recaptcha_check_if_spammable ( false ) { render_entity_json }
2017-11-01 17:35:14 +00:00
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 ,
2018-12-21 00:19:17 +00:00
task_status : issuable . task_status ,
lock_version : issuable . lock_version
2017-11-01 17:35:14 +00:00
}
if issuable . edited?
2018-01-11 16:22:00 +00:00
response [ :updated_at ] = issuable . last_edited_at . to_time . iso8601
2017-11-01 17:35:14 +00:00
response [ :updated_by_name ] = issuable . last_edited_by . name
response [ :updated_by_path ] = user_path ( issuable . last_edited_by )
end
render json : response
end
2016-03-21 13:12:52 +00:00
def destroy
2017-11-29 10:27:36 +00:00
Issuable :: DestroyService . new ( issuable . project , current_user ) . execute ( issuable )
2016-03-21 13:12:52 +00:00
2016-11-19 02:19:04 +00:00
name = issuable . human_class_name
2016-03-21 13:12:52 +00:00
flash [ :notice ] = " The #{ name } was successfully deleted. "
2017-11-02 14:51:42 +00:00
index_path = polymorphic_path ( [ parent , issuable . class ] )
2017-05-12 10:23:30 +00:00
respond_to do | format |
format . html { redirect_to index_path }
format . json do
render json : {
2017-05-25 10:58:40 +00:00
web_url : index_path
2017-05-12 10:23:30 +00:00
}
end
end
2016-03-21 13:12:52 +00:00
end
2019-08-29 04:57:54 +00:00
def check_destroy_confirmation!
return true if params [ :destroy_confirm ]
error_message = " Destroy confirmation not provided for #{ issuable . human_class_name } "
exception = RuntimeError . new ( error_message )
2019-12-16 12:07:43 +00:00
Gitlab :: ErrorTracking . track_exception (
2019-08-29 04:57:54 +00:00
exception ,
2019-12-13 12:07:41 +00:00
project_path : issuable . project . full_path ,
issuable_type : issuable . class . name ,
issuable_id : issuable . id
2019-08-29 04:57:54 +00:00
)
index_path = polymorphic_path ( [ parent , issuable . class ] )
respond_to do | format |
format . html do
flash [ :notice ] = error_message
redirect_to index_path
end
format . json do
render json : { errors : error_message } , status : :unprocessable_entity
end
end
end
2016-09-06 12:49:49 +00:00
def bulk_update
2019-12-02 09:06:58 +00:00
result = Issuable :: BulkUpdateService . new ( parent , current_user , bulk_update_params ) . execute ( resource_name )
2016-09-06 12:49:49 +00:00
2020-07-06 09:09:20 +00:00
if result . success?
quantity = result . payload [ :count ]
render json : { notice : " #{ quantity } #{ resource_name . pluralize ( quantity ) } updated " }
elsif result . error?
render json : { errors : result . message } , status : result . http_status
end
2016-09-06 12:49:49 +00:00
end
2019-07-30 18:25:49 +00:00
# rubocop:disable CodeReuse/ActiveRecord
2018-02-28 00:10:43 +00:00
def discussions
2019-07-30 18:25:49 +00:00
notes = NotesFinder . new ( current_user , finder_params_for_issuable ) . execute
. inc_relations_for_view
. includes ( :noteable )
. fresh
2018-02-28 00:10:43 +00:00
2018-10-23 09:49:45 +00:00
if notes_filter != UserPreference :: NOTES_FILTERS [ :only_comments ]
notes = ResourceEvents :: MergeIntoNotesService . new ( issuable , current_user ) . execute ( notes )
end
2018-02-28 00:10:43 +00:00
notes = prepare_notes_for_rendering ( notes )
2020-02-24 03:09:05 +00:00
notes = notes . select { | n | n . readable_by? ( current_user ) }
2018-02-28 00:10:43 +00:00
discussions = Discussion . build_collection ( notes , issuable )
2018-04-03 16:03:00 +00:00
render json : discussion_serializer . represent ( discussions , context : self )
2018-02-28 00:10:43 +00:00
end
2019-07-30 18:25:49 +00:00
# rubocop:enable CodeReuse/ActiveRecord
2018-02-28 00:10:43 +00:00
2016-03-21 13:12:52 +00:00
private
2018-10-23 09:49:45 +00:00
def notes_filter
strong_memoize ( :notes_filter ) do
notes_filter_param = params [ :notes_filter ] & . to_i
# GitLab Geo does not expect database UPDATE or INSERT statements to happen
# on GET requests.
# This is just a fail-safe in case notes_filter is sent via GET request in GitLab Geo.
2019-08-07 15:45:23 +00:00
# In some cases, we also force the filter to not be persisted with the `persist_filter` param
if Gitlab :: Database . read_only? || params [ :persist_filter ] == 'false'
2018-10-23 09:49:45 +00:00
notes_filter_param || current_user & . notes_filter_for ( issuable )
else
notes_filter = current_user & . set_notes_filter ( notes_filter_param , issuable ) || notes_filter_param
# We need to invalidate the cache for polling notes otherwise it will
# ignore the filter.
# The ideal would be to invalidate the cache for each user.
issuable . expire_note_etag_cache if notes_filter_updated?
notes_filter
end
end
end
def notes_filter_updated?
current_user & . user_preference & . previous_changes & . any?
end
2018-04-03 16:03:00 +00:00
def discussion_serializer
DiscussionSerializer . new ( project : project , noteable : issuable , current_user : current_user , note_entity : ProjectNoteEntity )
end
2017-12-08 18:19:51 +00:00
def recaptcha_check_if_spammable ( should_redirect = true , & block )
2017-12-15 11:37:57 +00:00
return yield unless issuable . is_a? Spammable
2017-12-08 18:19:51 +00:00
recaptcha_check_with_fallback ( should_redirect , & block )
end
2017-01-16 18:43:03 +00:00
def render_conflict_response
respond_to do | format |
format . html do
2017-11-22 07:50:36 +00:00
@conflict = true # rubocop:disable Gitlab/ModuleWithInstanceVariables
2017-01-16 18:43:03 +00:00
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. "
2019-01-23 21:58:46 +00:00
]
2018-07-02 10:43:06 +00:00
} , status : :conflict
2017-01-16 18:43:03 +00:00
end
end
end
2016-03-21 13:12:52 +00:00
def authorize_destroy_issuable!
2016-09-08 12:33:53 +00:00
unless can? ( current_user , :" destroy_ #{ issuable . to_ability_name } " , issuable )
2020-07-11 00:09:17 +00:00
access_denied!
2016-03-21 13:12:52 +00:00
end
end
2016-09-06 12:49:49 +00:00
def authorize_admin_issuable!
2019-07-09 11:46:16 +00:00
unless can? ( current_user , :" admin_ #{ resource_name } " , parent )
2020-07-11 00:09:17 +00:00
access_denied!
2016-09-06 12:49:49 +00:00
end
end
2017-11-01 17:35:14 +00:00
def authorize_update_issuable!
render_404 unless can? ( current_user , :" update_ #{ resource_name } " , issuable )
end
2016-09-06 12:49:49 +00:00
def bulk_update_params
2020-06-05 15:08:23 +00:00
params . require ( :update ) . permit ( bulk_update_permitted_keys )
end
2017-05-08 14:58:42 +00:00
2020-06-05 15:08:23 +00:00
def bulk_update_permitted_keys
[
:issuable_ids ,
:assignee_id ,
:milestone_id ,
:state_event ,
:subscription_event ,
assignee_ids : [ ] ,
add_label_ids : [ ] ,
remove_label_ids : [ ]
]
2016-09-06 12:49:49 +00:00
end
def resource_name
@resource_name || = controller_name . singularize
end
2017-11-01 17:35:14 +00:00
2017-11-22 07:50:36 +00:00
# rubocop:disable Gitlab/ModuleWithInstanceVariables
2017-11-01 17:35:14 +00:00
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
2017-11-22 07:50:36 +00:00
# rubocop:enable Gitlab/ModuleWithInstanceVariables
2017-11-01 17:35:14 +00:00
def serializer
raise NotImplementedError
end
def update_service
raise NotImplementedError
end
2017-11-02 14:51:42 +00:00
def parent
2017-11-22 07:50:36 +00:00
@project || @group # rubocop:disable Gitlab/ModuleWithInstanceVariables
2017-11-02 14:51:42 +00:00
end
2019-07-30 18:25:49 +00:00
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def finder_params_for_issuable
{
target : @issuable ,
notes_filter : notes_filter
2019-08-01 08:42:12 +00:00
} . tap { | new_params | new_params [ :project ] = project if respond_to? ( :project , true ) }
2019-07-30 18:25:49 +00:00
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
2016-03-21 13:12:52 +00:00
end
2019-09-13 13:26:31 +00:00
IssuableActions . prepend_if_ee ( 'EE::IssuableActions' )