Class: Issue

Inherits:
ActiveRecord::Base
  • Object
show all
Includes:
InternalId, Issuable, Referable, Sortable, Taskable
Defined in:
app/models/issue.rb

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

Constants included from Taskable

Taskable::COMPLETED, Taskable::INCOMPLETE, Taskable::ITEM_PATTERN

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Taskable

get_tasks, get_updated_tasks, #task_list_items, #task_status, #tasks, #tasks?

Methods included from Referable

#reference_link_text

Methods included from Issuable

#add_labels_by_names, #card_attributes, #downvotes, #is_assigned?, #is_being_reassigned?, #label_names, #new?, #notes_with_associations, #open?, #remove_labels, #subscribed_without_subscriptions?, #to_ability_name, #to_hook_data, #today?, #updated_tasks, #upvotes

Methods included from StripAttribute

#strip_attributes

Methods included from Subscribable

#subscribed?, #subscribed_without_subscriptions?, #subscribers, #toggle_subscription, #unsubscribe

Methods included from Mentionable

#all_references, #create_cross_references!, #create_new_cross_references!, #gfm_reference, #local_reference, #mentioned_users, #referenced_mentionables

Methods included from Participable

#participants

Methods included from InternalId

#set_iid, #to_param

Class Method Details


98
99
100
# File 'app/models/issue.rb', line 98

def self.link_reference_pattern
  @link_reference_pattern ||= super("issues", /(?<issue>\d+)/)
end

.reference_patternObject

Pattern used to extract `#123` issue references from text

This pattern supports cross-project references.


91
92
93
94
95
96
# File 'app/models/issue.rb', line 91

def self.reference_pattern
  @reference_pattern ||= %r{
    (#{Project.reference_pattern})?
    #{Regexp.escape(reference_prefix)}(?<issue>\d+)
  }x
end

.reference_prefixObject


84
85
86
# File 'app/models/issue.rb', line 84

def self.reference_prefix
  '#'
end

.sort(method) ⇒ Object


102
103
104
105
106
107
108
109
# File 'app/models/issue.rb', line 102

def self.sort(method)
  case method.to_s
  when 'due_date_asc' then order_due_date_asc
  when 'due_date_desc'  then order_due_date_desc
  else
    super
  end
end

.visible_to_user(user) ⇒ Object


77
78
79
80
81
82
# File 'app/models/issue.rb', line 77

def self.visible_to_user(user)
  return where(confidential: false) if user.blank?
  return all if user.admin?

  where('issues.confidential = false OR (issues.confidential = true AND (issues.author_id = :user_id OR issues.assignee_id = :user_id OR issues.project_id IN(:project_ids)))', user_id: user.id, project_ids: user.authorized_projects.select(:id))
end

Instance Method Details

#can_be_worked_on?(current_user) ⇒ Boolean


192
193
194
195
196
197
# File 'app/models/issue.rb', line 192

def can_be_worked_on?(current_user)
  !self.closed? &&
    !self.project.forked? &&
    self.related_branches(current_user).empty? &&
    self.closed_by_merge_requests(current_user).empty?
end

#can_move?(user, to_project = nil) ⇒ Boolean


175
176
177
178
179
180
181
182
# File 'app/models/issue.rb', line 175

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

#closed_by_merge_requests(current_user = nil) ⇒ Object

From all notes on this issue, we'll select the system notes about linked merge requests. Of those, the MRs closing `self` are returned.


163
164
165
166
167
168
169
# File 'app/models/issue.rb', line 163

def closed_by_merge_requests(current_user = nil)
  return [] unless open?

  notes.system.flat_map do |note|
    note.all_references(current_user).merge_requests
  end.uniq.select { |mr| mr.open? && mr.closes_issue?(self) }
end

#hook_attrsObject


73
74
75
# File 'app/models/issue.rb', line 73

def hook_attrs
  attributes
end

#moved?Boolean


171
172
173
# File 'app/models/issue.rb', line 171

def moved?
  !moved_to.nil?
end

#overdue?Boolean


199
200
201
# File 'app/models/issue.rb', line 199

def overdue?
  due_date.try(:past?) || false
end

#referenced_merge_requests(current_user = nil) ⇒ Object


121
122
123
124
125
126
127
128
129
130
# File 'app/models/issue.rb', line 121

def referenced_merge_requests(current_user = nil)
  @referenced_merge_requests ||= {}
  @referenced_merge_requests[current_user] ||= begin
    Gitlab::ReferenceExtractor.lazily do
      [self, *notes].flat_map do |note|
        note.all_references(current_user).merge_requests
      end
    end.sort_by(&:iid).uniq
  end
end

All branches containing the current issue's ID, except for those with a merge request open referencing the current issue.


134
135
136
137
138
139
140
141
142
# File 'app/models/issue.rb', line 134

def related_branches(current_user)
  branches_with_iid = project.repository.branch_names.select do |branch|
    branch =~ /\A#{iid}-(?!\d+-stable)/i
  end

  branches_with_merge_request = self.referenced_merge_requests(current_user).map(&:source_branch)

  branches_with_iid - branches_with_merge_request
end

#reset_events_cacheObject

Reset issue events cache

Since we do cache @event we need to reset cache in special cases:

  • when an issue is updated

Events cache stored like events/23-20130109142513. The cache key includes updated_at timestamp. Thus it will automatically generate a new fragment when the event is updated because the key changes.


152
153
154
# File 'app/models/issue.rb', line 152

def reset_events_cache
  Event.reset_event_cache_for(self)
end

#source_projectObject

To allow polymorphism with MergeRequest.


157
158
159
# File 'app/models/issue.rb', line 157

def source_project
  project
end

#to_branch_nameObject


184
185
186
187
188
189
190
# File 'app/models/issue.rb', line 184

def to_branch_name
  if self.confidential?
    "#{iid}-confidential-issue"
  else
    "#{iid}-#{title.parameterize}"
  end
end

#to_reference(from_project = nil) ⇒ Object


111
112
113
114
115
116
117
118
119
# File 'app/models/issue.rb', line 111

def to_reference(from_project = nil)
  reference = "#{self.class.reference_prefix}#{iid}"

  if cross_project_reference?(from_project)
    reference = project.to_reference + reference
  end

  reference
end