summaryrefslogtreecommitdiff
path: root/app/models/issue.rb
diff options
context:
space:
mode:
Diffstat (limited to 'app/models/issue.rb')
-rw-r--r--app/models/issue.rb63
1 files changed, 53 insertions, 10 deletions
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 10a5d9d2a24..a88dbb3e065 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -3,6 +3,7 @@ require 'carrierwave/orm/activerecord'
class Issue < ActiveRecord::Base
include InternalId
include Issuable
+ include Noteable
include Referable
include Sortable
include Spammable
@@ -23,12 +24,17 @@ class Issue < ActiveRecord::Base
has_many :merge_requests_closing_issues, class_name: 'MergeRequestsClosingIssues', dependent: :delete_all
+ has_many :issue_assignees
+ has_many :assignees, class_name: "User", through: :issue_assignees
+
validates :project, presence: true
- scope :cared, ->(user) { where(assignee_id: user) }
- scope :open_for, ->(user) { opened.assigned_to(user) }
scope :in_projects, ->(project_ids) { where(project_id: project_ids) }
+ scope :assigned, -> { where('EXISTS (SELECT TRUE FROM issue_assignees WHERE issue_id = issues.id)') }
+ scope :unassigned, -> { where('NOT EXISTS (SELECT TRUE FROM issue_assignees WHERE issue_id = issues.id)') }
+ scope :assigned_to, ->(u) { where('EXISTS (SELECT TRUE FROM issue_assignees WHERE user_id = ? AND issue_id = issues.id)', u.id)}
+
scope :without_due_date, -> { where(due_date: nil) }
scope :due_before, ->(date) { where('issues.due_date < ?', date) }
scope :due_between, ->(from_date, to_date) { where('issues.due_date >= ?', from_date).where('issues.due_date <= ?', to_date) }
@@ -38,11 +44,15 @@ class Issue < ActiveRecord::Base
scope :created_after, -> (datetime) { where("created_at >= ?", datetime) }
- scope :include_associations, -> { includes(:assignee, :labels, project: :namespace) }
+ scope :include_associations, -> { includes(:labels, project: :namespace) }
+
+ after_save :expire_etag_cache
attr_spammable :title, spam_title: true
attr_spammable :description, spam_description: true
+ participant :assignees
+
state_machine :state, initial: :opened do
event :close do
transition [:reopened, :opened] => :closed
@@ -59,17 +69,17 @@ class Issue < ActiveRecord::Base
before_transition any => :closed do |issue|
issue.closed_at = Time.zone.now
end
-
- before_transition closed: any do |issue|
- issue.closed_at = nil
- end
end
def hook_attrs
+ assignee_ids = self.assignee_ids
+
attrs = {
total_time_spent: total_time_spent,
human_total_time_spent: human_total_time_spent,
- human_time_estimate: human_time_estimate
+ human_time_estimate: human_time_estimate,
+ assignee_ids: assignee_ids,
+ assignee_id: assignee_ids.first # This key is deprecated
}
attributes.merge!(attrs)
@@ -117,6 +127,22 @@ class Issue < ActiveRecord::Base
"id DESC")
end
+ # Returns a Hash of attributes to be used for Twitter card metadata
+ def card_attributes
+ {
+ 'Author' => author.try(:name),
+ 'Assignee' => assignee_list
+ }
+ end
+
+ def assignee_or_author?(user)
+ author_id == user.id || assignees.exists?(user.id)
+ end
+
+ def assignee_list
+ assignees.map(&:name).to_sentence
+ end
+
# `from` argument can be a Namespace or Project.
def to_reference(from = nil, full: false)
reference = "#{self.class.reference_prefix}#{iid}"
@@ -146,6 +172,14 @@ class Issue < ActiveRecord::Base
branches_with_iid - branches_with_merge_request
end
+ # Returns boolean if a related branch exists for the current issue
+ # ignores merge requests branchs
+ def has_related_branch?
+ project.repository.branch_names.any? do |branch|
+ /\A#{iid}-(?!\d+-stable)/i =~ branch
+ end
+ end
+
# To allow polymorphism with MergeRequest.
def source_project
project
@@ -202,7 +236,7 @@ class Issue < ActiveRecord::Base
# Returns `true` if the current issue can be viewed by either a logged in User
# or an anonymous user.
def visible_to_user?(user = nil)
- return false unless project.feature_available?(:issues, user)
+ return false unless project && project.feature_available?(:issues, user)
user ? readable_by?(user) : publicly_visible?
end
@@ -243,7 +277,7 @@ class Issue < ActiveRecord::Base
true
elsif confidential?
author == user ||
- assignee == user ||
+ assignees.include?(user) ||
project.team.member?(user, Gitlab::Access::REPORTER)
else
project.public? ||
@@ -256,4 +290,13 @@ class Issue < ActiveRecord::Base
def publicly_visible?
project.public? && !confidential?
end
+
+ def expire_etag_cache
+ key = Gitlab::Routing.url_helpers.realtime_changes_namespace_project_issue_path(
+ project.namespace,
+ project,
+ self
+ )
+ Gitlab::EtagCaching::Store.new.touch(key)
+ end
end