rosa-build/app/models/comment.rb

215 lines
7.4 KiB
Ruby
Raw Normal View History

class Comment < ActiveRecord::Base
2014-03-11 11:47:03 +00:00
include Feed::Comment
2013-03-26 10:47:42 +00:00
# regexp take from http://code.google.com/p/concerto-platform/source/browse/v3/cms/lib/CodeMirror/mode/gfm/gfm.js?spec=svn861&r=861#71
# User/Project#Num
# User#Num
# #Num
2013-04-09 21:21:02 +01:00
ISSUES_REGEX = /(?:[a-zA-Z0-9\-_]*\/)?(?:[a-zA-Z0-9\-_]*)?#[0-9]+/
2013-03-26 10:47:42 +00:00
2014-03-17 20:18:46 +00:00
belongs_to :commentable, polymorphic: true
belongs_to :user
belongs_to :project
serialize :data
validates :body, :user, :commentable_id, :commentable_type, :project_id, presence: true
validates :body, length: { maximum: 10000 }
scope :for_commit, ->(c) { where(commentable_id: c.id.hex, commentable_type: c.class) }
default_scope { order(:created_at) }
2014-03-17 20:18:46 +00:00
before_save :touch_commentable
after_create :subscribe_on_reply, unless: ->(c) { c.commit_comment? }
2012-01-25 17:33:26 +00:00
after_create :subscribe_users
def commentable
2013-04-25 16:16:12 +01:00
commit_comment? ? project.repo.commit(Comment.hex_to_commit_hash commentable_id) : super
2012-01-20 15:17:05 +00:00
end
def commentable=(c)
if self.class.commit_comment?(c.class)
self.commentable_id = c.id.hex
self.commentable_type = c.class.name
else
super
end
end
def self.commit_comment?(class_name)
class_name.to_s == 'Grit::Commit'
end
def commit_comment?
self.class.commit_comment?(commentable_type)
end
def self.issue_comment?(class_name)
class_name.to_s == 'Issue'
end
def issue_comment?
self.class.issue_comment?(commentable_type)
end
def own_comment?(user)
user_id == user.id
end
def actual_inline_comment?(diff = nil, force = false)
unless force
raise "This is not inline comment!" if data.blank? # for debug
2012-10-17 19:38:42 +01:00
return data[:actual] unless data[:actual].nil?
return false if diff.nil?
end
return data[:actual] = true if commentable_type == 'Grit::Commit'
filepath, line_number = data[:path], data[:line]
2015-04-09 09:57:05 +01:00
diff_path = (diff || commentable.show ).select {|d| d.a_path == data[:path]}
comment_line = data[:line].to_i
# NB! also dont create a comment to the diff header
return data[:actual] = false if diff_path.blank? || comment_line == 0
res, ind = true, 0
diff_path[0].diff.each_line do |line|
if self.persisted? && (comment_line-2..comment_line+2).include?(ind) && data.try('[]', "line#{ind-comment_line}") != line.chomp
break res = false
end
ind = ind + 1
end
if ind < comment_line
return data[:actual] = false
else
return data[:actual] = res
end
end
2012-10-17 19:38:42 +01:00
def inline_diff
data[:strings] + data['line0']
end
def pull_comment?
commentable.is_a?(Issue) && commentable.pull_request.present?
end
2012-10-16 10:50:21 +01:00
def set_additional_data params
return true if params[:path].blank? && params[:line].blank? # not inline comment
2014-01-21 04:51:49 +00:00
if params[:in_reply].present? && reply = Comment.where(id: params[:in_reply]).first
2012-10-17 19:38:42 +01:00
self.data = reply.data
return true
end
2014-01-21 04:51:49 +00:00
self.data = {path: params[:path], line: params[:line]}
return actual_inline_comment?(nil, true) if commentable.is_a?(Grit::Commit)
2012-10-19 10:50:15 +01:00
if commentable.is_a?(Issue) && pull = commentable.pull_request
diff_path = pull.diff.select {|d| d.a_path == params[:path]}
return false unless actual_inline_comment?(pull.diff, true)
2012-10-16 10:50:21 +01:00
comment_line, line_number, strings = params[:line].to_i, -1, []
diff_path[0].diff.each_line do |line|
line_number = line_number.succ
# Save 2 lines above and bottom of the diff comment line
break if line_number > comment_line + 2
if (comment_line-2..comment_line+2).include? line_number
data["line#{line_number-comment_line}"] = line.chomp
end
# Save lines from the closest header for rendering in the discussion
2012-10-17 19:38:42 +01:00
if line_number < comment_line
2012-10-16 10:50:21 +01:00
# Header is the line like "@@ -47,9 +50,8 @@ def initialize(user)"
2012-10-17 19:38:42 +01:00
if line =~ Diff::Display::Unified::Generator::LINE_NUM_RE
strings = [line]
2012-10-16 10:50:21 +01:00
else
strings << line
end
end
end
2012-10-17 19:38:42 +01:00
## Bug with numbers of diff lines, now store all diff
data[:strings] = strings.join
# Limit stored diff to 10 lines (see inline_diff)
#data[:strings] = ((strings.count) <= 9 ? strings : [strings[0]] + strings.last(8)).join
##
2012-10-16 10:50:21 +01:00
data[:view_path] = h(diff_path[0].renamed_file ? "#{diff_path[0].a_path.rtruncate 60} -> #{diff_path[0].b_path.rtruncate 60}" : diff_path[0].a_path.rtruncate(120))
end
return true
end
def self.create_link_on_issues_from_item item, commits = nil
2013-04-01 12:41:18 +01:00
linker = item.user
2013-03-26 10:47:42 +00:00
case
when item.is_a?(GitHook)
elements = commits
opts = {}
when item.is_a?(Issue)
elements = [[item, item.title], [item, item.body]]
2014-01-21 04:51:49 +00:00
opts = {created_from_issue_id: item.id}
when item.commentable_type == 'Issue'
elements = [[item, item.body]]
2014-01-21 04:51:49 +00:00
opts = {created_from_issue_id: item.commentable_id}
when item.commentable_type == 'Grit::Commit'
elements = [[item, item.body]]
2014-01-21 04:51:49 +00:00
opts = {created_from_commit_hash: item.commentable_id}
else
raise "Unsupported item type #{item.class.name}!"
end
2013-03-26 10:47:42 +00:00
elements.each do |element|
element[1].scan(ISSUES_REGEX).each do |hash|
2015-03-14 22:10:04 +00:00
issue = Issue.find_by_hash_tag hash, linker, item.project
2013-03-22 16:46:26 +00:00
next unless issue
# dont create link to the same issue
next if opts[:created_from_issue_id] == issue.id
2014-01-21 04:51:49 +00:00
opts = {created_from_commit_hash: element[0].hex} if item.is_a?(GitHook)
# dont create duplicate link to issue
next if Comment.find_existing_automatic_comment issue, opts
# dont create link to outdated commit
2013-06-20 09:07:09 +01:00
next if item.is_a?(GitHook) && !item.project.repo.commit(element[0])
2014-01-21 04:51:49 +00:00
comment = linker.comments.new body: 'automatic comment'
2013-04-01 12:41:18 +01:00
comment.commentable, comment.project, comment.automatic = issue, issue.project, true
2014-01-21 04:51:49 +00:00
comment.data = {from_project_id: item.project.id}
if opts[:created_from_commit_hash]
comment.created_from_commit_hash = opts[:created_from_commit_hash]
elsif opts[:created_from_issue_id]
2014-01-21 04:51:49 +00:00
comment.data.merge!(comment_id: item.id) if item.is_a? Comment
comment.created_from_issue_id = opts[:created_from_issue_id]
else
raise 'Unsupported opts for automatic comment!'
2013-03-26 10:47:42 +00:00
end
2013-03-22 16:46:26 +00:00
comment.save
end
end
end
def self.hex_to_commit_hash hex
2013-04-25 16:20:51 +01:00
# '079d'.hex.to_s(16) => "79d"
t = hex.to_s(16)
2013-04-25 16:20:51 +01:00
'0'*(40-t.length) << t # commit hash has 40-character
end
2012-01-21 13:32:22 +00:00
protected
2014-03-17 20:18:46 +00:00
def touch_commentable
commentable.touch unless commit_comment?
end
def subscribe_on_reply
2014-03-17 20:18:46 +00:00
commentable.subscribes.where(user_id: user_id).first_or_create
end
2012-01-23 19:42:54 +00:00
def subscribe_users
if issue_comment?
2014-01-21 04:51:49 +00:00
commentable.subscribes.create(user: user) if !commentable.subscribes.exists?(user_id: user.id)
elsif commit_comment?
2015-05-05 13:25:29 +01:00
recipients = project.all_members
recipients << user << User.find_by(email: commentable.try(:committer).try(:email)) # commentor and committer
2012-01-29 20:18:14 +00:00
recipients.compact.uniq.each do |user|
2014-01-21 04:51:49 +00:00
options = {project_id: project.id, subscribeable_id: commentable_id, subscribeable_type: commentable.class.name, user_id: user.id}
Subscribe.subscribe_to_commit(options) if Subscribe.subscribed_to_commit?(project, user, commentable)
2012-01-29 20:18:14 +00:00
end
2012-01-25 17:33:26 +00:00
end
2012-01-23 19:42:54 +00:00
end
def self.find_existing_automatic_comment issue, opts
2014-01-21 04:51:49 +00:00
find_dup = opts.merge(automatic: true, commentable_type: issue.class.name,
commentable_id: issue.id)
Comment.exists? find_dup
end
end