Module: Issuable
- Extended by:
- ActiveSupport::Concern
- Includes:
- AfterCommitQueue, Awardable, CacheMarkdownField, ClosedAtFilterable, CreatedAtFilterable, Editable, Exportable, Gitlab::SQL::Pattern, Importable, Mentionable, Milestoneable, Participable, Redactable, Sortable, SortableTitle, StripAttribute, Subscribable, Taskable, Transitionable, UpdatedAtFilterable, VersionedDescription
- Included in:
- Issue, MergeRequest
- Defined in:
- app/models/concerns/issuable.rb,
app/services/issuable/callbacks/base.rb,
app/services/issuable/destroy_service.rb,
app/services/issuable/process_assignees.rb,
app/services/issuable/clone/base_service.rb,
app/services/issuable/bulk_update_service.rb,
app/services/issuable/callbacks/milestone.rb,
app/services/issuable/import_csv/base_service.rb,
app/services/issuable/discussions_list_service.rb,
app/workers/issuable/label_links_destroy_worker.rb,
app/services/issuable/common_system_notes_service.rb,
app/services/issuable/destroy_label_links_service.rb
Overview
This service return notes grouped by discussion ID and paginated per discussion. System notes also have a discussion ID assigned including Synthetic system notes.
Defined Under Namespace
Modules: Callbacks, Clone, ImportCsv Classes: BulkUpdateService, CommonSystemNotesService, DestroyLabelLinksService, DestroyService, DiscussionsListService, LabelLinksDestroyWorker, ProcessAssignees
Constant Summary collapse
- TITLE_LENGTH_MAX =
255
- TITLE_HTML_LENGTH_MAX =
800
- DESCRIPTION_LENGTH_MAX =
1.megabyte
- DESCRIPTION_HTML_LENGTH_MAX =
5.megabytes
- SEARCHABLE_FIELDS =
%w[title description].freeze
- MAX_NUMBER_OF_ASSIGNEES_OR_REVIEWERS =
200
- STATE_ID_MAP =
{ opened: 1, closed: 2, merged: 3, locked: 4 }.with_indifferent_access.freeze
Constants included from Taskable
Taskable::COMPLETED, Taskable::COMPLETE_PATTERN, Taskable::INCOMPLETE, Taskable::INCOMPLETE_PATTERN, Taskable::ITEM_PATTERN, Taskable::ITEM_PATTERN_UNTRUSTED, Taskable::REGEX
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_TERM
Instance Attribute Summary
Attributes included from Transitionable
Attributes included from Importable
Attributes included from CacheMarkdownField
#skip_markdown_cache_validation
Instance Method Summary collapse
- #allows_scoped_labels? ⇒ Boolean
- #assignee?(user) ⇒ Boolean
- #assignee_list ⇒ Object
- #assignee_or_author?(user) ⇒ Boolean
- #assignee_username_list ⇒ Object
- #can_assign_epic?(user) ⇒ Boolean
-
#can_move? ⇒ Boolean
Method that checks if issuable can be moved to another project.
-
#card_attributes ⇒ Object
Returns a Hash of attributes to be used for Twitter card metadata.
-
#draftless_title_changed(old_title) ⇒ Object
Overridden in MergeRequest.
-
#first_contribution? ⇒ Boolean
Override in issuable specialization.
- #hook_association_changes(old_associations) ⇒ Object
- #hook_reviewer_changes(old_associations) ⇒ Object
- #label_names ⇒ Object
- #labels_array ⇒ Object
- #labels_hook_attrs ⇒ Object
- #notes_with_associations ⇒ Object
- #open? ⇒ Boolean
- #overdue? ⇒ Boolean
- #read_ability_for(participable_source) ⇒ Object
- #resource_parent ⇒ Object
- #state ⇒ Object
- #state=(value) ⇒ Object
- #subscribed_without_subscriptions?(user, project) ⇒ Boolean
- #supports_health_status? ⇒ Boolean
-
#to_ability_name ⇒ Object
Convert this Issuable class name to a format usable by Ability definitions.
- #to_hook_data(user, old_associations: {}) ⇒ Object
- #updated_tasks ⇒ Object
- #user_notes_count ⇒ Object
Methods included from Exportable
#exportable_association?, #restricted_associations, #to_authorized_json
Methods included from AfterCommitQueue
#run_after_commit, #run_after_commit_or_now
Methods included from Editable
Methods included from Transitionable
#disable_transitioning, #enable_transitioning, #transitioning?
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
#lazy_subscription, #set_subscription, #subscribe, #subscribed?, #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, #user_mention_class, #user_mention_identifier
Methods included from Participable
#participant?, #participants, #visible_participants
Methods included from CacheMarkdownField
#attribute_invalidated?, #banzai_render_context, #cached_html_for, #cached_html_up_to_date?, #can_cache_field?, #invalidated_markdown_cache?, #latest_cached_markdown_version, #local_version, #mentionable_attributes_changed?, #mentioned_filtered_user_ids_for, #parent_user, #refresh_markdown_cache, #refresh_markdown_cache!, #rendered_field_content, #skip_project_check?, #store_mentions!, #updated_cached_html_for
Methods included from Gitlab::SQL::Pattern
Instance Method Details
#allows_scoped_labels? ⇒ Boolean
589 590 591 |
# File 'app/models/concerns/issuable.rb', line 589 def allows_scoped_labels? false end |
#assignee?(user) ⇒ Boolean
477 478 479 480 481 482 483 484 |
# File 'app/models/concerns/issuable.rb', line 477 def assignee?(user) # Necessary so we can preload the association and avoid N + 1 queries if assignees.loaded? assignees.to_a.include?(user) else assignees.exists?(user.id) end end |
#assignee_list ⇒ Object
611 612 613 |
# File 'app/models/concerns/issuable.rb', line 611 def assignee_list assignees.map(&:name).to_sentence end |
#assignee_or_author?(user) ⇒ Boolean
473 474 475 |
# File 'app/models/concerns/issuable.rb', line 473 def (user) == user.id || assignee?(user) end |
#assignee_username_list ⇒ Object
615 616 617 |
# File 'app/models/concerns/issuable.rb', line 615 def assignee_username_list assignees.map(&:username).to_sentence end |
#can_assign_epic?(user) ⇒ Boolean
510 511 512 |
# File 'app/models/concerns/issuable.rb', line 510 def can_assign_epic?(user) false end |
#can_move? ⇒ Boolean
Method that checks if issuable can be moved to another project.
Should be overridden if issuable can be moved.
651 652 653 |
# File 'app/models/concerns/issuable.rb', line 651 def can_move?(*) false end |
#card_attributes ⇒ Object
Returns a Hash of attributes to be used for Twitter card metadata
604 605 606 607 608 609 |
# File 'app/models/concerns/issuable.rb', line 604 def card_attributes { 'Author' => .try(:name), 'Assignee' => assignee_list } end |
#draftless_title_changed(old_title) ⇒ Object
Overridden in MergeRequest
665 666 667 |
# File 'app/models/concerns/issuable.rb', line 665 def draftless_title_changed(old_title) old_title != title end |
#first_contribution? ⇒ Boolean
Override in issuable specialization
658 659 660 |
# File 'app/models/concerns/issuable.rb', line 658 def first_contribution? false end |
#hook_association_changes(old_associations) ⇒ Object
514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 |
# File 'app/models/concerns/issuable.rb', line 514 def hook_association_changes(old_associations) changes = {} old_labels = old_associations.fetch(:labels, labels) old_assignees = old_associations.fetch(:assignees, assignees) old_severity = old_associations.fetch(:severity, severity) if old_labels != labels changes[:labels] = [old_labels.map(&:hook_attrs), labels.map(&:hook_attrs)] end if old_assignees != assignees changes[:assignees] = [old_assignees.map(&:hook_attrs), assignees.map(&:hook_attrs)] end if supports_severity? && old_severity != severity changes[:severity] = [old_severity, severity] end if supports_escalation? && escalation_status current_escalation_status = escalation_status.status_name old_escalation_status = old_associations.fetch(:escalation_status, current_escalation_status) if old_escalation_status != current_escalation_status changes[:escalation_status] = [old_escalation_status, current_escalation_status] end end if self.respond_to?(:total_time_spent) old_total_time_spent = old_associations.fetch(:total_time_spent, total_time_spent) old_time_change = old_associations.fetch(:time_change, time_change) if old_total_time_spent != total_time_spent changes[:total_time_spent] = [old_total_time_spent, total_time_spent] changes[:time_change] = [old_time_change, time_change] end end changes end |
#hook_reviewer_changes(old_associations) ⇒ Object
555 556 557 558 559 560 561 562 563 564 |
# File 'app/models/concerns/issuable.rb', line 555 def hook_reviewer_changes(old_associations) changes = {} old_reviewers = old_associations.fetch(:reviewers, reviewers) if old_reviewers != reviewers changes[:reviewers] = [old_reviewers.map(&:hook_attrs), reviewers.map(&:hook_attrs)] end changes end |
#label_names ⇒ Object
581 582 583 |
# File 'app/models/concerns/issuable.rb', line 581 def label_names labels.order('title ASC').pluck(:title) end |
#labels_array ⇒ Object
577 578 579 |
# File 'app/models/concerns/issuable.rb', line 577 def labels_array labels.to_a end |
#labels_hook_attrs ⇒ Object
585 586 587 |
# File 'app/models/concerns/issuable.rb', line 585 def labels_hook_attrs labels.map(&:hook_attrs) end |
#notes_with_associations ⇒ Object
619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 |
# File 'app/models/concerns/issuable.rb', line 619 def notes_with_associations # If A has_many Bs, and B has_many Cs, and you do # `A.includes(b: :c).each { |a| a.b.includes(:c) }`, sadly ActiveRecord # will do the inclusion again. So, we check if all notes in the relation # already have their authors loaded (possibly because the scope # `inc_notes_with_associations` was used) and skip the inclusion if that's # the case. includes = [] includes << :author unless notes. includes << :award_emoji unless notes.award_emojis_loaded? includes << :project unless notes.projects_loaded? includes << :system_note_metadata unless notes. if includes.any? notes.includes(includes) else notes end end |
#open? ⇒ Boolean
486 487 488 |
# File 'app/models/concerns/issuable.rb', line 486 def open? opened? end |
#overdue? ⇒ Boolean
490 491 492 493 494 |
# File 'app/models/concerns/issuable.rb', line 490 def overdue? return false unless respond_to?(:due_date) due_date.try(:past?) || false end |
#read_ability_for(participable_source) ⇒ Object
669 670 671 672 673 674 675 676 |
# File 'app/models/concerns/issuable.rb', line 669 def read_ability_for(participable_source) return super if participable_source == self return super if participable_source.is_a?(Note) && participable_source.system? name = participable_source.try(:issuable_ability_name) || :read_issuable_participables { name: name, subject: self } end |
#resource_parent ⇒ Object
469 470 471 |
# File 'app/models/concerns/issuable.rb', line 469 def resource_parent project end |
#state ⇒ Object
461 462 463 |
# File 'app/models/concerns/issuable.rb', line 461 def state self.class.available_states.key(state_id) end |
#state=(value) ⇒ Object
465 466 467 |
# File 'app/models/concerns/issuable.rb', line 465 def state=(value) self.state_id = self.class.available_states[value] end |
#subscribed_without_subscriptions?(user, project) ⇒ Boolean
506 507 508 |
# File 'app/models/concerns/issuable.rb', line 506 def subscribed_without_subscriptions?(user, project) participant?(user) end |
#supports_health_status? ⇒ Boolean
678 679 680 |
# File 'app/models/concerns/issuable.rb', line 678 def supports_health_status? false end |
#to_ability_name ⇒ Object
Convert this Issuable class name to a format usable by Ability definitions
Examples:
issuable.class # => MergeRequest
issuable.to_ability_name # => "merge_request"
599 600 601 |
# File 'app/models/concerns/issuable.rb', line 599 def to_ability_name self.class.to_ability_name end |
#to_hook_data(user, old_associations: {}) ⇒ Object
566 567 568 569 570 571 572 573 574 575 |
# File 'app/models/concerns/issuable.rb', line 566 def to_hook_data(user, old_associations: {}) changes = previous_changes if old_associations.present? changes.merge!(hook_association_changes(old_associations)) changes.merge!(hook_reviewer_changes(old_associations)) if allows_reviewers? end Gitlab::DataBuilder::Issuable.new(self).build(user: user, changes: changes) end |
#updated_tasks ⇒ Object
639 640 641 642 643 644 |
# File 'app/models/concerns/issuable.rb', line 639 def updated_tasks Taskable.get_updated_tasks( old_content: previous_changes['description'].first, new_content: description ) end |
#user_notes_count ⇒ Object
496 497 498 499 500 501 502 503 504 |
# File 'app/models/concerns/issuable.rb', line 496 def user_notes_count if notes.loaded? # Use the in-memory association to select and count to avoid hitting the db notes.to_a.count { |note| !note.system? } else # do the count query notes.user.count end end |