556 lines
17 KiB
Ruby
556 lines
17 KiB
Ruby
|
|
class Comment < ApplicationRecord
|
||
|
|
include HtmlCleaner
|
||
|
|
include AfterCommitEverywhere
|
||
|
|
|
||
|
|
belongs_to :pseud
|
||
|
|
belongs_to :commentable, polymorphic: true
|
||
|
|
belongs_to :parent, polymorphic: true
|
||
|
|
|
||
|
|
has_many :inbox_comments, foreign_key: 'feedback_comment_id', dependent: :destroy
|
||
|
|
has_many :users, through: :inbox_comments
|
||
|
|
|
||
|
|
has_many :reviewed_replies, -> { reviewed },
|
||
|
|
class_name: "Comment", as: :commentable, inverse_of: :commentable
|
||
|
|
|
||
|
|
has_many :thread_comments, class_name: 'Comment', foreign_key: :thread
|
||
|
|
|
||
|
|
validates :name, presence: { unless: :pseud_id }, not_forbidden_name: { if: :will_save_change_to_name? }
|
||
|
|
validates :email, email_format: { on: :create, unless: :pseud_id }, email_blacklist: { on: :create, unless: :pseud_id }
|
||
|
|
|
||
|
|
validates_presence_of :comment_content
|
||
|
|
validates_length_of :comment_content,
|
||
|
|
maximum: ArchiveConfig.COMMENT_MAX,
|
||
|
|
too_long: ts("must be less than %{count} characters long.", count: ArchiveConfig.COMMENT_MAX)
|
||
|
|
|
||
|
|
delegate :user, to: :pseud, allow_nil: true
|
||
|
|
|
||
|
|
# Whether the writer of the comment this is replying to allows guest replies
|
||
|
|
validate :guest_can_reply, if: :reply_comment?, unless: :pseud_id, on: :create
|
||
|
|
def guest_can_reply
|
||
|
|
errors.add(:commentable, :guest_replies_off) if commentable.guest_replies_disallowed?
|
||
|
|
end
|
||
|
|
|
||
|
|
# Whether the writer of this comment disallows guest replies
|
||
|
|
def guest_replies_disallowed?
|
||
|
|
return false unless user
|
||
|
|
|
||
|
|
user.preference.guest_replies_off && !user.is_author_of?(ultimate_parent)
|
||
|
|
end
|
||
|
|
|
||
|
|
# Check if the writer of this comment is blocked by the writer of the comment
|
||
|
|
# they're replying to:
|
||
|
|
validates :user, not_blocked: {
|
||
|
|
by: :commentable,
|
||
|
|
if: :reply_comment?,
|
||
|
|
unless: :on_tag?,
|
||
|
|
message: :blocked_reply
|
||
|
|
}
|
||
|
|
|
||
|
|
# Check if the writer of this comment is blocked by one of the creators of
|
||
|
|
# the work they're replying to:
|
||
|
|
validates :user, not_blocked: {
|
||
|
|
by: :ultimate_parent,
|
||
|
|
unless: :on_tag?,
|
||
|
|
message: :blocked_comment
|
||
|
|
}
|
||
|
|
|
||
|
|
def on_tag?
|
||
|
|
parent_type == "Tag"
|
||
|
|
end
|
||
|
|
|
||
|
|
def by_anonymous_creator?
|
||
|
|
ultimate_parent.try(:anonymous?) && user&.is_author_of?(ultimate_parent)
|
||
|
|
end
|
||
|
|
|
||
|
|
validate :check_for_spam, on: :create
|
||
|
|
|
||
|
|
def check_for_spam
|
||
|
|
self.approved = skip_spamcheck? || !spam?
|
||
|
|
|
||
|
|
errors.add(:base, :spam) unless approved
|
||
|
|
end
|
||
|
|
|
||
|
|
validate :edited_spam, on: :update, if: [:will_save_change_to_edited_at?, :will_save_change_to_comment_content?]
|
||
|
|
|
||
|
|
def edited_spam
|
||
|
|
return if skip_spamcheck? || !content_too_different?(comment_content, comment_content_in_database, ArchiveConfig.EDITED_COMMENT_SPAM_CHECK_THRESHOLD)
|
||
|
|
|
||
|
|
errors.add(:base, :spam) if spam?
|
||
|
|
end
|
||
|
|
|
||
|
|
validates :comment_content, uniqueness: {
|
||
|
|
scope: [:commentable_id, :commentable_type, :name, :email, :pseud_id],
|
||
|
|
unless: :is_deleted?,
|
||
|
|
message: :duplicate_comment
|
||
|
|
}
|
||
|
|
|
||
|
|
scope :ordered_by_date, -> { order('created_at DESC') }
|
||
|
|
scope :top_level, -> { where.not(commentable_type: "Comment") }
|
||
|
|
scope :include_pseud, -> { includes(:pseud) }
|
||
|
|
scope :not_deleted, -> { where(is_deleted: false) }
|
||
|
|
scope :reviewed, -> { where(unreviewed: false) }
|
||
|
|
scope :unreviewed_only, -> { where(unreviewed: true) }
|
||
|
|
|
||
|
|
scope :for_display, lambda {
|
||
|
|
includes(
|
||
|
|
pseud: { user: [:roles, :block_of_current_user, :block_by_current_user, :preference] },
|
||
|
|
parent: { work: [:pseuds, :users] }
|
||
|
|
).merge(Pseud.with_attached_icon)
|
||
|
|
}
|
||
|
|
|
||
|
|
# Gets methods and associations from acts_as_commentable plugin
|
||
|
|
acts_as_commentable
|
||
|
|
has_comment_methods
|
||
|
|
|
||
|
|
def akismet_attributes
|
||
|
|
# While we do have tag comments, those are from logged-in users with special
|
||
|
|
# access granted by admins, so we never spam check them, unlike comments on
|
||
|
|
# works or admin posts.
|
||
|
|
comment_type = ultimate_parent.is_a?(Work) ? "fanwork-comment" : "comment"
|
||
|
|
|
||
|
|
if pseud_id.nil?
|
||
|
|
user_role = "guest"
|
||
|
|
comment_author = name
|
||
|
|
else
|
||
|
|
user_role = "user"
|
||
|
|
comment_author = user.login
|
||
|
|
end
|
||
|
|
|
||
|
|
attributes = {
|
||
|
|
comment_type: comment_type,
|
||
|
|
key: ArchiveConfig.AKISMET_KEY,
|
||
|
|
blog: ArchiveConfig.AKISMET_NAME,
|
||
|
|
user_ip: ip_address,
|
||
|
|
user_agent: user_agent,
|
||
|
|
user_role: user_role,
|
||
|
|
comment_author: comment_author,
|
||
|
|
comment_author_email: comment_owner_email,
|
||
|
|
comment_content: comment_content
|
||
|
|
}
|
||
|
|
|
||
|
|
attributes[:recheck_reason] = "edit" if will_save_change_to_edited_at? && will_save_change_to_comment_content?
|
||
|
|
|
||
|
|
attributes
|
||
|
|
end
|
||
|
|
|
||
|
|
after_create :expire_parent_comments_count
|
||
|
|
after_update :expire_parent_comments_count, if: :saved_change_to_visibility?
|
||
|
|
after_destroy :expire_parent_comments_count
|
||
|
|
def expire_parent_comments_count
|
||
|
|
after_commit { parent&.expire_comments_count }
|
||
|
|
end
|
||
|
|
|
||
|
|
def saved_change_to_visibility?
|
||
|
|
pertinent_attributes = %w[is_deleted hidden_by_admin unreviewed approved]
|
||
|
|
(saved_changes.keys & pertinent_attributes).present?
|
||
|
|
end
|
||
|
|
|
||
|
|
before_validation :set_parent_and_unreviewed, on: :create
|
||
|
|
|
||
|
|
before_create :set_depth
|
||
|
|
before_create :set_thread_for_replies
|
||
|
|
before_create :set_parent_and_unreviewed
|
||
|
|
after_create :update_thread
|
||
|
|
before_create :adjust_threading, if: :reply_comment?
|
||
|
|
|
||
|
|
after_create :update_work_stats
|
||
|
|
after_destroy :update_work_stats
|
||
|
|
|
||
|
|
# If a comment has changed too much, we might need to put it back in moderation:
|
||
|
|
before_update :recheck_unreviewed
|
||
|
|
def recheck_unreviewed
|
||
|
|
return unless edited_at_changed? &&
|
||
|
|
comment_content_changed? &&
|
||
|
|
moderated_commenting_enabled? &&
|
||
|
|
!is_creator_comment? &&
|
||
|
|
content_too_different?(comment_content, comment_content_was, ArchiveConfig.COMMENT_MODERATION_THRESHOLD)
|
||
|
|
|
||
|
|
self.unreviewed = true
|
||
|
|
end
|
||
|
|
|
||
|
|
after_update :after_update
|
||
|
|
def after_update
|
||
|
|
users = []
|
||
|
|
|
||
|
|
if self.saved_change_to_edited_at? || (self.saved_change_to_unreviewed? && !self.unreviewed?)
|
||
|
|
# Reply to owner of parent comment if this is a reply comment
|
||
|
|
# Potentially we are notifying the original commenter of a newly-approved reply to their comment
|
||
|
|
if (parent_comment_owner = notify_parent_comment_owner)
|
||
|
|
users << parent_comment_owner
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
if self.saved_change_to_edited_at?
|
||
|
|
# notify the commenter
|
||
|
|
if self.comment_owner && notify_user_of_own_comments?(self.comment_owner)
|
||
|
|
users << self.comment_owner
|
||
|
|
end
|
||
|
|
if notify_user_by_email?(self.comment_owner) && notify_user_of_own_comments?(self.comment_owner)
|
||
|
|
if self.reply_comment?
|
||
|
|
CommentMailer.comment_reply_sent_notification(self).deliver_after_commit
|
||
|
|
else
|
||
|
|
CommentMailer.comment_sent_notification(self).deliver_after_commit
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
# send notification to the owner(s) of the ultimate parent, who can be users or admins
|
||
|
|
# at this point, users contains those who've already been notified
|
||
|
|
if users.empty?
|
||
|
|
users = self.ultimate_parent.commentable_owners
|
||
|
|
else
|
||
|
|
# replace with the owners of the commentable who haven't already been notified
|
||
|
|
users = self.ultimate_parent.commentable_owners - users
|
||
|
|
end
|
||
|
|
users.each do |user|
|
||
|
|
unless user == self.comment_owner && !notify_user_of_own_comments?(user)
|
||
|
|
if notify_user_by_email?(user) || self.ultimate_parent.is_a?(Tag)
|
||
|
|
CommentMailer.edited_comment_notification(user, self).deliver_after_commit
|
||
|
|
end
|
||
|
|
if user.is_a?(User) && notify_user_by_inbox?(user)
|
||
|
|
update_feedback_in_inbox(user)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
after_create :after_create
|
||
|
|
def after_create
|
||
|
|
self.reload
|
||
|
|
# eventually we will set the locale to the user's stored language of choice
|
||
|
|
#Locale.set ArchiveConfig.SUPPORTED_LOCALES[ArchiveConfig.DEFAULT_LOCALE]
|
||
|
|
users = []
|
||
|
|
|
||
|
|
# notify the commenter
|
||
|
|
if self.comment_owner && notify_user_of_own_comments?(self.comment_owner)
|
||
|
|
users << self.comment_owner
|
||
|
|
end
|
||
|
|
if notify_user_by_email?(self.comment_owner) && notify_user_of_own_comments?(self.comment_owner)
|
||
|
|
if self.reply_comment?
|
||
|
|
CommentMailer.comment_reply_sent_notification(self).deliver_after_commit
|
||
|
|
else
|
||
|
|
CommentMailer.comment_sent_notification(self).deliver_after_commit
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
# Reply to owner of parent comment if this is a reply comment
|
||
|
|
if (parent_comment_owner = notify_parent_comment_owner)
|
||
|
|
users << parent_comment_owner
|
||
|
|
end
|
||
|
|
|
||
|
|
# send notification to the owner(s) of the ultimate parent, who can be users or admins
|
||
|
|
# at this point, users contains those who've already been notified
|
||
|
|
if users.empty?
|
||
|
|
users = self.ultimate_parent.commentable_owners
|
||
|
|
else
|
||
|
|
# replace with the owners of the commentable who haven't already been notified
|
||
|
|
users = self.ultimate_parent.commentable_owners - users
|
||
|
|
end
|
||
|
|
users.each do |user|
|
||
|
|
unless user == self.comment_owner && !notify_user_of_own_comments?(user)
|
||
|
|
if notify_user_by_email?(user) || self.ultimate_parent.is_a?(Tag)
|
||
|
|
CommentMailer.comment_notification(user, self).deliver_after_commit
|
||
|
|
end
|
||
|
|
if user.is_a?(User) && notify_user_by_inbox?(user)
|
||
|
|
add_feedback_to_inbox(user)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
after_create :record_wrangling_activity, if: :on_tag?
|
||
|
|
def record_wrangling_activity
|
||
|
|
self.comment_owner&.update_last_wrangling_activity
|
||
|
|
end
|
||
|
|
|
||
|
|
protected
|
||
|
|
|
||
|
|
def notify_user_of_own_comments?(user)
|
||
|
|
if user.nil? || user == User.orphan_account
|
||
|
|
false
|
||
|
|
elsif user.is_a?(Admin)
|
||
|
|
true
|
||
|
|
else
|
||
|
|
!user.preference.comment_copy_to_self_off?
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
def notify_user_by_inbox?(user)
|
||
|
|
if user.nil? || user == User.orphan_account
|
||
|
|
false
|
||
|
|
elsif user.is_a?(Admin)
|
||
|
|
true
|
||
|
|
else
|
||
|
|
!user.preference.comment_inbox_off?
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
def notify_user_by_email?(user)
|
||
|
|
if user.nil? || user == User.orphan_account
|
||
|
|
false
|
||
|
|
elsif user.is_a?(Admin)
|
||
|
|
true
|
||
|
|
else
|
||
|
|
!user.preference.comment_emails_off?
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
def update_feedback_in_inbox(user)
|
||
|
|
if (edited_feedback = user.inbox_comments.find_by(feedback_comment_id: self.id))
|
||
|
|
edited_feedback.update_attribute(:read, false)
|
||
|
|
else # original inbox comment was deleted
|
||
|
|
add_feedback_to_inbox(user)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
def add_feedback_to_inbox(user)
|
||
|
|
new_feedback = user.inbox_comments.build
|
||
|
|
new_feedback.feedback_comment_id = self.id
|
||
|
|
new_feedback.save
|
||
|
|
end
|
||
|
|
|
||
|
|
def content_too_different?(new_content, old_content, threshold)
|
||
|
|
# we added more than the threshold # of chars, just return
|
||
|
|
return true if new_content.length > (old_content.length + threshold)
|
||
|
|
|
||
|
|
# quick and dirty iteration to compare the two strings
|
||
|
|
cost = 0
|
||
|
|
new_i = 0
|
||
|
|
old_i = 0
|
||
|
|
while new_i < new_content.length && old_i < old_content.length
|
||
|
|
if new_content[new_i] == old_content[old_i]
|
||
|
|
new_i += 1
|
||
|
|
old_i += 1
|
||
|
|
next
|
||
|
|
end
|
||
|
|
|
||
|
|
cost += 1
|
||
|
|
# interrupt as soon as we have changed > threshold chars
|
||
|
|
return true if cost > threshold
|
||
|
|
|
||
|
|
# peek ahead to see if we can catch up on either side eg if a letter has been inserted/deleted
|
||
|
|
if new_content[new_i + 1] == old_content[old_i]
|
||
|
|
new_i += 1
|
||
|
|
elsif new_content[new_i] == old_content[old_i + 1]
|
||
|
|
old_i += 1
|
||
|
|
else
|
||
|
|
# just keep going
|
||
|
|
new_i += 1
|
||
|
|
old_i += 1
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
cost > threshold
|
||
|
|
end
|
||
|
|
|
||
|
|
def not_user_commenter?(parent_comment)
|
||
|
|
(!parent_comment.comment_owner && parent_comment.comment_owner_email && parent_comment.comment_owner_name)
|
||
|
|
end
|
||
|
|
|
||
|
|
def different_owner?(parent_comment)
|
||
|
|
not_user_commenter?(parent_comment) || (parent_comment.comment_owner != self.comment_owner)
|
||
|
|
end
|
||
|
|
|
||
|
|
def notify_parent_comment_owner
|
||
|
|
return unless self.reply_comment? && !self.unreviewed?
|
||
|
|
|
||
|
|
parent_comment = self.commentable
|
||
|
|
parent_comment_owner = parent_comment.comment_owner # will be nil if not a user, including if an admin
|
||
|
|
|
||
|
|
# if I'm replying to a comment you left for me, mark your comment as replied to in my inbox
|
||
|
|
if self.comment_owner && (inbox_comment = self.comment_owner.inbox_comments.find_by(feedback_comment_id: parent_comment.id))
|
||
|
|
inbox_comment.update(replied_to: true, read: true)
|
||
|
|
end
|
||
|
|
|
||
|
|
return unless different_owner?(parent_comment)
|
||
|
|
|
||
|
|
# Never notify people who are not tag wranglers (any more) about comments on tags
|
||
|
|
return if self.ultimate_parent.is_a?(Tag) && !parent_comment_owner&.is_tag_wrangler?
|
||
|
|
|
||
|
|
# send notification to the owner of the original comment if they're not the same as the commenter
|
||
|
|
if !parent_comment_owner || notify_user_by_email?(parent_comment_owner) || self.ultimate_parent.is_a?(Tag)
|
||
|
|
if self.saved_change_to_edited_at?
|
||
|
|
CommentMailer.edited_comment_reply_notification(parent_comment, self).deliver_after_commit
|
||
|
|
else
|
||
|
|
CommentMailer.comment_reply_notification(parent_comment, self).deliver_after_commit
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
if parent_comment_owner && notify_user_by_inbox?(parent_comment_owner)
|
||
|
|
if self.saved_change_to_edited_at?
|
||
|
|
update_feedback_in_inbox(parent_comment_owner)
|
||
|
|
else
|
||
|
|
add_feedback_to_inbox(parent_comment_owner)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
parent_comment_owner
|
||
|
|
end
|
||
|
|
|
||
|
|
public
|
||
|
|
|
||
|
|
# Set the depth of the comment: 0 for a first-class comment, increasing with each level of nesting
|
||
|
|
def set_depth
|
||
|
|
self.depth = self.reply_comment? ? self.commentable.depth + 1 : 0
|
||
|
|
end
|
||
|
|
|
||
|
|
# The thread value for a reply comment should be the same as its parent comment
|
||
|
|
def set_thread_for_replies
|
||
|
|
self.thread = self.commentable.thread if self.reply_comment?
|
||
|
|
end
|
||
|
|
|
||
|
|
# Save the ultimate parent and reviewed status
|
||
|
|
def set_parent_and_unreviewed
|
||
|
|
self.parent = self.reply_comment? ? self.commentable.parent : self.commentable
|
||
|
|
# we only mark comments as unreviewed if moderated commenting is enabled on their parent
|
||
|
|
self.unreviewed = self.parent.respond_to?(:moderated_commenting_enabled?) &&
|
||
|
|
self.parent.moderated_commenting_enabled? &&
|
||
|
|
!User.current_user.try(:is_author_of?, self.ultimate_parent)
|
||
|
|
true
|
||
|
|
end
|
||
|
|
|
||
|
|
# is this a comment by the creator of the ultimate parent
|
||
|
|
def is_creator_comment?
|
||
|
|
pseud && pseud.user && pseud.user.try(:is_author_of?, ultimate_parent)
|
||
|
|
end
|
||
|
|
|
||
|
|
def moderated_commenting_enabled?
|
||
|
|
parent.respond_to?(:moderated_commenting_enabled?) && parent.moderated_commenting_enabled?
|
||
|
|
end
|
||
|
|
|
||
|
|
# We need a unique thread id for replies, so we'll make use of the fact
|
||
|
|
# that ids are unique
|
||
|
|
def update_thread
|
||
|
|
self.update_attribute(:thread, self.id) unless self.thread
|
||
|
|
end
|
||
|
|
|
||
|
|
def adjust_threading
|
||
|
|
self.commentable.add_child(self)
|
||
|
|
end
|
||
|
|
|
||
|
|
# Is this a first-class comment?
|
||
|
|
def top_level?
|
||
|
|
!self.reply_comment?
|
||
|
|
end
|
||
|
|
|
||
|
|
def comment_owner
|
||
|
|
self.pseud.try(:user)
|
||
|
|
end
|
||
|
|
|
||
|
|
def comment_owner_name
|
||
|
|
self.pseud.try(:name) || self.name
|
||
|
|
end
|
||
|
|
|
||
|
|
def comment_owner_email
|
||
|
|
comment_owner.try(:email) || self.email
|
||
|
|
end
|
||
|
|
|
||
|
|
# override this method from commentable_entity.rb
|
||
|
|
# to return the name of the ultimate parent this is on
|
||
|
|
# we have to do this somewhat roundabout because until the comment is
|
||
|
|
# set and saved, the ultimate_parent method will not work (the thread is not set)
|
||
|
|
# and this is being called from before then.
|
||
|
|
def commentable_name
|
||
|
|
self.reply_comment? ? self.commentable.ultimate_parent.commentable_name : self.commentable.commentable_name
|
||
|
|
end
|
||
|
|
|
||
|
|
# override this method from comment_methods.rb to return ultimate
|
||
|
|
alias :original_ultimate_parent :ultimate_parent
|
||
|
|
def ultimate_parent
|
||
|
|
myparent = self.original_ultimate_parent
|
||
|
|
myparent.kind_of?(Chapter) ? myparent.work : myparent
|
||
|
|
end
|
||
|
|
|
||
|
|
def self.commentable_object(commentable)
|
||
|
|
commentable.kind_of?(Work) ? commentable.last_posted_chapter : commentable
|
||
|
|
end
|
||
|
|
|
||
|
|
def find_all_comments
|
||
|
|
self.all_children
|
||
|
|
end
|
||
|
|
|
||
|
|
def count_all_comments
|
||
|
|
self.children_count
|
||
|
|
end
|
||
|
|
|
||
|
|
def count_visible_comments
|
||
|
|
self.children_count #FIXME
|
||
|
|
end
|
||
|
|
|
||
|
|
def skip_spamcheck?
|
||
|
|
return false unless pseud_id
|
||
|
|
|
||
|
|
on_tag? || !user.should_spam_check_comments? || is_creator_comment?
|
||
|
|
end
|
||
|
|
|
||
|
|
def spam?
|
||
|
|
return false unless %w[staging production].include?(Rails.env)
|
||
|
|
|
||
|
|
Akismetor.spam?(akismet_attributes)
|
||
|
|
end
|
||
|
|
|
||
|
|
def submit_spam
|
||
|
|
Rails.env.production? && Akismetor.submit_spam(akismet_attributes)
|
||
|
|
end
|
||
|
|
|
||
|
|
def submit_ham
|
||
|
|
Rails.env.production? && Akismetor.submit_ham(akismet_attributes)
|
||
|
|
end
|
||
|
|
|
||
|
|
def mark_as_spam!
|
||
|
|
update_attribute(:approved, false)
|
||
|
|
submit_spam
|
||
|
|
end
|
||
|
|
|
||
|
|
def mark_as_ham!
|
||
|
|
update_attribute(:approved, true)
|
||
|
|
submit_ham
|
||
|
|
end
|
||
|
|
|
||
|
|
# Freeze single comment.
|
||
|
|
def mark_frozen!
|
||
|
|
update_attribute(:iced, true)
|
||
|
|
end
|
||
|
|
|
||
|
|
# Freeze all comments.
|
||
|
|
def self.mark_all_frozen!(comments)
|
||
|
|
transaction do
|
||
|
|
comments.each(&:mark_frozen!)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
# Unfreeze single comment.
|
||
|
|
def mark_unfrozen!
|
||
|
|
update_attribute(:iced, false)
|
||
|
|
end
|
||
|
|
|
||
|
|
# Unfreeze all comments.
|
||
|
|
def self.mark_all_unfrozen!(comments)
|
||
|
|
transaction do
|
||
|
|
comments.each(&:mark_unfrozen!)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
def mark_hidden!
|
||
|
|
update_attribute(:hidden_by_admin, true)
|
||
|
|
end
|
||
|
|
|
||
|
|
def mark_unhidden!
|
||
|
|
update_attribute(:hidden_by_admin, false)
|
||
|
|
end
|
||
|
|
|
||
|
|
def sanitized_content
|
||
|
|
sanitize_field(self, :comment_content, image_safety_mode: use_image_safety_mode?)
|
||
|
|
end
|
||
|
|
|
||
|
|
def sanitized_mailer_content
|
||
|
|
sanitize_field(self, :comment_content, image_safety_mode: true)
|
||
|
|
end
|
||
|
|
|
||
|
|
def use_image_safety_mode?
|
||
|
|
pseud_id.nil? || hidden_by_admin || parent_type.in?(ArchiveConfig.PARENTS_WITH_IMAGE_SAFETY_MODE)
|
||
|
|
end
|
||
|
|
|
||
|
|
include Responder
|
||
|
|
end
|