Merge branch 'master' of github.com:discourse/discourse

This commit is contained in:
Sam Saffron 2013-02-06 16:28:38 +11:00
commit 31c5859bbe
3 changed files with 2138 additions and 84 deletions

View file

@ -37,7 +37,7 @@ class User < ActiveRecord::Base
after_save :update_tracked_topics after_save :update_tracked_topics
after_create :create_email_token after_create :create_email_token
# Whether we need to be sending a system message after creation # Whether we need to be sending a system message after creation
attr_accessor :send_welcome_message attr_accessor :send_welcome_message
@ -46,7 +46,7 @@ class User < ActiveRecord::Base
attr_accessor :notification_channel_position attr_accessor :notification_channel_position
def self.username_length def self.username_length
3..15 3..15
end end
def self.suggest_username(name) def self.suggest_username(name)
@ -141,7 +141,7 @@ class User < ActiveRecord::Base
end end
# Use a temporary key to find this user, store it in redis with an expiry # Use a temporary key to find this user, store it in redis with an expiry
def temporary_key def temporary_key
key = SecureRandom.hex(32) key = SecureRandom.hex(32)
$redis.setex "temporary_key:#{key}", 1.week, id.to_s $redis.setex "temporary_key:#{key}", 1.week, id.to_s
key key
@ -164,7 +164,7 @@ class User < ActiveRecord::Base
def invited_by def invited_by
used_invite = invites.where("redeemed_at is not null").includes(:invited_by).first used_invite = invites.where("redeemed_at is not null").includes(:invited_by).first
return nil unless used_invite.present? return nil unless used_invite.present?
used_invite.invited_by used_invite.invited_by
end end
# Approve this user # Approve this user
@ -176,11 +176,11 @@ class User < ActiveRecord::Base
end end
def self.email_hash(email) def self.email_hash(email)
Digest::MD5.hexdigest(email) Digest::MD5.hexdigest(email.strip.downcase)
end end
def email_hash def email_hash
User.email_hash(self.email) User.email_hash(self.email)
end end
def unread_notifications_by_type def unread_notifications_by_type
@ -200,12 +200,12 @@ class User < ActiveRecord::Base
def unread_notifications def unread_notifications
result = 0 result = 0
unread_notifications_by_type.each do |k,v| unread_notifications_by_type.each do |k,v|
result += v unless k == Notification.Types[:private_message] result += v unless k == Notification.Types[:private_message]
end end
result result
end end
def saw_notification_id(notification_id) def saw_notification_id(notification_id)
User.update_all ["seen_notification_id = ?", notification_id], ["seen_notification_id < ?", notification_id] User.update_all ["seen_notification_id = ?", notification_id], ["seen_notification_id < ?", notification_id]
end end
@ -220,15 +220,15 @@ class User < ActiveRecord::Base
# A selection of people to autocomplete on @mention # A selection of people to autocomplete on @mention
def self.mentionable_usernames def self.mentionable_usernames
User.select(:username).order('last_posted_at desc').limit(20) User.select(:username).order('last_posted_at desc').limit(20)
end end
def regular? def regular?
(not admin?) and (not has_trust_level?(:moderator)) (not admin?) and (not has_trust_level?(:moderator))
end end
def password=(password) def password=(password)
# special case for passwordless accounts # special case for passwordless accounts
unless password.blank? unless password.blank?
@raw_password = password @raw_password = password
end end
@ -250,9 +250,9 @@ class User < ActiveRecord::Base
if self.last_seen_at.nil? || self.last_seen_at.to_date < now_date if self.last_seen_at.nil? || self.last_seen_at.to_date < now_date
# count it # count it
row_count = User.exec_sql('insert into user_visits(user_id,visited_at) select :user_id, :visited_at row_count = User.exec_sql('insert into user_visits(user_id,visited_at) select :user_id, :visited_at
where not exists(select 1 from user_visits where user_id = :user_id and visited_at = :visited_at)', user_id: self.id, visited_at: now.to_date) where not exists(select 1 from user_visits where user_id = :user_id and visited_at = :visited_at)', user_id: self.id, visited_at: now.to_date)
if row_count.cmd_tuples == 1 if row_count.cmd_tuples == 1
User.update_all "days_visited = days_visited + 1", ["id = ? and days_visited = ?", self.id, self.days_visited] User.update_all "days_visited = days_visited + 1", ["id = ? and days_visited = ?", self.id, self.days_visited]
end end
end end
@ -291,7 +291,7 @@ class User < ActiveRecord::Base
# Update denormalized topics_entered # Update denormalized topics_entered
exec_sql "UPDATE users SET topics_entered = x.c exec_sql "UPDATE users SET topics_entered = x.c
FROM FROM
(SELECT v.user_id, (SELECT v.user_id,
COUNT(DISTINCT parent_id) AS c COUNT(DISTINCT parent_id) AS c
FROM views AS v FROM views AS v
WHERE parent_type = 'Topic' WHERE parent_type = 'Topic'
@ -306,7 +306,7 @@ class User < ActiveRecord::Base
FROM post_timings AS pt FROM post_timings AS pt
GROUP BY pt.user_id) AS X GROUP BY pt.user_id) AS X
WHERE x.user_id = users.id" WHERE x.user_id = users.id"
end end
# The following count methods are somewhat slow - definitely don't use them in a loop. # The following count methods are somewhat slow - definitely don't use them in a loop.
@ -341,7 +341,7 @@ class User < ActiveRecord::Base
# Use this helper to determine if the user has a particular trust level. # Use this helper to determine if the user has a particular trust level.
# Takes into account admin, etc. # Takes into account admin, etc.
def has_trust_level?(level) def has_trust_level?(level)
raise "Invalid trust level #{level}" unless TrustLevel.Levels.has_key?(level) raise "Invalid trust level #{level}" unless TrustLevel.Levels.has_key?(level)
# Admins can do everything # Admins can do everything
@ -370,19 +370,19 @@ class User < ActiveRecord::Base
if auto_track_topics_after_msecs < 0 if auto_track_topics_after_msecs < 0
User.exec_sql('update topic_users set notification_level = ? User.exec_sql('update topic_users set notification_level = ?
where notifications_reason_id is null and where notifications_reason_id is null and
user_id = ?' , TopicUser::NotificationLevel::REGULAR , self.id) user_id = ?' , TopicUser::NotificationLevel::REGULAR , self.id)
else else
User.exec_sql('update topic_users set notification_level = ? User.exec_sql('update topic_users set notification_level = ?
where notifications_reason_id is null and where notifications_reason_id is null and
user_id = ? and user_id = ? and
total_msecs_viewed < ?' , TopicUser::NotificationLevel::REGULAR , self.id, auto_track_topics_after_msecs) total_msecs_viewed < ?' , TopicUser::NotificationLevel::REGULAR , self.id, auto_track_topics_after_msecs)
User.exec_sql('update topic_users set notification_level = ? User.exec_sql('update topic_users set notification_level = ?
where notifications_reason_id is null and where notifications_reason_id is null and
user_id = ? and user_id = ? and
total_msecs_viewed >= ?' , TopicUser::NotificationLevel::TRACKING , self.id, auto_track_topics_after_msecs) total_msecs_viewed >= ?' , TopicUser::NotificationLevel::TRACKING , self.id, auto_track_topics_after_msecs)
end end
end end
@ -396,7 +396,7 @@ class User < ActiveRecord::Base
def ensure_password_is_hashed def ensure_password_is_hashed
if @raw_password if @raw_password
self.salt = SecureRandom.hex(16) self.salt = SecureRandom.hex(16)
self.password_hash = hash_password(@raw_password, salt) self.password_hash = hash_password(@raw_password, salt)
end end
end end
@ -404,27 +404,27 @@ class User < ActiveRecord::Base
PBKDF2.new(:password => password, :salt => salt, :iterations => Rails.configuration.pbkdf2_iterations).hex_string PBKDF2.new(:password => password, :salt => salt, :iterations => Rails.configuration.pbkdf2_iterations).hex_string
end end
def add_trust_level def add_trust_level
self.trust_level ||= SiteSetting.default_trust_level self.trust_level ||= SiteSetting.default_trust_level
rescue ActiveModel::MissingAttributeError rescue ActiveModel::MissingAttributeError
# Ignore it, safely - see http://www.tatvartha.com/2011/03/activerecordmissingattributeerror-missing-attribute-a-bug-or-a-features/ # Ignore it, safely - see http://www.tatvartha.com/2011/03/activerecordmissingattributeerror-missing-attribute-a-bug-or-a-features/
end end
def update_username_lower def update_username_lower
self.username_lower = username.downcase self.username_lower = username.downcase
end end
def password_validator def password_validator
if @raw_password if @raw_password
return errors.add(:password, "must be 6 letters or longer") if @raw_password.length < 6 return errors.add(:password, "must be 6 letters or longer") if @raw_password.length < 6
end end
end end
def username_validator def username_validator
unless username unless username
return errors.add(:username, I18n.t(:'user.username.blank')) return errors.add(:username, I18n.t(:'user.username.blank'))
end end
if username.length < User.username_length.begin if username.length < User.username_length.begin
return errors.add(:username, I18n.t(:'user.username.short', min: User.username_length.begin)) return errors.add(:username, I18n.t(:'user.username.short', min: User.username_length.begin))
end end

File diff suppressed because it is too large Load diff

View file

@ -36,7 +36,7 @@ describe User do
it "adds one to the topics entered" do it "adds one to the topics entered" do
User.update_view_counts User.update_view_counts
user.reload user.reload
user.topics_entered.should == 1 user.topics_entered.should == 1
end end
it "won't record a second view as a different topic" do it "won't record a second view as a different topic" do
@ -44,9 +44,9 @@ describe User do
User.update_view_counts User.update_view_counts
user.reload user.reload
user.topics_entered.should == 1 user.topics_entered.should == 1
end end
end end
end end
context 'posts_read_count' do context 'posts_read_count' do
@ -58,7 +58,7 @@ describe User do
context 'with a post timing' do context 'with a post timing' do
let!(:post) { Fabricate(:post) } let!(:post) { Fabricate(:post) }
let!(:post_timings) do let!(:post_timings) do
PostTiming.record_timing(msecs: 1234, topic_id: post.topic_id, user_id: user.id, post_number: post.post_number) PostTiming.record_timing(msecs: 1234, topic_id: post.topic_id, user_id: user.id, post_number: post.post_number)
end end
@ -116,7 +116,7 @@ describe User do
it 'has a value for approved_at' do it 'has a value for approved_at' do
user.approved_at.should be_present user.approved_at.should be_present
end end
end end
end end
@ -126,7 +126,7 @@ describe User do
end end
it "creates a bookmark with the true parameter" do it "creates a bookmark with the true parameter" do
lambda { lambda {
PostAction.act(@post.user, @post, PostActionType.Types[:bookmark]) PostAction.act(@post.user, @post, PostActionType.Types[:bookmark])
}.should change(PostAction, :count).by(1) }.should change(PostAction, :count).by(1)
end end
@ -138,7 +138,7 @@ describe User do
it 'reduces the bookmark count of the post' do it 'reduces the bookmark count of the post' do
active = PostAction.where(deleted_at: nil) active = PostAction.where(deleted_at: nil)
lambda { lambda {
PostAction.remove_act(@post.user, @post, PostActionType.Types[:bookmark]) PostAction.remove_act(@post.user, @post, PostActionType.Types[:bookmark])
}.should change(active, :count).by(-1) }.should change(active, :count).by(-1)
end end
@ -195,10 +195,10 @@ describe User do
context 'after_save' do context 'after_save' do
before do before do
subject.save subject.save
end end
its(:email_tokens) { should be_present } its(:email_tokens) { should be_present }
its(:bio_cooked) { should be_present } its(:bio_cooked) { should be_present }
its(:topics_entered) { should == 0 } its(:topics_entered) { should == 0 }
its(:posts_read_count) { should == 0 } its(:posts_read_count) { should == 0 }
@ -248,12 +248,12 @@ describe User do
it "is a moderator if the user level is moderator" do it "is a moderator if the user level is moderator" do
user.trust_level = TrustLevel.Levels[:moderator] user.trust_level = TrustLevel.Levels[:moderator]
user.has_trust_level?(:moderator).should be_true user.has_trust_level?(:moderator).should be_true
end end
it "is a moderator if the user is an admin" do it "is a moderator if the user is an admin" do
user.admin = true user.admin = true
user.has_trust_level?(:moderator).should be_true user.has_trust_level?(:moderator).should be_true
end end
end end
@ -283,40 +283,56 @@ describe User do
end end
describe 'email_hash' do describe 'email_hash' do
before do before do
@user = Fabricate(:user) @user = Fabricate(:user)
end end
it 'should have a sane email hash' do it 'should have a sane email hash' do
@user.email_hash.should =~ /^[0-9a-f]{32}$/ @user.email_hash.should =~ /^[0-9a-f]{32}$/
end end
it 'should use downcase email' do
@user.email = "example@example.com"
@user2 = Fabricate(:user)
@user2.email = "ExAmPlE@eXaMpLe.com"
@user.email_hash.should == @user2.email_hash
end
it 'should trim whitespace before hashing' do
@user.email = "example@example.com"
@user2 = Fabricate(:user)
@user2.email = " example@example.com "
@user.email_hash.should == @user2.email_hash
end
end end
describe 'name heuristics' do describe 'name heuristics' do
it 'is able to guess a decent username from an email' do it 'is able to guess a decent username from an email' do
User.suggest_username('bob@bob.com').should == 'bob' User.suggest_username('bob@bob.com').should == 'bob'
end end
it 'is able to guess a decent name from an email' do it 'is able to guess a decent name from an email' do
User.suggest_name('sam.saffron@gmail.com').should == 'Sam Saffron' User.suggest_name('sam.saffron@gmail.com').should == 'Sam Saffron'
end end
end end
describe 'username format' do describe 'username format' do
it "should always be 3 chars or longer" do it "should always be 3 chars or longer" do
@user = Fabricate.build(:user) @user = Fabricate.build(:user)
@user.username = 'ss' @user.username = 'ss'
@user.save.should == false @user.save.should == false
end end
it "should never end with a ." do it "should never end with a ." do
@user = Fabricate.build(:user) @user = Fabricate.build(:user)
@user.username = 'sam.' @user.username = 'sam.'
@user.save.should == false @user.save.should == false
end end
it "should never contain spaces" do it "should never contain spaces" do
@user = Fabricate.build(:user) @user = Fabricate.build(:user)
@user.username = 'sam s' @user.username = 'sam s'
@user.save.should == false @user.save.should == false
@ -337,13 +353,13 @@ describe User do
@user.save! @user.save!
@codinghorror = Fabricate.build(:coding_horror) @codinghorror = Fabricate.build(:coding_horror)
end end
it "should not allow saving if username is reused" do it "should not allow saving if username is reused" do
@codinghorror.username = @user.username @codinghorror.username = @user.username
@codinghorror.save.should be_false @codinghorror.save.should be_false
end end
it "should not allow saving if username is reused in different casing" do it "should not allow saving if username is reused in different casing" do
@codinghorror.username = @user.username.upcase @codinghorror.username = @user.username.upcase
@codinghorror.save.should be_false @codinghorror.save.should be_false
end end
@ -359,7 +375,7 @@ describe User do
end end
end end
describe '.suggest_username' do describe '.suggest_username' do
it 'corrects weird characters' do it 'corrects weird characters' do
User.suggest_username("Darth%^Vadar").should == "Darth_Vadar" User.suggest_username("Darth%^Vadar").should == "Darth_Vadar"
end end
@ -384,7 +400,7 @@ describe User do
it "makes room for the digit added if the username is too long" do it "makes room for the digit added if the username is too long" do
User.create(username: 'myreallylongnam', email: 'fake@discourse.org') User.create(username: 'myreallylongnam', email: 'fake@discourse.org')
User.suggest_username("myreallylongnam").should == 'myreallylongna1' User.suggest_username("myreallylongnam").should == 'myreallylongna1'
end end
it "removes leading character if it is not alphanumeric" do it "removes leading character if it is not alphanumeric" do
User.suggest_username("_myname").should == 'myname' User.suggest_username("_myname").should == 'myname'
@ -411,18 +427,18 @@ describe User do
end end
end end
describe 'passwords' do describe 'passwords' do
before do before do
@user = Fabricate.build(:user) @user = Fabricate.build(:user)
@user.password = "ilovepasta" @user.password = "ilovepasta"
@user.save! @user.save!
end end
it "should have a valid password after the initial save" do it "should have a valid password after the initial save" do
@user.confirm_password?("ilovepasta").should be_true @user.confirm_password?("ilovepasta").should be_true
end end
it "should not have an active account after initial save" do it "should not have an active account after initial save" do
@user.active.should be_false @user.active.should be_false
end end
end end
@ -509,10 +525,10 @@ describe User do
end end
describe "last_seen_at" do describe "last_seen_at" do
let(:user) { Fabricate(:user) } let(:user) { Fabricate(:user) }
it "should have a blank last seen on creation" do it "should have a blank last seen on creation" do
user.last_seen_at.should be_nil user.last_seen_at.should be_nil
end end
@ -535,11 +551,11 @@ describe User do
it "should have 0 for days_visited" do it "should have 0 for days_visited" do
user.reload user.reload
user.days_visited.should == 1 user.days_visited.should == 1
end end
it "should log a user_visit with the date" do it "should log a user_visit with the date" do
user.user_visits.first.visited_at.should == date.to_date user.user_visits.first.visited_at.should == date.to_date
end end
context "called twice" do context "called twice" do
@ -571,7 +587,7 @@ describe User do
end end
end end