Merge pull request #300 from goshakkk/cleanup

Minor cleanup, using AR querying DSL over raw SQL in some places
This commit is contained in:
Robin Ward 2013-03-01 07:52:34 -08:00
commit b66b17bf19
41 changed files with 230 additions and 341 deletions

View File

@ -1,6 +1,5 @@
class Category < ActiveRecord::Base class Category < ActiveRecord::Base
belongs_to :topic, dependent: :destroy
belongs_to :topic
belongs_to :user belongs_to :user
has_many :topics has_many :topics
@ -18,15 +17,13 @@ class Category < ActiveRecord::Base
after_save :invalidate_site_cache after_save :invalidate_site_cache
after_destroy :invalidate_site_cache after_destroy :invalidate_site_cache
scope :popular, lambda { order('topic_count desc') }
def uncategorized_validator def uncategorized_validator
return errors.add(:name, I18n.t(:is_reserved)) if name == SiteSetting.uncategorized_name return errors.add(:name, I18n.t(:is_reserved)) if name == SiteSetting.uncategorized_name
return errors.add(:slug, I18n.t(:is_reserved)) if slug == SiteSetting.uncategorized_name return errors.add(:slug, I18n.t(:is_reserved)) if slug == SiteSetting.uncategorized_name
end end
def self.popular
order('topic_count desc')
end
# Recalculates `topics_year`, `topics_month`, and `topics_week` # Recalculates `topics_year`, `topics_month`, and `topics_week`
# for each Category. # for each Category.
def self.update_stats def self.update_stats
@ -69,9 +66,4 @@ class Category < ActiveRecord::Base
def invalidate_site_cache def invalidate_site_cache
Site.invalidate_cache Site.invalidate_cache
end end
before_destroy do
topic.destroy
end
end end

View File

@ -4,19 +4,16 @@ class CategoryFeaturedTopic < ActiveRecord::Base
# Populates the category featured topics # Populates the category featured topics
def self.feature_topics def self.feature_topics
transaction do transaction do
Category.all.each do |c| Category.all.each do |c|
feature_topics_for(c) feature_topics_for(c)
CategoryFeaturedUser.feature_users_in(c) CategoryFeaturedUser.feature_users_in(c)
end end
end end
nil
end end
def self.feature_topics_for(c) def self.feature_topics_for(c)
return unless c.present? return if c.blank?
CategoryFeaturedTopic.transaction do CategoryFeaturedTopic.transaction do
exec_sql "DELETE FROM category_featured_topics WHERE category_id = :category_id", category_id: c.id exec_sql "DELETE FROM category_featured_topics WHERE category_id = :category_id", category_id: c.id

View File

@ -20,7 +20,7 @@ class CategoryFeaturedUser < ActiveRecord::Base
", category_id: category.id, max_featured_users: max_featured_users ", category_id: category.id, max_featured_users: max_featured_users
transaction do transaction do
CategoryFeaturedUser.delete_all ['category_id = ?', category.id] CategoryFeaturedUser.delete_all category_id: category.id
user_counts.each do |uc| user_counts.each do |uc|
create(category_id: category.id, user_id: uc['user_id']) create(category_id: category.id, user_id: uc['user_id'])
end end

View File

@ -47,25 +47,22 @@ class CategoryList
# Remove categories with no featured topics unless we have the ability to edit one # Remove categories with no featured topics unless we have the ability to edit one
unless Guardian.new(current_user).can_create?(Category) unless Guardian.new(current_user).can_create?(Category)
@categories.delete_if {|c| c.featured_topics.blank? } @categories.delete_if { |c| c.featured_topics.blank? }
end end
# Get forum topic user records if appropriate # Get forum topic user records if appropriate
if current_user.present? if current_user.present?
topics = [] topics = []
@categories.each {|c| topics << c.featured_topics} @categories.each { |c| topics << c.featured_topics }
topics << @uncategorized topics << @uncategorized
topics.flatten! if topics.present? topics.flatten! if topics.present?
topics.compact! if topics.present? topics.compact! if topics.present?
topic_lookup = TopicUser.lookup_for(current_user, topics) topic_lookup = TopicUser.lookup_for(current_user, topics)
# Attach some data for serialization to each topic # Attach some data for serialization to each topic
topics.each {|ft| ft.user_data = topic_lookup[ft.id]} topics.each { |ft| ft.user_data = topic_lookup[ft.id] }
end end
end end
end end

View File

@ -1,5 +1,4 @@
class Draft < ActiveRecord::Base class Draft < ActiveRecord::Base
NEW_TOPIC = 'new_topic' NEW_TOPIC = 'new_topic'
NEW_PRIVATE_MESSAGE = 'new_private_message' NEW_PRIVATE_MESSAGE = 'new_private_message'
EXISTING_TOPIC = 'topic_' EXISTING_TOPIC = 'topic_'
@ -20,8 +19,6 @@ class Draft < ActiveRecord::Base
d = find_draft(user,key) d = find_draft(user,key)
if d && d.sequence == sequence if d && d.sequence == sequence
d.data d.data
else
nil
end end
end end
@ -29,8 +26,6 @@ class Draft < ActiveRecord::Base
d = find_draft(user,key) d = find_draft(user,key)
if d && d.sequence <= sequence if d && d.sequence <= sequence
d.destroy d.destroy
else
nil
end end
end end
@ -41,5 +36,4 @@ class Draft < ActiveRecord::Base
user_id = user.id if User === user user_id = user.id if User === user
Draft.where(user_id: user_id, draft_key: key).first Draft.where(user_id: user_id, draft_key: key).first
end end
end end

View File

@ -2,7 +2,7 @@ class DraftSequence < ActiveRecord::Base
def self.next!(user,key) def self.next!(user,key)
user_id = user user_id = user
user_id = user.id unless user.class == Fixnum user_id = user.id unless user.class == Fixnum
h = {user_id: user_id, draft_key: key} h = { user_id: user_id, draft_key: key }
c = DraftSequence.where(h).first c = DraftSequence.where(h).first
c ||= DraftSequence.new(h) c ||= DraftSequence.new(h)
c.sequence ||= 0 c.sequence ||= 0
@ -20,10 +20,6 @@ class DraftSequence < ActiveRecord::Base
# perf critical path # perf critical path
r = exec_sql('select sequence from draft_sequences where user_id = ? and draft_key = ?', user_id, key).values r = exec_sql('select sequence from draft_sequences where user_id = ? and draft_key = ?', user_id, key).values
if r.length == 0 r.length.zero? ? 0 : r[0][0].to_i
0
else
r[0][0].to_i
end
end end
end end

View File

@ -1,12 +1,10 @@
class EmailLog < ActiveRecord::Base class EmailLog < ActiveRecord::Base
belongs_to :user belongs_to :user
validates_presence_of :email_type validates_presence_of :email_type
validates_presence_of :to_address validates_presence_of :to_address
after_create do after_create do
# Update last_emailed_at if the user_id is present # Update last_emailed_at if the user_id is present
User.update_all("last_emailed_at = CURRENT_TIMESTAMP", ["id = ?", user_id]) if user_id.present? User.update_all("last_emailed_at = CURRENT_TIMESTAMP", id: user_id) if user_id.present?
end end
end end

View File

@ -5,7 +5,7 @@ class EmailToken < ActiveRecord::Base
validates_presence_of :user_id validates_presence_of :user_id
validates_presence_of :email validates_presence_of :email
before_validation(:on => :create) do before_validation(on: :create) do
self.token = EmailToken.generate_token self.token = EmailToken.generate_token
end end
@ -38,14 +38,13 @@ class EmailToken < ActiveRecord::Base
return unless token.present? return unless token.present?
return unless token.length/2 == EmailToken.token_length return unless token.length/2 == EmailToken.token_length
email_token = EmailToken.where("token = ? AND expired = FALSE and created_at >= ?", token, EmailToken.valid_after).includes(:user).first email_token = EmailToken.where("token = ? and expired = FALSE and created_at >= ?", token, EmailToken.valid_after).includes(:user).first
return if email_token.blank? return if email_token.blank?
user = email_token.user user = email_token.user
User.transaction do User.transaction do
row_count = EmailToken.update_all 'confirmed = true', ['id = ? AND confirmed = false', email_token.id] row_count = EmailToken.update_all 'confirmed = true', id: email_token.id, expired: false
if row_count == 1 if row_count == 1
# If we are activating the user, send the welcome message # If we are activating the user, send the welcome message
user.send_welcome_message = !user.active? user.send_welcome_message = !user.active?
@ -57,7 +56,5 @@ class EmailToken < ActiveRecord::Base
user user
rescue ActiveRecord::RecordInvalid rescue ActiveRecord::RecordInvalid
# If the user's email is already taken, just return nil (failure) # If the user's email is already taken, just return nil (failure)
nil
end end
end end

View File

@ -2,7 +2,6 @@
# a mechanism to iterate through errors in reverse # a mechanism to iterate through errors in reverse
# async logging should queue, if dupe stack traces are found in batch error should be merged into prev one # async logging should queue, if dupe stack traces are found in batch error should be merged into prev one
class ErrorLog class ErrorLog
@lock = Mutex.new @lock = Mutex.new
@ -12,7 +11,7 @@ class ErrorLog
end end
def self.clear!(guid) def self.clear!(guid)
raise "not implemented" raise NotImplementedError
end end
def self.clear_all!() def self.clear_all!()
@ -21,22 +20,22 @@ class ErrorLog
def self.report_async!(exception, controller, request, user) def self.report_async!(exception, controller, request, user)
Thread.new do Thread.new do
self.report!(exception, controller, request, user) report!(exception, controller, request, user)
end end
end end
def self.report!(exception, controller, request, user) def self.report!(exception, controller, request, user)
add_row!( add_row!(
:date => DateTime.now, date: DateTime.now,
:guid => SecureRandom.uuid, guid: SecureRandom.uuid,
:user_id => user && user.id, user_id: user && user.id,
:request => filter_sensitive_post_data_parameters(controller, request.parameters).inspect, request: filter_sensitive_post_data_parameters(controller, request.parameters).inspect,
:action => controller.action_name, action: controller.action_name,
:controller => controller.controller_name, controller: controller.controller_name,
:backtrace => sanitize_backtrace(exception.backtrace).join("\n"), backtrace: sanitize_backtrace(exception.backtrace).join("\n"),
:message => exception.message, message: exception.message,
:url => "#{request.protocol}#{request.env["HTTP_X_FORWARDED_HOST"] || request.env["HTTP_HOST"]}#{request.fullpath}", url: "#{request.protocol}#{request.env["HTTP_X_FORWARDED_HOST"] || request.env["HTTP_HOST"]}#{request.fullpath}",
:exception_class => exception.class.to_s exception_class: exception.class.to_s
) )
end end
@ -44,7 +43,7 @@ class ErrorLog
data = hash.to_xml(skip_instruct: true) data = hash.to_xml(skip_instruct: true)
# use background thread to write the log cause it may block if it gets backed up # use background thread to write the log cause it may block if it gets backed up
@lock.synchronize do @lock.synchronize do
File.open(self.filename, "a") do |f| File.open(filename, "a") do |f|
f.flock(File::LOCK_EX) f.flock(File::LOCK_EX)
f.write(data) f.write(data)
f.close f.close
@ -54,13 +53,12 @@ class ErrorLog
def self.each(&blk) def self.each(&blk)
skip(0,&blk) skip(0, &blk)
end end
def self.skip(skip=0) def self.skip(skip=0)
data = nil
pos = 0 pos = 0
return [] unless File.exists?(self.filename) return [] unless File.exists?(filename)
loop do loop do
lines = "" lines = ""
@ -107,5 +105,4 @@ class ErrorLog
return PARAM_FILTER_REPLACEMENT if (env_key =~ /RAW_POST_DATA/i) return PARAM_FILTER_REPLACEMENT if (env_key =~ /RAW_POST_DATA/i)
return controller.__send__(:filter_parameters, {env_key => env_value}).values[0] return controller.__send__(:filter_parameters, {env_key => env_value}).values[0]
end end
end end

View File

@ -1,13 +1,12 @@
class IncomingLink < ActiveRecord::Base class IncomingLink < ActiveRecord::Base
belongs_to :topic belongs_to :topic
validates :domain, :length => { :in => 1..100 } validates :domain, length: { in: 1..100 }
validates :referer, :length => { :in => 3..1000 } validates :referer, length: { in: 3..1000 }
validates_presence_of :url validates_presence_of :url
# Extract the domain # Extract the domain
before_validation do before_validation do
# Referer (remote URL) # Referer (remote URL)
if referer.present? if referer.present?
parsed = URI.parse(referer) parsed = URI.parse(referer)
@ -27,7 +26,6 @@ class IncomingLink < ActiveRecord::Base
# If we can't route to the url, that's OK. Don't save those two fields. # If we can't route to the url, that's OK. Don't save those two fields.
end end
end end
end end
# Update appropriate incoming link counts # Update appropriate incoming link counts
@ -43,5 +41,4 @@ class IncomingLink < ActiveRecord::Base
end end
end end
end end
end end

View File

@ -2,7 +2,7 @@ class Invite < ActiveRecord::Base
belongs_to :user belongs_to :user
belongs_to :topic belongs_to :topic
belongs_to :invited_by, class_name: User belongs_to :invited_by, class_name: 'User'
has_many :topic_invites has_many :topic_invites
has_many :topics, through: :topic_invites, source: :topic has_many :topics, through: :topic_invites, source: :topic
@ -17,7 +17,7 @@ class Invite < ActiveRecord::Base
end end
before_save do before_save do
self.email = self.email.downcase self.email = email.downcase
end end
validate :user_doesnt_already_exist validate :user_doesnt_already_exist
@ -45,13 +45,13 @@ class Invite < ActiveRecord::Base
Invite.transaction do Invite.transaction do
# Avoid a race condition # Avoid a race condition
row_count = Invite.update_all('redeemed_at = CURRENT_TIMESTAMP', row_count = Invite.update_all('redeemed_at = CURRENT_TIMESTAMP',
['id = ? AND redeemed_at IS NULL AND created_at >= ?', self.id, SiteSetting.invite_expiry_days.days.ago]) ['id = ? AND redeemed_at IS NULL AND created_at >= ?', id, SiteSetting.invite_expiry_days.days.ago])
if row_count == 1 if row_count == 1
# Create the user if we are redeeming the invite and the user doesn't exist # Create the user if we are redeeming the invite and the user doesn't exist
result = User.where(email: email).first result = User.where(email: email).first
result = User.create_for_email(email, trust_level: SiteSetting.default_invitee_trust_level) if result.blank? result ||= User.create_for_email(email, trust_level: SiteSetting.default_invitee_trust_level)
result.send_welcome_message = false result.send_welcome_message = false
# If there are topic invites for private topics # If there are topic invites for private topics
@ -61,19 +61,19 @@ class Invite < ActiveRecord::Base
# Check for other invites by the same email. Don't redeem them, but approve their # Check for other invites by the same email. Don't redeem them, but approve their
# topics. # topics.
Invite.where('invites.email = ? and invites.id != ?', self.email, self.id).includes(:topics).where('topics.archetype = ?', Archetype::private_message).each do |i| Invite.where('invites.email = ? and invites.id != ?', email, id).includes(:topics).where(topics: { archetype: Archetype::private_message }).each do |i|
i.topics.each do |t| i.topics.each do |t|
t.topic_allowed_users.create(user_id: result.id) t.topic_allowed_users.create(user_id: result.id)
end end
end end
if Invite.update_all(['user_id = ?', result.id], ['email = ?', self.email]) == 1 if Invite.update_all(['user_id = ?', result.id], ['email = ?', email]) == 1
result.send_welcome_message = true result.send_welcome_message = true
end end
# Notify the invitee # Notify the invitee
invited_by.notifications.create(notification_type: Notification.Types[:invitee_accepted], invited_by.notifications.create(notification_type: Notification.Types[:invitee_accepted],
data: {display_username: result.username}.to_json) data: { display_username: result.username }.to_json)
else else
# Otherwise return the existing user # Otherwise return the existing user

View File

@ -6,6 +6,9 @@ class Notification < ActiveRecord::Base
validates_presence_of :data validates_presence_of :data
validates_presence_of :notification_type validates_presence_of :notification_type
scope :unread, lambda { where(read: false) }
scope :recent, lambda { order('created_at desc').limit(10) }
def self.Types def self.Types
{:mentioned => 1, {:mentioned => 1,
:replied => 2, :replied => 2,
@ -23,16 +26,8 @@ class Notification < ActiveRecord::Base
@inverted_types ||= Notification.Types.invert @inverted_types ||= Notification.Types.invert
end end
def self.unread
where(read: false)
end
def self.mark_posts_read(user, topic_id, post_numbers) def self.mark_posts_read(user, topic_id, post_numbers)
Notification.update_all "read = 't'", ["user_id = ? and topic_id = ? and post_number in (?) and read = ?", user.id, topic_id, post_numbers, false] Notification.update_all "read = 't'", user_id: user.id, topic_id: topic_id, post_number: post_numbers, read: false
end
def self.recent
order('created_at desc').limit(10)
end end
def self.interesting_after(min_date) def self.interesting_after(min_date)
@ -68,7 +63,7 @@ class Notification < ActiveRecord::Base
def data_hash def data_hash
@data_hash ||= begin @data_hash ||= begin
return nil if data.blank? return nil if data.blank?
::JSON.parse(data).with_indifferent_access JSON.parse(data).with_indifferent_access
end end
end end
@ -81,15 +76,12 @@ class Notification < ActiveRecord::Base
if topic.present? if topic.present?
return topic.relative_url(post_number) return topic.relative_url(post_number)
end end
nil
end end
def post def post
return nil unless topic_id.present? return if topic_id.blank? || post_number.blank?
return nil unless post_number.present?
Post.where(topic_id: topic_id, post_number: post_number).first Post.where(topic_id: topic_id, post_number: post_number).first
end end
end end

View File

@ -1,10 +1,8 @@
class OneboxRender < ActiveRecord::Base class OneboxRender < ActiveRecord::Base
validates_presence_of :url validates_presence_of :url
validates_presence_of :cooked validates_presence_of :cooked
validates_presence_of :expires_at validates_presence_of :expires_at
has_many :post_onebox_renders, :dependent => :delete_all has_many :post_onebox_renders, dependent: :delete_all
has_many :posts, through: :post_onebox_renders has_many :posts, through: :post_onebox_renders
end end

View File

@ -29,7 +29,7 @@ class Post < ActiveRecord::Base
has_many :post_actions has_many :post_actions
validates_presence_of :raw, :user_id, :topic_id validates_presence_of :raw, :user_id, :topic_id
validates :raw, stripped_length: {in: SiteSetting.min_post_length..SiteSetting.max_post_length} validates :raw, stripped_length: { in: SiteSetting.post_length }
validate :raw_quality validate :raw_quality
validate :max_mention_validator validate :max_mention_validator
validate :max_images_validator validate :max_images_validator
@ -54,15 +54,14 @@ class Post < ActiveRecord::Base
after_commit :store_unique_post_key, on: :create after_commit :store_unique_post_key, on: :create
after_create do after_create do
TopicUser.auto_track(self.user_id, self.topic_id, TopicUser::NotificationReasons::CREATED_POST) TopicUser.auto_track(user_id, topic_id, TopicUser::NotificationReasons::CREATED_POST)
end end
scope :by_newest, order('created_at desc, id desc') scope :by_newest, order('created_at desc, id desc')
scope :with_user, includes(:user) scope :with_user, includes(:user)
def raw_quality def raw_quality
sentinel = TextSentinel.new(raw, min_entropy: SiteSetting.body_min_entropy)
sentinel = TextSentinel.new(self.raw, min_entropy: SiteSetting.body_min_entropy)
if sentinel.valid? if sentinel.valid?
# It's possible the sentinel has cleaned up the title a bit # It's possible the sentinel has cleaned up the title a bit
self.raw = sentinel.text self.raw = sentinel.text
@ -75,7 +74,7 @@ class Post < ActiveRecord::Base
# Stop us from posting the same thing too quickly # Stop us from posting the same thing too quickly
def unique_post_validator def unique_post_validator
return if SiteSetting.unique_posts_mins == 0 return if SiteSetting.unique_posts_mins == 0
return if user.admin? or user.has_trust_level?(:moderator) return if user.admin? || user.moderator?
# If the post is empty, default to the validates_presence_of # If the post is empty, default to the validates_presence_of
return if raw.blank? return if raw.blank?
@ -97,13 +96,13 @@ class Post < ActiveRecord::Base
end end
def raw_hash def raw_hash
return nil if raw.blank? return if raw.blank?
Digest::SHA1.hexdigest(raw.gsub(/\s+/, "").downcase) Digest::SHA1.hexdigest(raw.gsub(/\s+/, "").downcase)
end end
def cooked_document def cooked_document
self.cooked ||= cook(self.raw, topic_id: topic_id) self.cooked ||= cook(raw, topic_id: topic_id)
@cooked_document ||= Nokogiri::HTML.fragment(self.cooked) @cooked_document ||= Nokogiri::HTML.fragment(cooked)
end end
def reset_cooked def reset_cooked
@ -116,20 +115,18 @@ class Post < ActiveRecord::Base
end end
def image_count def image_count
return 0 unless self.raw.present? return 0 unless raw.present?
cooked_document.search("img").reject{ |t| cooked_document.search("img").reject do |t|
dom_class = t["class"] dom_class = t["class"]
if dom_class if dom_class
(Post.white_listed_image_classes & dom_class.split(" ")).count > 0 (Post.white_listed_image_classes & dom_class.split(" ")).count > 0
else
false
end end
}.count end.count
end end
def link_count def link_count
return 0 unless self.raw.present? return 0 unless raw.present?
cooked_document.search("a[href]").count cooked_document.search("a[href]").count
end end
@ -138,12 +135,12 @@ class Post < ActiveRecord::Base
end end
def max_images_validator def max_images_validator
return if user.present? and user.has_trust_level?(:basic) return if user.present? && user.has_trust_level?(:basic)
errors.add(:raw, I18n.t(:too_many_images)) if image_count > 0 errors.add(:raw, I18n.t(:too_many_images)) if image_count > 0
end end
def max_links_validator def max_links_validator
return if user.present? and user.has_trust_level?(:basic) return if user.present? && user.has_trust_level?(:basic)
errors.add(:raw, I18n.t(:too_many_links)) if link_count > 1 errors.add(:raw, I18n.t(:too_many_links)) if link_count > 1
end end
@ -161,22 +158,18 @@ class Post < ActiveRecord::Base
doc.search("code").remove doc.search("code").remove
results = doc.to_html.scan(PrettyText.mention_matcher) results = doc.to_html.scan(PrettyText.mention_matcher)
if results.present? @raw_mentions = results.uniq.map { |un| un.first.downcase.gsub!(/^@/, '') }
@raw_mentions = results.uniq.map {|un| un.first.downcase.gsub!(/^@/, '')}
else
@raw_mentions = []
end
end end
# The rules for deletion change depending on who is doing it. # The rules for deletion change depending on who is doing it.
def delete_by(deleted_by) def delete_by(deleted_by)
if deleted_by.has_trust_level?(:moderator) if deleted_by.moderator?
# As a moderator, delete the post. # As a moderator, delete the post.
Post.transaction do Post.transaction do
self.destroy self.destroy
Topic.reset_highest(self.topic_id) Topic.reset_highest(topic_id)
end end
elsif deleted_by.id == self.user_id elsif deleted_by.id == user_id
# As the poster, make a revision that says deleted. # As the poster, make a revision that says deleted.
Post.transaction do Post.transaction do
revise(deleted_by, I18n.t('js.post.deleted_by_author'), force_new_version: true) revise(deleted_by, I18n.t('js.post.deleted_by_author'), force_new_version: true)
@ -205,7 +198,7 @@ class Post < ActiveRecord::Base
PostAction.update_flagged_posts_count PostAction.update_flagged_posts_count
end end
def filter_quotes(parent_post=nil) def filter_quotes(parent_post = nil)
return cooked if parent_post.blank? return cooked if parent_post.blank?
# We only filter quotes when there is exactly 1 # We only filter quotes when there is exactly 1
@ -229,31 +222,30 @@ class Post < ActiveRecord::Base
end end
def quoteless? def quoteless?
(quote_count == 0) and (reply_to_post_number.present?) (quote_count == 0) && (reply_to_post_number.present?)
end end
# Get the post that we reply to. # Get the post that we reply to.
def reply_to_user def reply_to_user
return nil unless reply_to_post_number.present? return if reply_to_post_number.blank?
User.where('id = (select user_id from posts where topic_id = ? and post_number = ?)', topic_id, reply_to_post_number).first Post.where(topic_id: topic_id, post_number: reply_to_post_number).first.try(:user)
end end
def reply_notification_target def reply_notification_target
return nil unless reply_to_post_number.present? return if reply_to_post_number.blank?
reply_post = Post.where("topic_id = :topic_id AND post_number = :post_number AND user_id <> :user_id", Post.where("topic_id = :topic_id AND post_number = :post_number AND user_id <> :user_id",
topic_id: topic_id, topic_id: topic_id,
post_number: reply_to_post_number, post_number: reply_to_post_number,
user_id: user_id).first user_id: user_id).first.try(:user)
return reply_post.try(:user)
end end
def self.excerpt(cooked, maxlength=nil) def self.excerpt(cooked, maxlength = nil)
maxlength ||= SiteSetting.post_excerpt_maxlength maxlength ||= SiteSetting.post_excerpt_maxlength
PrettyText.excerpt(cooked, maxlength) PrettyText.excerpt(cooked, maxlength)
end end
# Strip out most of the markup # Strip out most of the markup
def excerpt(maxlength=nil) def excerpt(maxlength = nil)
Post.excerpt(cooked, maxlength) Post.excerpt(cooked, maxlength)
end end
@ -279,22 +271,22 @@ class Post < ActiveRecord::Base
# A list of versions including the initial version # A list of versions including the initial version
def all_versions def all_versions
result = [] result = []
result << {number: 1, display_username: user.name, created_at: created_at} result << { number: 1, display_username: user.name, created_at: created_at }
versions.order(:number).includes(:user).each do |v| versions.order(:number).includes(:user).each do |v|
result << {number: v.number, display_username: v.user.name, created_at: v.created_at} result << { number: v.number, display_username: v.user.name, created_at: v.created_at }
end end
result result
end end
def is_flagged? def is_flagged?
post_actions.where('post_action_type_id in (?) and deleted_at is null', PostActionType.FlagTypes).count != 0 post_actions.where(post_action_type_id: PostActionType.FlagTypes, deleted_at: nil).count != 0
end end
def unhide! def unhide!
self.hidden = false self.hidden = false
self.hidden_reason_id = nil self.hidden_reason_id = nil
self.topic.update_attributes(visible: true) self.topic.update_attributes(visible: true)
self.save save
end end
def url def url
@ -305,7 +297,7 @@ class Post < ActiveRecord::Base
user.readable_name user.readable_name
end end
def revise(updated_by, new_raw, opts={}) def revise(updated_by, new_raw, opts = {})
PostRevisor.new(self).revise!(updated_by, new_raw, opts) PostRevisor.new(self).revise!(updated_by, new_raw, opts)
end end
@ -320,21 +312,20 @@ class Post < ActiveRecord::Base
# TODO: Move some of this into an asynchronous job? # TODO: Move some of this into an asynchronous job?
after_create do after_create do
# Update attributes on the topic - featured users and last posted. # Update attributes on the topic - featured users and last posted.
attrs = {last_posted_at: self.created_at, last_post_user_id: self.user_id} attrs = {last_posted_at: created_at, last_post_user_id: user_id}
attrs[:bumped_at] = self.created_at unless no_bump attrs[:bumped_at] = created_at unless no_bump
topic.update_attributes(attrs) topic.update_attributes(attrs)
# Update the user's last posted at date # Update the user's last posted at date
user.update_column(:last_posted_at, self.created_at) user.update_column(:last_posted_at, created_at)
# Update topic user data # Update topic user data
TopicUser.change(user, TopicUser.change(user,
topic.id, topic.id,
posted: true, posted: true,
last_read_post_number: self.post_number, last_read_post_number: post_number,
seen_post_count: self.post_number) seen_post_count: post_number)
end end
def email_private_message def email_private_message
@ -371,57 +362,54 @@ class Post < ActiveRecord::Base
end end
before_save do before_save do
self.last_editor_id ||= self.user_id self.last_editor_id ||= user_id
self.cooked = cook(raw, topic_id: topic_id) unless new_record? self.cooked = cook(raw, topic_id: topic_id) unless new_record?
end end
before_destroy do before_destroy do
# Update the last post id to the previous post if it exists # Update the last post id to the previous post if it exists
last_post = Post.where("topic_id = ? and id <> ?", self.topic_id, self.id).order('created_at desc').limit(1).first last_post = Post.where("topic_id = ? and id <> ?", topic_id, id).order('created_at desc').limit(1).first
if last_post.present? if last_post.present?
topic.update_attributes(last_posted_at: last_post.created_at, topic.update_attributes(last_posted_at: last_post.created_at,
last_post_user_id: last_post.user_id, last_post_user_id: last_post.user_id,
highest_post_number: last_post.post_number) highest_post_number: last_post.post_number)
# If the poster doesn't have any other posts in the topic, clear their posted flag # If the poster doesn't have any other posts in the topic, clear their posted flag
unless Post.exists?(["topic_id = ? and user_id = ? and id <> ?", self.topic_id, self.user_id, self.id]) unless Post.exists?(["topic_id = ? and user_id = ? and id <> ?", topic_id, user_id, id])
TopicUser.update_all 'posted = false', ['topic_id = ? and user_id = ?', self.topic_id, self.user_id] TopicUser.update_all 'posted = false', topic_id: topic_id, user_id: user_id
end end
end end
# Feature users in the topic # Feature users in the topic
Jobs.enqueue(:feature_topic_users, topic_id: topic_id, except_post_id: self.id) Jobs.enqueue(:feature_topic_users, topic_id: topic_id, except_post_id: id)
end end
after_destroy do after_destroy do
# Remove any reply records that point to deleted posts # Remove any reply records that point to deleted posts
post_ids = PostReply.select(:post_id).where(reply_id: self.id).map(&:post_id) post_ids = PostReply.select(:post_id).where(reply_id: id).map(&:post_id)
PostReply.delete_all ["reply_id = ?", self.id] PostReply.delete_all reply_id: id
if post_ids.present? if post_ids.present?
Post.where(id: post_ids).each {|p| p.update_column :reply_count, p.replies.count} Post.where(id: post_ids).each { |p| p.update_column :reply_count, p.replies.count }
end end
# Remove any notifications that point to this deleted post # Remove any notifications that point to this deleted post
Notification.delete_all ["topic_id = ? and post_number = ?", self.topic_id, self.post_number] Notification.delete_all topic_id: topic_id, post_number: post_number
end end
after_save do after_save do
DraftSequence.next! last_editor_id, topic.draft_key if topic # could be deleted
DraftSequence.next! self.last_editor_id, self.topic.draft_key if self.topic # could be deleted
quoted_post_numbers << reply_to_post_number if reply_to_post_number.present? quoted_post_numbers << reply_to_post_number if reply_to_post_number.present?
# Create a reply relationship between quoted posts and this new post # Create a reply relationship between quoted posts and this new post
if self.quoted_post_numbers.present? if quoted_post_numbers.present?
self.quoted_post_numbers.map! {|pid| pid.to_i}.uniq! quoted_post_numbers.map(&:to_i).uniq.each do |p|
self.quoted_post_numbers.each do |p|
post = Post.where(topic_id: topic_id, post_number: p).first post = Post.where(topic_id: topic_id, post_number: p).first
if post.present? if post.present?
post_reply = post.post_replies.new(reply_id: self.id) post_reply = post.post_replies.new(reply_id: id)
if post_reply.save if post_reply.save
Post.update_all ['reply_count = reply_count + 1'], id: post.id Post.update_all ['reply_count = reply_count + 1'], id: post.id
end end
@ -443,7 +431,7 @@ class Post < ActiveRecord::Base
if args[:topic].present? if args[:topic].present?
# If the topic attribute is present, ensure it's the same topic # If the topic attribute is present, ensure it's the same topic
self.quoted_post_numbers << args[:post] if self.topic_id == args[:topic] self.quoted_post_numbers << args[:post] if topic_id == args[:topic]
else else
self.quoted_post_numbers << args[:post] self.quoted_post_numbers << args[:post]
end end
@ -452,15 +440,14 @@ class Post < ActiveRecord::Base
end end
self.quoted_post_numbers.uniq! self.quoted_post_numbers.uniq!
self.quote_count = self.quoted_post_numbers.size self.quote_count = quoted_post_numbers.size
end end
# Process this post after comitting it # Process this post after comitting it
def trigger_post_process def trigger_post_process
args = {post_id: self.id} args = { post_id: id }
args[:image_sizes] = self.image_sizes if self.image_sizes.present? args[:image_sizes] = image_sizes if image_sizes.present?
args[:invalidate_oneboxes] = true if self.invalidate_oneboxes.present? args[:invalidate_oneboxes] = true if invalidate_oneboxes.present?
Jobs.enqueue(:process_post, args) Jobs.enqueue(:process_post, args)
end end
end end

View File

@ -19,7 +19,6 @@ class PostAction < ActiveRecord::Base
validate :message_quality validate :message_quality
def self.update_flagged_posts_count def self.update_flagged_posts_count
posts_flagged_count = PostAction.joins(post: :topic) posts_flagged_count = PostAction.joins(post: :topic)
.where('post_actions.post_action_type_id' => PostActionType.FlagTypes, .where('post_actions.post_action_type_id' => PostActionType.FlagTypes,
'posts.deleted_at' => nil, 'posts.deleted_at' => nil,
@ -27,7 +26,7 @@ class PostAction < ActiveRecord::Base
$redis.set('posts_flagged_count', posts_flagged_count) $redis.set('posts_flagged_count', posts_flagged_count)
admins = User.where(admin: true).select(:id).map {|u| u.id} admins = User.where(admin: true).select(:id).map {|u| u.id}
MessageBus.publish('/flagged_counts', {total: posts_flagged_count}, {user_ids: admins}) MessageBus.publish('/flagged_counts', { total: posts_flagged_count }, { user_ids: admins })
end end
def self.flagged_posts_count def self.flagged_posts_count
@ -35,7 +34,6 @@ class PostAction < ActiveRecord::Base
end end
def self.counts_for(collection, user) def self.counts_for(collection, user)
return {} if collection.blank? return {} if collection.blank?
collection_ids = collection.map {|p| p.id} collection_ids = collection.map {|p| p.id}
@ -53,7 +51,6 @@ class PostAction < ActiveRecord::Base
end end
def self.clear_flags!(post, moderator_id, action_type_id = nil) def self.clear_flags!(post, moderator_id, action_type_id = nil)
# -1 is the automatic system cleary # -1 is the automatic system cleary
actions = if action_type_id actions = if action_type_id
[action_type_id] [action_type_id]
@ -61,10 +58,10 @@ class PostAction < ActiveRecord::Base
moderator_id == -1 ? PostActionType.AutoActionFlagTypes : PostActionType.FlagTypes moderator_id == -1 ? PostActionType.AutoActionFlagTypes : PostActionType.FlagTypes
end end
PostAction.update_all({deleted_at: Time.now, deleted_by: moderator_id}, {post_id: post.id, post_action_type_id: actions}) PostAction.update_all({ deleted_at: Time.now, deleted_by: moderator_id }, { post_id: post.id, post_action_type_id: actions })
r = PostActionType.Types.invert r = PostActionType.Types.invert
f = actions.map{|t| ["#{r[t]}_count", 0]} f = actions.map { |t| ["#{r[t]}_count", 0] }
Post.with_deleted.update_all(Hash[*f.flatten], id: post.id) Post.with_deleted.update_all(Hash[*f.flatten], id: post.id)
@ -82,7 +79,7 @@ class PostAction < ActiveRecord::Base
end end
def self.remove_act(user, post, post_action_type_id) def self.remove_act(user, post, post_action_type_id)
if action = self.where(post_id: post.id, user_id: user.id, post_action_type_id: post_action_type_id).first if action = where(post_id: post.id, user_id: user.id, post_action_type_id: post_action_type_id).first
action.destroy action.destroy
action.deleted_at = Time.now action.deleted_at = Time.now
action.run_callbacks(:save) action.run_callbacks(:save)
@ -103,7 +100,7 @@ class PostAction < ActiveRecord::Base
# A custom rate limiter for this model # A custom rate limiter for this model
def post_action_rate_limiter def post_action_rate_limiter
return nil unless is_flag? or is_bookmark? or is_like? return unless is_flag? || is_bookmark? || is_like?
return @rate_limiter if @rate_limiter.present? return @rate_limiter if @rate_limiter.present?
@ -127,7 +124,7 @@ class PostAction < ActiveRecord::Base
end end
before_create do before_create do
raise AlreadyFlagged if is_flag? and PostAction.where(user_id: user_id, raise AlreadyFlagged if is_flag? && PostAction.where(user_id: user_id,
post_id: post_id, post_id: post_id,
post_action_type_id: PostActionType.FlagTypes).exists? post_action_type_id: PostActionType.FlagTypes).exists?
end end
@ -135,12 +132,12 @@ class PostAction < ActiveRecord::Base
after_save do after_save do
# Update denormalized counts # Update denormalized counts
post_action_type = PostActionType.Types.invert[post_action_type_id] post_action_type = PostActionType.Types.invert[post_action_type_id]
column = "#{post_action_type.to_s}_count" column = "#{post_action_type}_count"
delta = deleted_at.nil? ? 1 : -1 delta = deleted_at.nil? ? 1 : -1
# Voting also changes the sort_order # Voting also changes the sort_order
if post_action_type == :vote if post_action_type == :vote
Post.update_all ["vote_count = vote_count + :delta, sort_order = :max - (vote_count + :delta)", delta: delta, max: Topic::MAX_SORT_ORDER], ["id = ?", post_id] Post.update_all ["vote_count = vote_count + :delta, sort_order = :max - (vote_count + :delta)", delta: delta, max: Topic::MAX_SORT_ORDER], id: post_id
else else
Post.update_all ["#{column} = #{column} + ?", delta], id: post_id Post.update_all ["#{column} = #{column} + ?", delta], id: post_id
end end
@ -162,18 +159,16 @@ class PostAction < ActiveRecord::Base
if new_flags >= SiteSetting.flags_required_to_hide_post if new_flags >= SiteSetting.flags_required_to_hide_post
reason = old_flags > 0 ? Post::HiddenReason::FLAG_THRESHOLD_REACHED_AGAIN : Post::HiddenReason::FLAG_THRESHOLD_REACHED reason = old_flags > 0 ? Post::HiddenReason::FLAG_THRESHOLD_REACHED_AGAIN : Post::HiddenReason::FLAG_THRESHOLD_REACHED
Post.update_all(["hidden = true, hidden_reason_id = COALESCE(hidden_reason_id, ?)", reason], id: post_id) Post.update_all(["hidden = true, hidden_reason_id = COALESCE(hidden_reason_id, ?)", reason], id: post_id)
Topic.update_all({visible: false}, Topic.update_all({ visible: false },
["id = :topic_id AND NOT EXISTS(SELECT 1 FROM POSTS WHERE topic_id = :topic_id AND NOT hidden)", topic_id: post.topic_id]) ["id = :topic_id AND NOT EXISTS(SELECT 1 FROM POSTS WHERE topic_id = :topic_id AND NOT hidden)", topic_id: post.topic_id])
# inform user # inform user
if self.post.user if post.user
SystemMessage.create(self.post.user, :post_hidden, SystemMessage.create(post.user, :post_hidden,
url: self.post.url, url: post.url,
edit_delay: SiteSetting.cooldown_minutes_after_hiding_posts) edit_delay: SiteSetting.cooldown_minutes_after_hiding_posts)
end end
end end
end end
end end
end end

View File

@ -1,19 +1,19 @@
class PostActionType < ActiveRecord::Base class PostActionType < ActiveRecord::Base
attr_accessible :id, :is_flag, :name_key, :icon attr_accessible :id, :is_flag, :name_key, :icon
def self.ordered def self.ordered
self.order('position asc').all order('position asc').all
end end
def self.Types def self.Types
@types ||= {:bookmark => 1, {
:like => 2, bookmark: 1,
:off_topic => 3, like: 2,
:inappropriate => 4, off_topic: 3,
:vote => 5, inappropriate: 4,
:custom_flag => 6, vote: 5,
:spam => 8 custom_flag: 6,
spam: 8
} }
end end
@ -28,5 +28,4 @@ class PostActionType < ActiveRecord::Base
def self.FlagTypes def self.FlagTypes
@flag_types ||= self.AutoActionFlagTypes + [self.Types[:custom_flag]] @flag_types ||= self.AutoActionFlagTypes + [self.Types[:custom_flag]]
end end
end end

View File

@ -25,12 +25,11 @@ class PostAlertObserver < ActiveRecord::Observer
def after_save_post_action(post_action) def after_save_post_action(post_action)
# We only care about deleting post actions for now # We only care about deleting post actions for now
return unless post_action.deleted_at.present? return if post_action.deleted_at.blank?
Notification.where(["post_action_id = ?", post_action.id]).each {|n| n.destroy} Notification.where(post_action_id: post_action.id).each(&:destroy)
end end
def after_create_post_action(post_action) def after_create_post_action(post_action)
# We only notify on likes for now # We only notify on likes for now
return unless post_action.is_like? return unless post_action.is_like?
@ -59,7 +58,7 @@ class PostAlertObserver < ActiveRecord::Observer
def after_create_post(post) def after_create_post(post)
if post.topic.private_message? if post.topic.private_message?
# If it's a private message, notify the topic_allowed_users # If it's a private message, notify the topic_allowed_users
post.topic.topic_allowed_users.reject{|a| a.user_id == post.user_id}.each do |a| post.topic.topic_allowed_users.reject { |a| a.user_id == post.user_id }.each do |a|
create_notification(a.user, Notification.Types[:private_message], post) create_notification(a.user, Notification.Types[:private_message], post)
end end
else else
@ -91,13 +90,13 @@ class PostAlertObserver < ActiveRecord::Observer
topic_id: post.topic_id, topic_id: post.topic_id,
post_number: post.post_number, post_number: post.post_number,
post_action_id: opts[:post_action_id], post_action_id: opts[:post_action_id],
data: {topic_title: post.topic.title, data: { topic_title: post.topic.title,
display_username: opts[:display_username] || post.user.username}.to_json) display_username: opts[:display_username] || post.user.username }.to_json)
end end
# Returns a list users who have been mentioned # Returns a list users who have been mentioned
def extract_mentioned_users(post) def extract_mentioned_users(post)
User.where("username_lower in (?)", post.raw_mentions).where("id <> ?", post.user_id) User.where(username_lower: post.raw_mentions).where("id <> ?", post.user_id)
end end
# Returns a list of users who were quoted in the post # Returns a list of users who were quoted in the post
@ -121,24 +120,21 @@ class PostAlertObserver < ActiveRecord::Observer
# TODO: This should use javascript for parsing rather than re-doing it this way. # TODO: This should use javascript for parsing rather than re-doing it this way.
def notify_post_users(post) def notify_post_users(post)
# Is this post a reply to a user? # Is this post a reply to a user?
reply_to_user = post.reply_notification_target reply_to_user = post.reply_notification_target
notify_users(reply_to_user, :replied, post) notify_users(reply_to_user, :replied, post)
# find all users watching # find all users watching
if post.post_number > 1 if post.post_number > 1
exclude_user_ids = [] exclude_user_ids = []
exclude_user_ids << post.user_id exclude_user_ids << post.user_id
exclude_user_ids << reply_to_user.id if reply_to_user.present? exclude_user_ids << reply_to_user.id if reply_to_user.present?
exclude_user_ids << extract_mentioned_users(post).map{|u| u.id} exclude_user_ids << extract_mentioned_users(post).map(&:id)
exclude_user_ids << extract_quoted_users(post).map{|u| u.id} exclude_user_ids << extract_quoted_users(post).map(&:id)
exclude_user_ids.flatten! exclude_user_ids.flatten!
TopicUser.where(topic_id: post.topic_id, notification_level: TopicUser::NotificationLevel::WATCHING).includes(:user).each do |tu| TopicUser.where(topic_id: post.topic_id, notification_level: TopicUser::NotificationLevel::WATCHING).includes(:user).each do |tu|
create_notification(tu.user, Notification.Types[:posted], post) unless exclude_user_ids.include?(tu.user_id) create_notification(tu.user, Notification.Types[:posted], post) unless exclude_user_ids.include?(tu.user_id)
end end
end end
end end
end end

View File

@ -1,5 +1,4 @@
class PostReply < ActiveRecord::Base class PostReply < ActiveRecord::Base
belongs_to :post belongs_to :post
belongs_to :reply, class_name: 'Post' belongs_to :reply, class_name: 'Post'

View File

@ -1,5 +1,4 @@
class PostTiming < ActiveRecord::Base class PostTiming < ActiveRecord::Base
belongs_to :topic belongs_to :topic
belongs_to :user belongs_to :user
@ -9,7 +8,6 @@ class PostTiming < ActiveRecord::Base
# Increases a timer if a row exists, otherwise create it # Increases a timer if a row exists, otherwise create it
def self.record_timing(args) def self.record_timing(args)
rows = exec_sql_row_count("UPDATE post_timings rows = exec_sql_row_count("UPDATE post_timings
SET msecs = msecs + :msecs SET msecs = msecs + :msecs
WHERE topic_id = :topic_id WHERE topic_id = :topic_id
@ -28,7 +26,6 @@ class PostTiming < ActiveRecord::Base
args) args)
end end
end end
@ -43,7 +40,6 @@ class PostTiming < ActiveRecord::Base
def self.process_timings(current_user, topic_id, highest_seen, topic_time, timings) def self.process_timings(current_user, topic_id, highest_seen, topic_time, timings)
current_user.update_time_read! current_user.update_time_read!
original_unread = current_user.unread_notifications_by_type
timings.each do |post_number, time| timings.each do |post_number, time|
if post_number >= 0 if post_number >= 0
PostTiming.record_timing(topic_id: topic_id, PostTiming.record_timing(topic_id: topic_id,
@ -64,7 +60,5 @@ class PostTiming < ActiveRecord::Base
current_user.reload current_user.reload
current_user.publish_notifications_state current_user.publish_notifications_state
end end
end end
end end

View File

@ -58,7 +58,6 @@ class SearchObserver < ActiveRecord::Observer
end end
class HtmlScrubber < Nokogiri::XML::SAX::Document class HtmlScrubber < Nokogiri::XML::SAX::Document
attr_reader :scrubbed attr_reader :scrubbed
@ -67,7 +66,7 @@ class SearchObserver < ActiveRecord::Observer
end end
def self.scrub(html) def self.scrub(html)
me = self.new me = new
parser = Nokogiri::HTML::SAX::Parser.new(me) parser = Nokogiri::HTML::SAX::Parser.new(me)
begin begin
copy = "<div>" copy = "<div>"
@ -98,6 +97,5 @@ class SearchObserver < ActiveRecord::Observer
scrubbed << " " scrubbed << " "
end end
end end
end end

View File

@ -26,7 +26,7 @@ class Site
end end
def archetypes def archetypes
Archetype.list.reject{|t| t.id==Archetype.private_message} Archetype.list.reject { |t| t.id == Archetype.private_message }
end end
def self.cache_key def self.cache_key
@ -45,5 +45,4 @@ class Site
def self.invalidate_cache def self.invalidate_cache
Rails.cache.delete(Site.cache_key) Rails.cache.delete(Site.cache_key)
end end
end end

View File

@ -1,5 +1,4 @@
class SiteCustomization < ActiveRecord::Base class SiteCustomization < ActiveRecord::Base
ENABLED_KEY = '7e202ef2-56d7-47d5-98d8-a9c8d15e57dd' ENABLED_KEY = '7e202ef2-56d7-47d5-98d8-a9c8d15e57dd'
# placing this in uploads to ease deployment rules # placing this in uploads to ease deployment rules
CACHE_PATH = 'uploads/stylesheet-cache' CACHE_PATH = 'uploads/stylesheet-cache'
@ -13,9 +12,9 @@ class SiteCustomization < ActiveRecord::Base
end end
before_save do before_save do
if self.stylesheet_changed? if stylesheet_changed?
begin begin
self.stylesheet_baked = Sass.compile self.stylesheet self.stylesheet_baked = Sass.compile stylesheet
rescue Sass::SyntaxError => e rescue Sass::SyntaxError => e
error = e.sass_backtrace_str("custom stylesheet") error = e.sass_backtrace_str("custom stylesheet")
error.gsub!("\n", '\A ') error.gsub!("\n", '\A ')
@ -30,23 +29,23 @@ footer:after{ content: '#{error}' }"
end end
after_save do after_save do
if self.stylesheet_changed? if stylesheet_changed?
if File.exists?(self.stylesheet_fullpath) if File.exists?(stylesheet_fullpath)
File.delete self.stylesheet_fullpath File.delete stylesheet_fullpath
end end
end end
self.remove_from_cache! remove_from_cache!
if self.stylesheet_changed? if stylesheet_changed?
self.ensure_stylesheet_on_disk! ensure_stylesheet_on_disk!
MessageBus.publish "/file-change/#{self.key}", self.stylesheet_hash MessageBus.publish "/file-change/#{key}", stylesheet_hash
end end
MessageBus.publish "/header-change/#{self.key}", self.header if self.header_changed? MessageBus.publish "/header-change/#{key}", header if header_changed?
end end
after_destroy do after_destroy do
if File.exists?(self.stylesheet_fullpath) if File.exists?(stylesheet_fullpath)
File.delete self.stylesheet_fullpath File.delete stylesheet_fullpath
end end
self.remove_from_cache! self.remove_from_cache!
end end
@ -57,18 +56,17 @@ footer:after{ content: '#{error}' }"
def self.enabled_style_key def self.enabled_style_key
@cache ||= {} @cache ||= {}
preview_style = @cache[self.enabled_key] preview_style = @cache[enabled_key]
return nil if preview_style == :none return if preview_style == :none
return preview_style if preview_style return preview_style if preview_style
@lock.synchronize do @lock.synchronize do
style = self.where(enabled: true).first style = where(enabled: true).first
if style if style
@cache[self.enabled_key] = style.key @cache[enabled_key] = style.key
return style.key
else else
@cache[self.enabled_key] = :none @cache[enabled_key] = :none
return nil nil
end end
end end
end end
@ -96,7 +94,6 @@ footer:after{ content: '#{error}' }"
end end
def self.lookup_style(key) def self.lookup_style(key)
return if key.blank? return if key.blank?
# cache is cross site resiliant cause key is secure random # cache is cross site resiliant cause key is secure random
@ -106,7 +103,7 @@ footer:after{ content: '#{error}' }"
return style if style return style if style
@lock.synchronize do @lock.synchronize do
style = self.where(key: key).first style = where(key: key).first
style.ensure_stylesheet_on_disk! if style style.ensure_stylesheet_on_disk! if style
@cache[key] = style @cache[key] = style
end end
@ -124,8 +121,8 @@ footer:after{ content: '#{error}' }"
end end
end end
def self.remove_from_cache!(key, broadcast=true) def self.remove_from_cache!(key, broadcast = true)
MessageBus.publish('/site_customization', {key: key}) if broadcast MessageBus.publish('/site_customization', key: key) if broadcast
if @cache if @cache
@lock.synchronize do @lock.synchronize do
@cache[key] = nil @cache[key] = nil
@ -135,11 +132,11 @@ footer:after{ content: '#{error}' }"
def remove_from_cache! def remove_from_cache!
self.class.remove_from_cache!(self.class.enabled_key) self.class.remove_from_cache!(self.class.enabled_key)
self.class.remove_from_cache!(self.key) self.class.remove_from_cache!(key)
end end
def stylesheet_hash def stylesheet_hash
Digest::MD5.hexdigest(self.stylesheet) Digest::MD5.hexdigest(stylesheet)
end end
def cache_fullpath def cache_fullpath
@ -152,7 +149,7 @@ footer:after{ content: '#{error}' }"
FileUtils.mkdir_p(dir) FileUtils.mkdir_p(dir)
unless File.exists?(path) unless File.exists?(path)
File.open(path, "w") do |f| File.open(path, "w") do |f|
f.puts self.stylesheet_baked f.puts stylesheet_baked
end end
end end
end end
@ -162,14 +159,13 @@ footer:after{ content: '#{error}' }"
end end
def stylesheet_fullpath def stylesheet_fullpath
"#{self.cache_fullpath}#{self.stylesheet_filename}" "#{cache_fullpath}#{stylesheet_filename}"
end end
def stylesheet_link_tag def stylesheet_link_tag
return "" unless self.stylesheet.present? return "" unless stylesheet.present?
return @stylesheet_link_tag if @stylesheet_link_tag return @stylesheet_link_tag if @stylesheet_link_tag
ensure_stylesheet_on_disk! ensure_stylesheet_on_disk!
@stylesheet_link_tag = "<link class=\"custom-css\" rel=\"stylesheet\" href=\"/#{CACHE_PATH}#{self.stylesheet_filename}?#{self.stylesheet_hash}\" type=\"text/css\" media=\"screen\">" @stylesheet_link_tag = "<link class=\"custom-css\" rel=\"stylesheet\" href=\"/#{CACHE_PATH}#{stylesheet_filename}?#{stylesheet_hash}\" type=\"text/css\" media=\"screen\">"
end end
end end

View File

@ -162,4 +162,7 @@ class SiteSetting < ActiveRecord::Base
min_topic_title_length..max_topic_title_length min_topic_title_length..max_topic_title_length
end end
def self.post_length
min_post_length..max_post_length
end
end end

View File

@ -68,28 +68,28 @@ class Topic < ActiveRecord::Base
end end
before_validation do before_validation do
if self.title.present? if title.present?
self.title = sanitize(self.title) self.title = sanitize(title)
self.title.strip! self.title.strip!
end end
end end
before_create do before_create do
self.bumped_at ||= Time.now self.bumped_at ||= Time.now
self.last_post_user_id ||= self.user_id self.last_post_user_id ||= user_id
end end
after_create do after_create do
changed_to_category(category) changed_to_category(category)
TopicUser.change( TopicUser.change(
self.user_id, self.id, user_id, id,
notification_level: TopicUser::NotificationLevel::WATCHING, notification_level: TopicUser::NotificationLevel::WATCHING,
notifications_reason_id: TopicUser::NotificationReasons::CREATED_TOPIC notifications_reason_id: TopicUser::NotificationReasons::CREATED_TOPIC
) )
if self.archetype == Archetype.private_message if archetype == Archetype.private_message
DraftSequence.next!(self.user, Draft::NEW_PRIVATE_MESSAGE) DraftSequence.next!(user, Draft::NEW_PRIVATE_MESSAGE)
else else
DraftSequence.next!(self.user, Draft::NEW_TOPIC) DraftSequence.next!(user, Draft::NEW_TOPIC)
end end
end end
@ -170,7 +170,7 @@ class Topic < ActiveRecord::Base
end end
def meta_data_string(key) def meta_data_string(key)
return nil unless meta_data.present? return unless meta_data.present?
meta_data[key.to_s] meta_data[key.to_s]
end end
@ -199,7 +199,7 @@ class Topic < ActiveRecord::Base
WHERE ftl.topic_id = ? WHERE ftl.topic_id = ?
GROUP BY ftl.url, ft.title, ftl.link_topic_id, ftl.reflection, ftl.internal GROUP BY ftl.url, ft.title, ftl.link_topic_id, ftl.reflection, ftl.internal
ORDER BY clicks DESC", ORDER BY clicks DESC",
self.id).to_a id).to_a
end end
def update_status(property, status, user) def update_status(property, status, user)
@ -218,7 +218,7 @@ class Topic < ActiveRecord::Base
end end
# Atomically creates the next post number # Atomically creates the next post number
def self.next_post_number(topic_id, reply=false) def self.next_post_number(topic_id, reply = false)
highest = exec_sql("select coalesce(max(post_number),0) as max from posts where topic_id = ?", topic_id).first['max'].to_i highest = exec_sql("select coalesce(max(post_number),0) as max from posts where topic_id = ?", topic_id).first['max'].to_i
reply_sql = reply ? ", reply_count = reply_count + 1" : "" reply_sql = reply ? ", reply_count = reply_count + 1" : ""
@ -265,7 +265,7 @@ class Topic < ActiveRecord::Base
def changed_to_category(cat) def changed_to_category(cat)
return if cat.blank? return if cat.blank?
return if Category.where(topic_id: self.id).first.present? return if Category.where(topic_id: id).first.present?
Topic.transaction do Topic.transaction do
old_category = category old_category = category
@ -275,10 +275,10 @@ class Topic < ActiveRecord::Base
end end
self.category_id = cat.id self.category_id = cat.id
self.save save
CategoryFeaturedTopic.feature_topics_for(old_category) CategoryFeaturedTopic.feature_topics_for(old_category)
Category.update_all 'topic_count = topic_count + 1', ['id = ?', cat.id] Category.update_all 'topic_count = topic_count + 1', id: cat.id
CategoryFeaturedTopic.feature_topics_for(cat) unless old_category.try(:id) == cat.try(:id) CategoryFeaturedTopic.feature_topics_for(cat) unless old_category.try(:id) == cat.try(:id)
end end
end end
@ -310,10 +310,10 @@ class Topic < ActiveRecord::Base
if name.blank? if name.blank?
if category_id.present? if category_id.present?
CategoryFeaturedTopic.feature_topics_for(category) CategoryFeaturedTopic.feature_topics_for(category)
Category.update_all 'topic_count = topic_count - 1', ['id = ?', category_id] Category.update_all 'topic_count = topic_count - 1', id: category_id
end end
self.category_id = nil self.category_id = nil
self.save save
return return
end end
@ -335,10 +335,10 @@ class Topic < ActiveRecord::Base
if topic_allowed_users.create!(user_id: user.id) if topic_allowed_users.create!(user_id: user.id)
# Notify the user they've been invited # Notify the user they've been invited
user.notifications.create(notification_type: Notification.Types[:invited_to_private_message], user.notifications.create(notification_type: Notification.Types[:invited_to_private_message],
topic_id: self.id, topic_id: id,
post_number: 1, post_number: 1,
data: {topic_title: self.title, data: { topic_title: title,
display_username: invited_by.username}.to_json) display_username: invited_by.username }.to_json)
return true return true
end end
elsif username_or_email =~ /^.+@.+$/ elsif username_or_email =~ /^.+@.+$/
@ -371,7 +371,7 @@ class Topic < ActiveRecord::Base
topic_allowed_users.create!(user_id: user.id) topic_allowed_users.create!(user_id: user.id)
end end
return nil return
end end
end end
@ -387,15 +387,14 @@ class Topic < ActiveRecord::Base
topic = nil topic = nil
first_post_number = nil first_post_number = nil
Topic.transaction do Topic.transaction do
topic = Topic.create(user: moved_by, title: new_title, category: self.category) topic = Topic.create(user: moved_by, title: new_title, category: category)
to_move = posts.where(id: post_ids).order(:created_at) to_move = posts.where(id: post_ids).order(:created_at)
raise Discourse::InvalidParameters.new(:post_ids) if to_move.blank? raise Discourse::InvalidParameters.new(:post_ids) if to_move.blank?
to_move.each_with_index do |post, i| to_move.each_with_index do |post, i|
first_post_number ||= post.post_number first_post_number ||= post.post_number
row_count = Post.update_all ["post_number = :post_number, topic_id = :topic_id, sort_order = :post_number", post_number: i+1, topic_id: topic.id], row_count = Post.update_all ["post_number = :post_number, topic_id = :topic_id, sort_order = :post_number", post_number: i+1, topic_id: topic.id], id: post.id, topic_id: id
['id = ? AND topic_id = ?', post.id, self.id]
# We raise an error if any of the posts can't be moved # We raise an error if any of the posts can't be moved
raise Discourse::InvalidParameters.new(:post_ids) if row_count == 0 raise Discourse::InvalidParameters.new(:post_ids) if row_count == 0
@ -403,7 +402,7 @@ class Topic < ActiveRecord::Base
# Update denormalized values since we've manually moved stuff # Update denormalized values since we've manually moved stuff
Topic.reset_highest(topic.id) Topic.reset_highest(topic.id)
Topic.reset_highest(self.id) Topic.reset_highest(id)
end end
# Add a moderator post explaining that the post was moved # Add a moderator post explaining that the post was moved
@ -425,7 +424,7 @@ class Topic < ActiveRecord::Base
# Create the summary of the interesting posters in a topic. Cheats to avoid # Create the summary of the interesting posters in a topic. Cheats to avoid
# many queries. # many queries.
def posters_summary(topic_user=nil, current_user=nil, opts={}) def posters_summary(topic_user = nil, current_user = nil, opts={})
return @posters_summary if @posters_summary.present? return @posters_summary if @posters_summary.present?
descriptions = {} descriptions = {}
@ -484,7 +483,7 @@ class Topic < ActiveRecord::Base
# Enable/disable the star on the topic # Enable/disable the star on the topic
def toggle_star(user, starred) def toggle_star(user, starred)
Topic.transaction do Topic.transaction do
TopicUser.change(user, self.id, starred: starred, starred_at: starred ? DateTime.now : nil) TopicUser.change(user, id, starred: starred, starred_at: starred ? DateTime.now : nil)
# Update the star count # Update the star count
exec_sql "UPDATE topics exec_sql "UPDATE topics
@ -492,7 +491,7 @@ class Topic < ActiveRecord::Base
FROM topic_users AS ftu FROM topic_users AS ftu
WHERE ftu.topic_id = topics.id WHERE ftu.topic_id = topics.id
AND ftu.starred = true) AND ftu.starred = true)
WHERE id = ?", self.id WHERE id = ?", id
if starred if starred
FavoriteLimiter.new(user).performed! FavoriteLimiter.new(user).performed!
@ -517,7 +516,7 @@ class Topic < ActiveRecord::Base
def relative_url(post_number=nil) def relative_url(post_number=nil)
url = "/t/#{slug}/#{id}" url = "/t/#{slug}/#{id}"
url << "/#{post_number}" if post_number.present? and post_number.to_i > 1 url << "/#{post_number}" if post_number.present? && post_number.to_i > 1
url url
end end
@ -528,23 +527,23 @@ class Topic < ActiveRecord::Base
end end
def draft_key def draft_key
"#{Draft::EXISTING_TOPIC}#{self.id}" "#{Draft::EXISTING_TOPIC}#{id}"
end end
# notification stuff # notification stuff
def notify_watch!(user) def notify_watch!(user)
TopicUser.change(user, self.id, notification_level: TopicUser::NotificationLevel::WATCHING) TopicUser.change(user, id, notification_level: TopicUser::NotificationLevel::WATCHING)
end end
def notify_tracking!(user) def notify_tracking!(user)
TopicUser.change(user, self.id, notification_level: TopicUser::NotificationLevel::TRACKING) TopicUser.change(user, id, notification_level: TopicUser::NotificationLevel::TRACKING)
end end
def notify_regular!(user) def notify_regular!(user)
TopicUser.change(user, self.id, notification_level: TopicUser::NotificationLevel::REGULAR) TopicUser.change(user, id, notification_level: TopicUser::NotificationLevel::REGULAR)
end end
def notify_muted!(user) def notify_muted!(user)
TopicUser.change(user, self.id, notification_level: TopicUser::NotificationLevel::MUTED) TopicUser.change(user, id, notification_level: TopicUser::NotificationLevel::MUTED)
end end
end end

View File

@ -1,5 +1,4 @@
class TopicInvite < ActiveRecord::Base class TopicInvite < ActiveRecord::Base
belongs_to :topic belongs_to :topic
belongs_to :invite belongs_to :invite

View File

@ -2,7 +2,6 @@ require 'uri'
require_dependency 'slug' require_dependency 'slug'
class TopicLink < ActiveRecord::Base class TopicLink < ActiveRecord::Base
belongs_to :topic belongs_to :topic
belongs_to :user belongs_to :user
belongs_to :post belongs_to :post
@ -118,8 +117,6 @@ class TopicLink < ActiveRecord::Base
else else
TopicLink.delete_all ["post_id = :post_id OR link_post_id = :post_id", post_id: post.id] TopicLink.delete_all ["post_id = :post_id OR link_post_id = :post_id", post_id: post.id]
end end
end end
end end
end end

View File

@ -2,7 +2,6 @@ require_dependency 'discourse'
require 'ipaddr' require 'ipaddr'
class TopicLinkClick < ActiveRecord::Base class TopicLinkClick < ActiveRecord::Base
belongs_to :topic_link, counter_cache: :clicks belongs_to :topic_link, counter_cache: :clicks
belongs_to :user belongs_to :user
@ -54,5 +53,4 @@ class TopicLinkClick < ActiveRecord::Base
result result
end end
end end

View File

@ -67,10 +67,8 @@ class TopicList
query = TopicQuery.new(@current_user, only_category: catSplit[1], limit: false) query = TopicQuery.new(@current_user, only_category: catSplit[1], limit: false)
s[name] = query.unread_count + query.new_count s[name] = query.unread_count + query.new_count
end end
end end
s s
end end
end end

View File

@ -14,5 +14,4 @@ class TopicPoster < OpenStruct
def [](attr) def [](attr)
send(attr) send(attr)
end end
end end

View File

@ -1,5 +1,4 @@
class TopicUser < ActiveRecord::Base class TopicUser < ActiveRecord::Base
belongs_to :user belongs_to :user
belongs_to :topic belongs_to :topic
@ -19,7 +18,7 @@ class TopicUser < ActiveRecord::Base
def self.auto_track(user_id, topic_id, reason) def self.auto_track(user_id, topic_id, reason)
if TopicUser.where(user_id: user_id, topic_id: topic_id, notifications_reason_id: nil).exists? if TopicUser.where(user_id: user_id, topic_id: topic_id, notifications_reason_id: nil).exists?
self.change(user_id, topic_id, change(user_id, topic_id,
notification_level: NotificationLevel::TRACKING, notification_level: NotificationLevel::TRACKING,
notifications_reason_id: reason notifications_reason_id: reason
) )
@ -34,12 +33,10 @@ class TopicUser < ActiveRecord::Base
# Find the information specific to a user in a forum topic # Find the information specific to a user in a forum topic
def self.lookup_for(user, topics) def self.lookup_for(user, topics)
# If the user isn't logged in, there's no last read posts # If the user isn't logged in, there's no last read posts
return {} if user.blank? return {} if user.blank? || topics.blank?
return {} if topics.blank?
topic_ids = topics.map {|ft| ft.id} topic_ids = topics.map(&:id)
create_lookup(TopicUser.where(topic_id: topic_ids, user_id: user.id)) create_lookup(TopicUser.where(topic_id: topic_ids, user_id: user.id))
end end
@ -70,7 +67,6 @@ class TopicUser < ActiveRecord::Base
# since there's more likely to be an existing record than not. If the update returns 0 rows affected # since there's more likely to be an existing record than not. If the update returns 0 rows affected
# it then creates the row instead. # it then creates the row instead.
def self.change(user_id, topic_id, attrs) def self.change(user_id, topic_id, attrs)
# Sometimes people pass objs instead of the ids. We can handle that. # Sometimes people pass objs instead of the ids. We can handle that.
topic_id = topic_id.id if topic_id.is_a?(Topic) topic_id = topic_id.id if topic_id.is_a?(Topic)
user_id = user_id.id if user_id.is_a?(User) user_id = user_id.id if user_id.is_a?(User)
@ -85,9 +81,9 @@ class TopicUser < ActiveRecord::Base
end end
attrs_array = attrs.to_a attrs_array = attrs.to_a
attrs_sql = attrs_array.map {|t| "#{t[0]} = ?"}.join(", ") attrs_sql = attrs_array.map { |t| "#{t[0]} = ?" }.join(", ")
vals = attrs_array.map {|t| t[1]} vals = attrs_array.map { |t| t[1] }
rows = TopicUser.update_all([attrs_sql, *vals], ["topic_id = ? and user_id = ?", topic_id.to_i, user_id]) rows = TopicUser.update_all([attrs_sql, *vals], topic_id: topic_id.to_i, user_id: user_id)
if rows == 0 if rows == 0
now = DateTime.now now = DateTime.now
@ -101,7 +97,6 @@ class TopicUser < ActiveRecord::Base
TopicUser.create(attrs.merge!(user_id: user_id, topic_id: topic_id.to_i, first_visited_at: now ,last_visited_at: now)) TopicUser.create(attrs.merge!(user_id: user_id, topic_id: topic_id.to_i, first_visited_at: now ,last_visited_at: now))
end end
end end
rescue ActiveRecord::RecordNotUnique rescue ActiveRecord::RecordNotUnique
# In case of a race condition to insert, do nothing # In case of a race condition to insert, do nothing
@ -119,7 +114,6 @@ class TopicUser < ActiveRecord::Base
values(?,?,?,?)', values(?,?,?,?)',
topic.id, user.id, now, now) topic.id, user.id, now, now)
end end
end end
# Update the last read and the last seen post count, but only if it doesn't exist. # Update the last read and the last seen post count, but only if it doesn't exist.
@ -174,9 +168,6 @@ class TopicUser < ActiveRecord::Base
end end
if rows.length == 0 if rows.length == 0
self
args[:tracking] = TopicUser::NotificationLevel::TRACKING args[:tracking] = TopicUser::NotificationLevel::TRACKING
args[:regular] = TopicUser::NotificationLevel::REGULAR args[:regular] = TopicUser::NotificationLevel::REGULAR
args[:site_setting] = SiteSetting.auto_track_topics_after args[:site_setting] = SiteSetting.auto_track_topics_after
@ -192,6 +183,4 @@ class TopicUser < ActiveRecord::Base
args) args)
end end
end end
end end

View File

@ -20,7 +20,6 @@ class Upload < ActiveRecord::Base
# Store uploads on s3 # Store uploads on s3
def self.create_on_imgur(user, file, topic_id) def self.create_on_imgur(user, file, topic_id)
@imgur_loaded = require 'imgur' unless @imgur_loaded @imgur_loaded = require 'imgur' unless @imgur_loaded
@ -56,7 +55,6 @@ class Upload < ActiveRecord::Base
end end
def self.create_on_s3(user, file, topic_id) def self.create_on_s3(user, file, topic_id)
@fog_loaded = require 'fog' unless @fog_loaded @fog_loaded = require 'fog' unless @fog_loaded
tempfile = file.tempfile tempfile = file.tempfile
@ -90,5 +88,4 @@ class Upload < ActiveRecord::Base
upload upload
end end
end end

View File

@ -55,7 +55,7 @@ class User < ActiveRecord::Base
end end
def self.suggest_username(name) def self.suggest_username(name)
return nil unless name.present? return unless name.present?
# If it's an email # If it's an email
if name =~ /([^@]+)@([^\.]+)/ if name =~ /([^@]+)@([^\.]+)/
@ -266,7 +266,7 @@ class User < ActiveRecord::Base
end end
def has_visit_record?(date) def has_visit_record?(date)
user_visits.where(["visited_at =? ", date]).first user_visits.where(visited_at: date).first
end end
def adding_visit_record(date) def adding_visit_record(date)
@ -361,7 +361,7 @@ class User < ActiveRecord::Base
end end
def flags_received_count def flags_received_count
posts.includes(:post_actions).where('post_actions.post_action_type_id in (?)', PostActionType.FlagTypes).count posts.includes(:post_actions).where(post_actions: { post_action_type_id: PostActionType.FlagTypes }).count
end end
def private_topics_count def private_topics_count
@ -435,7 +435,7 @@ class User < ActiveRecord::Base
if last_seen.present? if last_seen.present?
diff = (Time.now.to_f - last_seen.to_f).round diff = (Time.now.to_f - last_seen.to_f).round
if diff > 0 && diff < MAX_TIME_READ_DIFF if diff > 0 && diff < MAX_TIME_READ_DIFF
User.update_all ["time_read = time_read + ?", diff], ["id = ? and time_read = ?", id, time_read] User.update_all ["time_read = time_read + ?", diff], id: id, time_read: time_read
end end
end end
$redis.set(last_seen_key, Time.now.to_f) $redis.set(last_seen_key, Time.now.to_f)

View File

@ -55,7 +55,7 @@ class UserAction < ActiveRecord::Base
results = results.to_a results = results.to_a
results.sort!{|a,b| ORDER[a.action_type] <=> ORDER[b.action_type]} results.sort! { |a,b| ORDER[a.action_type] <=> ORDER[b.action_type] }
results.each do |row| results.each do |row|
row.description = self.description(row.action_type, detailed: true) row.description = self.description(row.action_type, detailed: true)
end end
@ -64,13 +64,13 @@ class UserAction < ActiveRecord::Base
end end
def self.stream_item(action_id, guardian) def self.stream_item(action_id, guardian)
stream(action_id:action_id, guardian: guardian)[0] stream(action_id: action_id, guardian: guardian).first
end end
def self.stream(opts={}) def self.stream(opts={})
user_id = opts[:user_id] user_id = opts[:user_id]
offset = opts[:offset]||0 offset = opts[:offset] || 0
limit = opts[:limit] ||60 limit = opts[:limit] || 60
action_id = opts[:action_id] action_id = opts[:action_id]
action_types = opts[:action_types] action_types = opts[:action_types]
guardian = opts[:guardian] guardian = opts[:guardian]
@ -198,7 +198,7 @@ JOIN users pu on pu.id = COALESCE(p.user_id, t.user_id)
require_parameters(hash, :action_type, :user_id, :acting_user_id, :target_topic_id, :target_post_id) require_parameters(hash, :action_type, :user_id, :acting_user_id, :target_topic_id, :target_post_id)
transaction(requires_new: true) do transaction(requires_new: true) do
begin begin
action = self.new(hash) action = new(hash)
if hash[:created_at] if hash[:created_at]
action.created_at = hash[:created_at] action.created_at = hash[:created_at]
@ -225,5 +225,4 @@ JOIN users pu on pu.id = COALESCE(p.user_id, t.user_id)
raise Discourse::InvalidParameters.new(p) if data[p].nil? raise Discourse::InvalidParameters.new(p) if data[p].nil?
end end
end end
end end

View File

@ -1,10 +1,9 @@
class UserActionObserver < ActiveRecord::Observer class UserActionObserver < ActiveRecord::Observer
observe :post_action, :topic, :post, :notification, :topic_user observe :post_action, :topic, :post, :notification, :topic_user
def after_save(model) def after_save(model)
case case
when (model.is_a?(PostAction) and (model.is_bookmark? or model.is_like?)) when (model.is_a?(PostAction) && (model.is_bookmark? || model.is_like?))
log_post_action(model) log_post_action(model)
when (model.is_a?(Topic)) when (model.is_a?(Topic))
log_topic(model) log_topic(model)
@ -39,7 +38,6 @@ class UserActionObserver < ActiveRecord::Observer
end end
def log_notification(model) def log_notification(model)
action = action =
case model.notification_type case model.notification_type
when Notification.Types[:quoted] when Notification.Types[:quoted]
@ -77,11 +75,9 @@ class UserActionObserver < ActiveRecord::Observer
end end
def log_post(model) def log_post(model)
# first post gets nada # first post gets nada
return if model.post_number == 1 return if model.post_number == 1
row = { row = {
action_type: UserAction::POST, action_type: UserAction::POST,
user_id: model.user_id, user_id: model.user_id,
@ -103,11 +99,11 @@ class UserActionObserver < ActiveRecord::Observer
end end
end end
rows.each do |row| rows.each do |r|
if model.deleted_at.nil? if model.deleted_at.nil?
UserAction.log_action!(row) UserAction.log_action!(r)
else else
UserAction.remove_action!(row) UserAction.remove_action!(r)
end end
end end
end end
@ -125,7 +121,7 @@ class UserActionObserver < ActiveRecord::Observer
rows = [row] rows = [row]
if model.private_message? if model.private_message?
model.topic_allowed_users.reject{|a| a.user_id == model.user_id}.each do |ta| model.topic_allowed_users.reject { |a| a.user_id == model.user_id }.each do |ta|
row = row.dup row = row.dup
row[:user_id] = ta.user_id row[:user_id] = ta.user_id
row[:action_type] = UserAction::GOT_PRIVATE_MESSAGE row[:action_type] = UserAction::GOT_PRIVATE_MESSAGE
@ -133,11 +129,11 @@ class UserActionObserver < ActiveRecord::Observer
end end
end end
rows.each do |row| rows.each do |r|
if model.deleted_at.nil? if model.deleted_at.nil?
UserAction.log_action!(row) UserAction.log_action!(r)
else else
UserAction.remove_action!(row) UserAction.remove_action!(r)
end end
end end
end end

View File

@ -55,5 +55,4 @@ class UserEmailObserver < ActiveRecord::Observer
user_id: notification.user_id, user_id: notification.user_id,
notification_id: notification.id) notification_id: notification.id)
end end
end end

View File

@ -4,5 +4,4 @@ class UserOpenId < ActiveRecord::Base
validates_presence_of :email validates_presence_of :email
validates_presence_of :url validates_presence_of :url
end end

View File

@ -1,5 +1,4 @@
class UserSearch class UserSearch
def self.search term, topic_id = nil def self.search term, topic_id = nil
sql = User.sql_builder( sql = User.sql_builder(
"select id, username, name, email from users u "select id, username, name, email from users u
@ -34,5 +33,4 @@ class UserSearch
sql.exec sql.exec
end end
end end

View File

@ -5,5 +5,4 @@ class UserVisit < ActiveRecord::Base
def self.by_day def self.by_day
where("visited_at > ?", 1.month.ago).group(:visited_at).order(:visited_at).count where("visited_at > ?", 1.month.ago).group(:visited_at).order(:visited_at).count
end end
end end

View File

@ -1,5 +1,4 @@
class UsernameValidator class UsernameValidator
def initialize(username) def initialize(username)
@username = username @username = username
@errors = [] @errors = []
@ -56,5 +55,4 @@ class UsernameValidator
self.errors << I18n.t(:'user.username.must_begin_with_alphanumeric') self.errors << I18n.t(:'user.username.must_begin_with_alphanumeric')
end end
end end
end end

View File

@ -1,7 +1,6 @@
require 'ipaddr' require 'ipaddr'
class View < ActiveRecord::Base class View < ActiveRecord::Base
belongs_to :parent, polymorphic: true belongs_to :parent, polymorphic: true
belongs_to :user belongs_to :user
validates_presence_of :parent_type, :parent_id, :ip, :viewed_at validates_presence_of :parent_type, :parent_id, :ip, :viewed_at
@ -21,16 +20,13 @@ class View < ActiveRecord::Base
$redis.expire(redis_key, 1.day.to_i) $redis.expire(redis_key, 1.day.to_i)
View.transaction do View.transaction do
view = View.create(parent: parent, ip: IPAddr.new(ip).to_i, viewed_at: Date.today, user: user) View.create(parent: parent, ip: IPAddr.new(ip).to_i, viewed_at: Date.today, user: user)
# Update the views count in the parent, if it exists. # Update the views count in the parent, if it exists.
if parent.respond_to?(:views) if parent.respond_to?(:views)
parent.class.update_all 'views = views + 1', ['id = ?', parent.id] parent.class.update_all 'views = views + 1', id: parent.id
end end
end
end
end end
end end
end end

View File

@ -116,4 +116,12 @@ describe SiteSetting do
end end
end end
describe 'post_length' do
it 'returns a range of min/max post length' do
SiteSetting.min_post_length = 1
SiteSetting.max_post_length = 2
SiteSetting.post_length.should == (1..2)
end
end
end end