diff --git a/Gemfile b/Gemfile
index f5a1d3c16..23f73e8bd 100644
--- a/Gemfile
+++ b/Gemfile
@@ -25,6 +25,8 @@ gem 'eventmachine'
gem 'amqp'
gem 'logging-rails', :require => 'logging/rails'
gem 'tire'
+gem 'sendgrid'
+gem 'recaptcha'
group :development, :test do
gem 'rspec-rails', '2.11.0'
diff --git a/app/controllers/api_users_controller.rb b/app/controllers/api_users_controller.rb
index 2a3ef1ebe..3fba6553a 100644
--- a/app/controllers/api_users_controller.rb
+++ b/app/controllers/api_users_controller.rb
@@ -1,6 +1,6 @@
-class ApiUsersController < ApplicationController
+class ApiUsersController < ApiController
- before_filter :signed_in_user, only: [:index, :edit, :update, :delete,
+ before_filter :signed_in_user, only: [:index, :show, :edit, :update, :delete,
:friend_request_index, :friend_request_show,
:friend_request_create, :friend_request_update,
:friend_index, :friend_destroy]
@@ -8,24 +8,54 @@ class ApiUsersController < ApplicationController
respond_to :json
def index
- @users = User.paginate(page: params[:page])
+ # don't return users that aren't yet confirmed
+ @users = User.where('email_confirmed=TRUE').paginate(page: params[:page])
end
def show
- @user = User.find(params[:id])
+ # don't return users that aren't yet confirmed
+ @user = User.where('email_confirmed=TRUE').find(params[:id])
end
def create
- @user = User.save(params)
-
- # check for errors
- if @user.errors.nil? || @user.errors.size == 0
- respond_with @user, responder: ApiResponder, :location => api_user_detail_url(@user)
+ UserManager.active_record_transaction do |user_manager|
+ # sends email to email account for confirmation
+ @user = user_manager.signup(params[:name],
+ params[:email],
+ params[:password],
+ params[:password_confirmation],
+ params[:city],
+ params[:state],
+ params[:country],
+ params[:instruments],
+ ApplicationHelper.base_uri(request) + "/confirm")
- else
- raise ActiveRecord::Rollback
- response.status = :unprocessable_entity
- respond_with @user
+ # check recaptcha; if any errors seen, contribute it to the model
+ verify_recaptcha(:model => @user, :message => "recaptcha")
+
+ # check for errors
+ unless @user.errors.any?
+ render :json => {}, :status => :ok # an empty response, but 200 OK
+ else
+ raise ActiveRecord::Rollback
+ response.status = :unprocessable_entity
+ respond_with @user, responder: ApiResponder
+ end
+ end
+ end
+
+ def signup_confirm
+ UserManager.active_record_transaction do |user_manager|
+
+ @user = user_manager.signup_confirm(params[:signup_token])
+
+ unless @user.errors.any?
+ respond_with @user, responder: ApiResponder, :location => api_user_detail_url(@user)
+ else
+ raise ActiveRecord::Rollback
+ response.status = :unprocessable_entity
+ respond_with @user, responder: ApiResponder
+ end
end
end
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 46988a7f2..d6bea549a 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -1,9 +1,10 @@
class UsersController < ApplicationController
- before_filter :signed_in_user,
+ before_filter :signed_in_user,
only: [:index, :edit, :update, :destroy]
before_filter :correct_user, only: [:edit, :update]
before_filter :admin_user, only: :destroy
+
def index
@users = User.paginate(page: params[:page])
end
@@ -17,16 +18,57 @@ class UsersController < ApplicationController
end
def create
- @user = User.new(params[:jam_ruby_user])
- if @user.save
- sign_in @user
- flash[:success] = "Welcome to Jamkazam!"
- redirect_to @user
- else
+
+ @user = User.new
+
+ # check recaptcha; if any errors seen, contribute it to the model
+ unless verify_recaptcha(:model => @user, :message => "recaptcha")
+ # let the template render errors on the user model
render 'new'
+ else
+ # sends email to email account for confirmation
+ @user = UserManager.new.signup(params[:jam_ruby_user][:name],
+ params[:jam_ruby_user][:email],
+ params[:jam_ruby_user][:password],
+ params[:jam_ruby_user][:password_confirmation],
+ params[:jam_ruby_user][:city],
+ params[:jam_ruby_user][:state],
+ params[:jam_ruby_user][:country],
+ params[:jam_ruby_user][:instruments],
+ ApplicationHelper.base_uri(request) + "/confirm")
+
+
+ # check for errors
+ if @user.errors.any?
+ # render any @user.errors on error
+ render 'new'
+ else
+ # if success, redirect to 'email_sent' page
+ flash[:success] = "Please check your email and confirm your signup"
+ redirect_to :email_sent
+ end
end
end
+ def email_sent
+
+ end
+
+ def signup_confirm
+ begin
+ @user = UserManager.new.signup_confirm(params[:signup_token])
+ rescue ActiveRecord::RecordNotFound
+ @user = nil
+ end
+
+ unless @user.nil? || @user.errors.any?
+ sign_in @user
+ redirect_to :client
+ end
+
+ # let errors fall through to signup_confirm.html.erb
+ end
+
def edit
end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 9d88e495a..88fe36dae 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -9,4 +9,8 @@ module ApplicationHelper
"#{base_title} | #{page_title}"
end
end
+
+ def self.base_uri(request)
+ (request.ssl? ? "https://" : "http://") + request.host_with_port
+ end
end
diff --git a/app/views/api_users/show.rabl b/app/views/api_users/show.rabl
index a4589cac9..e4bfc855f 100644
--- a/app/views/api_users/show.rabl
+++ b/app/views/api_users/show.rabl
@@ -1,6 +1,6 @@
object @user
-attributes :id, :name, :city, :state, :country, :email, :online, :photo_url, :friend_count, :follower_count, :following_count
+attributes :id, :name, :city, :state, :country, :online, :photo_url, :friend_count, :follower_count, :following_count
unless @user.friends.nil? || @user.friends.size == 0
child :friends => :friends do
diff --git a/app/views/api_users/signup_confirm.rabl b/app/views/api_users/signup_confirm.rabl
new file mode 100644
index 000000000..e7df79f18
--- /dev/null
+++ b/app/views/api_users/signup_confirm.rabl
@@ -0,0 +1,3 @@
+object @user
+
+extends "api_users/show"
\ No newline at end of file
diff --git a/app/views/users/email_sent.html.erb b/app/views/users/email_sent.html.erb
new file mode 100644
index 000000000..83d199210
--- /dev/null
+++ b/app/views/users/email_sent.html.erb
@@ -0,0 +1,8 @@
+<% provide(:title, 'Confirmation Email Sent') %>
+
Confirm Email Next
+
+
\ No newline at end of file
diff --git a/app/views/users/new.html.erb b/app/views/users/new.html.erb
index 30f15fb1a..aa648c7b1 100644
--- a/app/views/users/new.html.erb
+++ b/app/views/users/new.html.erb
@@ -11,12 +11,23 @@
<%= f.label :email %>
<%= f.text_field :email %>
+ <%= f.label :city %>
+ <%= f.text_field :city %>
+
+ <%= f.label :state %>
+ <%= f.text_field :state %>
+
+ <%= f.label :country %>
+ <%= f.text_field :country %>
+
<%= f.label :password %>
<%= f.password_field :password %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation %>
+ <%= recaptcha_tags %>
+
<%= f.submit "Create my account", class: "btn btn-large btn-primary" %>
<% end %>
diff --git a/app/views/users/signup_confirm.html.erb b/app/views/users/signup_confirm.html.erb
new file mode 100644
index 000000000..d36e92765
--- /dev/null
+++ b/app/views/users/signup_confirm.html.erb
@@ -0,0 +1,8 @@
+<% provide(:title, 'Signup Confirmation') %>
+Signup Confirmation Failure
+
+
+
+ Unable to confirm registration email
+
+
\ No newline at end of file
diff --git a/config/initializers/email.rb b/config/initializers/email.rb
new file mode 100644
index 000000000..818dc6e67
--- /dev/null
+++ b/config/initializers/email.rb
@@ -0,0 +1,11 @@
+ActionMailer::Base.raise_delivery_errors = true
+ActionMailer::Base.delivery_method = Rails.env == "test" ? :test : :smtp
+ActionMailer::Base.smtp_settings = {
+ :address => "smtp.sendgrid.net",
+ :port => 587,
+ :domain => "www.jamkazam.com",
+ :authentication => :plain,
+ :user_name => "jamkazam",
+ :password => "jamjamblueberryjam",
+ :enable_starttls_auto => true
+}
\ No newline at end of file
diff --git a/config/initializers/recaptcha.rb b/config/initializers/recaptcha.rb
new file mode 100644
index 000000000..86767ff86
--- /dev/null
+++ b/config/initializers/recaptcha.rb
@@ -0,0 +1,17 @@
+# this gem turns recaptcha verification off during tests by default.
+# The public key/private keys shown below valid for all jamkazam.com domains
+# note that all recaptcha keys work on localhost/127.0.0.1
+# the keys are created at https://www.google.com/recaptcha/admin/create
+Recaptcha.configure do |config|
+ # created using seth@jamkazam.com; can't see way to delegate
+ config.public_key = '6Let8dgSAAAAAFheKGWrs6iaq_hIlPOZ2f3Bb56B'
+ config.private_key = '6Let8dgSAAAAAJzFxL9w2QR5auxjk0ol1_xAtOGO'
+
+ # other config options available with this gem:
+ #nonssl_api_server_url,
+ #ssl_api_server_url,
+ #verify_url,
+ #skip_verify_env,
+ #proxy,
+ #handle_timeouts_gracefully
+end
\ No newline at end of file
diff --git a/config/routes.rb b/config/routes.rb
index c861a0482..52318f41a 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -14,6 +14,7 @@ SampleApp::Application.routes.draw do
root to: 'static_pages#home'
match '/signup', to: 'users#new'
+ match '/email_sent', to: 'users#email_sent'
match '/signin', to: 'sessions#new'
match '/signout', to: 'sessions#destroy', via: :delete
@@ -23,6 +24,8 @@ SampleApp::Application.routes.draw do
match '/client', to: 'clients#index'
+ match '/confirm/:signup_token', to: 'users#signup_confirm'
+
scope '/api' do
# music sessions
match '/sessions/:id/participants' => 'api_music_sessions#participant_create', :via => :post
@@ -42,6 +45,7 @@ SampleApp::Application.routes.draw do
match '/users' => 'api_users#create', :via => :post
match '/users/:id' => 'api_users#update', :via => :post
match '/users/:id' => 'api_users#destroy', :via => :delete
+ match '/users/confirm/:signup_token' => 'api_users#signup_confirm', :via => :post, :as => 'api_signup_confirmation'
# friend requests
match '/users/:id/friend_requests' => 'api_users#friend_request_index', :via => :get
diff --git a/spec/factories.rb b/spec/factories.rb
index e93d3c3e7..dc5552132 100644
--- a/spec/factories.rb
+++ b/spec/factories.rb
@@ -4,6 +4,7 @@ FactoryGirl.define do
sequence(:email) { |n| "person_#{n}@example.com"}
password "foobar"
password_confirmation "foobar"
+ email_confirmed true
factory :admin do
admin true
@@ -26,4 +27,8 @@ FactoryGirl.define do
factory :invitation, :class => JamRuby::Invitation do
end
+
+ factory :band, :class => JamRuby::Band do
+
+ end
end
diff --git a/spec/requests/search_api_spec.rb b/spec/requests/search_api_spec.rb
index 8ad5cc24c..9dfc98f89 100644
--- a/spec/requests/search_api_spec.rb
+++ b/spec/requests/search_api_spec.rb
@@ -28,10 +28,8 @@ describe "Search API ", :type => :api do
it "simple search" do
User.delete_search_index # so that the user created before the test and logged in doesn't show up
User.create_search_index
- @musician = User.save(name: "Peach", email: "user@example.com",
- password: "foobar", password_confirmation: "foobar", musician: true)
- @fan = User.save(name: "Peach Peach", email: "fan@example.com",
- password: "foobar", password_confirmation: "foobar", musician: false)
+ @musician = FactoryGirl.create(:user, name: "Peach", email: "user@example.com", musician: true)
+ @fan = FactoryGirl.create(:user, name: "Peach Peach", email: "fan@example.com", musician: false)
@band = Band.save(name: "Peach pit", website: "www.bands.com", biography: "zomg we rock")
@band2 = Band.save(name: "Peach", website: "www.bands2.com", biography: "zomg we rock")
User.search_index.refresh
diff --git a/spec/requests/user_pages_spec.rb b/spec/requests/user_pages_spec.rb
index eb1f7f461..c9d8d122b 100644
--- a/spec/requests/user_pages_spec.rb
+++ b/spec/requests/user_pages_spec.rb
@@ -114,6 +114,9 @@ describe "User pages" do
before do
fill_in "Name", with: "Example User"
fill_in "Email", with: "user@example.com"
+ fill_in "City", with: "Austin"
+ fill_in "State", with: "TX"
+ fill_in "Country", with: "USA"
fill_in "Password", with: "foobar"
fill_in "Confirmation", with: "foobar"
end
@@ -127,9 +130,8 @@ describe "User pages" do
let(:user) { User.find_by_email('user@example.com') }
- it { should have_selector('title', text: user.name) }
- it { should have_selector('div.alert.alert-success', text: 'Welcome') }
- it { should have_link('Sign out') }
+ it { should have_selector('title', text: "Confirmation Email Sent") }
+ it { should have_selector('div.alert.alert-success', text: 'check your email') }
end
end
end
diff --git a/spec/requests/users_api_spec.rb b/spec/requests/users_api_spec.rb
new file mode 100644
index 000000000..a854936cc
--- /dev/null
+++ b/spec/requests/users_api_spec.rb
@@ -0,0 +1,50 @@
+require 'spec_helper'
+
+describe "User API ", :type => :api do
+
+ include Rack::Test::Methods
+
+ subject { page }
+
+ def login(user)
+ post '/sessions', "session[email]" => user.email, "session[password]" => user.password
+ rack_mock_session.cookie_jar["remember_token"].should == user.remember_token
+ end
+
+ describe "profile page" do
+ let(:user) { FactoryGirl.create(:user) }
+
+ before(:each) do
+ UserMailer.deliveries.clear
+ end
+
+ it "successful signup" do
+ post '/api/users.json', { :name => "user1", :email => "user1@jamkazam.com", :password => "jam123", :password_confirmation => "jam123",
+ :city => "Austin", :state => "TX", :country => "United States" }.to_json, "CONTENT_TYPE" => 'application/json'
+ last_response.status.should == 200
+ last_response.body.should == "{}"
+
+ created_user = User.find_by_email("user1@jamkazam.com")
+
+ # login as another user, and verify that this user can't be seen yet because email_confired=false
+ login(user)
+ get "/api/users/#{created_user.id}.json"
+ last_response.status.should == 404
+
+ # we should see one email created as a result of creating the user
+ UserMailer.deliveries.length.should == 1
+ email = UserMailer.deliveries[0]
+
+ # check that the signup url is in the email
+ email.html_part.body.include?(created_user.signup_token).should be_true
+
+ post "/api/users/confirm/#{created_user.signup_token}.json", "{}"
+ last_response.status.should == 201
+
+ get last_response.headers["Location"] + ".json"
+ last_response.status.should == 200
+ found_user = JSON.parse(last_response.body)
+ found_user["id"].should == created_user.id
+ end
+ end
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 0232d68a7..664966e7a 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -4,11 +4,15 @@ require 'spork'
#require 'spork/ext/ruby-debug'
require 'active_record'
+require 'action_mailer'
require 'jam_db'
require 'spec_db'
include JamRuby
+# put ActionMailer into test mode
+ActionMailer::Base.delivery_method = :test
+
# recreate test database and migrate it
db_config = YAML::load(File.open('config/database.yml'))["test"]
SpecDb::recreate_database(db_config)