2012-01-30 20:39:34 +00:00
|
|
|
# -*- encoding : utf-8 -*-
|
2011-12-19 15:30:14 +00:00
|
|
|
class Comment < ActiveRecord::Base
|
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
|
|
|
|
ISSUES_REGEX = /(?:[a-zA-Z0-9\-_]*\/)?(?:[a-zA-Z0-9\-_]*)?#[0-9]+/
|
|
|
|
ISSUE_REGEX = /([a-zA-Z0-9\-_]*\/)?([a-zA-Z0-9\-_]*)?#([0-9]+)/
|
|
|
|
|
2013-03-22 17:58:20 +00:00
|
|
|
belongs_to :commentable, :polymorphic => true, :touch => true
|
2011-12-20 10:20:00 +00:00
|
|
|
belongs_to :user
|
2012-03-06 21:49:29 +00:00
|
|
|
belongs_to :project
|
2012-10-04 19:40:12 +01:00
|
|
|
serialize :data
|
2011-12-19 15:30:14 +00:00
|
|
|
|
2013-03-22 17:58:20 +00:00
|
|
|
validates :body, :user_id, :commentable_id, :commentable_type, :project_id, :presence => true
|
2011-12-26 15:48:57 +00:00
|
|
|
|
2012-04-04 22:43:06 +01:00
|
|
|
scope :for_commit, lambda {|c| where(:commentable_id => c.id.hex, :commentable_type => c.class)}
|
2013-02-28 15:27:50 +00:00
|
|
|
default_scope order("#{table_name}.created_at")
|
2012-03-01 19:10:12 +00:00
|
|
|
|
2012-03-02 23:38:43 +00:00
|
|
|
after_create :subscribe_on_reply, :unless => lambda {|c| c.commit_comment?}
|
2012-01-25 17:33:26 +00:00
|
|
|
after_create :subscribe_users
|
2012-04-04 22:43:06 +01:00
|
|
|
|
2012-10-04 19:40:12 +01:00
|
|
|
attr_accessible :body, :data
|
2012-04-04 22:43:06 +01:00
|
|
|
|
|
|
|
def commentable
|
2012-07-26 22:15:57 +01:00
|
|
|
# raise commentable_id.inspect
|
|
|
|
# raise commentable_id.to_s(16).inspect
|
|
|
|
commit_comment? ? project.repo.commit(commentable_id.to_s(16)) : super # TODO leading zero problem
|
2012-01-20 15:17:05 +00:00
|
|
|
end
|
2011-12-26 15:48:57 +00:00
|
|
|
|
2012-04-04 22:43:06 +01:00
|
|
|
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
|
2012-03-27 16:09:04 +01:00
|
|
|
|
2012-04-04 22:43:06 +01:00
|
|
|
def self.commit_comment?(class_name)
|
|
|
|
class_name.to_s == 'Grit::Commit'
|
2011-12-26 15:48:57 +00:00
|
|
|
end
|
2012-01-13 15:07:01 +00:00
|
|
|
|
2012-02-14 16:05:41 +00:00
|
|
|
def commit_comment?
|
2012-04-04 22:43:06 +01:00
|
|
|
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
|
2012-02-14 16:05:41 +00:00
|
|
|
end
|
|
|
|
|
2012-01-25 08:31:49 +00:00
|
|
|
def can_notify_on_new_comment?(subscribe)
|
|
|
|
User.find(subscribe.user).notifier.new_comment && User.find(subscribe.user).notifier.can_notify
|
2011-12-26 15:48:57 +00:00
|
|
|
end
|
2012-01-13 15:07:01 +00:00
|
|
|
|
2012-10-26 15:03:46 +01:00
|
|
|
def actual_inline_comment?(diff = nil, force = false)
|
2012-10-12 20:56:02 +01:00
|
|
|
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?
|
2012-10-26 15:03:46 +01:00
|
|
|
return false if diff.nil?
|
2012-10-12 20:56:02 +01:00
|
|
|
end
|
2012-12-15 05:17:11 +00:00
|
|
|
return data[:actual] = true if commentable_type == 'Grit::Commit'
|
2012-10-11 18:50:40 +01:00
|
|
|
filepath, line_number = data[:path], data[:line]
|
2012-10-12 20:56:02 +01:00
|
|
|
diff_path = (diff || commentable.diffs ).select {|d| d.a_path == data[:path]}
|
2012-10-11 18:50:40 +01:00
|
|
|
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']
|
2012-10-11 18:50:40 +01:00
|
|
|
end
|
|
|
|
|
2012-10-12 20:56:02 +01:00
|
|
|
def pull_comment?
|
2013-03-22 17:17:10 +00:00
|
|
|
commentable.is_a?(Issue) && commentable.pull_request.present?
|
2012-10-12 20:56:02 +01:00
|
|
|
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
|
2012-10-17 19:38:42 +01:00
|
|
|
if params[:in_reply].present? && reply = Comment.where(:id => params[:in_reply]).first
|
|
|
|
self.data = reply.data
|
|
|
|
return true
|
|
|
|
end
|
2012-10-16 10:50:21 +01:00
|
|
|
self.data = {:path => params[:path], :line => params[:line]}
|
2012-12-15 05:17:11 +00:00
|
|
|
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
|
2012-10-18 13:09:12 +01:00
|
|
|
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
|
2012-10-16 14:21:47 +01:00
|
|
|
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
|
|
|
|
|
2013-03-26 10:47:42 +00:00
|
|
|
def self.create_link_on_issues_from_item item, opts = {}
|
|
|
|
linker = item.user.present? ? item.user : User.find_by_uname('rosa_system')
|
|
|
|
elements = if item.is_a? Comment
|
|
|
|
[[item, item.body]]
|
|
|
|
elsif item.is_a? GitHook
|
|
|
|
opts[:commits]
|
|
|
|
end
|
|
|
|
|
|
|
|
elements.each do |element|
|
|
|
|
element[1].scan(ISSUES_REGEX).each do |hash|
|
|
|
|
hash =~ ISSUE_REGEX
|
2013-03-27 09:32:39 +00:00
|
|
|
owner_uname = Regexp.last_match[1].presence || Regexp.last_match[2].presence || item.project.owner.uname
|
|
|
|
project_name = Regexp.last_match[1] ? Regexp.last_match[2] : item.project.name
|
2013-03-22 16:46:26 +00:00
|
|
|
serial_id = Regexp.last_match[3]
|
|
|
|
project = Project.find_by_owner_and_name(owner_uname.chomp('/'), project_name)
|
|
|
|
next unless project
|
2013-03-26 10:47:42 +00:00
|
|
|
next unless Ability.new(item.user).can? :read, project
|
2013-03-22 16:46:26 +00:00
|
|
|
issue = project.issues.where(:serial_id => serial_id).first
|
|
|
|
next unless issue
|
2013-03-26 10:47:42 +00:00
|
|
|
next if issue == item.try(:commentable) # dont create link to the same issue
|
2013-03-26 17:51:32 +00:00
|
|
|
# dont create duplicate link to issue
|
2013-03-27 16:18:15 +00:00
|
|
|
next if item.is_a?(Comment) && Comment.exists?(:automatic => true, :commentable_type => issue.class.name,
|
|
|
|
:commentable_id => issue.id, :created_from_issue_id => item.commentable_id)
|
2013-03-26 10:47:42 +00:00
|
|
|
comment = linker.comments.new :body => 'automatic comment'
|
|
|
|
comment.commentable, comment.project, comment.automatic = issue, project, true
|
|
|
|
if item.is_a? Comment
|
|
|
|
comment.data = {:issue_serial_id => item.commentable.serial_id, :comment_id => item.id}
|
2013-03-26 17:51:32 +00:00
|
|
|
comment.created_from_issue_id = item.commentable_id
|
2013-03-26 10:47:42 +00:00
|
|
|
elsif item.is_a? GitHook
|
|
|
|
repo_commit = git_hook.project.repo.commit element[0]
|
|
|
|
next unless repo_commit
|
2013-03-26 17:51:32 +00:00
|
|
|
comment.data = {commit_hash => commit[0]}
|
2013-03-26 10:47:42 +00:00
|
|
|
end
|
|
|
|
comment.data.merge! :from_project_id => item.project.id
|
2013-03-22 16:46:26 +00:00
|
|
|
comment.save
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-01-21 13:32:22 +00:00
|
|
|
protected
|
|
|
|
|
2012-01-13 15:07:01 +00:00
|
|
|
def subscribe_on_reply
|
2012-04-04 22:43:06 +01:00
|
|
|
commentable.subscribes.create(:user_id => user_id) if !commentable.subscribes.exists?(:user_id => user_id)
|
2012-01-13 15:07:01 +00:00
|
|
|
end
|
2012-02-06 15:46:32 +00:00
|
|
|
|
2012-01-23 19:42:54 +00:00
|
|
|
def subscribe_users
|
2012-04-04 22:43:06 +01:00
|
|
|
if issue_comment?
|
|
|
|
commentable.subscribes.create(:user => user) if !commentable.subscribes.exists?(:user_id => user.id)
|
|
|
|
elsif commit_comment?
|
2012-12-13 18:18:22 +00:00
|
|
|
recipients = project.admins
|
|
|
|
recipients << user << User.where(:email => commentable.try(:committer).try(:email)).first # commentor and committer
|
2012-01-29 20:18:14 +00:00
|
|
|
recipients.compact.uniq.each do |user|
|
2012-04-04 22:43:06 +01: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
|
2011-12-19 15:30:14 +00:00
|
|
|
end
|