2012-11-19 13:24:05 -05:00
# == Schema Information
#
# Table name: issues
#
2015-09-06 10:48:48 -04:00
# id :integer not null, primary key
# title :string(255)
# assignee_id :integer
# author_id :integer
# project_id :integer
# created_at :datetime
# updated_at :datetime
# position :integer default(0)
# branch_name :string(255)
# description :text
# milestone_id :integer
# state :string(255)
# iid :integer
# updated_by_id :integer
2012-11-19 13:24:05 -05:00
#
2014-05-23 04:22:00 -04:00
require 'carrierwave/orm/activerecord'
require 'file_size_validator'
2011-10-08 17:36:38 -04:00
class Issue < ActiveRecord :: Base
2013-08-21 05:16:26 -04:00
include InternalId
2015-05-02 23:11:21 -04:00
include Issuable
include Referable
2015-02-05 19:49:41 -05:00
include Sortable
2015-05-02 23:11:21 -04:00
include Taskable
2012-06-07 08:44:57 -04:00
2013-10-02 09:18:28 -04:00
ActsAsTaggableOn . strict_case_match = true
2013-04-25 10:15:33 -04:00
belongs_to :project
validates :project , presence : true
2016-01-07 09:34:37 -05:00
scope :of_group ,
- > ( group ) { where ( project_id : group . projects . select ( :id ) . reorder ( nil ) ) }
2013-04-02 18:28:12 -04:00
scope :cared , - > ( user ) { where ( assignee_id : user ) }
2013-06-17 06:29:50 -04:00
scope :open_for , - > ( user ) { opened . assigned_to ( user ) }
2016-01-22 04:24:38 -05:00
scope :in_projects , - > ( project_ids ) { where ( project_id : project_ids ) }
2013-02-19 04:01:19 -05:00
2013-02-18 08:22:18 -05:00
state_machine :state , initial : :opened do
2013-02-18 04:10:58 -05:00
event :close do
transition [ :reopened , :opened ] = > :closed
end
event :reopen do
2013-02-18 08:22:18 -05:00
transition closed : :reopened
2013-02-18 04:10:58 -05:00
end
state :opened
state :reopened
state :closed
end
2013-04-09 08:04:31 -04:00
2015-05-14 16:59:39 -04:00
def hook_attrs
attributes
end
2016-03-17 15:38:51 -04:00
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
2015-05-02 23:11:21 -04:00
def self . reference_prefix
'#'
end
2015-05-14 16:59:39 -04:00
# Pattern used to extract `#123` issue references from text
#
# This pattern supports cross-project references.
def self . reference_pattern
%r{
2015-12-01 09:51:27 -05:00
( #{Project.reference_pattern})?
#{Regexp.escape(reference_prefix)}(?<issue>\d+)
2015-05-14 16:59:39 -04:00
} x
2014-09-15 03:10:35 -04:00
end
2015-11-30 15:14:46 -05:00
def self . link_reference_pattern
super ( " issues " , / (?<issue> \ d+) / )
end
2015-05-02 23:11:21 -04:00
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
2016-01-12 12:10:06 -05:00
def referenced_merge_requests ( current_user = nil )
2016-03-15 15:17:51 -04:00
@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
2016-02-22 03:20:04 -05:00
end
2015-12-04 14:00:07 -05:00
end
2016-02-12 13:42:25 -05:00
def related_branches
2016-03-17 06:48:07 -04:00
return [ ] if self . project . empty_repo?
2016-03-15 15:17:51 -04:00
self . project . repository . branch_names . select do | branch |
branch =~ / \ A #{ iid } -(?! \ d+-stable) /i
end
2016-02-12 13:42:25 -05:00
end
2013-12-13 14:40:45 -05:00
# 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.
def reset_events_cache
2014-07-16 14:44:24 -04:00
Event . reset_event_cache_for ( self )
2013-12-13 14:40:45 -05:00
end
2014-09-20 10:06:35 -04:00
# To allow polymorphism with MergeRequest.
def source_project
project
end
2015-10-12 06:04:20 -04:00
# From all notes on this issue, we'll select the system notes about linked
# merge requests. Of those, the MRs closing `self` are returned.
2015-10-13 03:41:46 -04:00
def closed_by_merge_requests ( current_user = nil )
return [ ] unless open ?
2015-10-12 06:04:20 -04:00
notes . system . flat_map do | note |
2015-10-13 03:41:46 -04:00
note . all_references ( current_user ) . merge_requests
end . uniq . select { | mr | mr . open? && mr . closes_issue? ( self ) }
2015-10-12 06:04:20 -04:00
end
2016-02-12 13:42:25 -05:00
def to_branch_name
2016-02-17 01:11:48 -05:00
" #{ iid } - #{ title . parameterize } "
2016-02-12 13:42:25 -05:00
end
2016-02-17 01:11:48 -05:00
def can_be_worked_on? ( current_user )
2016-02-12 13:42:25 -05:00
! self . closed? &&
2016-02-17 01:11:48 -05:00
! self . project . forked? &&
2016-02-22 03:20:04 -05:00
self . related_branches . empty? &&
2016-03-15 15:17:51 -04:00
self . closed_by_merge_requests ( current_user ) . empty?
2016-02-12 13:42:25 -05:00
end
2011-10-08 17:36:38 -04:00
end