journalduhacker/app/models/user.rb

453 lines
10 KiB
Ruby
Raw Normal View History

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
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
has_many :tag_filter_tags,
:class_name => "Tag",
:through => :tag_filters,
:source => :tag,
:dependent => :delete_all
belongs_to :invited_by_user,
:class_name => "User"
2014-01-12 22:09:32 +01:00
belongs_to :banned_by_user,
:class_name => "User"
belongs_to :disabled_invite_by_user,
:class_name => "User"
has_many :invitations
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
has_many :hats
2012-07-04 05:24:18 +02:00
has_secure_password
2017-05-17 10:23:25 +02:00
typed_store :settings do |s|
s.boolean :email_notifications, :default => false
s.boolean :email_replies, :default => false
s.boolean :pushover_replies, :default => false
s.string :pushover_user_key
s.boolean :email_messages, :default => false
s.boolean :pushover_messages, :default => false
s.boolean :email_mentions, :default => false
s.boolean :show_avatars, :default => true
s.boolean :show_story_previews, :default => false
s.boolean :show_submitted_story_threads, :default => false
s.boolean :hide_dragons, :default => false
s.string :totp_secret
s.string :github_oauth_token
s.string :github_username
2017-05-23 10:41:09 +02:00
s.string :twitter_oauth_token
s.string :twitter_oauth_token_secret
s.string :twitter_username
s.string :theme, :default => "light"
2017-05-17 10:23:25 +02:00
end
validates :email, :format => { :with => /\A[^@ ]+@[^@ ]+\.[^@ ]+\Z/ },
:uniqueness => { :case_sensitive => false }
validates :theme, inclusion: { in: ["light", "dark", "neo_dark", "solarized_dark"] }
validates :password, :presence => true, :on => :create
2017-05-20 15:29:35 +02:00
VALID_USERNAME = /[A-Za-z0-9][A-Za-z0-9_-]{0,24}/
validates :username,
2017-05-20 15:34:34 +02:00
:format => { :with => /\A#{VALID_USERNAME}\z/ },
:uniqueness => { :case_sensitive => false }
validates_each :username do |record,attr,value|
if BANNED_USERNAMES.include?(value.to_s.downcase)
record.errors.add(attr, "is not permitted")
end
end
2017-05-23 13:43:36 +02:00
scope :active, -> { where(:banned_at => nil, :deleted_at => nil) }
before_save :check_session_token
before_validation :on => :create do
self.create_rss_token
self.create_mailing_list_token
end
2017-05-16 19:33:17 +02:00
BANNED_USERNAMES = [ "admin", "administrator", "contact", "fraud", "guest",
"help", "hostmaster", "mailer-daemon", "moderator", "moderators", "nobody",
"postmaster", "root", "security", "support", "sysop", "webmaster" ]
# days old accounts are considered new for
NEW_USER_DAYS = 7
# minimum karma required to be able to offer title/tag suggestions
MIN_KARMA_TO_SUGGEST = 10
# minimum karma required to be able to downvote comments
MIN_KARMA_TO_DOWNVOTE = 50
# minimum karma required to be able to submit new stories
MIN_KARMA_TO_SUBMIT_STORIES = -4
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
2017-05-23 10:35:14 +02:00
def self.username_regex_s
"/^" + VALID_USERNAME.to_s.gsub(/(\?-mix:|\(|\))/, "") + "$/"
end
def as_json(options = {})
2015-08-01 05:26:34 +02:00
attrs = [
:username,
:created_at,
:is_admin,
:is_moderator,
2015-08-01 05:26:34 +02:00
]
if !self.is_admin?
attrs.push :karma
end
attrs.push :about
h = super(:only => attrs)
2017-05-20 14:06:20 +02:00
h[:avatar_url] = self.avatar_url
if self.github_username.present?
h[:github_username] = self.github_username
end
2017-05-23 10:41:09 +02:00
if self.twitter_username.present?
h[:twitter_username] = self.twitter_username
end
h
end
def authenticate_totp(code)
totp = ROTP::TOTP.new(self.totp_secret)
totp.verify(code)
end
def avatar_url(size = 100)
"https://secure.gravatar.com/avatar/" +
Digest::MD5.hexdigest(self.email.strip.downcase) +
"?r=pg&d=identicon&s=#{size}"
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
def disable_invite_by_user_for_reason!(disabler, reason)
self.disabled_invite_at = Time.now
self.disabled_invite_by_user_id = disabler.id
self.disabled_invite_reason = reason
msg = Message.new
msg.deleted_by_author = true
msg.author_user_id = disabler.id
msg.recipient_user_id = self.id
msg.subject = "Your invite privileges have been revoked"
msg.body = "The reason given:\n" <<
"\n" <<
"> *#{reason}*\n" <<
"\n" <<
"*This is an automated message.*"
msg.save
m = Moderation.new
m.moderator_user_id = disabler.id
m.user_id = self.id
m.action = "Disabled invitations"
m.reason = reason
m.save!
true
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
self.delete!
2014-01-12 22:09:32 +01:00
BanNotification.notify(self, banner, reason)
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
def banned_from_inviting?
disabled_invite_at?
end
def can_downvote?(obj)
if is_new?
return false
elsif obj.is_a?(Story)
if obj.is_downvotable?
return true
elsif obj.vote == -1
# user can unvote
return true
end
elsif obj.is_a?(Comment) && obj.is_downvotable?
return !self.is_new? && (self.karma >= MIN_KARMA_TO_DOWNVOTE)
end
false
2014-01-12 22:09:32 +01:00
end
def can_invite?
!banned_from_inviting? && self.can_submit_stories?
end
def can_offer_suggestions?
!self.is_new? && (self.karma >= MIN_KARMA_TO_SUGGEST)
end
def can_submit_stories?
self.karma >= MIN_KARMA_TO_SUBMIT_STORIES
end
def check_session_token
if self.session_token.blank?
2012-06-30 23:41:34 +02:00
self.session_token = Utils.random_str(60)
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
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
end
def update_comments_posted_count!
Keystore.put("user:#{self.id}:comments_posted", self.comments.active.count)
end
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
def undelete!
User.transaction do
self.comments.each{|c| c.undelete_for_user(self) }
self.sent_messages.each do |m|
m.deleted_by_author = false
m.save
end
self.received_messages.each do |m|
m.deleted_by_recipient = false
m.save
end
self.deleted_at = nil
self.save!
end
end
def disable_2fa!
self.totp_secret = nil
self.save!
end
2016-02-10 15:39:10 +01:00
def grant_moderatorship_by_user!(user)
User.transaction do
self.is_moderator = true
self.save!
m = Moderation.new
m.moderator_user_id = user.id
m.user_id = self.id
m.action = "Granted moderator status"
m.save!
h = Hat.new
h.user_id = self.id
h.granted_by_user_id = user.id
h.hat = "Sysop"
h.save!
end
true
end
2013-07-01 22:43:51 +02:00
def initiate_password_reset_for_ip(ip)
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
PasswordReset.password_reset_link(self, ip).deliver_now
end
def has_2fa?
self.totp_secret.present?
end
def is_active?
!(deleted_at? || is_banned?)
end
def is_banned?
banned_at?
end
def is_new?
Time.now - self.created_at <= NEW_USER_DAYS.days
end
2013-07-01 22:43:51 +02:00
def linkified_about
Markdowner.to_html(self.about)
2012-09-07 19:25:59 +02:00
end
def most_common_story_tag
Tag.active.joins(
:stories
).where(
:stories => { :user_id => self.id }
).group(
Tag.arel_table[:id]
).order(
'COUNT(*) desc'
).first
end
def pushover!(params)
if self.pushover_user_key.present?
Pushover.push(self.pushover_user_key, params)
end
end
def recent_threads(amount, include_submitted_stories = false)
thread_ids = self.comments.group(:thread_id).order('MAX(created_at) DESC').
limit(amount).pluck(:thread_id)
if include_submitted_stories && self.show_submitted_story_threads
thread_ids += Comment.joins(:story).
where(:stories => { :user_id => self.id }).group(:thread_id).
order("MAX(comments.created_at) DESC").limit(amount).pluck(:thread_id)
thread_ids = thread_ids.uniq.sort.reverse[0, amount]
end
thread_ids
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
def unban_by_user!(unbanner)
2014-01-12 22:09:32 +01:00
self.banned_at = nil
self.banned_by_user_id = nil
self.banned_reason = nil
self.deleted_at = nil
2014-01-12 22:09:32 +01:00
self.save!
m = Moderation.new
m.moderator_user_id = unbanner.id
m.user_id = self.id
m.action = "Unbanned"
m.save!
true
2014-01-12 22:09:32 +01:00
end
def enable_invite_by_user!(mod)
self.disabled_invite_at = nil
self.disabled_invite_by_user_id = nil
self.disabled_invite_reason = nil
self.save!
m = Moderation.new
m.moderator_user_id = mod.id
m.user_id = self.id
m.action = "Enabled invitations"
m.save!
true
end
def undeleted_received_messages
received_messages.where(:deleted_by_recipient => false)
end
2013-02-14 01:50:51 +01:00
def undeleted_sent_messages
sent_messages.where(:deleted_by_author => false)
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",
self.received_messages.unread.count)
2012-06-30 21:14:35 +02:00
end
def votes_for_others
self.votes.joins(:story, :comment).
where("comments.user_id <> votes.user_id AND " <<
"stories.user_id <> votes.user_id").
order("id DESC")
end
end