305 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Ruby
		
	
	
	
			
		
		
	
	
			305 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Ruby
		
	
	
	
| # frozen_string_literal: true
 | |
| 
 | |
| module Gitlab
 | |
|   module QuickActions
 | |
|     module IssueAndMergeRequestActions
 | |
|       extend ActiveSupport::Concern
 | |
|       include Gitlab::QuickActions::Dsl
 | |
| 
 | |
|       included do
 | |
|         # Issue, MergeRequest: quick actions definitions
 | |
|         desc { _('Assign') }
 | |
|         explanation do |users|
 | |
|           _('Assigns %{assignee_users_sentence}.') % { assignee_users_sentence: assignee_users_sentence(users) }
 | |
|         end
 | |
|         execution_message do |users = nil|
 | |
|           if users.blank?
 | |
|             _("Failed to assign a user because no user was found.")
 | |
|           else
 | |
|             users = [users.first] unless quick_action_target.allows_multiple_assignees?
 | |
| 
 | |
|             _('Assigned %{assignee_users_sentence}.') % { assignee_users_sentence: assignee_users_sentence(users) }
 | |
|           end
 | |
|         end
 | |
|         params do
 | |
|           quick_action_target.allows_multiple_assignees? ? '@user1 @user2' : '@user'
 | |
|         end
 | |
|         types Issue, MergeRequest
 | |
|         condition do
 | |
|           quick_action_target.supports_assignee? && current_user.can?(:"set_#{quick_action_target.to_ability_name}_metadata", quick_action_target)
 | |
|         end
 | |
|         parse_params do |assignee_param|
 | |
|           extract_users(assignee_param)
 | |
|         end
 | |
|         command :assign do |users|
 | |
|           next if users.empty?
 | |
| 
 | |
|           if quick_action_target.allows_multiple_assignees?
 | |
|             @updates[:assignee_ids] ||= quick_action_target.assignees.map(&:id)
 | |
|             @updates[:assignee_ids] |= users.map(&:id)
 | |
|           else
 | |
|             @updates[:assignee_ids] = [users.first.id]
 | |
|           end
 | |
|         end
 | |
| 
 | |
|         desc do
 | |
|           if quick_action_target.allows_multiple_assignees?
 | |
|             _('Remove all or specific assignees')
 | |
|           else
 | |
|             _('Remove assignee')
 | |
|           end
 | |
|         end
 | |
|         explanation do |users = nil|
 | |
|           assignees = assignees_for_removal(users)
 | |
|           _("Removes %{assignee_text} %{assignee_references}.") %
 | |
|             { assignee_text: 'assignee'.pluralize(assignees.size), assignee_references: assignees.map(&:to_reference).to_sentence }
 | |
|         end
 | |
|         execution_message do |users = nil|
 | |
|           assignees = assignees_for_removal(users)
 | |
|           _("Removed %{assignee_text} %{assignee_references}.") %
 | |
|             { assignee_text: 'assignee'.pluralize(assignees.size), assignee_references: assignees.map(&:to_reference).to_sentence }
 | |
|         end
 | |
|         params do
 | |
|           quick_action_target.allows_multiple_assignees? ? '@user1 @user2' : ''
 | |
|         end
 | |
|         types Issue, MergeRequest
 | |
|         condition do
 | |
|           quick_action_target.persisted? &&
 | |
|             quick_action_target.assignees.any? &&
 | |
|             current_user.can?(:"set_#{quick_action_target.to_ability_name}_metadata", quick_action_target)
 | |
|         end
 | |
|         parse_params do |unassign_param|
 | |
|           # When multiple users are assigned, all will be unassigned if multiple assignees are no longer allowed
 | |
|           extract_users(unassign_param) if quick_action_target.allows_multiple_assignees?
 | |
|         end
 | |
|         command :unassign do |users = nil|
 | |
|           if quick_action_target.allows_multiple_assignees? && users&.any?
 | |
|             @updates[:assignee_ids] ||= quick_action_target.assignees.map(&:id)
 | |
|             @updates[:assignee_ids] -= users.map(&:id)
 | |
|           else
 | |
|             @updates[:assignee_ids] = []
 | |
|           end
 | |
|         end
 | |
| 
 | |
|         desc { _('Set milestone') }
 | |
|         explanation do |milestone|
 | |
|           _("Sets the milestone to %{milestone_reference}.") % { milestone_reference: milestone.to_reference } if milestone
 | |
|         end
 | |
|         execution_message do |milestone|
 | |
|           _("Set the milestone to %{milestone_reference}.") % { milestone_reference: milestone.to_reference } if milestone
 | |
|         end
 | |
|         params '%"milestone"'
 | |
|         types Issue, MergeRequest
 | |
|         condition do
 | |
|           quick_action_target.supports_milestone? &&
 | |
|             current_user.can?(:"set_#{quick_action_target.to_ability_name}_metadata", quick_action_target) &&
 | |
|             find_milestones(project, state: 'active').any?
 | |
|         end
 | |
|         parse_params do |milestone_param|
 | |
|           extract_references(milestone_param, :milestone).first ||
 | |
|             find_milestones(project, title: milestone_param.strip).first
 | |
|         end
 | |
|         command :milestone do |milestone|
 | |
|           @updates[:milestone_id] = milestone.id if milestone
 | |
|         end
 | |
| 
 | |
|         desc { _('Remove milestone') }
 | |
|         explanation do
 | |
|           _("Removes %{milestone_reference} milestone.") % { milestone_reference: quick_action_target.milestone.to_reference(format: :name) }
 | |
|         end
 | |
|         execution_message do
 | |
|           _("Removed %{milestone_reference} milestone.") % { milestone_reference: quick_action_target.milestone.to_reference(format: :name) }
 | |
|         end
 | |
|         types Issue, MergeRequest
 | |
|         condition do
 | |
|           quick_action_target.persisted? &&
 | |
|             quick_action_target.milestone_id? &&
 | |
|             quick_action_target.supports_milestone? &&
 | |
|             current_user.can?(:"set_#{quick_action_target.to_ability_name}_metadata", quick_action_target)
 | |
|         end
 | |
|         command :remove_milestone do
 | |
|           @updates[:milestone_id] = nil
 | |
|         end
 | |
| 
 | |
|         desc { _('Copy labels and milestone from other issue or merge request in this project') }
 | |
|         explanation do |source_issuable|
 | |
|           _("Copy labels and milestone from %{source_issuable_reference}.") % { source_issuable_reference: source_issuable.to_reference }
 | |
|         end
 | |
|         params '#issue | !merge_request'
 | |
|         types Issue, MergeRequest
 | |
|         condition do
 | |
|           current_user.can?(:"set_#{quick_action_target.to_ability_name}_metadata", quick_action_target)
 | |
|         end
 | |
|         parse_params do |issuable_param|
 | |
|           extract_references(issuable_param, :issue).first ||
 | |
|             extract_references(issuable_param, :merge_request).first
 | |
|         end
 | |
|         command :copy_metadata do |source_issuable|
 | |
|           if can_copy_metadata?(source_issuable)
 | |
|             @updates[:add_label_ids] = source_issuable.labels.map(&:id)
 | |
|             @updates[:milestone_id] = source_issuable.milestone.id if source_issuable.milestone
 | |
| 
 | |
|             @execution_message[:copy_metadata] = _("Copied labels and milestone from %{source_issuable_reference}.") % { source_issuable_reference: source_issuable.to_reference }
 | |
|           end
 | |
|         end
 | |
| 
 | |
|         desc { _('Set time estimate') }
 | |
|         explanation do |time_estimate|
 | |
|           next unless time_estimate
 | |
| 
 | |
|           if time_estimate == 0
 | |
|             _('Removes time estimate.')
 | |
|           elsif time_estimate > 0
 | |
|             formatted_time_estimate = format_time_estimate(time_estimate)
 | |
|             _("Sets time estimate to %{time_estimate}.") % { time_estimate: formatted_time_estimate } if formatted_time_estimate
 | |
|           end
 | |
|         end
 | |
|         execution_message do |time_estimate|
 | |
|           next _('Removed time estimate.') if time_estimate == 0
 | |
| 
 | |
|           formatted_time_estimate = format_time_estimate(time_estimate)
 | |
|           _("Set time estimate to %{time_estimate}.") % { time_estimate: formatted_time_estimate } if formatted_time_estimate
 | |
|         end
 | |
|         params '<1w 3d 2h 14m>'
 | |
|         types Issue, MergeRequest
 | |
|         condition do
 | |
|           quick_action_target.supports_time_tracking? &&
 | |
|             current_user.can?(:"admin_#{quick_action_target.to_ability_name}", project)
 | |
|         end
 | |
|         parse_params do |raw_duration|
 | |
|           Gitlab::TimeTrackingFormatter.parse(raw_duration, keep_zero: true)
 | |
|         end
 | |
|         command :estimate, :estimate_time do |time_estimate|
 | |
|           @updates[:time_estimate] = time_estimate
 | |
|         end
 | |
| 
 | |
|         desc { _('Add or subtract spent time') }
 | |
|         explanation do |time_spent, time_spent_date|
 | |
|           spend_time_message(time_spent, time_spent_date, false)
 | |
|         end
 | |
|         execution_message do |time_spent, time_spent_date|
 | |
|           spend_time_message(time_spent, time_spent_date, true)
 | |
|         end
 | |
| 
 | |
|         params do
 | |
|           base_params = 'time(1h30m | -1h30m) <date(YYYY-MM-DD)>'
 | |
|           if Feature.enabled?(:timelog_categories, quick_action_target.project)
 | |
|             "#{base_params} <[timecategory:category-name]>"
 | |
|           else
 | |
|             base_params
 | |
|           end
 | |
|         end
 | |
|         types Issue, MergeRequest
 | |
|         condition do
 | |
|           quick_action_target.supports_time_tracking? &&
 | |
|             current_user.can?(:"admin_#{quick_action_target.to_ability_name}", quick_action_target)
 | |
|         end
 | |
|         parse_params do |raw_time_date|
 | |
|           Gitlab::QuickActions::SpendTimeAndDateSeparator.new(raw_time_date).execute
 | |
|         end
 | |
|         command :spend, :spent, :spend_time do |time_spent, time_spent_date, category|
 | |
|           if time_spent
 | |
|             @updates[:spend_time] = {
 | |
|               duration: time_spent,
 | |
|               user_id: current_user.id,
 | |
|               spent_at: time_spent_date,
 | |
|               category: category
 | |
|             }
 | |
|           end
 | |
|         end
 | |
| 
 | |
|         desc { _('Remove time estimate') }
 | |
|         explanation { _('Removes time estimate.') }
 | |
|         execution_message { _('Removed time estimate.') }
 | |
|         types Issue, MergeRequest
 | |
|         condition do
 | |
|           quick_action_target.persisted? &&
 | |
|             current_user.can?(:"admin_#{quick_action_target.to_ability_name}", project)
 | |
|         end
 | |
|         command :remove_estimate, :remove_time_estimate do
 | |
|           @updates[:time_estimate] = 0
 | |
|         end
 | |
| 
 | |
|         desc { _('Remove spent time') }
 | |
|         explanation { _('Removes spent time.') }
 | |
|         execution_message { _('Removed spent time.') }
 | |
|         condition do
 | |
|           quick_action_target.persisted? &&
 | |
|             current_user.can?(:"admin_#{quick_action_target.to_ability_name}", project)
 | |
|         end
 | |
|         types Issue, MergeRequest
 | |
|         command :remove_time_spent do
 | |
|           @updates[:spend_time] = { duration: :reset, user_id: current_user.id }
 | |
|         end
 | |
| 
 | |
|         desc { _("Lock the discussion") }
 | |
|         explanation { _("Locks the discussion.") }
 | |
|         execution_message { _("Locked the discussion.") }
 | |
|         types Issue, MergeRequest
 | |
|         condition do
 | |
|           quick_action_target.persisted? &&
 | |
|             !quick_action_target.discussion_locked? &&
 | |
|             current_user.can?(:"set_#{quick_action_target.to_ability_name}_metadata", quick_action_target)
 | |
|         end
 | |
|         command :lock do
 | |
|           @updates[:discussion_locked] = true
 | |
|         end
 | |
| 
 | |
|         desc { _("Unlock the discussion") }
 | |
|         explanation { _("Unlocks the discussion.") }
 | |
|         execution_message { _("Unlocked the discussion.") }
 | |
|         types Issue, MergeRequest
 | |
|         condition do
 | |
|           quick_action_target.persisted? &&
 | |
|             quick_action_target.discussion_locked? &&
 | |
|             current_user.can?(:"set_#{quick_action_target.to_ability_name}_metadata", quick_action_target)
 | |
|         end
 | |
|         command :unlock do
 | |
|           @updates[:discussion_locked] = false
 | |
|         end
 | |
| 
 | |
|         private
 | |
| 
 | |
|         def assignee_users_sentence(users)
 | |
|           if quick_action_target.allows_multiple_assignees?
 | |
|             users
 | |
|           else
 | |
|             [users.first]
 | |
|           end.map(&:to_reference).to_sentence
 | |
|         end
 | |
| 
 | |
|         def assignees_for_removal(users)
 | |
|           assignees = quick_action_target.assignees
 | |
|           if users.present? && quick_action_target.allows_multiple_assignees?
 | |
|             users
 | |
|           else
 | |
|             assignees
 | |
|           end
 | |
|         end
 | |
| 
 | |
|         def can_copy_metadata?(source_issuable)
 | |
|           source_issuable.present? && source_issuable.project_id == quick_action_target.project_id
 | |
|         end
 | |
| 
 | |
|         def format_time_estimate(time_estimate)
 | |
|           Gitlab::TimeTrackingFormatter.output(time_estimate)
 | |
|         end
 | |
| 
 | |
|         def spend_time_message(time_spent, time_spent_date, paste_tense)
 | |
|           return unless time_spent
 | |
| 
 | |
|           if time_spent > 0
 | |
|             verb = paste_tense ? _('Added') : _('Adds')
 | |
|             value = time_spent
 | |
|           else
 | |
|             verb = paste_tense ? _('Subtracted') : _('Subtracts')
 | |
|             value = -time_spent
 | |
|           end
 | |
| 
 | |
|           _("%{verb} %{time_spent_value} spent time.") % { verb: verb, time_spent_value: format_time_estimate(value) }
 | |
|         end
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| end
 |