Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/models/milestone.rb')
-rw-r--r--app/models/milestone.rb195
1 files changed, 18 insertions, 177 deletions
diff --git a/app/models/milestone.rb b/app/models/milestone.rb
index 4ccfe314526..b5e4f62792e 100644
--- a/app/models/milestone.rb
+++ b/app/models/milestone.rb
@@ -1,88 +1,37 @@
# frozen_string_literal: true
class Milestone < ApplicationRecord
- # Represents a "No Milestone" state used for filtering Issues and Merge
- # Requests that have no milestone assigned.
- MilestoneStruct = Struct.new(:title, :name, :id) do
- # Ensure these models match the interface required for exporting
- def serializable_hash(_opts = {})
- { title: title, name: name, id: id }
- end
- end
-
- None = MilestoneStruct.new('No Milestone', 'No Milestone', 0)
- Any = MilestoneStruct.new('Any Milestone', '', -1)
- Upcoming = MilestoneStruct.new('Upcoming', '#upcoming', -2)
- Started = MilestoneStruct.new('Started', '#started', -3)
-
- include CacheMarkdownField
- include AtomicInternalId
- include IidRoutes
include Sortable
include Referable
- include StripAttribute
+ include Timebox
include Milestoneish
include FromUnion
include Importable
- include Gitlab::SQL::Pattern
prepend_if_ee('::EE::Milestone') # rubocop: disable Cop/InjectEnterpriseEditionModule
- cache_markdown_field :title, pipeline: :single_line
- cache_markdown_field :description
-
- belongs_to :project
- belongs_to :group
-
has_many :milestone_releases
has_many :releases, through: :milestone_releases
has_internal_id :iid, scope: :project, track_if: -> { !importing? }, init: ->(s) { s&.project&.milestones&.maximum(:iid) }
has_internal_id :iid, scope: :group, track_if: -> { !importing? }, init: ->(s) { s&.group&.milestones&.maximum(:iid) }
- has_many :issues
- has_many :labels, -> { distinct.reorder('labels.title') }, through: :issues
- has_many :merge_requests
has_many :events, as: :target, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
- scope :of_projects, ->(ids) { where(project_id: ids) }
- scope :of_groups, ->(ids) { where(group_id: ids) }
scope :active, -> { with_state(:active) }
- scope :closed, -> { with_state(:closed) }
- scope :for_projects, -> { where(group: nil).includes(:project) }
scope :started, -> { active.where('milestones.start_date <= CURRENT_DATE') }
-
- scope :for_projects_and_groups, -> (projects, groups) do
- projects = projects.compact if projects.is_a? Array
- projects = [] if projects.nil?
-
- groups = groups.compact if groups.is_a? Array
- groups = [] if groups.nil?
-
- where(project_id: projects).or(where(group_id: groups))
- end
-
- scope :within_timeframe, -> (start_date, end_date) do
- where('start_date is not NULL or due_date is not NULL')
- .where('start_date is NULL or start_date <= ?', end_date)
- .where('due_date is NULL or due_date >= ?', start_date)
+ scope :not_started, -> { active.where('milestones.start_date > CURRENT_DATE') }
+ scope :not_upcoming, -> do
+ active
+ .where('milestones.due_date <= CURRENT_DATE')
+ .order(:project_id, :group_id, :due_date)
end
scope :order_by_name_asc, -> { order(Arel::Nodes::Ascending.new(arel_table[:title].lower)) }
scope :reorder_by_due_date_asc, -> { reorder(Gitlab::Database.nulls_last_order('due_date', 'ASC')) }
- validates :group, presence: true, unless: :project
- validates :project, presence: true, unless: :group
- validates :title, presence: true
-
- validate :uniqueness_of_title, if: :title_changed?
- validate :milestone_type_check
- validate :start_date_should_be_less_than_due_date, if: proc { |m| m.start_date.present? && m.due_date.present? }
- validate :dates_within_4_digits
validates_associated :milestone_releases, message: -> (_, obj) { obj[:value].map(&:errors).map(&:full_messages).join(",") }
- strip_attributes :title
-
state_machine :state, initial: :active do
event :close do
transition active: :closed
@@ -97,52 +46,6 @@ class Milestone < ApplicationRecord
state :active
end
- alias_attribute :name, :title
-
- class << self
- # Searches for milestones with a matching title or description.
- #
- # This method uses ILIKE on PostgreSQL and LIKE on MySQL.
- #
- # query - The search query as a String
- #
- # Returns an ActiveRecord::Relation.
- def search(query)
- fuzzy_search(query, [:title, :description])
- end
-
- # Searches for milestones with a matching title.
- #
- # This method uses ILIKE on PostgreSQL and LIKE on MySQL.
- #
- # query - The search query as a String
- #
- # Returns an ActiveRecord::Relation.
- def search_title(query)
- fuzzy_search(query, [:title])
- end
-
- def filter_by_state(milestones, state)
- case state
- when 'closed' then milestones.closed
- when 'all' then milestones
- else milestones.active
- end
- end
-
- def count_by_state
- reorder(nil).group(:state).count
- end
-
- def predefined_id?(id)
- [Any.id, None.id, Upcoming.id, Started.id].include?(id)
- end
-
- def predefined?(milestone)
- predefined_id?(milestone&.id)
- end
- end
-
def self.reference_prefix
'%'
end
@@ -220,7 +123,7 @@ class Milestone < ApplicationRecord
end
##
- # Returns the String necessary to reference this Milestone in Markdown. Group
+ # Returns the String necessary to reference a Milestone in Markdown. Group
# milestones only support name references, and do not support cross-project
# references.
#
@@ -248,10 +151,6 @@ class Milestone < ApplicationRecord
self.class.reference_prefix + self.title
end
- def milestoneish_id
- id
- end
-
def for_display
self
end
@@ -264,62 +163,24 @@ class Milestone < ApplicationRecord
nil
end
- def title=(value)
- write_attribute(:title, sanitize_title(value)) if value.present?
- end
+ # TODO: remove after all code paths use `timebox_id`
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/215688
+ alias_method :milestoneish_id, :timebox_id
+ # TODO: remove after all code paths use (group|project)_timebox?
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/215690
+ alias_method :group_milestone?, :group_timebox?
+ alias_method :project_milestone?, :project_timebox?
- def safe_title
- title.to_slug.normalize.to_s
- end
-
- def resource_parent
- group || project
- end
-
- def to_ability_name
- model_name.singular
- end
-
- def group_milestone?
- group_id.present?
- end
-
- def project_milestone?
- project_id.present?
- end
-
- def merge_requests_enabled?
+ def parent
if group_milestone?
- # Assume that groups have at least one project with merge requests enabled.
- # Otherwise, we would need to load all of the projects from the database.
- true
- elsif project_milestone?
- project&.merge_requests_enabled?
+ group
+ else
+ project
end
end
private
- # Milestone titles must be unique across project milestones and group milestones
- def uniqueness_of_title
- if project
- relation = Milestone.for_projects_and_groups([project_id], [project.group&.id])
- elsif group
- relation = Milestone.for_projects_and_groups(group.projects.select(:id), [group.id])
- end
-
- title_exists = relation.find_by_title(title)
- errors.add(:title, _("already being used for another group or project milestone.")) if title_exists
- end
-
- # Milestone should be either a project milestone or a group milestone
- def milestone_type_check
- if group_id && project_id
- field = project_id_changed? ? :project_id : :group_id
- errors.add(field, _("milestone should belong either to a project or a group."))
- end
- end
-
def milestone_format_reference(format = :iid)
raise ArgumentError, _('Unknown format') unless [:iid, :name].include?(format)
@@ -334,26 +195,6 @@ class Milestone < ApplicationRecord
end
end
- def sanitize_title(value)
- CGI.unescape_html(Sanitize.clean(value.to_s))
- end
-
- def start_date_should_be_less_than_due_date
- if due_date <= start_date
- errors.add(:due_date, _("must be greater than start date"))
- end
- end
-
- def dates_within_4_digits
- if start_date && start_date > Date.new(9999, 12, 31)
- errors.add(:start_date, _("date must not be after 9999-12-31"))
- end
-
- if due_date && due_date > Date.new(9999, 12, 31)
- errors.add(:due_date, _("date must not be after 9999-12-31"))
- end
- end
-
def issues_finder_params
{ project_id: project_id, group_id: group_id, include_subgroups: group_id.present? }.compact
end