From 42429d43954e93b46cb89c177c6d9f7318adbaca Mon Sep 17 00:00:00 2001 From: Seth Call Date: Thu, 14 Mar 2013 23:22:31 -0500 Subject: [PATCH] * VRFS-259: beta signup --- Gemfile | 2 +- lib/jam_ruby.rb | 5 + .../app/mailers/invited_user_mailer.rb | 54 +++ lib/jam_ruby/app/mailers/user_mailer.rb | 12 - .../friend_invitation.html.erb | 10 + .../friend_invitation.text.erb | 7 + .../welcome_betauser.html.erb | 6 + .../welcome_betauser.text.erb | 2 + .../user_mailer/welcome_betauser.html.erb | 6 - .../user_mailer/welcome_betauser.text.erb | 2 - lib/jam_ruby/models/friendship.rb | 17 + lib/jam_ruby/models/instrument.rb | 5 + lib/jam_ruby/models/invited_user.rb | 59 +++ lib/jam_ruby/models/invited_user_observer.rb | 14 + lib/jam_ruby/models/user.rb | 351 +++++++++++------- spec/factories.rb | 20 + spec/jam_ruby/connection_manager_spec.rb | 2 +- spec/jam_ruby/models/invited_user_spec.rb | 68 ++++ spec/jam_ruby/models/user_spec.rb | 13 +- spec/spec_helper.rb | 2 + 20 files changed, 485 insertions(+), 172 deletions(-) create mode 100644 lib/jam_ruby/app/mailers/invited_user_mailer.rb create mode 100644 lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.html.erb create mode 100644 lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.text.erb create mode 100644 lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/welcome_betauser.html.erb create mode 100644 lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/welcome_betauser.text.erb delete mode 100644 lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_betauser.html.erb delete mode 100644 lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_betauser.text.erb create mode 100644 lib/jam_ruby/models/invited_user.rb create mode 100644 lib/jam_ruby/models/invited_user_observer.rb create mode 100644 spec/jam_ruby/models/invited_user_spec.rb diff --git a/Gemfile b/Gemfile index d5a500310..75a43a941 100644 --- a/Gemfile +++ b/Gemfile @@ -21,7 +21,7 @@ gem 'sendgrid' gem 'aws-sdk', '1.8.0' gem 'carrierwave' gem 'aasm', '3.0.16' - +gem 'devise', '>= 1.1.2' if devenv gem 'jam_db', :path=> "#{workspace}/jam-db/target/ruby_package" gem 'jampb', :path => "#{workspace}/jam-pb/target/ruby/jampb" diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index f52126e8b..ea03e80c5 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -8,6 +8,7 @@ require "logging" require "will_paginate" require "will_paginate/active_record" require "action_mailer" +require "devise" require "sendgrid" require "jam_ruby/constants/validation_messages" require "jam_ruby/constants/limits" @@ -21,6 +22,7 @@ require "jam_ruby/version" require "jam_ruby/environment" require "jam_ruby/init" require "jam_ruby/app/mailers/user_mailer" +require "jam_ruby/app/mailers/invited_user_mailer" require "jam_ruby/app/uploaders/artifact_uploader" require "jam_ruby/message_factory" require "jam_ruby/models/genre" @@ -28,6 +30,9 @@ require "jam_ruby/models/user" require "jam_ruby/models/user_authorization" require "jam_ruby/models/join_request" require "jam_ruby/models/band" +require "jam_ruby/models/invited_user" +require "jam_ruby/models/invited_user_observer" +require "jam_ruby/models/admin_user" require "jam_ruby/models/artifact_update" require "jam_ruby/models/band_invitation" require "jam_ruby/models/band_liker" diff --git a/lib/jam_ruby/app/mailers/invited_user_mailer.rb b/lib/jam_ruby/app/mailers/invited_user_mailer.rb new file mode 100644 index 000000000..4f01d3743 --- /dev/null +++ b/lib/jam_ruby/app/mailers/invited_user_mailer.rb @@ -0,0 +1,54 @@ +module JamRuby + # InvitedUserMailer must be configured to work + # Some common configs occur in jam_ruby/init.rb + # Environment specific configs occur in spec_helper.rb in jam-ruby and jam-web (to put it into test mode), + # and in config/initializers/email.rb in rails to configure sendmail account settings + # If InvitedUserMailer were to be used in another project, it would need to be configured there, as well. + + # Templates for InvitedUserMailer can be found in jam_ruby/app/views/jam_ruby/user_mailer + class InvitedUserMailer < ActionMailer::Base + include SendGrid + + DEFAULT_SENDER = "support@jamkazam.com" + + default :from => DEFAULT_SENDER + + sendgrid_category :use_subject_lines + sendgrid_enable :opentrack, :clicktrack + sendgrid_unique_args :env => Environment.mode + + # sent in the case of a general 'service invitation', from no one person in particular + def welcome_betauser(invited_user) + + @signup_url = generate_signup_url(invited_user) + + sendgrid_category "Welcome" + sendgrid_unique_args :type => "welcome_betauser" + + mail(:to => invited_user.email, :subject => "Welcome to the Jamkazam Beta release") do |format| + format.text + format.html + end + end + + # used when sending an invitation from one user to another + def friend_invitation(invited_user) + + @signup_url = generate_signup_url(invited_user) + @sender = invited_user.sender + @note = invited_user.note + + sendgrid_category "Invitation" + sendgrid_unique_args :type => "friend_invitation" + + mail(:to => invited_user.email, :subject => "You've been invited to Jamkazam by #{invited_user.sender.name}") do |format| + format.text + format.html + end + end + + def generate_signup_url(invited_user) + "http://www.jamkazam.com/signup?invitation_code=#{invited_user.invitation_code}" + end + end +end diff --git a/lib/jam_ruby/app/mailers/user_mailer.rb b/lib/jam_ruby/app/mailers/user_mailer.rb index eb14724f9..8be2e6c0f 100644 --- a/lib/jam_ruby/app/mailers/user_mailer.rb +++ b/lib/jam_ruby/app/mailers/user_mailer.rb @@ -29,18 +29,6 @@ module JamRuby end end - def welcome_betauser(user, signup_confirm_url) - @user = user - @signup_confirm_url = signup_confirm_url - sendgrid_category "Welcome" - sendgrid_unique_args :type => "welcome_betauser" - - mail(:to => user.email, :subject => "Welcome #{user.first_name} to the Jamkazam Beta release") do |format| - format.text - format.html - end - end - def password_changed(user) @user = user sendgrid_unique_args :type => "password_changed" diff --git a/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.html.erb b/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.html.erb new file mode 100644 index 000000000..081997e08 --- /dev/null +++ b/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.html.erb @@ -0,0 +1,10 @@ + + +

You've been invited to Jamkazam by <%= @sender.name %>!

+<% unless @note.nil? %> +

<%= @sender.name %> says:

+

<%= @note %>

+<% end %> +

To signup, please go to the Create Account page.

+ + diff --git a/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.text.erb b/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.text.erb new file mode 100644 index 000000000..59543c753 --- /dev/null +++ b/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.text.erb @@ -0,0 +1,7 @@ +You've been invited to Jamkazam by <%= @sender.name %>! +<% unless @note.nil? %> +<%= @sender.name %> says: +<%= @note %> +<% end %> + +To signup, please go to the Create Account page: <%= @signup_url %>. diff --git a/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/welcome_betauser.html.erb b/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/welcome_betauser.html.erb new file mode 100644 index 000000000..d3f3e544f --- /dev/null +++ b/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/welcome_betauser.html.erb @@ -0,0 +1,6 @@ + + +

Welcome to the Jamkazam beta release!

+

To signup, please go to the Create Account page.

+ + diff --git a/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/welcome_betauser.text.erb b/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/welcome_betauser.text.erb new file mode 100644 index 000000000..16806cad4 --- /dev/null +++ b/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/welcome_betauser.text.erb @@ -0,0 +1,2 @@ +Welcome to the JamKazam beta release! +To signup, please go to the Create Account page: <%= @signup_url %>. diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_betauser.html.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_betauser.html.erb deleted file mode 100644 index 5c1f1c611..000000000 --- a/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_betauser.html.erb +++ /dev/null @@ -1,6 +0,0 @@ - - -

Welcome to the Jamkazam beta release! <%= @user.first_name %>.

-

To confirm your registration, please go to the signup confirmation page..

- - diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_betauser.text.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_betauser.text.erb deleted file mode 100644 index 47bb3f9da..000000000 --- a/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_betauser.text.erb +++ /dev/null @@ -1,2 +0,0 @@ -Welcome to the JamKazam beta release! <%= @user.first_name %> -To confirm your registration, please go to the signup confirmation page at : <%= @signup_confirm_url %>. diff --git a/lib/jam_ruby/models/friendship.rb b/lib/jam_ruby/models/friendship.rb index 729757de6..2a72ca143 100644 --- a/lib/jam_ruby/models/friendship.rb +++ b/lib/jam_ruby/models/friendship.rb @@ -17,6 +17,23 @@ module JamRuby end end + + # not like .save() in that it does not check for an existing friendship. The caller is responsible + # for checking for errors on the models + def self.save_using_models(user, friend) + this = Friendship.new + this.user = user + this.friend = friend + + that = Friendship.new + that.user = friend + that.friend = user + + this.save + that.save + return [this, that] + end + def self.search(query, user_id, options = { :limit => 10 }) # only issue search if at least 2 characters are specified if query.nil? || query.length < 2 diff --git a/lib/jam_ruby/models/instrument.rb b/lib/jam_ruby/models/instrument.rb index cd3699b26..6b5936e87 100644 --- a/lib/jam_ruby/models/instrument.rb +++ b/lib/jam_ruby/models/instrument.rb @@ -11,5 +11,10 @@ module JamRuby # music sessions has_and_belongs_to_many :music_sessions, :class_name => "JamRuby::MusicSession", :join_table => "genres_music_sessions" + + def self.standard_list + return Instrument.where('instruments.popularity > 0').order('instruments.popularity DESC, instruments.description ASC') + end + end end diff --git a/lib/jam_ruby/models/invited_user.rb b/lib/jam_ruby/models/invited_user.rb new file mode 100644 index 000000000..98efcc4c7 --- /dev/null +++ b/lib/jam_ruby/models/invited_user.rb @@ -0,0 +1,59 @@ +module JamRuby + class InvitedUser < ActiveRecord::Base + + VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i + + attr_accessible :email, :sender_id, :personal, :autofriend, :note + attr_accessor :accepted_twice + + self.primary_key = 'id' + + ### Who sent this invitatio? + # either admin_sender or user_sender is not null. If an administrator sends the invitation, then + belongs_to :sender , :inverse_of => :invited_users, :class_name => "JamRuby::User", :foreign_key => "sender_id" + + # who is the invitation sent to? + validates :email, :presence => true, format: {with: VALID_EMAIL_REGEX} + validates :autofriend, :inclusion => {:in => [nil, true, false]} + validates :invitation_code, :presence => true + validates :note, length: {maximum: 400} # 400 == arbitrary. + + validate :valid_personalized_invitation + validate :not_accepted_twice + + # ensure invitation code is always created + before_validation(:on => :create) do + self.invitation_code = SecureRandom.urlsafe_base64 if self.invitation_code.nil? + self.sender_id = nil if self.sender_id.blank? # this coercion was done just to make activeadmin work + end + + def sender_display_name + return sender.name + end + + def accept! + if self.accepted + accepted_twice = true + end + + self.accepted = true + end + + + + def invited_by_administrator? + sender.nil? || sender.admin # a nil sender can only be created by someone using jam-admin + end + private + + def valid_personalized_invitation + errors.add(:autofriend, "autofriend must be true if sender is specified") if autofriend && sender.nil? + end + + def not_accepted_twice + errors.add(:accepted, "you can only accept an invitation once") if accepted_twice + end + + + end +end diff --git a/lib/jam_ruby/models/invited_user_observer.rb b/lib/jam_ruby/models/invited_user_observer.rb new file mode 100644 index 000000000..acb4fca69 --- /dev/null +++ b/lib/jam_ruby/models/invited_user_observer.rb @@ -0,0 +1,14 @@ +module JamRuby + class InvitedUserObserver < ActiveRecord::Observer + + observe JamRuby::InvitedUser + + def after_save(invited_user) + if invited_user.sender.nil? + InvitedUserMailer.welcome_betauser(invited_user).deliver + else + InvitedUserMailer.friend_invitation(invited_user).deliver + end + end + end +end \ No newline at end of file diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 99afc9041..389d6bbbe 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -1,10 +1,16 @@ +include Devise::Models + module JamRuby class User < ActiveRecord::Base - attr_accessible :first_name, :last_name, :email, :password, :password_confirmation, :city, :state, :country - attr_accessor :updating_password, :administratively_created + #devise: for later: :trackable - self.primary_key = 'id' + devise :database_authenticatable, + :recoverable, :rememberable, :validatable + + + attr_accessible :first_name, :last_name, :email, :password, :password_confirmation, :city, :state, :country, :subscribe_email, :terms_of_service + attr_accessor :updating_password, :administratively_created # authorizations (for facebook, etc -- omniauth) has_many :user_authorizations, :class_name => "JamRuby::UserAuthorization" @@ -83,14 +89,17 @@ module JamRuby # saved tracks has_many :recorded_tracks, :foreign_key => "user_id", :class_name => "JamRuby::RecordedTrack", :inverse_of => :user + # invited users + has_many :invited_users, :foreign_key => "sender_id", :class_name => "JamRuby::InvitedUser" + # This causes the authenticate method to be generated (among other stuff) - has_secure_password + #has_secure_password before_save { |user| user.email = email.downcase } before_save :create_remember_token, :if => :should_validate_password? - validates :first_name, presence: true, length: {maximum: 50}, :if => :end_user_created? - validates :last_name, presence: true, length: {maximum: 50}, :if => :end_user_created? + validates :first_name, presence: true, length: {maximum: 50} + validates :last_name, presence: true, length: {maximum: 50} VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i validates :email, presence: true, format: {with: VALID_EMAIL_REGEX}, uniqueness: {case_sensitive: false} @@ -98,6 +107,16 @@ module JamRuby validates_presence_of :password_confirmation, :if => :should_validate_password? validates_confirmation_of :password, :if => :should_validate_password? + validates :terms_of_service, :acceptance => {:accept => true, :on => :create, :allow_nil => false } + validates :subscribe_email, :inclusion => {:in => [nil, true, false]} + + validate :validate_musician_instruments + + def validate_musician_instruments + errors.add(:musician_instruments, ValidationMessages::INSTRUMENT_MINIMUM_NOT_MET) if !administratively_created && musician && musician_instruments.length == 0 + errors.add(:musician_instruments, ValidationMessages::INSTRUMENT_LIMIT_EXCEEDED) if !administratively_created && musician && musician_instruments.length > 5 + end + def online @online ||= !self.connections.nil? && self.connections.size > 0 @@ -122,8 +141,12 @@ module JamRuby end if self.city.blank? end + def musician? + return musician + end + def should_validate_password? - !administratively_created && (updating_password || new_record?) + (updating_password || new_record?) end def end_user_created? @@ -205,7 +228,7 @@ module JamRuby end def set_password(old_password, new_password, new_password_confirmation) - raise JamRuby::JamArgumentError unless authenticate old_password + raise JamRuby::JamArgumentError unless valid_password? old_password change_password(new_password, new_password_confirmation) save end @@ -271,14 +294,106 @@ module JamRuby return recordings end + def easy_save(first_name, last_name, email, password, password_confirmation, musician, gender, + birth_date, internet_service_provider, city, state, country, instruments, photo_url) + + # first name + unless first_name.nil? + self.first_name = first_name + end + + # last name + unless last_name.nil? + self.last_name = last_name + end + + # email + unless email.nil? + self.email = email + end + + # password + unless password.nil? + self.password = password + end + + # password confirmation + unless password_confirmation.nil? + self.password_confirmation = password_confirmation + end + + # musician flag + unless musician.nil? + self.musician = musician + end + + # gender + unless gender.nil? + self.gender = gender + end + + # birthdate + unless birth_date.nil? + self.birth_date = birth_date + end + + # ISP + unless internet_service_provider.nil? + self.internet_service_provider = internet_service_provider + end + + # city + unless city.nil? + self.city = city + end + + # state + unless state.nil? + self.state = state + end + + # country + unless country.nil? + self.country = country + end + + # instruments + unless instruments.nil? + UserManager.active_record_transaction do |user_manager| + # delete all instruments for this user first + unless self.new_record? + MusicianInstrument.delete_all(["user_id = ?", self.id]) + end + + # loop through each instrument in the array and save to the db + instruments.each do |musician_instrument_param| + instrument = Instrument.find(musician_instrument_param[:instrument_id]) + musician_instrument = MusicianInstrument.new + musician_instrument.user = self + musician_instrument.instrument = instrument + musician_instrument.proficiency_level = musician_instrument_param[:proficiency_level] + musician_instrument.priority = musician_instrument_param[:priority] + musician_instrument.save + self.musician_instruments << musician_instrument + end + end + end + + # photo url + unless photo_url.nil? + self.photo_url = photo_url + end + + self.updated_at = Time.now.getutc + self.save + end + # helper method for creating / updating a User def self.save(id, updater_id, first_name, last_name, email, password, password_confirmation, musician, gender, - birth_date, internet_service_provider, city, state, country, instruments, photo_url) + birth_date, internet_service_provider, city, state, country, instruments, photo_url) if id.nil? - validate_instruments(instruments, musician) user = User.new() else - validate_instruments(instruments, true) user = User.find(id) end @@ -286,95 +401,8 @@ module JamRuby raise PermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR end - # first name - unless first_name.nil? - user.first_name = first_name - end - - # last name - unless last_name.nil? - user.last_name = last_name - end - - # email - unless email.nil? - user.email = email - end - - # password - unless password.nil? - user.password = password - end - - # password confirmation - unless password_confirmation.nil? - user.password_confirmation = password_confirmation - end - - # musician flag - unless musician.nil? - user.musician = musician - end - - # gender - unless gender.nil? - account.gender = gender - end - - # birthdate - unless birth_date.nil? - account.birth_date = birth_date - end - - # ISP - unless internet_service_provider.nil? - account.internet_service_provider = internet_service_provider - end - - # city - unless city.nil? - user.city = city - end - - # state - unless state.nil? - user.state = state - end - - # country - unless country.nil? - user.country = country - end - - # instruments - unless instruments.nil? - UserManager.active_record_transaction do |user_manager| - # delete all instruments for this user first - unless user.id.nil? || user.id.length == 0 - MusicianInstrument.delete_all(["user_id = ?", user.id]) - end - - # loop through each instrument in the array and save to the db - instruments.each do |musician_instrument_param| - instrument = Instrument.find(musician_instrument_param[:instrument_id]) - musician_instrument = MusicianInstrument.new - musician_instrument.user = user - musician_instrument.instrument = instrument - musician_instrument.proficiency_level = musician_instrument_param[:proficiency_level] - musician_instrument.priority = musician_instrument_param[:priority] - musician_instrument.save - user.musician_instruments << musician_instrument - end - end - end - - # photo url - unless photo_url.nil? - user.photo_url = photo_url - end - - user.updated_at = Time.now.getutc - user.save + user.easy_save(first_name, last_name, email, password, password_confirmation, musician, gender, + birth_date, internet_service_provider, city, state, country, instruments, photo_url) return user end @@ -485,32 +513,37 @@ module JamRuby # throws ActiveRecord::RecordNotFound if instrument is invalid # throws an email delivery error if unable to connect out to SMTP - def self.signup(first_name, last_name, email, password, password_confirmation, - city, state, country, instruments, photo_url, signup_confirm_url) + def self.signup(first_name, last_name, email, password, password_confirmation, terms_of_service, subscribe_email, + location, instruments, birth_date, photo_url, invited_user, signup_confirm_url) user = User.new UserManager.active_record_transaction do |user_manager| user.first_name = first_name user.last_name = last_name user.email = email + user.subscribe_email = subscribe_email + user.terms_of_service = terms_of_service - #FIXME: Setting random password for social network logins. This + # FIXME: Setting random password for social network logins. This # is because we have validations all over the place on this. # The right thing would be to have this null + + # Seth: I think we need a flag in the signature of signup to say 'social_signup=true'. If that flag is set, + # then you can do use.updating_password = false and instead set a null password if password.nil? - user.password = "blahblahblah" - user.password_confirmation = "blahblahblah" + user.password = user.password_confirmation = SecureRandom.urlsafe_base64 else user.password = password user.password_confirmation = password_confirmation end user.admin = false - user.email_confirmed = false user.musician = true - user.city = city - user.state = state - user.country = country + user.city = location[:city] + user.state = location[:state] + user.country = location[:country] + user.birth_date = birth_date + unless instruments.nil? instruments.each do |musician_instrument_param| instrument = Instrument.find(musician_instrument_param[:instrument_id]) @@ -519,29 +552,65 @@ module JamRuby musician_instrument.instrument = instrument musician_instrument.proficiency_level = musician_instrument_param[:proficiency_level] musician_instrument.priority = musician_instrument_param[:priority] - musician_instrument.save user.musician_instruments << musician_instrument end end user.photo_url = photo_url - user.signup_token = SecureRandom.urlsafe_base64 - user.can_invite = Limits::USERS_CAN_INVITE + if invited_user.nil? + user.can_invite = Limits::USERS_CAN_INVITE + user.email_confirmed = false + user.signup_token = SecureRandom.urlsafe_base64 + else + # if you are invited by an admin, we'll say you can invite too. + # but if not, then you can not invite + user.can_invite = invited_user.invited_by_administrator? + + # if you came in from an invite and used the same email to signup, + # then we know you are a real human and that your email is valid. + # lucky! we'll log you in immediately + if invited_user.email.casecmp(user.email).zero? + user.email_confirmed = true + user.signup_token = nil + else + user.email_confirmed = false + user.signup_token = SecureRandom.urlsafe_base64 + end + + + # now that the user is saved, let's + if invited_user.autofriend && !invited_user.sender.nil? + # hookup this user with the sender + Friendship.save_using_models(user, invited_user.sender) + end + + invited_user.accept! + invited_user.save + + if invited_user.errors.any? + raise ActiveRecord::Rollback + end + end user.save if user.errors.any? raise ActiveRecord::Rollback else - # FIXME: - # It's not standard to require a confirmation when a user signs up with Facebook. - # We should stop asking for it. - # - # any errors here should also rollback the transaction; that's OK. If emails aren't going to be delivered, - # it's already a really bad situation; make user signup again - UserMailer.welcome_message(user, signup_confirm_url.nil? ? nil : (signup_confirm_url + "/" + user.signup_token) ).deliver + # don't send an signup email if the user was invited already *and* they used the same email that they were invited with + if !invited_user.nil? && invited_user.email.casecmp(user.email).zero? + else + + # FIXME: + # It's not standard to require a confirmation when a user signs up with Facebook. + # We should stop asking for it. + # + # any errors here should also rollback the transaction; that's OK. If emails aren't going to be delivered, + # it's already a really bad situation; make user signup again + UserMailer.welcome_message(user, signup_confirm_url.nil? ? nil : (signup_confirm_url + "/" + user.signup_token) ).deliver + end end end @@ -577,6 +646,7 @@ module JamRuby user.city = city user.state = state user.country = country + user.terms_of_service = true if instruments.nil? instruments = [{:instrument_id => "acoustic guitar", :proficiency_level => 3, :priority => 1}] @@ -593,7 +663,6 @@ module JamRuby musician_instrument.instrument = instrument musician_instrument.proficiency_level = musician_instrument_param[:proficiency_level] musician_instrument.priority = musician_instrument_param[:priority] - musician_instrument.save user.musician_instruments << musician_instrument end @@ -609,6 +678,12 @@ module JamRuby return user end + def signup_confirm + self.signup_token = nil + self.confirm_email! + self.save + end + # throws RecordNotFound if signup token is invalid; i.e., if it's nil, empty string, or not belonging to a user def self.signup_confirm(signup_token) if signup_token.nil? || signup_token.empty? @@ -618,9 +693,7 @@ module JamRuby UserManager.active_record_transaction do |user_manager| # throws ActiveRecord::RecordNotFound if invalid user = User.find_by_signup_token!(signup_token) - user.signup_token = nil - user.confirm_email! - user.save + user.signup_confirm return user end end @@ -632,7 +705,7 @@ module JamRuby # we only allow users that have confirmed email to authenticate user = User.where('email_confirmed=true').find_by_email(email) - if user && user.authenticate(password) + if user && user.valid_password?(password) return user else return nil @@ -656,27 +729,21 @@ module JamRuby return User.where("email_confirmed = true and name_tsv @@ to_tsquery('jamenglish', ?)", query).limit(options[:limit]) end + # devise compatibility + + #def encrypted_password + # logger.debug("password digest returned #{self.password_digest}") + # self.password_digest + #end + + #def encrypted_password=(encrypted_password) + # self.password_digest = encrypted_password + #end + + # end devise compatibility private def create_remember_token self.remember_token = SecureRandom.urlsafe_base64 end - - def self.validate_instruments(instruments, is_nil_ok) - if is_nil_ok && instruments.nil? - return - end - - if instruments.nil? - raise JamRuby::JamArgumentError, ValidationMessages::INSTRUMENT_MINIMUM_NOT_MET - else - if instruments.size < Limits::MIN_INSTRUMENTS_PER_MUSICIAN - raise JamRuby::JamArgumentError, ValidationMessages::INSTRUMENT_MINIMUM_NOT_MET - end - - if instruments.size > Limits::MAX_INSTRUMENTS_PER_MUSICIAN - raise JamRuby::JamArgumentError, ValidationMessages::INSTRUMENT_LIMIT_EXCEEDED - end - end - end end end diff --git a/spec/factories.rb b/spec/factories.rb index 696694503..009d43314 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -10,6 +10,13 @@ FactoryGirl.define do state "NC" country "USA" musician true + terms_of_service true + + #u.association :musician_instrument, factory: :musician_instrument, user: u + + before(:create) do |user| + user.musician_instruments << FactoryGirl.build(:musician_instrument, user: user) + end factory :admin do admin true @@ -67,9 +74,22 @@ FactoryGirl.define do end factory :instrument, :class => JamRuby::Instrument do + end factory :recording, :class => JamRuby::Recording do end + + factory :musician_instrument, :class => JamRuby::MusicianInstrument do + instrument { Instrument.find('electric guitar') } + proficiency_level 1 + priority 0 + end + + factory :invited_user, :class => JamRuby::InvitedUser do + sequence(:email) { |n| "user#{n}@someservice.com" } + autofriend false + end + end diff --git a/spec/jam_ruby/connection_manager_spec.rb b/spec/jam_ruby/connection_manager_spec.rb index 9f3c036f6..00ac1db65 100644 --- a/spec/jam_ruby/connection_manager_spec.rb +++ b/spec/jam_ruby/connection_manager_spec.rb @@ -12,7 +12,7 @@ describe ConnectionManager do end def create_user(first_name, last_name, email, options = {:musician => true}) - @conn.exec("INSERT INTO users (first_name, last_name, email, musician, password_digest, city, state, country) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING id", [first_name, last_name, email, options[:musician], '1', 'Apex', 'NC', 'USA']) do |result| + @conn.exec("INSERT INTO users (first_name, last_name, email, musician, encrypted_password, city, state, country) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING id", [first_name, last_name, email, options[:musician], '1', 'Apex', 'NC', 'USA']) do |result| return result.getvalue(0, 0) end end diff --git a/spec/jam_ruby/models/invited_user_spec.rb b/spec/jam_ruby/models/invited_user_spec.rb new file mode 100644 index 000000000..123db9c2b --- /dev/null +++ b/spec/jam_ruby/models/invited_user_spec.rb @@ -0,0 +1,68 @@ +require 'spec_helper' + +describe InvitedUser do + + before(:each) do + UserMailer.deliveries.clear + end + + it 'create an invitation from end-user' do + + # create an end user + user1 = FactoryGirl.create(:user) + + # create the invitation from the end-user + invited_user = FactoryGirl.create(:invited_user, :sender => user1) + + invited_user.email.should_not be_nil + invited_user.sender.should_not be_nil + invited_user.note.should be_nil + invited_user.invited_by_administrator?.should be_false + end + + it 'create an invitation from admin-user' do + + # create an admin user + user1 = FactoryGirl.create(:admin) + + # create the invitation from the end-user + invited_user = FactoryGirl.create(:invited_user, :sender => user1) + + invited_user.email.should_not be_nil + invited_user.sender.should_not be_nil + invited_user.note.should be_nil + invited_user.invited_by_administrator?.should be_true + end + + it 'create an invitation from no one in particular' do + # create the invitation from the end-user + invited_user = FactoryGirl.build(:invited_user) + + invited_user.invited_by_administrator?.should be_true + end + + it 'email is sent automatically by virtue of observer' do + # create an admin user + user1 = FactoryGirl.create(:admin) + + # create the invitation from the end-user + invited_user = FactoryGirl.create(:invited_user, :sender => user1) + + InvitedUserMailer.deliveries.length.should == 1 + end + + it 'accept an invitation' do + # create an admin user + user1 = FactoryGirl.create(:admin) + + # create the invitation from the end-user + invited_user = FactoryGirl.create(:invited_user, :sender => user1) + + invited_user.accepted.should be_false + + invited_user.accept! + invited_user.save + + invited_user.accepted.should be_true + end +end diff --git a/spec/jam_ruby/models/user_spec.rb b/spec/jam_ruby/models/user_spec.rb index e2ca1683f..1821d72a0 100644 --- a/spec/jam_ruby/models/user_spec.rb +++ b/spec/jam_ruby/models/user_spec.rb @@ -4,7 +4,8 @@ describe User do before do @user = User.new(first_name: "Example", last_name: "User", email: "user@example.com", - password: "foobar", password_confirmation: "foobar", city: "Apex", state: "NC", country: "USA") + password: "foobar", password_confirmation: "foobar", city: "Apex", state: "NC", country: "USA", terms_of_service: true, musician: true) + @user.musician_instruments << FactoryGirl.build(:musician_instrument, user: @user) end subject { @user } @@ -17,7 +18,7 @@ describe User do it { should respond_to(:password_confirmation) } it { should respond_to(:remember_token) } it { should respond_to(:admin) } - it { should respond_to(:authenticate) } + it { should respond_to(:valid_password?) } it { should respond_to(:can_invite) } it { should be_valid } @@ -126,10 +127,6 @@ describe User do it { should be_invalid } end - describe "when password and password digest are not present, but admininstrator created it" do - before { @user.password = @user.password_confirmation = nil; @user.administratively_created = true } - it { should be_valid } - end describe "set_password" do before do @@ -196,11 +193,11 @@ describe User do let(:found_user) { User.find_by_email(@user.email) } describe "with valid password" do - it { should == found_user.authenticate(@user.password) } + it { found_user.valid_password?(@user.password).should be_true } end describe "with invalid password" do - let(:user_for_invalid_password) { found_user.authenticate("invalid") } + let(:user_for_invalid_password) { found_user.valid_password?("invalid") } it { should_not == user_for_invalid_password } specify { user_for_invalid_password.should be_false } diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index e0a74ae8e..52e4f5527 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -19,6 +19,8 @@ require 'factories' include JamRuby +# manually register observers +ActiveRecord::Base.add_observer InvitedUserObserver.instance # put ActionMailer into test mode ActionMailer::Base.delivery_method = :test