Skip to content

Commit

Permalink
beginnings of a beta invitation system in place
Browse files Browse the repository at this point in the history
  • Loading branch information
activefx committed Sep 12, 2008
1 parent a2150a0 commit d15de95
Show file tree
Hide file tree
Showing 28 changed files with 272 additions and 48 deletions.
6 changes: 3 additions & 3 deletions .gitmodules
Expand Up @@ -19,9 +19,6 @@
[submodule "vendor/plugins/recaptcha"]
path = vendor/plugins/recaptcha
url = git://github.com/ambethia/recaptcha.git
[submodule "vendor/plugins/open_id_authentication"]
path = vendor/plugins/open_id_authentication
url = git://github.com/rails/open_id_authentication.git
[submodule "vendor/plugins/footnotes"]
path = vendor/plugins/footnotes
url = git://github.com/activefx/rails-footnotes.git
Expand All @@ -31,3 +28,6 @@
[submodule "vendor/plugins/auto_migrations"]
path = vendor/plugins/auto_migrations
url = git://github.com/pjhyett/auto_migrations.git
[submodule "vendor/plugins/open_id_authentication"]
path = vendor/plugins/open_id_authentication
url = git://github.com/activefx/open_id_authentication.git
3 changes: 2 additions & 1 deletion README
Expand Up @@ -25,6 +25,7 @@ CURRENT FEATURES
- Set roles, activate, enable / disable users
- Member list and public profiles for logged in users
- Activation, with option to resend activation code
- Beta invitation system with easy on/off functionality
- Forgot Password / Reset Password
- Change Password
- Failed login attempts database logging
Expand Down Expand Up @@ -58,7 +59,6 @@ TODO
- Set up admin interface on a subdomain and require a second password
- Better access and permission denied redirects
- Make the ActivationsController "activate" action restful
- Beta invitations
- Integrate user interface plugins / dry form builders
- Move query stats and rows logger to footnotes plugin
- Refactoring, google authentication, facebook connect, oauth, api metering
Expand All @@ -75,6 +75,7 @@ Rails-footnotes by Jose Valim and Duane Johnson is released under the MIT Licens
- http://github.com/activefx/rails-footnotes/tree/master (modified for restful_authentication_tutorial)
Open_id_authentication by David Heinemeier Hansson is released under the MIT License
- http://github.com/rails/open_id_authentication/tree/master
- http://github.com/activefx/open_id_authentication/tree/master (modified for restful_authentication_tutorial)
Permalink_fu by Rick Olson is released under the MIT License
- http://github.com/technoweenie/permalink_fu/tree/master
Query_analyzer by Bob Silva is released under the MIT License
Expand Down
4 changes: 4 additions & 0 deletions app/controllers/application.rb
Expand Up @@ -30,6 +30,10 @@ def nested_layout
"default"
end

def in_beta?
APP_CONFIG['settings']['in_beta']
end

protected

def check_visitor
Expand Down
5 changes: 5 additions & 0 deletions app/controllers/openid_sessions_controller.rb
@@ -1,4 +1,9 @@
class OpenidSessionsController < SessionsController
before_filter :login_prohibited

def index

end

def new
# Display recaptcha only if the number of failed logins have
Expand Down
14 changes: 10 additions & 4 deletions app/controllers/sessions_controller.rb
Expand Up @@ -58,7 +58,9 @@ def open_id_authentication(identity_url_params)
# Pass optional :required and :optional keys to specify what sreg fields you want.
# Be sure to yield registration, a third argument in the #authenticate_with_open_id block.
authenticate_with_open_id(identity_url_params,
:optional => [ :nickname, :email, :fullname]) do |result, identity_url, registration|
:optional => [ :nickname, :email, :fullname],
:invitation_token => params[:invitation_token],
:remember_me => params[:remember_me]) do |result, identity_url, registration, extensions|
case result.status
when :missing
failed_login("Sorry, the OpenID server couldn't be found.", identity_url, true)
Expand All @@ -73,11 +75,12 @@ def open_id_authentication(identity_url_params)
if user = OpenidUser.find_with_identity_url(identity_url)
successful_login(user)
else
@user = OpenidUser.new
@user = OpenidUser.new(:invitation_token => params[:invitation_token])
assign_registration_attributes!(registration, identity_url)
if @user.save
redirect_to root_path
flash[:notice] = "Thanks for signing up! We're sending you an email with your activation code."
flash[:notice] = "Thanks for signing up! "
flash[:notice] += ((in_beta? && @user.emails_match?) ? "You can now log into your account." : "We're sending you an email with your activation code.")
else
flash.now[:error] = "We need some additional details before we can create your account."
render :template => "user/openid_accounts/new"
Expand Down Expand Up @@ -129,7 +132,10 @@ def failed_login(message, login_name, openid = nil)
@remember_me = params[:remember_me]
@openid_identifier = params[:openid_identifier]
@recaptcha = @bad_visitor
if openid
case
when openid && params[:invitation_token]
render :template => 'openid_sessions/index'
when openid
render :template => 'openid_sessions/new'
else
render :action => 'new'
Expand Down
25 changes: 25 additions & 0 deletions app/controllers/user/invitations_controller.rb
@@ -0,0 +1,25 @@
class User::InvitationsController < ApplicationController

def new
@invitation = Invitation.new
end

def create
@invitation = Invitation.new(params[:invitation])
@invitation.sender = current_user
if @invitation.save
if logged_in?
UserMailer.deliver_invitation(@invitation)
flash[:notice] = "Thank you, invitation sent."
redirect_to root_path
else
flash[:notice] = "Thank you, we will notify you when an invitation becomes available."
redirect_to root_path
end
else
render :action => 'new'
end
end

end

6 changes: 4 additions & 2 deletions app/controllers/user/openid_accounts_controller.rb
Expand Up @@ -10,13 +10,15 @@ def create
logout_keeping_session!
@user = OpenidUser.new(:login => params[:user][:login],
:email => params[:user][:email],
:name => params[:user][:name])
:name => params[:user][:name],
:invitation_token => params[:user][:invitation_token])
@user.identity_url = params[:user][:identity_url]
success = @user && @user.save
if success && @user.errors.empty?
session[:identity_url] = nil
redirect_back_or_default('/')
flash[:notice] = "Thanks for signing up! We're sending you an email with your activation code."
flash[:notice] = "Thanks for signing up! "
flash[:notice] += ((in_beta? && @user.emails_match?) ? "You can now log into your account." : "We're sending you an email with your activation code.")
else
flash.now[:error] = "Sorry, we couldn't set up that account. Please correct the errors and try again, or %s."
flash[:error_item] = ["sign up for a regular account", new_user_profile_path]
Expand Down
8 changes: 5 additions & 3 deletions app/controllers/user/profiles_controller.rb
Expand Up @@ -9,7 +9,7 @@ def show

# render new.rhtml
def new
@user = SiteUser.new
@user = SiteUser.new(:invitation_token => params[:invitation_token])
end

def create
Expand All @@ -21,11 +21,13 @@ def create
:email => params[:user][:email],
:name => params[:user][:name],
:password => params[:user][:password],
:password_confirmation => params[:user][:password_confirmation])
:password_confirmation => params[:user][:password_confirmation],
:invitation_token => params[:user][:invitation_token])
success = @user && @user.save
if success && @user.errors.empty?
redirect_back_or_default('/')
flash[:notice] = "Thanks for signing up! We're sending you an email with your activation code."
flash[:notice] = "Thanks for signing up! "
flash[:notice] += ((in_beta? && @user.emails_match?) ? "You can now log into your account." : "We're sending you an email with your activation code.")
else
flash.now[:error] = "We couldn't set up that account, sorry. Please try again, or %s."
flash[:error_item] = ["contact us", contact_site]
Expand Down
12 changes: 12 additions & 0 deletions app/helpers/application_helper.rb
Expand Up @@ -43,4 +43,16 @@ def if_recaptcha?
yield if @recaptcha
end

def in_beta?
APP_CONFIG['settings']['in_beta']
end

def if_in_beta?
yield if in_beta?
end

def unless_in_beta?
yield unless in_beta?
end

end
34 changes: 34 additions & 0 deletions app/models/invitation.rb
@@ -0,0 +1,34 @@
class Invitation < ActiveRecord::Base
belongs_to :sender, :class_name => 'User'
has_one :recipient, :class_name => 'User'

validates_presence_of :email
validate :recipient_is_not_registered
validate :sender_has_invitations, :if => :sender

before_create :generate_token
before_create :decrement_sender_count, :if => :sender

attr_accessible :email

private

def recipient_is_not_registered
errors.add :email, 'is already registered' if User.find_by_email(email)
end

def sender_has_invitations
unless sender.invitation_limit > 0
errors.add_to_base 'You have reached your limit of invitations to send.'
end
end

def generate_token
self.token = User.make_token #Digest::SHA1.hexdigest([Time.now, rand].join)
end

def decrement_sender_count
sender.decrement! :invitation_limit
end

end
2 changes: 1 addition & 1 deletion app/models/user.rb
Expand Up @@ -12,7 +12,7 @@ class User < ActiveRecord::Base
# anything else you want your user to change should be added here.
# Add identity_url if you want users to be able to update their OpenID identity
# Because role_ids is included, never mass assign a User, SiteUser, or OpenidUser
attr_accessible :login, :email, :name, :password, :password_confirmation, :role_ids
attr_accessible :login, :email, :name, :password, :password_confirmation, :role_ids, :invitation_token

def self.member_list(page)
paginate :all,
Expand Down
13 changes: 10 additions & 3 deletions app/models/user_mailer.rb
@@ -1,7 +1,7 @@
class UserMailer < ActionMailer::Base
def signup_notification(user)
setup_email(user)
@subject += 'Please activate your new account'
@subject += 'Please activate your new account.'
@body[:url] = "http://#{APP_CONFIG['settings']['domain']}/activate/#{user.activation_code}"
end

Expand All @@ -13,7 +13,7 @@ def activation(user)

def forgot_password(user)
setup_email(user)
@subject += 'You have requested to change your password'
@subject += 'You have requested to change your password.'
@body[:url] = "http://#{APP_CONFIG['settings']['domain']}/reset_password/#{user.password_reset_code}"
end

Expand All @@ -22,11 +22,18 @@ def reset_password(user)
@subject += 'Your password has been reset.'
end

def invitation(invitation)
setup_email(invitation)
@subject += 'You have been invited to our private beta!'
@body[:url] = "http://#{APP_CONFIG['settings']['domain']}/signup/#{invitation.token}"
invitation.update_attribute(:sent_at, Time.now)
end

protected
def setup_email(user)
@recipients = "#{user.email}"
@from = APP_CONFIG['mail']['sender']
@subject = "[#{APP_CONFIG['settings']['name']}] "
@subject = "#{APP_CONFIG['settings']['name']} - "
@sent_on = Time.now
@body[:user] = user
end
Expand Down
7 changes: 4 additions & 3 deletions app/models/user_observer.rb
@@ -1,12 +1,13 @@
class UserObserver < ActiveRecord::Observer
def after_create(user)
UserMailer.deliver_signup_notification(user)
end

#def after_create(user)
#end

def after_save(user)
UserMailer.deliver_activation(user) if user.recently_activated?
UserMailer.deliver_forgot_password(user) if user.recently_forgot_password?
UserMailer.deliver_reset_password(user) if user.recently_reset_password?
UserMailer.deliver_signup_notification(user) if user.lost_activation_code?
end

end
21 changes: 21 additions & 0 deletions app/views/openid_sessions/index.html.erb
@@ -0,0 +1,21 @@
<h1>Sign up with OpenID</h1>

<% form_tag session_path do -%>
<%= hidden_field_tag 'openid', true %>
<% if_in_beta? do -%>
<p><%= label_tag 'invitation_token', 'Invitation Code' %><br/>
<%= text_field_tag :invitation_token, params[:invitation_token] %>
<span>*we are currently in private beta and an invitation code is required</span></p>
<% end -%>

<p><%= label_tag 'openid_indentifier', 'OpenID Url' %><br/>
<%= text_field_tag "openid_identifier", @openid_identifier %></p>

<% if_recaptcha? do %>
<p>Please enter the following words below.<br/>
<%= recaptcha_tags %></p>
<% end %>

<p><%= submit_tag 'Log in', :disable_with => "Signing in&hellip;" %></p>
<% end -%>
12 changes: 12 additions & 0 deletions app/views/root/index.html.erb
@@ -1 +1,13 @@
<% content_for :side do -%>
<% unless logged_in? -%><% if_in_beta? do -%>
<p>We are currently in private beta. Please enter your email address below and we will let know when an invitation becomes available.</p>
<% uberform_for [:user, Invitation.new] do |f| -%>
<%= f.text_field :email, :label => "Your Email:" %>
<%= f.submit 'Submit' %>
<% end -%>
<% end -%><% end -%>
<% end -%>

<h1>Welcome to my application.</h1>


7 changes: 7 additions & 0 deletions app/views/user/invitations/new.html.erb
@@ -0,0 +1,7 @@
<h1>Send invitation</h1>

<%= error_messages_for :invitation %>
<% uberform_for [:user, @invitation] do |f| %>
<%= f.text_field :email, :label => "Friend's email address:" %>
<%= f.submit "Invite!" %>
<% end %>
29 changes: 14 additions & 15 deletions app/views/user/openid_accounts/new.html.erb
@@ -1,18 +1,17 @@
<h1>Sign up as a new user</h1>
<h1>Sign up as a new OpenID user</h1>

<%= error_messages_for :user %>
<% form_for :user, :url => user_openid_accounts_path do |f| -%>
<%= f.hidden_field :identity_url %>

<p><label for="name">Name (optional)</label><br/>
<%= f.text_field :name %></p>

<p><label for="login">Username</label><br/>
<%= f.text_field :login %></p>

<p><label for="email">Email</label><br/>
<%= f.text_field :email %></p>

<p><%= submit_tag 'Sign up' %></p>
<% form_for :user, :url => user_openid_accounts_path, :builder => Uberkit::Forms::Builder do |f| -%>
<%= f.hidden_field :identity_url %>
<%= f.hidden_field :invitation_token %>
<%= f.text_field :name, :help => "*optional" %>
<%= f.text_field :login, :label => "Username" %>
<% if in_beta? -%>
<%= f.text_field :email,
:help => "*email should match the one your invitation code was sent to, or you will have to activate your account" %>
<% else -%>
<%= f.text_field :email %>
<% end -%>
<%= f.submit 'Sign up' %>
<% end -%>
<%= "#{@token}" %>
17 changes: 15 additions & 2 deletions app/views/user/profiles/new.html.erb
@@ -1,12 +1,25 @@
<h1>Sign up as a new user</h1>
<p>New OpenID users should <%= link_to "log in here", new_openid_session_path %>.</p>

<p>New OpenID users should <%= link_to "log in here",
(beta_openid_signup_path(params[:invitation_token]) || openid_signup_path) %>.</p>

<% @user.password = @user.password_confirmation = nil %>
<%= error_messages_for :user %>
<% form_for :user, :url => user_profiles_path, :builder => Uberkit::Forms::Builder do |f| -%>
<% if_in_beta? do -%>
<%= f.text_field :invitation_token,
:label => "Invitation Code",
:help => "*we are currently in private beta and an invitation code is required" %>
<% end -%>
<%= f.text_field :name, :help => "*optional" %>
<%= f.text_field :login, :label => "Username" %>
<%= f.text_field :email %>
<% if in_beta? -%>
<%= f.text_field :email,
:help => "*email should match the one your invitation code was sent to, or you will have to activate your account" %>
<% else -%>
<%= f.text_field :email %>
<% end -%>
<%= f.password_field :password %>
<%= f.password_field :password_confirmation %>
<%= f.submit "Sign up"%>
Expand Down
4 changes: 4 additions & 0 deletions app/views/user_mailer/invitation.erb
@@ -0,0 +1,4 @@
You have been invited to join our private beta!

<%= @url %>

0 comments on commit d15de95

Please sign in to comment.