2018-03-19 00:12:01 +00:00
|
|
|
class Comment < ApplicationRecord
|
2012-06-30 22:43:45 +00:00
|
|
|
belongs_to :user
|
2014-01-07 22:58:22 +00:00
|
|
|
belongs_to :story,
|
2018-03-14 16:44:26 +00:00
|
|
|
:inverse_of => :comments
|
2012-06-30 16:18:36 +00:00
|
|
|
has_many :votes,
|
2018-03-14 16:44:26 +00:00
|
|
|
:dependent => :delete_all
|
2012-07-03 16:59:50 +00:00
|
|
|
belongs_to :parent_comment,
|
2018-03-19 00:28:06 +00:00
|
|
|
:class_name => "Comment",
|
2018-08-21 14:04:46 +00:00
|
|
|
:inverse_of => false,
|
|
|
|
:required => false
|
2012-12-19 03:30:53 +00:00
|
|
|
has_one :moderation,
|
2018-03-19 00:28:06 +00:00
|
|
|
:class_name => "Moderation",
|
2018-03-19 00:56:05 +00:00
|
|
|
:inverse_of => :comment,
|
|
|
|
:dependent => :destroy
|
2018-08-21 14:04:46 +00:00
|
|
|
belongs_to :hat,
|
|
|
|
:required => false
|
2018-06-27 13:31:50 +00:00
|
|
|
has_many :taggings, through: :story
|
2013-01-23 06:15:05 +00:00
|
|
|
|
2017-04-26 17:49:54 +00:00
|
|
|
attr_accessor :current_vote, :previewing, :indent_level
|
2012-06-30 16:18:36 +00:00
|
|
|
|
2013-07-20 03:05:00 +00:00
|
|
|
before_validation :on => :create do
|
|
|
|
self.assign_short_id_and_upvote
|
|
|
|
self.assign_initial_confidence
|
|
|
|
self.assign_thread_id
|
|
|
|
end
|
2018-03-14 16:44:26 +00:00
|
|
|
after_create :record_initial_upvote, :mark_submitter, :deliver_reply_notifications,
|
|
|
|
:deliver_mention_notifications, :log_hat_use
|
2012-06-30 16:18:36 +00:00
|
|
|
after_destroy :unassign_votes
|
|
|
|
|
2016-07-31 17:33:17 +00:00
|
|
|
scope :active, -> { where(:is_deleted => false, :is_moderated => false) }
|
|
|
|
|
2014-02-17 16:13:08 +00:00
|
|
|
DOWNVOTABLE_DAYS = 7
|
2018-05-31 17:48:10 +00:00
|
|
|
DELETEABLE_DAYS = DOWNVOTABLE_DAYS * 2
|
2014-02-17 16:13:08 +00:00
|
|
|
|
2018-02-09 15:26:51 +00:00
|
|
|
# the lowest a score can go
|
|
|
|
DOWNVOTABLE_MIN_SCORE = -10
|
|
|
|
|
|
|
|
# the score at which a comment should be collapsed
|
|
|
|
COLLAPSE_SCORE = -5
|
2016-11-29 23:55:15 +00:00
|
|
|
|
2014-02-17 16:13:08 +00:00
|
|
|
# after this many minutes old, a comment cannot be edited
|
2015-12-08 02:08:36 +00:00
|
|
|
MAX_EDIT_MINS = (60 * 6)
|
2012-06-30 19:14:35 +00:00
|
|
|
|
2017-05-10 03:24:24 +00:00
|
|
|
SCORE_RANGE_TO_HIDE = (-2 .. 4)
|
|
|
|
|
2012-06-30 16:18:36 +00:00
|
|
|
validate do
|
2012-06-30 22:43:45 +00:00
|
|
|
self.comment.to_s.strip == "" &&
|
|
|
|
errors.add(:comment, "cannot be blank.")
|
2012-06-17 01:15:46 +00:00
|
|
|
|
2012-06-30 22:43:45 +00:00
|
|
|
self.user_id.blank? &&
|
2012-06-30 16:18:36 +00:00
|
|
|
errors.add(:user_id, "cannot be blank.")
|
|
|
|
|
2012-06-30 22:43:45 +00:00
|
|
|
self.story_id.blank? &&
|
|
|
|
errors.add(:story_id, "cannot be blank.")
|
2012-07-01 18:31:31 +00:00
|
|
|
|
2012-06-30 16:18:36 +00:00
|
|
|
(m = self.comment.to_s.strip.match(/\A(t)his([\.!])?$\z/i)) &&
|
2012-06-30 22:43:45 +00:00
|
|
|
errors.add(:base, (m[1] == "T" ? "N" : "n") + "ope" + m[2].to_s)
|
2015-02-10 05:11:25 +00:00
|
|
|
|
|
|
|
self.comment.to_s.strip.match(/\Atl;?dr.?$\z/i) &&
|
|
|
|
errors.add(:base, "Wow! A blue car!")
|
2017-04-13 01:45:33 +00:00
|
|
|
|
|
|
|
self.comment.to_s.strip.match(/\Ame too.?\z/i) &&
|
|
|
|
errors.add(:base, "Please just upvote the parent post instead.")
|
2018-05-23 13:59:31 +00:00
|
|
|
|
|
|
|
self.hat.present? && self.user.wearable_hats.exclude?(self.hat) &&
|
|
|
|
errors.add(:hat, "not wearable by user")
|
2012-06-30 16:18:36 +00:00
|
|
|
end
|
|
|
|
|
2014-02-17 16:01:44 +00:00
|
|
|
def self.arrange_for_user(user)
|
2018-06-19 14:15:41 +00:00
|
|
|
parents = self.order(Arel.sql("(upvotes - downvotes) < 0 ASC, confidence DESC"))
|
2018-03-02 05:01:39 +00:00
|
|
|
.group_by(&:parent_comment_id)
|
2014-02-17 16:01:44 +00:00
|
|
|
|
|
|
|
# top-down list of comments, regardless of indent level
|
|
|
|
ordered = []
|
|
|
|
|
|
|
|
ancestors = [nil] # nil sentinel so indent_level starts at 1 without add op.
|
|
|
|
subtree = parents[nil]
|
|
|
|
|
|
|
|
while subtree
|
|
|
|
if (node = subtree.shift)
|
|
|
|
children = parents[node.id]
|
|
|
|
|
|
|
|
# for deleted comments, if they have no children, they can be removed
|
|
|
|
# from the tree. otherwise they have to stay and a "[deleted]" stub
|
|
|
|
# will be shown
|
2017-10-24 18:32:20 +00:00
|
|
|
if node.is_gone? && # deleted or moderated
|
2018-03-22 15:25:07 +00:00
|
|
|
!children.present? && # don't have child comments
|
2018-03-14 15:15:35 +00:00
|
|
|
(!user || (!user.is_moderator? && node.user_id != user.id))
|
2014-02-17 16:01:44 +00:00
|
|
|
# admins and authors should be able to see their deleted comments
|
|
|
|
next
|
|
|
|
end
|
|
|
|
|
|
|
|
node.indent_level = ancestors.length
|
|
|
|
ordered << node
|
|
|
|
|
|
|
|
# no children to recurse
|
|
|
|
next unless children
|
|
|
|
|
|
|
|
# drill down a level
|
|
|
|
ancestors << subtree
|
|
|
|
subtree = children
|
|
|
|
else
|
|
|
|
# climb back out
|
|
|
|
subtree = ancestors.pop
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
ordered
|
2014-01-16 03:55:31 +00:00
|
|
|
end
|
|
|
|
|
2012-07-09 23:59:38 +00:00
|
|
|
def self.regenerate_markdown
|
2012-07-12 18:30:20 +00:00
|
|
|
Comment.record_timestamps = false
|
|
|
|
|
2018-03-19 00:36:14 +00:00
|
|
|
Comment.all.find_each do |c|
|
2012-07-09 23:59:38 +00:00
|
|
|
c.markeddown_comment = c.generated_markeddown_comment
|
|
|
|
c.save(:validate => false)
|
|
|
|
end
|
2012-07-12 18:30:20 +00:00
|
|
|
|
|
|
|
Comment.record_timestamps = true
|
|
|
|
|
2012-07-09 23:59:38 +00:00
|
|
|
nil
|
|
|
|
end
|
2013-01-23 06:15:05 +00:00
|
|
|
|
2015-01-28 21:02:40 +00:00
|
|
|
def self.score_sql
|
2018-06-19 14:15:41 +00:00
|
|
|
Arel.sql("(CAST(upvotes AS #{Story.votes_cast_type}) - " <<
|
|
|
|
"CAST(downvotes AS #{Story.votes_cast_type}))")
|
2015-01-28 21:02:40 +00:00
|
|
|
end
|
|
|
|
|
2018-03-02 13:19:50 +00:00
|
|
|
def as_json(_options = {})
|
2015-08-04 00:12:39 +00:00
|
|
|
h = [
|
2012-12-30 18:13:19 +00:00
|
|
|
:short_id,
|
2016-03-24 18:19:26 +00:00
|
|
|
:short_id_url,
|
2012-12-30 18:13:19 +00:00
|
|
|
:created_at,
|
|
|
|
:updated_at,
|
|
|
|
:is_deleted,
|
|
|
|
:is_moderated,
|
2015-08-04 00:12:39 +00:00
|
|
|
:score,
|
|
|
|
:upvotes,
|
|
|
|
:downvotes,
|
2018-03-14 13:01:14 +00:00
|
|
|
{ :comment => (self.is_gone? ? "<em>#{self.gone_text}</em>" : :markeddown_comment) },
|
2015-08-04 00:12:39 +00:00
|
|
|
:url,
|
|
|
|
:indent_level,
|
|
|
|
{ :commenting_user => :user },
|
|
|
|
]
|
|
|
|
|
|
|
|
js = {}
|
|
|
|
h.each do |k|
|
|
|
|
if k.is_a?(Symbol)
|
|
|
|
js[k] = self.send(k)
|
|
|
|
elsif k.is_a?(Hash)
|
|
|
|
if k.values.first.is_a?(Symbol)
|
|
|
|
js[k.keys.first] = self.send(k.values.first)
|
|
|
|
else
|
|
|
|
js[k.keys.first] = k.values.first
|
|
|
|
end
|
|
|
|
end
|
2012-12-30 18:13:19 +00:00
|
|
|
end
|
|
|
|
|
2015-08-04 00:12:39 +00:00
|
|
|
js
|
2012-12-30 18:13:19 +00:00
|
|
|
end
|
2012-07-09 23:59:38 +00:00
|
|
|
|
2014-02-17 16:01:44 +00:00
|
|
|
def assign_initial_confidence
|
|
|
|
self.confidence = self.calculated_confidence
|
|
|
|
end
|
|
|
|
|
2012-06-30 22:43:45 +00:00
|
|
|
def assign_short_id_and_upvote
|
2013-01-23 06:15:05 +00:00
|
|
|
self.short_id = ShortId.new(self.class).generate
|
2012-06-30 16:18:36 +00:00
|
|
|
self.upvotes = 1
|
2012-06-30 22:43:45 +00:00
|
|
|
end
|
2012-06-17 01:15:46 +00:00
|
|
|
|
2014-02-17 16:01:44 +00:00
|
|
|
def assign_thread_id
|
|
|
|
if self.parent_comment_id.present?
|
|
|
|
self.thread_id = self.parent_comment.thread_id
|
|
|
|
else
|
|
|
|
self.thread_id = Keystore.incremented_value_for("thread_id")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# http://evanmiller.org/how-not-to-sort-by-average-rating.html
|
|
|
|
# https://github.com/reddit/reddit/blob/master/r2/r2/lib/db/_sorts.pyx
|
|
|
|
def calculated_confidence
|
|
|
|
n = (upvotes + downvotes).to_f
|
|
|
|
if n == 0.0
|
|
|
|
return 0
|
2012-09-03 17:42:32 +00:00
|
|
|
end
|
|
|
|
|
2014-02-17 16:01:44 +00:00
|
|
|
z = 1.281551565545 # 80% confidence
|
|
|
|
p = upvotes.to_f / n
|
|
|
|
|
|
|
|
left = p + (1 / ((2.0 * n) * z * z))
|
|
|
|
right = z * Math.sqrt((p * ((1.0 - p) / n)) + (z * (z / (4.0 * n * n))))
|
|
|
|
under = 1.0 + ((1.0 / n) * z * z)
|
|
|
|
|
|
|
|
return (left - right) / under
|
2012-09-03 17:42:32 +00:00
|
|
|
end
|
2012-12-09 04:37:30 +00:00
|
|
|
|
2014-02-17 16:01:44 +00:00
|
|
|
def comment=(com)
|
|
|
|
self[:comment] = com.to_s.rstrip
|
|
|
|
self.markeddown_comment = self.generated_markeddown_comment
|
2012-07-12 18:30:20 +00:00
|
|
|
end
|
2012-06-17 01:15:46 +00:00
|
|
|
|
2016-02-10 14:57:38 +00:00
|
|
|
def delete_for_user(user, reason = nil)
|
2014-02-17 16:01:44 +00:00
|
|
|
Comment.record_timestamps = false
|
|
|
|
|
|
|
|
self.is_deleted = true
|
|
|
|
|
|
|
|
if user.is_moderator? && user.id != self.user_id
|
|
|
|
self.is_moderated = true
|
|
|
|
|
|
|
|
m = Moderation.new
|
|
|
|
m.comment_id = self.id
|
|
|
|
m.moderator_user_id = user.id
|
|
|
|
m.action = "deleted comment"
|
2016-02-10 14:57:38 +00:00
|
|
|
|
|
|
|
if reason.present?
|
|
|
|
m.reason = reason
|
|
|
|
end
|
|
|
|
|
2014-02-17 16:01:44 +00:00
|
|
|
m.save
|
2012-12-30 18:11:47 +00:00
|
|
|
end
|
|
|
|
|
2014-02-17 16:01:44 +00:00
|
|
|
self.save(:validate => false)
|
|
|
|
Comment.record_timestamps = true
|
|
|
|
|
|
|
|
self.story.update_comments_count!
|
2016-07-31 17:33:17 +00:00
|
|
|
self.user.update_comments_posted_count!
|
2012-07-01 18:38:01 +00:00
|
|
|
end
|
|
|
|
|
2012-09-10 17:40:33 +00:00
|
|
|
def deliver_mention_notifications
|
2012-09-16 20:51:25 +00:00
|
|
|
self.plaintext_comment.scan(/\B\@([\w\-]+)/).flatten.uniq.each do |mention|
|
2018-03-17 19:00:46 +00:00
|
|
|
if (u = User.find_by(:username => mention))
|
2013-01-07 22:29:00 +00:00
|
|
|
if u.id == self.user.id
|
|
|
|
next
|
|
|
|
end
|
|
|
|
|
2014-01-09 02:40:03 +00:00
|
|
|
if u.email_mentions?
|
|
|
|
begin
|
2017-03-19 02:08:55 +00:00
|
|
|
EmailReply.mention(self, u).deliver_now
|
2014-01-09 02:40:03 +00:00
|
|
|
rescue => e
|
|
|
|
Rails.logger.error "error e-mailing #{u.email}: #{e}"
|
2012-09-16 20:51:25 +00:00
|
|
|
end
|
2014-01-09 02:40:03 +00:00
|
|
|
end
|
2012-09-16 20:51:25 +00:00
|
|
|
|
2014-01-21 07:05:27 +00:00
|
|
|
if u.pushover_mentions?
|
2018-03-18 00:31:06 +00:00
|
|
|
u.pushover!(
|
2014-01-21 07:05:27 +00:00
|
|
|
:title => "#{Rails.application.name} mention by " <<
|
|
|
|
"#{self.user.username} on #{self.story.title}",
|
|
|
|
:message => self.plaintext_comment,
|
|
|
|
:url => self.url,
|
|
|
|
:url_title => "Reply to #{self.user.username}",
|
2018-03-18 00:31:06 +00:00
|
|
|
)
|
2012-09-10 17:40:33 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-07-04 01:48:01 +00:00
|
|
|
def deliver_reply_notifications
|
2018-03-14 15:15:35 +00:00
|
|
|
if self.parent_comment_id &&
|
|
|
|
(u = self.parent_comment.try(:user)) &&
|
|
|
|
u.id != self.user.id
|
2014-01-09 02:40:03 +00:00
|
|
|
if u.email_replies?
|
|
|
|
begin
|
2017-03-19 02:08:55 +00:00
|
|
|
EmailReply.reply(self, u).deliver_now
|
2014-01-09 02:40:03 +00:00
|
|
|
rescue => e
|
|
|
|
Rails.logger.error "error e-mailing #{u.email}: #{e}"
|
2012-07-03 16:59:50 +00:00
|
|
|
end
|
2014-01-09 02:40:03 +00:00
|
|
|
end
|
2012-07-03 16:59:50 +00:00
|
|
|
|
2014-01-21 07:05:27 +00:00
|
|
|
if u.pushover_replies?
|
2018-03-18 00:31:06 +00:00
|
|
|
u.pushover!(
|
2014-01-21 07:05:27 +00:00
|
|
|
:title => "#{Rails.application.name} reply from " <<
|
|
|
|
"#{self.user.username} on #{self.story.title}",
|
|
|
|
:message => self.plaintext_comment,
|
|
|
|
:url => self.url,
|
|
|
|
:url_title => "Reply to #{self.user.username}",
|
2018-03-18 00:31:06 +00:00
|
|
|
)
|
2012-07-03 16:59:50 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2012-07-12 18:30:20 +00:00
|
|
|
|
2014-02-17 16:01:44 +00:00
|
|
|
def generated_markeddown_comment
|
|
|
|
Markdowner.to_html(self.comment)
|
2012-07-12 18:30:20 +00:00
|
|
|
end
|
|
|
|
|
2012-07-03 19:29:00 +00:00
|
|
|
def give_upvote_or_downvote_and_recalculate_confidence!(upvote, downvote)
|
|
|
|
self.upvotes += upvote.to_i
|
|
|
|
self.downvotes += downvote.to_i
|
|
|
|
|
|
|
|
Comment.connection.execute("UPDATE #{Comment.table_name} SET " <<
|
|
|
|
"upvotes = COALESCE(upvotes, 0) + #{upvote.to_i}, " <<
|
|
|
|
"downvotes = COALESCE(downvotes, 0) + #{downvote.to_i}, " <<
|
2018-03-19 21:53:47 +00:00
|
|
|
"confidence = '#{self.calculated_confidence}' WHERE id = #{self.id}")
|
2014-12-09 16:07:36 +00:00
|
|
|
|
|
|
|
self.story.recalculate_hotness!
|
2012-07-03 19:29:00 +00:00
|
|
|
end
|
2012-07-03 16:59:50 +00:00
|
|
|
|
2014-02-17 16:01:44 +00:00
|
|
|
def gone_text
|
|
|
|
if self.is_moderated?
|
2017-10-24 18:18:18 +00:00
|
|
|
"Comment removed by moderator " <<
|
2014-02-17 16:01:44 +00:00
|
|
|
self.moderation.try(:moderator).try(:username).to_s << ": " <<
|
|
|
|
(self.moderation.try(:reason) || "No reason given")
|
2016-02-10 22:12:11 +00:00
|
|
|
elsif self.user.is_banned?
|
|
|
|
"Comment from banned user removed"
|
2013-06-25 18:55:40 +00:00
|
|
|
else
|
2014-02-17 16:01:44 +00:00
|
|
|
"Comment removed by author"
|
2013-06-25 18:55:40 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-06-25 18:58:52 +00:00
|
|
|
def has_been_edited?
|
|
|
|
self.updated_at && (self.updated_at - self.created_at > 1.minute)
|
|
|
|
end
|
|
|
|
|
2015-10-16 18:08:05 +00:00
|
|
|
def html_class_for_user
|
|
|
|
c = []
|
|
|
|
if !self.user.is_active?
|
|
|
|
c.push "inactive_user"
|
|
|
|
elsif self.user.is_new?
|
|
|
|
c.push "new_user"
|
|
|
|
elsif self.story && self.story.user_is_author? &&
|
2018-03-14 15:15:35 +00:00
|
|
|
self.story.user_id == self.user_id
|
2015-10-16 18:08:05 +00:00
|
|
|
c.push "user_is_author"
|
|
|
|
end
|
|
|
|
|
|
|
|
c.join("")
|
|
|
|
end
|
|
|
|
|
2014-02-17 16:01:44 +00:00
|
|
|
def is_deletable_by_user?(user)
|
|
|
|
if user && user.is_moderator?
|
|
|
|
return true
|
|
|
|
elsif user && user.id == self.user_id
|
2018-05-31 17:48:10 +00:00
|
|
|
return self.created_at >= DELETEABLE_DAYS.days.ago
|
2014-02-17 16:01:44 +00:00
|
|
|
else
|
|
|
|
return false
|
2014-01-07 23:07:10 +00:00
|
|
|
end
|
2012-06-30 22:43:45 +00:00
|
|
|
end
|
2012-12-30 18:11:47 +00:00
|
|
|
|
2018-05-31 17:48:10 +00:00
|
|
|
def is_disownable_by_user?(user)
|
|
|
|
user && user.id == self.user_id && self.created_at && self.created_at < DELETEABLE_DAYS.days.ago
|
|
|
|
end
|
|
|
|
|
2014-02-17 16:13:08 +00:00
|
|
|
def is_downvotable?
|
2016-12-08 00:32:37 +00:00
|
|
|
if self.created_at && self.score > DOWNVOTABLE_MIN_SCORE
|
2018-03-19 00:07:16 +00:00
|
|
|
Time.current - self.created_at <= DOWNVOTABLE_DAYS.days
|
2014-02-17 16:13:08 +00:00
|
|
|
else
|
|
|
|
false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-06-30 19:14:35 +00:00
|
|
|
def is_editable_by_user?(user)
|
2012-09-02 14:50:07 +00:00
|
|
|
if user && user.id == self.user_id
|
2012-07-12 18:30:20 +00:00
|
|
|
if self.is_moderated?
|
|
|
|
return false
|
|
|
|
else
|
2018-03-19 00:07:16 +00:00
|
|
|
return (Time.current.to_i - (self.updated_at ? self.updated_at.to_i :
|
2012-07-12 18:30:20 +00:00
|
|
|
self.created_at.to_i) < (60 * MAX_EDIT_MINS))
|
|
|
|
end
|
|
|
|
else
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
end
|
2013-01-23 06:15:05 +00:00
|
|
|
|
2014-02-17 16:01:44 +00:00
|
|
|
def is_gone?
|
|
|
|
is_deleted? || is_moderated?
|
2012-09-02 14:50:07 +00:00
|
|
|
end
|
|
|
|
|
2012-07-12 18:30:20 +00:00
|
|
|
def is_undeletable_by_user?(user)
|
2012-09-02 14:50:07 +00:00
|
|
|
if user && user.is_moderator?
|
2012-07-12 18:30:20 +00:00
|
|
|
return true
|
|
|
|
elsif user && user.id == self.user_id && !self.is_moderated?
|
|
|
|
return true
|
|
|
|
else
|
2012-06-30 19:14:35 +00:00
|
|
|
return false
|
|
|
|
end
|
2012-06-30 22:43:45 +00:00
|
|
|
end
|
2012-07-03 16:59:50 +00:00
|
|
|
|
2017-10-25 08:31:53 +00:00
|
|
|
def log_hat_use
|
|
|
|
return unless self.hat && self.hat.modlog_use
|
|
|
|
|
|
|
|
m = Moderation.new
|
|
|
|
m.created_at = self.created_at
|
|
|
|
m.comment_id = self.id
|
|
|
|
m.moderator_user_id = user.id
|
|
|
|
m.action = "used #{self.hat.hat} hat"
|
|
|
|
m.save!
|
|
|
|
end
|
|
|
|
|
2014-02-17 16:01:44 +00:00
|
|
|
def mark_submitter
|
|
|
|
Keystore.increment_value_for("user:#{self.user_id}:comments_posted")
|
|
|
|
end
|
|
|
|
|
|
|
|
def mailing_list_message_id
|
2015-12-02 19:45:50 +00:00
|
|
|
[
|
|
|
|
"comment",
|
|
|
|
self.short_id,
|
|
|
|
self.is_from_email ? "email" : nil,
|
2018-03-13 02:47:24 +00:00
|
|
|
created_at.to_i,
|
2018-03-18 05:51:58 +00:00
|
|
|
].reject(&:!).join(".") << "@" << Rails.application.domain
|
2014-02-17 16:01:44 +00:00
|
|
|
end
|
|
|
|
|
2015-01-02 23:02:55 +00:00
|
|
|
def path
|
2018-04-18 15:06:09 +00:00
|
|
|
self.story.comments_path + "#c_#{self.short_id}"
|
2015-01-02 23:02:55 +00:00
|
|
|
end
|
|
|
|
|
2014-02-17 16:01:44 +00:00
|
|
|
def plaintext_comment
|
|
|
|
# TODO: linkify then strip tags and convert entities back
|
|
|
|
comment
|
|
|
|
end
|
|
|
|
|
2014-03-03 23:13:00 +00:00
|
|
|
def record_initial_upvote
|
2018-03-14 16:44:26 +00:00
|
|
|
Vote.vote_thusly_on_story_or_comment_for_user_because(
|
|
|
|
1, self.story_id, self.id, self.user_id, nil, false
|
|
|
|
)
|
2014-03-03 23:13:00 +00:00
|
|
|
|
|
|
|
self.story.update_comments_count!
|
|
|
|
end
|
|
|
|
|
2014-02-17 16:01:44 +00:00
|
|
|
def score
|
|
|
|
self.upvotes - self.downvotes
|
|
|
|
end
|
|
|
|
|
2017-05-14 20:12:02 +00:00
|
|
|
def score_for_user(u)
|
|
|
|
if self.showing_downvotes_for_user?(u)
|
2017-05-10 03:24:24 +00:00
|
|
|
score
|
2017-07-27 03:40:10 +00:00
|
|
|
elsif u && u.can_downvote?(self)
|
|
|
|
"~"
|
2017-05-10 03:24:24 +00:00
|
|
|
else
|
2017-07-27 03:40:10 +00:00
|
|
|
" ".html_safe
|
2017-05-10 03:24:24 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-08-12 01:06:54 +00:00
|
|
|
def short_id_url
|
2016-03-24 18:19:26 +00:00
|
|
|
Rails.application.root_url + "c/#{self.short_id}"
|
2012-08-12 01:06:54 +00:00
|
|
|
end
|
|
|
|
|
2017-05-14 20:12:02 +00:00
|
|
|
def showing_downvotes_for_user?(u)
|
|
|
|
return (u && u.is_moderator?) ||
|
2018-03-14 15:15:35 +00:00
|
|
|
(self.created_at && self.created_at < 36.hours.ago) ||
|
|
|
|
!SCORE_RANGE_TO_HIDE.include?(self.score)
|
2017-05-14 20:12:02 +00:00
|
|
|
end
|
|
|
|
|
2014-02-17 16:01:44 +00:00
|
|
|
def to_param
|
|
|
|
self.short_id
|
|
|
|
end
|
|
|
|
|
|
|
|
def unassign_votes
|
|
|
|
self.story.update_comments_count!
|
|
|
|
end
|
|
|
|
|
2012-07-03 16:59:50 +00:00
|
|
|
def url
|
2017-04-30 19:32:19 +00:00
|
|
|
self.story.comments_url + "#c_#{self.short_id}"
|
2012-07-03 16:59:50 +00:00
|
|
|
end
|
2014-02-17 16:01:44 +00:00
|
|
|
|
2016-02-10 14:38:28 +00:00
|
|
|
def vote_summary_for_user(u)
|
2014-02-17 16:01:44 +00:00
|
|
|
r_counts = {}
|
2016-02-10 14:38:28 +00:00
|
|
|
r_users = {}
|
2017-04-13 16:02:56 +00:00
|
|
|
# don't includes(:user) here and assume the caller did this already
|
|
|
|
self.votes.each do |v|
|
2014-02-17 16:01:44 +00:00
|
|
|
r_counts[v.reason.to_s] ||= 0
|
|
|
|
r_counts[v.reason.to_s] += v.vote
|
2016-02-10 14:38:28 +00:00
|
|
|
|
|
|
|
r_users[v.reason.to_s] ||= []
|
|
|
|
r_users[v.reason.to_s].push v.user.username
|
2014-02-17 16:01:44 +00:00
|
|
|
end
|
|
|
|
|
2018-03-13 02:47:24 +00:00
|
|
|
r_counts.keys.sort.map {|k|
|
2016-02-10 14:38:28 +00:00
|
|
|
if k == ""
|
|
|
|
"+#{r_counts[k]}"
|
|
|
|
else
|
|
|
|
o = "#{r_counts[k]} #{Vote::COMMENT_REASONS[k]}"
|
|
|
|
if u && u.is_moderator? && self.user_id != u.id
|
2018-03-18 00:00:47 +00:00
|
|
|
o << " (#{r_users[k].join(', ')})"
|
2016-02-10 14:38:28 +00:00
|
|
|
end
|
|
|
|
o
|
|
|
|
end
|
2014-02-17 16:01:44 +00:00
|
|
|
}.join(", ")
|
|
|
|
end
|
|
|
|
|
|
|
|
def undelete_for_user(user)
|
|
|
|
Comment.record_timestamps = false
|
|
|
|
|
|
|
|
self.is_deleted = false
|
|
|
|
|
|
|
|
if user.is_moderator?
|
|
|
|
self.is_moderated = false
|
|
|
|
|
|
|
|
if user.id != self.user_id
|
|
|
|
m = Moderation.new
|
|
|
|
m.comment_id = self.id
|
|
|
|
m.moderator_user_id = user.id
|
|
|
|
m.action = "undeleted comment"
|
|
|
|
m.save
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
self.save(:validate => false)
|
|
|
|
Comment.record_timestamps = true
|
|
|
|
|
|
|
|
self.story.update_comments_count!
|
2016-07-31 17:33:17 +00:00
|
|
|
self.user.update_comments_posted_count!
|
2014-02-17 16:01:44 +00:00
|
|
|
end
|
2012-06-17 01:15:46 +00:00
|
|
|
end
|