2013-02-05 13:16:51 -06:00
|
|
|
require_dependency 'rate_limiter'
|
|
|
|
require_dependency 'system_message'
|
|
|
|
|
|
|
|
class PostAction < ActiveRecord::Base
|
2013-02-06 17:45:58 -06:00
|
|
|
class AlreadyFlagged < StandardError; end
|
2013-02-07 09:45:24 -06:00
|
|
|
|
2013-02-05 13:16:51 -06:00
|
|
|
include RateLimiter::OnCreateRecord
|
|
|
|
|
2013-04-22 02:45:03 -05:00
|
|
|
attr_accessible :post_action_type_id, :post_id, :user_id, :post, :user, :post_action_type, :message, :related_post_id
|
2013-02-05 13:16:51 -06:00
|
|
|
|
|
|
|
belongs_to :post
|
|
|
|
belongs_to :user
|
|
|
|
belongs_to :post_action_type
|
|
|
|
|
2013-02-08 15:14:53 -06:00
|
|
|
acts_as_paranoid
|
|
|
|
|
2013-02-05 13:16:51 -06:00
|
|
|
rate_limit :post_action_rate_limiter
|
|
|
|
|
2013-02-08 15:55:40 -06:00
|
|
|
validate :message_quality
|
|
|
|
|
2013-02-05 13:16:51 -06:00
|
|
|
def self.update_flagged_posts_count
|
2013-02-08 14:54:28 -06:00
|
|
|
posts_flagged_count = PostAction.joins(post: :topic)
|
2013-04-14 22:09:52 -05:00
|
|
|
.where('post_actions.post_action_type_id' => PostActionType.notify_flag_types.values,
|
2013-02-08 14:54:28 -06:00
|
|
|
'posts.deleted_at' => nil,
|
|
|
|
'topics.deleted_at' => nil).count('DISTINCT posts.id')
|
|
|
|
|
|
|
|
$redis.set('posts_flagged_count', posts_flagged_count)
|
2013-05-02 00:15:17 -05:00
|
|
|
user_ids = User.where("admin = 't' or moderator = 't'").select(:id).map {|u| u.id}
|
|
|
|
MessageBus.publish('/flagged_counts', { total: posts_flagged_count }, { user_ids: user_ids })
|
2013-02-05 13:16:51 -06:00
|
|
|
end
|
|
|
|
|
|
|
|
def self.flagged_posts_count
|
|
|
|
$redis.get('posts_flagged_count').to_i
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.counts_for(collection, user)
|
|
|
|
return {} if collection.blank?
|
|
|
|
|
|
|
|
collection_ids = collection.map {|p| p.id}
|
|
|
|
|
2013-02-07 09:45:24 -06:00
|
|
|
user_id = user.present? ? user.id : 0
|
2013-02-05 13:16:51 -06:00
|
|
|
|
2013-02-08 15:14:53 -06:00
|
|
|
result = PostAction.where(post_id: collection_ids, user_id: user_id)
|
2013-02-05 13:16:51 -06:00
|
|
|
user_actions = {}
|
|
|
|
result.each do |r|
|
|
|
|
user_actions[r.post_id] ||= {}
|
|
|
|
user_actions[r.post_id][r.post_action_type_id] = r
|
|
|
|
end
|
2013-02-07 09:45:24 -06:00
|
|
|
|
2013-02-05 13:16:51 -06:00
|
|
|
user_actions
|
2013-02-07 09:45:24 -06:00
|
|
|
end
|
2013-02-05 13:16:51 -06:00
|
|
|
|
2013-04-18 13:27:22 -05:00
|
|
|
def self.count_per_day_for_type(sinceDaysAgo = 30, post_action_type)
|
2013-04-30 11:52:12 -05:00
|
|
|
unscoped.where(post_action_type_id: post_action_type).where('created_at > ?', sinceDaysAgo.days.ago).group('date(created_at)').order('date(created_at)').count
|
2013-03-17 12:53:00 -05:00
|
|
|
end
|
|
|
|
|
2013-02-06 22:15:48 -06:00
|
|
|
def self.clear_flags!(post, moderator_id, action_type_id = nil)
|
2013-02-05 13:16:51 -06:00
|
|
|
# -1 is the automatic system cleary
|
2013-02-06 22:15:48 -06:00
|
|
|
actions = if action_type_id
|
2013-02-07 09:45:24 -06:00
|
|
|
[action_type_id]
|
2013-02-06 22:15:48 -06:00
|
|
|
else
|
2013-03-01 06:07:44 -06:00
|
|
|
moderator_id == -1 ? PostActionType.auto_action_flag_types.values : PostActionType.flag_types.values
|
2013-02-06 22:15:48 -06:00
|
|
|
end
|
2013-02-05 13:16:51 -06:00
|
|
|
|
2013-04-30 11:52:12 -05:00
|
|
|
PostAction.update_all({ deleted_at: Time.zone.now, deleted_by: moderator_id }, { post_id: post.id, post_action_type_id: actions })
|
2013-02-05 13:16:51 -06:00
|
|
|
|
2013-03-01 06:07:44 -06:00
|
|
|
f = actions.map{|t| ["#{PostActionType.types[t]}_count", 0]}
|
2013-02-05 13:16:51 -06:00
|
|
|
|
2013-02-08 18:04:14 -06:00
|
|
|
Post.with_deleted.update_all(Hash[*f.flatten], id: post.id)
|
2013-02-05 13:16:51 -06:00
|
|
|
|
|
|
|
update_flagged_posts_count
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.act(user, post, post_action_type_id, message = nil)
|
2013-02-07 09:45:24 -06:00
|
|
|
begin
|
2013-04-16 15:56:18 -05:00
|
|
|
title, target_usernames, subtype, body = nil
|
2013-04-14 22:09:52 -05:00
|
|
|
|
2013-04-12 02:55:45 -05:00
|
|
|
if message
|
|
|
|
[:notify_moderators, :notify_user].each do |k|
|
|
|
|
if post_action_type_id == PostActionType.types[k]
|
2013-04-14 22:09:52 -05:00
|
|
|
target_usernames = k == :notify_moderators ? target_moderators(user) : post.user.username
|
|
|
|
title = I18n.t("post_action_types.#{k}.email_title",
|
|
|
|
title: post.topic.title)
|
|
|
|
body = I18n.t("post_action_types.#{k}.email_body",
|
|
|
|
message: message,
|
2013-04-12 02:55:45 -05:00
|
|
|
link: "#{Discourse.base_url}#{post.url}")
|
2013-04-16 15:56:18 -05:00
|
|
|
subtype = k == :notify_moderators ? TopicSubtype.notify_moderators : TopicSubtype.notify_user
|
2013-04-12 02:55:45 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-04-22 02:45:03 -05:00
|
|
|
related_post_id = nil
|
2013-04-12 02:55:45 -05:00
|
|
|
if target_usernames.present?
|
2013-04-22 02:45:03 -05:00
|
|
|
related_post_id = PostCreator.new(user,
|
2013-04-14 22:09:52 -05:00
|
|
|
target_usernames: target_usernames,
|
2013-04-12 02:55:45 -05:00
|
|
|
archetype: Archetype.private_message,
|
2013-04-16 15:56:18 -05:00
|
|
|
subtype: subtype,
|
2013-04-12 02:55:45 -05:00
|
|
|
title: title,
|
|
|
|
raw: body
|
2013-04-22 02:45:03 -05:00
|
|
|
).create.id
|
2013-04-12 02:55:45 -05:00
|
|
|
end
|
2013-04-22 02:45:03 -05:00
|
|
|
create( post_id: post.id,
|
|
|
|
user_id: user.id,
|
|
|
|
post_action_type_id: post_action_type_id,
|
|
|
|
message: message,
|
|
|
|
related_post_id: related_post_id )
|
2013-02-05 13:16:51 -06:00
|
|
|
rescue ActiveRecord::RecordNotUnique
|
|
|
|
# can happen despite being .create
|
|
|
|
# since already bookmarked
|
|
|
|
true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.remove_act(user, post, post_action_type_id)
|
2013-02-28 12:54:12 -06:00
|
|
|
if action = where(post_id: post.id, user_id: user.id, post_action_type_id: post_action_type_id).first
|
2013-02-08 15:14:53 -06:00
|
|
|
action.destroy
|
2013-04-30 11:52:12 -05:00
|
|
|
action.deleted_at = Time.zone.now
|
2013-02-25 10:42:20 -06:00
|
|
|
action.run_callbacks(:save)
|
2013-02-05 13:16:51 -06:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-02-07 09:45:24 -06:00
|
|
|
def is_bookmark?
|
2013-03-01 06:07:44 -06:00
|
|
|
post_action_type_id == PostActionType.types[:bookmark]
|
2013-02-05 13:16:51 -06:00
|
|
|
end
|
|
|
|
|
2013-02-07 09:45:24 -06:00
|
|
|
def is_like?
|
2013-03-01 06:07:44 -06:00
|
|
|
post_action_type_id == PostActionType.types[:like]
|
2013-02-05 13:16:51 -06:00
|
|
|
end
|
|
|
|
|
|
|
|
def is_flag?
|
2013-03-01 06:07:44 -06:00
|
|
|
PostActionType.flag_types.values.include?(post_action_type_id)
|
2013-02-07 09:45:24 -06:00
|
|
|
end
|
2013-02-05 13:16:51 -06:00
|
|
|
|
2013-04-12 02:55:45 -05:00
|
|
|
def is_private_message?
|
|
|
|
post_action_type_id == PostActionType.types[:notify_user] ||
|
|
|
|
post_action_type_id == PostActionType.types[:notify_moderators]
|
|
|
|
end
|
2013-02-05 13:16:51 -06:00
|
|
|
# A custom rate limiter for this model
|
|
|
|
def post_action_rate_limiter
|
2013-02-28 12:54:12 -06:00
|
|
|
return unless is_flag? || is_bookmark? || is_like?
|
2013-02-05 13:16:51 -06:00
|
|
|
|
|
|
|
return @rate_limiter if @rate_limiter.present?
|
|
|
|
|
|
|
|
%w(like flag bookmark).each do |type|
|
|
|
|
if send("is_#{type}?")
|
2013-02-07 09:45:24 -06:00
|
|
|
@rate_limiter = RateLimiter.new(user, "create_#{type}:#{Date.today.to_s}", SiteSetting.send("max_#{type}s_per_day"), 1.day.to_i)
|
2013-02-05 13:16:51 -06:00
|
|
|
return @rate_limiter
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-02-08 15:55:40 -06:00
|
|
|
def message_quality
|
|
|
|
return if message.blank?
|
|
|
|
sentinel = TextSentinel.title_sentinel(message)
|
2013-04-10 04:00:50 -05:00
|
|
|
errors.add(:message, I18n.t(:is_invalid)) unless sentinel.valid?
|
2013-02-08 15:55:40 -06:00
|
|
|
end
|
|
|
|
|
2013-02-07 09:45:24 -06:00
|
|
|
before_create do
|
2013-03-04 18:42:44 -06:00
|
|
|
raise AlreadyFlagged if is_flag? && PostAction.where(user_id: user_id,
|
|
|
|
post_id: post_id,
|
|
|
|
post_action_type_id: PostActionType.flag_types.values).exists?
|
2013-02-06 17:45:58 -06:00
|
|
|
end
|
|
|
|
|
2013-02-05 13:16:51 -06:00
|
|
|
after_save do
|
|
|
|
# Update denormalized counts
|
2013-03-01 06:07:44 -06:00
|
|
|
post_action_type = PostActionType.types[post_action_type_id]
|
|
|
|
column = "#{post_action_type.to_s}_count"
|
2013-02-05 13:16:51 -06:00
|
|
|
delta = deleted_at.nil? ? 1 : -1
|
|
|
|
|
|
|
|
# Voting also changes the sort_order
|
|
|
|
if post_action_type == :vote
|
2013-03-12 11:33:42 -05:00
|
|
|
Post.update_all ["vote_count = vote_count + :delta, sort_order = :max - (vote_count + :delta)", delta: delta, max: Topic.max_sort_order], id: post_id
|
2013-02-05 13:16:51 -06:00
|
|
|
else
|
2013-02-08 14:54:28 -06:00
|
|
|
Post.update_all ["#{column} = #{column} + ?", delta], id: post_id
|
2013-02-05 13:16:51 -06:00
|
|
|
end
|
2013-02-08 14:54:28 -06:00
|
|
|
Topic.update_all ["#{column} = #{column} + ?", delta], id: post.topic_id
|
2013-02-05 13:16:51 -06:00
|
|
|
|
|
|
|
|
2013-04-14 22:09:52 -05:00
|
|
|
if PostActionType.notify_flag_types.values.include?(post_action_type_id)
|
2013-02-05 13:16:51 -06:00
|
|
|
PostAction.update_flagged_posts_count
|
|
|
|
end
|
|
|
|
|
2013-04-14 22:09:52 -05:00
|
|
|
if PostActionType.auto_action_flag_types.include?(post_action_type) && SiteSetting.flags_required_to_hide_post > 0
|
2013-02-08 14:54:28 -06:00
|
|
|
# automatic hiding of posts
|
|
|
|
flag_counts = exec_sql("SELECT SUM(CASE WHEN deleted_at IS NULL THEN 1 ELSE 0 END) AS new_flags,
|
|
|
|
SUM(CASE WHEN deleted_at IS NOT NULL THEN 1 ELSE 0 END) AS old_flags
|
|
|
|
FROM post_actions
|
2013-03-01 06:07:44 -06:00
|
|
|
WHERE post_id = ? AND post_action_type_id IN (?)", post.id, PostActionType.auto_action_flag_types.values).first
|
2013-02-08 14:54:28 -06:00
|
|
|
old_flags, new_flags = flag_counts['old_flags'].to_i, flag_counts['new_flags'].to_i
|
2013-02-05 13:16:51 -06:00
|
|
|
|
|
|
|
if new_flags >= SiteSetting.flags_required_to_hide_post
|
2013-03-18 13:59:34 -05:00
|
|
|
reason = old_flags > 0 ? Post.hidden_reasons[:flag_threshold_reached_again] : Post.hidden_reasons[:flag_threshold_reached]
|
2013-02-08 14:54:28 -06:00
|
|
|
Post.update_all(["hidden = true, hidden_reason_id = COALESCE(hidden_reason_id, ?)", reason], id: post_id)
|
2013-02-28 12:54:12 -06:00
|
|
|
Topic.update_all({ visible: false },
|
2013-02-08 14:54:28 -06:00
|
|
|
["id = :topic_id AND NOT EXISTS(SELECT 1 FROM POSTS WHERE topic_id = :topic_id AND NOT hidden)", topic_id: post.topic_id])
|
2013-02-05 13:16:51 -06:00
|
|
|
|
|
|
|
# inform user
|
2013-02-28 12:54:12 -06:00
|
|
|
if post.user
|
|
|
|
SystemMessage.create(post.user, :post_hidden,
|
|
|
|
url: post.url,
|
2013-02-08 14:54:28 -06:00
|
|
|
edit_delay: SiteSetting.cooldown_minutes_after_hiding_posts)
|
2013-02-05 13:16:51 -06:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2013-04-12 02:55:45 -05:00
|
|
|
|
2013-04-14 22:09:52 -05:00
|
|
|
protected
|
2013-04-12 02:55:45 -05:00
|
|
|
|
|
|
|
def self.target_moderators(me)
|
|
|
|
User
|
|
|
|
.where("moderator = 't' or admin = 't'")
|
|
|
|
.where('id <> ?', [me.id])
|
|
|
|
.select('username')
|
|
|
|
.map{|u| u.username}
|
|
|
|
.join(',')
|
|
|
|
end
|
|
|
|
|
2013-02-05 13:16:51 -06:00
|
|
|
end
|