2012-06-17 03:15:46 +02:00
|
|
|
class User < ActiveRecord::Base
|
2012-07-01 00:43:45 +02:00
|
|
|
has_many :stories,
|
2013-12-30 23:29:00 +01:00
|
|
|
-> { includes :user }
|
2012-07-04 03:48:01 +02:00
|
|
|
has_many :comments
|
2013-01-24 21:06:10 +01:00
|
|
|
has_many :sent_messages,
|
2012-07-04 03:48:01 +02:00
|
|
|
:class_name => "Message",
|
|
|
|
:foreign_key => "author_user_id"
|
|
|
|
has_many :received_messages,
|
|
|
|
:class_name => "Message",
|
|
|
|
:foreign_key => "recipient_user_id"
|
2012-07-04 05:24:18 +02:00
|
|
|
has_many :tag_filters
|
2014-01-09 07:44:03 +01:00
|
|
|
has_many :tag_filter_tags,
|
|
|
|
:class_name => "Tag",
|
|
|
|
:through => :tag_filters,
|
|
|
|
:source => :tag,
|
|
|
|
:dependent => :delete_all
|
2012-09-16 22:14:26 +02:00
|
|
|
belongs_to :invited_by_user,
|
|
|
|
:class_name => "User"
|
2014-01-12 22:09:32 +01:00
|
|
|
belongs_to :banned_by_user,
|
|
|
|
:class_name => "User"
|
2014-01-13 17:12:17 +01:00
|
|
|
has_many :invitations
|
2014-08-04 04:44:10 +02:00
|
|
|
has_many :weblogs
|
2013-03-11 19:19:26 +01:00
|
|
|
has_many :votes
|
|
|
|
has_many :voted_stories, -> { where('votes.comment_id' => nil) },
|
|
|
|
:through => :votes,
|
|
|
|
:source => :story
|
|
|
|
has_many :upvoted_stories,
|
|
|
|
-> { where('votes.comment_id' => nil, 'votes.vote' => 1) },
|
|
|
|
:through => :votes,
|
|
|
|
:source => :story
|
2012-07-04 05:24:18 +02:00
|
|
|
|
2012-06-17 03:15:46 +02:00
|
|
|
has_secure_password
|
|
|
|
|
2013-06-22 03:37:15 +02:00
|
|
|
validates :email, :format => { :with => /\A[^@ ]+@[^@ ]+\.[^@ ]+\Z/ },
|
|
|
|
:uniqueness => { :case_sensitive => false }
|
2012-06-17 03:15:46 +02:00
|
|
|
|
2013-06-22 03:37:15 +02:00
|
|
|
validates :password, :presence => true, :on => :create
|
2012-06-17 03:15:46 +02:00
|
|
|
|
2014-03-18 06:57:13 +01:00
|
|
|
validates :username,
|
|
|
|
:format => { :with => /\A[A-Za-z0-9][A-Za-z0-9_-]{0,24}\Z/ },
|
2013-06-22 03:37:15 +02:00
|
|
|
:uniqueness => { :case_sensitive => false }
|
|
|
|
|
2014-08-04 04:44:10 +02:00
|
|
|
validates :weblog_feed_url,
|
|
|
|
:format => { :with => /\Ahttps?:\/\/([^\.]+\.)+[a-z]+(\/|\z)/ },
|
|
|
|
:allow_blank => true
|
|
|
|
|
2013-06-22 03:37:15 +02:00
|
|
|
validates_each :username do |record,attr,value|
|
|
|
|
if BANNED_USERNAMES.include?(value.to_s.downcase)
|
|
|
|
record.errors.add(attr, "is not permitted")
|
|
|
|
end
|
|
|
|
end
|
2012-06-17 03:15:46 +02:00
|
|
|
|
|
|
|
before_save :check_session_token
|
2013-07-20 05:05:00 +02:00
|
|
|
before_validation :on => :create do
|
|
|
|
self.create_rss_token
|
|
|
|
self.create_mailing_list_token
|
|
|
|
end
|
2012-06-17 03:15:46 +02:00
|
|
|
|
2013-06-22 03:37:15 +02:00
|
|
|
BANNED_USERNAMES = [ "admin", "administrator", "hostmaster", "mailer-daemon",
|
2014-02-21 18:41:47 +01:00
|
|
|
"postmaster", "root", "security", "support", "webmaster", "moderator",
|
|
|
|
"moderators", ]
|
2013-06-22 03:37:15 +02:00
|
|
|
|
2014-02-17 17:09:08 +01:00
|
|
|
# days old accounts are considered new for
|
|
|
|
NEW_USER_DAYS = 7
|
|
|
|
|
2014-01-21 03:22:45 +01:00
|
|
|
def self.recalculate_all_karmas!
|
|
|
|
User.all.each do |u|
|
|
|
|
u.karma = u.stories.map(&:score).sum + u.comments.map(&:score).sum
|
|
|
|
u.save!
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-03-18 06:57:13 +01:00
|
|
|
def self.username_regex
|
|
|
|
User.validators_on(:username).select{|v|
|
|
|
|
v.class == ActiveModel::Validations::FormatValidator }.first.
|
|
|
|
options[:with].inspect
|
|
|
|
end
|
|
|
|
|
2012-12-17 03:00:41 +01:00
|
|
|
def as_json(options = {})
|
|
|
|
h = super(:only => [
|
|
|
|
:username,
|
2012-12-19 00:26:29 +01:00
|
|
|
:created_at,
|
2012-12-17 03:00:41 +01:00
|
|
|
:is_admin,
|
|
|
|
:is_moderator,
|
|
|
|
])
|
|
|
|
h[:avatar_url] = avatar_url
|
|
|
|
h
|
|
|
|
end
|
|
|
|
|
|
|
|
def avatar_url
|
2014-01-21 07:22:40 +01:00
|
|
|
"https://secure.gravatar.com/avatar/" +
|
|
|
|
Digest::MD5.hexdigest(self.email.strip.downcase) + "?r=pg&d=mm&s=100"
|
2012-12-17 03:00:41 +01:00
|
|
|
end
|
|
|
|
|
2013-07-01 22:43:51 +02:00
|
|
|
def average_karma
|
|
|
|
if (k = self.karma) == 0
|
|
|
|
0
|
|
|
|
else
|
|
|
|
k.to_f / (self.stories_submitted_count + self.comments_posted_count)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-01-12 22:09:32 +01:00
|
|
|
def ban_by_user_for_reason!(banner, reason)
|
|
|
|
self.banned_at = Time.now
|
|
|
|
self.banned_by_user_id = banner.id
|
|
|
|
self.banned_reason = reason
|
|
|
|
|
2014-01-13 17:12:17 +01:00
|
|
|
self.delete!
|
2014-01-12 22:09:32 +01:00
|
|
|
|
|
|
|
BanNotification.notify(self, banner, reason)
|
|
|
|
|
2014-01-13 02:22:08 +01:00
|
|
|
m = Moderation.new
|
|
|
|
m.moderator_user_id = banner.id
|
|
|
|
m.user_id = self.id
|
|
|
|
m.action = "Banned"
|
|
|
|
m.reason = reason
|
|
|
|
m.save!
|
|
|
|
|
2014-01-12 22:09:32 +01:00
|
|
|
true
|
|
|
|
end
|
|
|
|
|
2014-02-17 17:13:08 +01:00
|
|
|
def can_downvote?(obj)
|
|
|
|
if is_new?
|
|
|
|
return false
|
|
|
|
elsif obj.is_a?(Story)
|
2014-03-18 06:06:43 +01:00
|
|
|
if obj.is_downvotable?
|
|
|
|
return true
|
|
|
|
elsif obj.vote == -1
|
|
|
|
# user can unvote
|
|
|
|
return true
|
|
|
|
end
|
2014-02-17 17:13:08 +01:00
|
|
|
elsif obj.is_a?(Comment)
|
|
|
|
if obj.is_downvotable?
|
|
|
|
return true
|
|
|
|
elsif obj.current_vote.try(:vote).to_i == -1
|
|
|
|
# user can unvote
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
false
|
2014-01-12 22:09:32 +01:00
|
|
|
end
|
|
|
|
|
2012-06-17 03:15:46 +02:00
|
|
|
def check_session_token
|
|
|
|
if self.session_token.blank?
|
2012-06-30 23:41:34 +02:00
|
|
|
self.session_token = Utils.random_str(60)
|
2012-06-17 03:15:46 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-07-01 22:43:51 +02:00
|
|
|
def create_mailing_list_token
|
|
|
|
if self.mailing_list_token.blank?
|
|
|
|
self.mailing_list_token = Utils.random_str(10)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-09-18 17:38:03 +02:00
|
|
|
def create_rss_token
|
|
|
|
if self.rss_token.blank?
|
|
|
|
self.rss_token = Utils.random_str(60)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-07-01 22:43:51 +02:00
|
|
|
def comments_posted_count
|
|
|
|
Keystore.value_for("user:#{self.id}:comments_posted").to_i
|
2013-06-22 04:19:57 +02:00
|
|
|
end
|
|
|
|
|
2014-01-13 17:12:17 +01:00
|
|
|
def delete!
|
|
|
|
User.transaction do
|
|
|
|
self.comments.each{|c| c.delete_for_user(self) }
|
|
|
|
|
|
|
|
self.sent_messages.each do |m|
|
|
|
|
m.deleted_by_author = true
|
|
|
|
m.save
|
|
|
|
end
|
|
|
|
self.received_messages.each do |m|
|
|
|
|
m.deleted_by_recipient = true
|
|
|
|
m.save
|
|
|
|
end
|
|
|
|
|
|
|
|
self.invitations.destroy_all
|
|
|
|
|
|
|
|
self.session_token = nil
|
|
|
|
self.check_session_token
|
|
|
|
|
|
|
|
self.deleted_at = Time.now
|
|
|
|
self.save!
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-07-01 22:43:51 +02:00
|
|
|
def initiate_password_reset_for_ip(ip)
|
2014-04-15 07:46:14 +02:00
|
|
|
self.password_reset_token = "#{Time.now.to_i}-#{Utils.random_str(30)}"
|
2013-07-01 22:43:51 +02:00
|
|
|
self.save!
|
2012-07-04 03:48:01 +02:00
|
|
|
|
2013-07-01 22:43:51 +02:00
|
|
|
PasswordReset.password_reset_link(self, ip).deliver
|
2012-06-17 03:15:46 +02:00
|
|
|
end
|
|
|
|
|
2014-01-13 17:12:17 +01:00
|
|
|
def is_active?
|
|
|
|
!(deleted_at? || is_banned?)
|
|
|
|
end
|
|
|
|
|
2014-01-13 06:17:09 +01:00
|
|
|
def is_banned?
|
|
|
|
banned_at?
|
|
|
|
end
|
|
|
|
|
|
|
|
def is_new?
|
2014-02-17 17:09:08 +01:00
|
|
|
Time.now - self.created_at <= NEW_USER_DAYS.days
|
2014-01-13 06:17:09 +01:00
|
|
|
end
|
|
|
|
|
2013-07-01 22:43:51 +02:00
|
|
|
def linkified_about
|
|
|
|
# most users are probably mentioning "@username" to mean a twitter url, not
|
|
|
|
# a link to a profile on this site
|
|
|
|
Markdowner.to_html(self.about, { :disable_profile_links => true })
|
2012-09-07 19:25:59 +02:00
|
|
|
end
|
|
|
|
|
2013-07-01 22:53:24 +02:00
|
|
|
def most_common_story_tag
|
2014-02-21 17:51:48 +01:00
|
|
|
Tag.active.joins(
|
2013-12-30 19:48:57 +01:00
|
|
|
:stories
|
|
|
|
).where(
|
|
|
|
:stories => { :user_id => self.id }
|
|
|
|
).group(
|
|
|
|
Tag.arel_table[:id]
|
|
|
|
).order(
|
|
|
|
'COUNT(*) desc'
|
|
|
|
).first
|
2013-07-01 22:53:24 +02:00
|
|
|
end
|
|
|
|
|
2014-01-21 08:05:27 +01:00
|
|
|
def pushover!(params)
|
|
|
|
if self.pushover_user_key.present?
|
|
|
|
Pushover.push(self.pushover_user_key, self.pushover_device,
|
|
|
|
params.merge({ :sound => self.pushover_sound.to_s }))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-07-01 22:43:51 +02:00
|
|
|
def recent_threads(amount)
|
2014-01-17 03:33:00 +01:00
|
|
|
self.comments.group(:thread_id).order('MAX(created_at) DESC').limit(
|
|
|
|
amount).pluck(:thread_id)
|
2012-06-30 21:14:35 +02:00
|
|
|
end
|
2013-02-14 01:50:51 +01:00
|
|
|
|
2013-07-01 22:43:51 +02:00
|
|
|
def stories_submitted_count
|
|
|
|
Keystore.value_for("user:#{self.id}:stories_submitted").to_i
|
2012-06-30 21:14:35 +02:00
|
|
|
end
|
|
|
|
|
2014-01-12 21:22:47 +01:00
|
|
|
def to_param
|
|
|
|
username
|
|
|
|
end
|
|
|
|
|
2014-01-12 22:09:32 +01:00
|
|
|
def unban!
|
|
|
|
self.banned_at = nil
|
|
|
|
self.banned_by_user_id = nil
|
|
|
|
self.banned_reason = nil
|
|
|
|
self.save!
|
|
|
|
end
|
|
|
|
|
2012-09-03 18:25:14 +02:00
|
|
|
def undeleted_received_messages
|
2013-01-24 21:06:10 +01:00
|
|
|
received_messages.where(:deleted_by_recipient => false)
|
|
|
|
end
|
2013-02-14 01:50:51 +01:00
|
|
|
|
2013-01-24 21:06:10 +01:00
|
|
|
def undeleted_sent_messages
|
2014-01-22 03:53:29 +01:00
|
|
|
sent_messages.where(:deleted_by_author => false)
|
2012-09-03 18:25:14 +02:00
|
|
|
end
|
|
|
|
|
2013-07-01 22:43:51 +02:00
|
|
|
def unread_message_count
|
|
|
|
Keystore.value_for("user:#{self.id}:unread_messages").to_i
|
2012-06-30 21:14:35 +02:00
|
|
|
end
|
|
|
|
|
2013-07-01 22:43:51 +02:00
|
|
|
def update_unread_message_count!
|
|
|
|
Keystore.put("user:#{self.id}:unread_messages",
|
2014-03-06 22:23:29 +01:00
|
|
|
Message.where("recipient_user_id = ? AND (has_been_read = ? AND " <<
|
|
|
|
"deleted_by_recipient = ?)", self.id, false, false).count)
|
2012-06-30 21:14:35 +02:00
|
|
|
end
|
2012-06-17 03:15:46 +02:00
|
|
|
end
|