Class: Issue
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- Issue
- Includes:
- AtomicInternalId, FasterCacheKeys, IdInOrdered, IgnorableColumns, IidRoutes, Issuable, LabelEventable, MilestoneEventable, Noteable, Referable, RelativePositioning, Spammable, StateEventable, ThrottledTouch, TimeTrackable, WhereComposite
- Defined in:
- app/models/issue.rb
Defined Under Namespace
Classes: Metrics
Constant Summary collapse
- DueDateStruct =
Struct.new(:title, :name).freeze
- NoDueDate =
DueDateStruct.new('No Due Date', '0').freeze
- AnyDueDate =
DueDateStruct.new('Any Due Date', '').freeze
- Overdue =
DueDateStruct.new('Overdue', 'overdue').freeze
- DueThisWeek =
DueDateStruct.new('Due This Week', 'week').freeze
- DueThisMonth =
DueDateStruct.new('Due This Month', 'month').freeze
- DueNextMonthAndPreviousTwoWeeks =
DueDateStruct.new('Due Next Month And Previous Two Weeks', 'next_month_and_previous_two_weeks').freeze
- SORTING_PREFERENCE_FIELD =
:issues_sort
Constants included from ThrottledTouch
ThrottledTouch::TOUCH_INTERVAL
Constants included from RelativePositioning
RelativePositioning::IDEAL_DISTANCE, RelativePositioning::MAX_GAP, RelativePositioning::MAX_POSITION, RelativePositioning::MIN_GAP, RelativePositioning::MIN_POSITION, RelativePositioning::NoSpaceLeft, RelativePositioning::START_POSITION, RelativePositioning::STEPS
Constants included from Noteable
Constants included from Issuable
Issuable::DESCRIPTION_HTML_LENGTH_MAX, Issuable::DESCRIPTION_LENGTH_MAX, Issuable::STATE_ID_MAP, Issuable::TITLE_HTML_LENGTH_MAX, Issuable::TITLE_LENGTH_MAX
Constants included from Taskable
Taskable::COMPLETED, Taskable::COMPLETE_PATTERN, Taskable::INCOMPLETE, Taskable::INCOMPLETE_PATTERN, Taskable::ITEM_PATTERN
Constants included from CacheMarkdownField
CacheMarkdownField::INVALIDATED_BY
Constants included from Redactable
Redactable::UNSUBSCRIBE_PATTERN
Constants included from Gitlab::SQL::Pattern
Gitlab::SQL::Pattern::MIN_CHARS_FOR_PARTIAL_MATCHING, Gitlab::SQL::Pattern::REGEX_QUOTED_WORD
Instance Attribute Summary
Attributes included from Noteable
Attributes included from Importable
Class Method Summary collapse
- .link_reference_pattern ⇒ Object
-
.order_by_position_and_priority(with_cte: false) ⇒ Object
`with_cte` argument allows sorting when using CTE queries and prevents errors in postgres when using CTE search optimisation.
- .project_foreign_key ⇒ Object
-
.reference_pattern ⇒ Object
Pattern used to extract `#123` issue references from text.
- .reference_prefix ⇒ Object
- .reference_valid?(reference) ⇒ Boolean
- .relative_positioning_parent_column ⇒ Object
- .relative_positioning_query_base(issue) ⇒ Object
- .simple_sorts ⇒ Object
- .sort_by_attribute(method, excluded_labels: []) ⇒ Object
Instance Method Summary collapse
- #as_json(options = {}) ⇒ Object
- #banzai_render_context(field) ⇒ Object
- #can_be_worked_on? ⇒ Boolean
- #can_move?(user, to_project = nil) ⇒ Boolean
- #check_for_spam? ⇒ Boolean
- #design_collection ⇒ Object
- #discussions_rendered_on_frontend? ⇒ Boolean
- #duplicated? ⇒ Boolean
- #etag_caching_enabled? ⇒ Boolean
- #from_service_desk? ⇒ Boolean
-
#has_related_branch? ⇒ Boolean
Returns boolean if a related branch exists for the current issue ignores merge requests branchs.
- #hook_attrs ⇒ Object
- #issue_link_type ⇒ Object
- #labels_hook_attrs ⇒ Object
-
#merge_requests_count(user = nil) ⇒ Object
rubocop: enable CodeReuse/ServiceClass.
- #moved? ⇒ Boolean
- #previous_updated_at ⇒ Object
- #related_issues(current_user, preload: nil) ⇒ Object
-
#source_project ⇒ Object
To allow polymorphism with MergeRequest.
- #suggested_branch_name ⇒ Object
- #to_branch_name ⇒ Object
-
#to_reference(from = nil, full: false) ⇒ Object
`from` argument can be a Namespace or Project.
-
#update_project_counter_caches ⇒ Object
rubocop: disable CodeReuse/ServiceClass.
-
#visible_to_user?(user = nil) ⇒ Boolean
Returns `true` if the current issue can be viewed by either a logged in User or an anonymous user.
Methods included from ThrottledTouch
Methods included from TimeTrackable
#human_time_estimate, #human_total_time_spent, #spend_time, #time_estimate=, #total_time_spent
Methods included from RelativePositioning
#max_relative_position, #min_relative_position, #move_after, #move_before, #move_between, #move_sequence_after, #move_sequence_before, #move_to_end, #move_to_start, #next_relative_position, #prev_relative_position
Methods included from FasterCacheKeys
Methods included from Spammable
#allow_possible_spam?, #clear_spam_flags!, #invalidate_if_spam, #needs_recaptcha!, #recaptcha_error!, #spam!, #spam_description, #spam_title, #spammable_entity_type, #spammable_text, #submittable_as_spam?, #submittable_as_spam_by?, #unrecoverable_spam_error!
Methods included from Referable
#referable_inspect, #reference_link_text, #to_reference_base
Methods included from Noteable
#after_note_created, #after_note_destroyed, #base_class_name, #capped_notes_count, #discussion_ids_relation, #discussion_notes, #discussions, #discussions_can_be_resolved_by?, #discussions_resolvable?, #discussions_resolved?, #discussions_to_be_resolved, #expire_note_etag_cache, #grouped_diff_discussions, #has_any_diff_note_positions?, #human_class_name, #lockable?, #note_etag_key, #preloads_discussion_diff_highlighting?, #resolvable_discussions, #supports_discussions?, #supports_replying_to_individual_notes?, #supports_resolvable_notes?, #supports_suggestion?
Methods included from Issuable
#assignee_list, #assignee_or_author?, #assignee_username_list, #can_assign_epic?, #card_attributes, #created_hours_ago, #first_contribution?, #label_names, #labels_array, #new?, #notes_with_associations, #open?, #overdue?, #resource_parent, #state, #state=, #subscribed_without_subscriptions?, #to_ability_name, #to_hook_data, #today?, #updated_tasks, #user_notes_count, #wipless_title_changed
Methods included from AfterCommitQueue
#run_after_commit, #run_after_commit_or_now
Methods included from Editable
Methods included from Taskable
get_tasks, get_updated_tasks, #task_completion_status, #task_list_items, #task_status, #task_status_short, #tasks, #tasks?
Methods included from Awardable
#awarded_emoji?, #downvotes, #emoji_awardable?, #grouped_awards, #upvotes, #user_authored?, #user_can_award?
Methods included from StripAttribute
Methods included from Subscribable
#set_subscription, #subscribe, #subscribed?, #subscribed_without_subscriptions?, #subscribers, #toggle_subscription, #unsubscribe
Methods included from Milestoneable
#milestone_available?, #supports_milestone?
Methods included from Mentionable
#all_references, #create_cross_references!, #create_new_cross_references!, #directly_addressed_users, #extractors, #gfm_reference, #local_reference, #matches_cross_reference_regex?, #mentioned_users, #referenced_group_users, #referenced_groups, #referenced_mentionables, #referenced_project_users, #referenced_projects, #referenced_users, #store_mentions!
Methods included from Participable
Methods included from CacheMarkdownField
#attribute_invalidated?, #cached_html_for, #cached_html_up_to_date?, #can_cache_field?, #invalidated_markdown_cache?, #latest_cached_markdown_version, #local_version, #parent_user, #refresh_markdown_cache, #refresh_markdown_cache!, #rendered_field_content, #skip_project_check?, #updated_cached_html_for
Methods included from IidRoutes
Methods included from AtomicInternalId
#internal_id_read_scope, #internal_id_scope_attrs, #internal_id_scope_usage
Methods inherited from ApplicationRecord
at_most, id_in, id_not_in, iid_in, pluck_primary_key, primary_key_in, safe_ensure_unique, safe_find_or_create_by, safe_find_or_create_by!, underscore, without_order
Class Method Details
.link_reference_pattern ⇒ Object
198 199 200 |
# File 'app/models/issue.rb', line 198 def self.link_reference_pattern @link_reference_pattern ||= super("issues", Gitlab::Regex.issue) end |
.order_by_position_and_priority(with_cte: false) ⇒ Object
`with_cte` argument allows sorting when using CTE queries and prevents errors in postgres when using CTE search optimisation
237 238 239 240 241 242 |
# File 'app/models/issue.rb', line 237 def self.order_by_position_and_priority(with_cte: false) order_labels_priority(with_cte: with_cte) .reorder(Gitlab::Database.nulls_last_order('relative_position', 'ASC'), Gitlab::Database.nulls_last_order('highest_priority', 'ASC'), "id DESC") end |
.project_foreign_key ⇒ Object
206 207 208 |
# File 'app/models/issue.rb', line 206 def self.project_foreign_key 'project_id' end |
.reference_pattern ⇒ Object
Pattern used to extract `#123` issue references from text
This pattern supports cross-project references.
191 192 193 194 195 196 |
# File 'app/models/issue.rb', line 191 def self.reference_pattern @reference_pattern ||= %r{ (#{Project.reference_pattern})? #{Regexp.escape(reference_prefix)}#{Gitlab::Regex.issue} }x end |
.reference_prefix ⇒ Object
184 185 186 |
# File 'app/models/issue.rb', line 184 def self.reference_prefix '#' end |
.reference_valid?(reference) ⇒ Boolean
202 203 204 |
# File 'app/models/issue.rb', line 202 def self.reference_valid?(reference) reference.to_i > 0 && reference.to_i <= Gitlab::Database::MAX_INT_VALUE end |
.relative_positioning_parent_column ⇒ Object
180 181 182 |
# File 'app/models/issue.rb', line 180 def self.relative_positioning_parent_column :project_id end |
.relative_positioning_query_base(issue) ⇒ Object
176 177 178 |
# File 'app/models/issue.rb', line 176 def self.relative_positioning_query_base(issue) in_projects(issue.parent_ids) end |
.simple_sorts ⇒ Object
210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'app/models/issue.rb', line 210 def self.simple_sorts super.merge( { 'closest_future_date' => -> { order_closest_future_date }, 'closest_future_date_asc' => -> { order_closest_future_date }, 'due_date' => -> { order_due_date_asc.with_order_id_desc }, 'due_date_asc' => -> { order_due_date_asc.with_order_id_desc }, 'due_date_desc' => -> { order_due_date_desc.with_order_id_desc }, 'relative_position' => -> { order_relative_position_asc.with_order_id_desc }, 'relative_position_asc' => -> { order_relative_position_asc.with_order_id_desc } } ) end |
.sort_by_attribute(method, excluded_labels: []) ⇒ Object
224 225 226 227 228 229 230 231 232 233 |
# File 'app/models/issue.rb', line 224 def self.sort_by_attribute(method, excluded_labels: []) case method.to_s when 'closest_future_date', 'closest_future_date_asc' then order_closest_future_date when 'due_date', 'due_date_asc' then order_due_date_asc.with_order_id_desc when 'due_date_desc' then order_due_date_desc.with_order_id_desc when 'relative_position', 'relative_position_asc' then order_relative_position_asc.with_order_id_desc else super end end |
Instance Method Details
#as_json(options = {}) ⇒ Object
352 353 354 355 356 357 358 359 360 361 362 |
# File 'app/models/issue.rb', line 352 def as_json( = {}) super().tap do |json| if .key?(:labels) json[:labels] = labels.as_json( project: project, only: [:id, :title, :description, :color, :priority], methods: [:text_color] ) end end end |
#banzai_render_context(field) ⇒ Object
390 391 392 |
# File 'app/models/issue.rb', line 390 def banzai_render_context(field) super.merge(label_url_method: :project_issues_url) end |
#can_be_worked_on? ⇒ Boolean
329 330 331 |
# File 'app/models/issue.rb', line 329 def can_be_worked_on? !self.closed? && !self.project.forked? end |
#can_move?(user, to_project = nil) ⇒ Boolean
285 286 287 288 289 290 291 292 |
# File 'app/models/issue.rb', line 285 def can_move?(user, to_project = nil) if to_project return false unless user.can?(:admin_issue, to_project) end !moved? && persisted? && user.can?(:admin_issue, self.project) end |
#check_for_spam? ⇒ Boolean
347 348 349 350 |
# File 'app/models/issue.rb', line 347 def check_for_spam? publicly_visible? && (title_changed? || description_changed? || confidential_changed?) end |
#design_collection ⇒ Object
394 395 396 |
# File 'app/models/issue.rb', line 394 def design_collection @design_collection ||= ::DesignManagement::DesignCollection.new(self) end |
#discussions_rendered_on_frontend? ⇒ Boolean
368 369 370 |
# File 'app/models/issue.rb', line 368 def discussions_rendered_on_frontend? true end |
#duplicated? ⇒ Boolean
281 282 283 |
# File 'app/models/issue.rb', line 281 def duplicated? !duplicated_to_id.nil? end |
#etag_caching_enabled? ⇒ Boolean
364 365 366 |
# File 'app/models/issue.rb', line 364 def etag_caching_enabled? true end |
#from_service_desk? ⇒ Boolean
398 399 400 |
# File 'app/models/issue.rb', line 398 def from_service_desk? .id == User.support_bot.id end |
#has_related_branch? ⇒ Boolean
Returns boolean if a related branch exists for the current issue ignores merge requests branchs
266 267 268 269 270 |
# File 'app/models/issue.rb', line 266 def project.repository.branch_names.any? do |branch| /\A#{iid}-(?!\d+-stable)/i =~ branch end end |
#hook_attrs ⇒ Object
244 245 246 |
# File 'app/models/issue.rb', line 244 def hook_attrs Gitlab::HookData::IssueBuilder.new(self).build end |
#issue_link_type ⇒ Object
402 403 404 405 406 407 408 409 |
# File 'app/models/issue.rb', line 402 def issue_link_type return unless respond_to?(:issue_link_type_value) && respond_to?(:issue_link_source_id) type = IssueLink.link_types.key(issue_link_type_value) || IssueLink::TYPE_RELATES_TO return type if issue_link_source_id == id IssueLink.inverse_link_type(type) end |
#labels_hook_attrs ⇒ Object
382 383 384 |
# File 'app/models/issue.rb', line 382 def labels_hook_attrs labels.map(&:hook_attrs) end |
#merge_requests_count(user = nil) ⇒ Object
rubocop: enable CodeReuse/ServiceClass
378 379 380 |
# File 'app/models/issue.rb', line 378 def merge_requests_count(user = nil) ::MergeRequestsClosingIssues.count_for_issue(self.id, user) end |
#moved? ⇒ Boolean
277 278 279 |
# File 'app/models/issue.rb', line 277 def moved? !moved_to_id.nil? end |
#previous_updated_at ⇒ Object
386 387 388 |
# File 'app/models/issue.rb', line 386 def previous_updated_at previous_changes['updated_at']&.first || updated_at end |
#related_issues(current_user, preload: nil) ⇒ Object
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 |
# File 'app/models/issue.rb', line 311 def (current_user, preload: nil) = ::Issue .select(['issues.*', 'issue_links.id AS issue_link_id', 'issue_links.link_type as issue_link_type_value', 'issue_links.target_id as issue_link_source_id']) .joins("INNER JOIN issue_links ON (issue_links.source_id = issues.id AND issue_links.target_id = #{id}) OR (issue_links.target_id = issues.id AND issue_links.source_id = #{id})") .preload(preload) .reorder('issue_link_id') cross_project_filter = -> (issues) { issues.where(project: project) } Ability.issues_readable_by_user(, current_user, filters: { read_cross_project: cross_project_filter }) end |
#source_project ⇒ Object
To allow polymorphism with MergeRequest.
273 274 275 |
# File 'app/models/issue.rb', line 273 def source_project project end |
#suggested_branch_name ⇒ Object
255 256 257 258 259 260 261 262 |
# File 'app/models/issue.rb', line 255 def suggested_branch_name return to_branch_name unless project.repository.branch_exists?(to_branch_name) start_counting_from = 2 Uniquify.new(start_counting_from).string(-> (counter) { "#{to_branch_name}-#{counter}" }) do |suggested_branch_name| project.repository.branch_exists?(suggested_branch_name) end end |
#to_branch_name ⇒ Object
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 |
# File 'app/models/issue.rb', line 294 def to_branch_name if self.confidential? "#{iid}-confidential-issue" else branch_name = "#{iid}-#{title.parameterize}" if branch_name.length > 100 truncated_string = branch_name[0, 100] # Delete everything dangling after the last hyphen so as not to risk # existence of unintended words in the branch name due to mid-word split. branch_name = truncated_string[0, truncated_string.rindex("-")] end branch_name end end |
#to_reference(from = nil, full: false) ⇒ Object
`from` argument can be a Namespace or Project.
249 250 251 252 253 |
# File 'app/models/issue.rb', line 249 def to_reference(from = nil, full: false) reference = "#{self.class.reference_prefix}#{iid}" "#{project.to_reference_base(from, full: full)}#{reference}" end |
#update_project_counter_caches ⇒ Object
rubocop: disable CodeReuse/ServiceClass
373 374 375 |
# File 'app/models/issue.rb', line 373 def update_project_counter_caches Projects::OpenIssuesCountService.new(project).refresh_cache end |
#visible_to_user?(user = nil) ⇒ Boolean
Returns `true` if the current issue can be viewed by either a logged in User or an anonymous user.
335 336 337 338 339 340 341 342 343 344 345 |
# File 'app/models/issue.rb', line 335 def visible_to_user?(user = nil) return false unless project && project.feature_available?(:issues, user) return publicly_visible? unless user return false unless readable_by?(user) user.can_read_all_resources? || ::Gitlab::ExternalAuthorization.access_allowed?( user, project.) end |