Permalink
Browse files

finished beta invitation system

  • Loading branch information...
1 parent d15de95 commit 25c338e61088cc0867bd647fee0de8df4744f2d5 activefx committed Sep 14, 2008
View
8 README
@@ -25,7 +25,8 @@ 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
+ - Beta invitation system
+ - easy on/off functionality, add/remove invites, send emails to pending users
- Forgot Password / Reset Password
- Change Password
- Failed login attempts database logging
@@ -45,6 +46,8 @@ CURRENT FEATURES
- auto_migrations
- Testing
- rspec, rspec_rails
+ - Gems
+ - ruby-openid-2.1.2
KNOWN ISSUES
- View and Layout notes for the rails-footnotes plugin do not work due to changes to ActionView::Base
@@ -56,7 +59,6 @@ KNOWN ISSUES
TODO
- Fix known issues
- Full rSpec test suite
- - Set up admin interface on a subdomain and require a second password
- Better access and permission denied redirects
- Make the ActivationsController "activate" action restful
- Integrate user interface plugins / dry form builders
@@ -113,6 +115,8 @@ Exception Logger:
- http://railscasts.com/episodes/104
Beta Invitations:
- http://railscasts.com/episodes/124-beta-invitations
+ar_mailer gem:
+ - http://www.ameravant.com/posts/sending-tons-of-emails-in-ruby-on-rails-with-ar_mailer
Configuration File:
- http://railscasts.com/episodes/85-yaml-configuration-file
- https://peepcode.com/products/draft-rails-code-review-pdf
@@ -0,0 +1,22 @@
+class Admin::InviteActionsController < ApplicationController
+ before_filter :login_required
+ require_role :admin
+
+ # Show users waiting for an invitation code
+ def index
+ @users = Invitation.pending_users(params[:page])
+ end
+
+ # Add invitations to all users
+ def create
+ if User.add_to_invitation_limit(params[:add_invites].to_i)
+ flash[:notice] = "Invitation limit updated."
+ redirect_to admin_users_path
+ else
+ flash.now[:error] = "There was a problem updating the invitation limit."
+ render :action => 'index'
+ end
+ end
+
+end
+
@@ -0,0 +1,37 @@
+class Admin::InvitesController < ApplicationController
+ before_filter :login_required
+ require_role :admin
+
+ def index
+ end
+
+ # Remove invitations from all current users
+ def create
+ if params[:remove_invites]
+ User.remove_all_invitations
+ flash[:notice] = "Invitation limit updated."
+ redirect_to admin_users_path
+ else
+ flash.now[:error] = "There was a problem updating the invitation limit."
+ render :action => 'index'
+ end
+ end
+
+ # Edit a user's invitation limit
+ def edit
+ @user = User.find_by_login(params[:id])
+ end
+
+ def update
+ @user = User.find_by_login(params[:id])
+ @user.invitation_limit = params[:user][:invitation_limit]
+ if @user.save
+ flash[:notice] = "Invitation limit updated."
+ redirect_to :action => 'edit', :id => params[:id]
+ else
+ flash.now[:error] = "There was a problem updating the invitation limit."
+ render :action => 'edit'
+ end
+ end
+
+end
@@ -0,0 +1,16 @@
+class Admin::MailingsController < ApplicationController
+ before_filter :login_required
+ require_role :admin
+
+ # Send invite emails
+ def create
+ if Invitation.send_to_pending_users(params[:limit].to_i)
+ flash[:notice] = "Sending invitations."
+ redirect_to admin_invites_path
+ else
+ flash[:error] = "There was a problem sending the invitations."
+ redirect_to admin_invites_path
+ end
+ end
+
+end
@@ -60,7 +60,7 @@ def open_id_authentication(identity_url_params)
authenticate_with_open_id(identity_url_params,
:optional => [ :nickname, :email, :fullname],
:invitation_token => params[:invitation_token],
- :remember_me => params[:remember_me]) do |result, identity_url, registration, extensions|
+ :remember_me => params[:remember_me]) do |result, identity_url, registration|
case result.status
when :missing
failed_login("Sorry, the OpenID server couldn't be found.", identity_url, true)
@@ -7,7 +7,7 @@ def activate
if user = User.find_with_activation_code(params[:activation_code])
user.activate!
flash[:notice] = "Signup complete! Please sign in to continue."
- redirect_to login_path
+ (user.user_type == "SiteUser") ? (redirect_to login_path) : (redirect_to login_with_openid_path)
else
logger.warn "Invalid activation code from #{request.remote_ip} at #{Time.now.utc}"
flash[:error] = "We couldn't find a user with that activation code, please check your email and try again, or %s."
@@ -24,11 +24,11 @@ def activate
end
# Enter email address to resend activation
- def edit
+ def new
end
# Resend activation action
- def update
+ def create
begin
if !params[:email].blank? && User.send_new_activation_code(params[:email])
flash[:notice] = "A new activation code has been sent to your email address."
@@ -55,4 +55,8 @@ def unless_in_beta?
yield unless in_beta?
end
+ def if_invites_available?
+ yield if in_beta? and logged_in? and (current_user.invitation_limit > 0)
+ end
+
end
View
@@ -2,7 +2,12 @@ class Invitation < ActiveRecord::Base
belongs_to :sender, :class_name => 'User'
has_one :recipient, :class_name => 'User'
- validates_presence_of :email
+ validates_presence_of :email
+ validates_uniqueness_of :email, :message => '^This email address has already been submitted'
+ validates_length_of :email, :within => 6..100
+ validates_format_of :email, :with => Authentication.email_regex,
+ :message => Authentication.bad_email_message
+
validate :recipient_is_not_registered
validate :sender_has_invitations, :if => :sender
@@ -11,6 +16,30 @@ class Invitation < ActiveRecord::Base
attr_accessible :email
+ def self.pending_users(page)
+ paginate :all,
+ :per_page => 50, :page => page,
+ :conditions => ['sent_at IS NULL'],
+ :order => 'created_at ASC'
+ end
+
+ def self.users_for_mailing(limit)
+ return nil if ((limit > 20) || (limit < 1))
+ find :all,
+ :conditions => ['sent_at IS NULL'],
+ :order => 'created_at ASC',
+ :limit => limit
+ end
+
+ def self.send_to_pending_users(limit)
+ if users = Invitation.users_for_mailing(limit)
+ users.each do |user|
+ UserMailer.deliver_invitation(user)
+ end
+ return true
+ end
+ end
+
private
def recipient_is_not_registered
@@ -24,7 +53,7 @@ def sender_has_invitations
end
def generate_token
- self.token = User.make_token #Digest::SHA1.hexdigest([Time.now, rand].join)
+ self.token = User.make_token
end
def decrement_sender_count
@@ -3,11 +3,11 @@ class UserObserver < ActiveRecord::Observer
#def after_create(user)
#end
- def after_save(user)
+ 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?
+ UserMailer.deliver_signup_notification(user) if (user.recently_created? || user.lost_activation_code?)
end
end
@@ -2,5 +2,8 @@
<ul>
<li><%= link_to "Administer Users (#{@users})", admin_users_path %></li>
+ <% if_in_beta? do -%>
+ <li><%= link_to 'Beta Invitations', admin_invites_path %></li>
+ <% end -%>
<li><%= link_to "Logged Exceptions (#{@exceptions})", logged_exceptions_path %></li>
</ul>
@@ -0,0 +1,19 @@
+<h1>Users waiting to sign up (<%=h @users.total_entries %>)</h1>
+ <p><%= link_to 'User Administration', admin_users_path %> |
+<%= link_to 'Manage Beta Invitations for All Users', admin_invites_path %></p>
+
+<p>
+<% form_tag url_for(:controller => 'mailings', :action => 'create') do -%>
+Send invitations to users who have signed up on the front page for the private beta. Recommended maximum of 20 (enforced in the find) sent emails at a time, if you consistantly use that many or need more you should implement the ar_mailer gem or a background process.<br/>
+ Number of invitations to send (max 20): <%= text_field_tag 'limit' %><br/>
+ <%= submit_tag 'Send invitations' %>
+<% end -%>
+</p>
+
+<ul>
+ <% for user in @users -%>
+ <li><%=h user.email %></li>
+ <% end -%>
+</ul>
+
+<%= will_paginate @users %>
@@ -0,0 +1,9 @@
+<h1>Edit invitations for <%= @user.login %></h1>
+<p><%= link_to 'Back to user administration', admin_users_path %>.</p>
+<%= error_messages_for :user %>
+<% form_for :user, :url => admin_invite_path(@user),
+ :html => { :method => :put },
+ :builder => Uberkit::Forms::Builder do |f| -%>
+ <%= f.text_field :invitation_limit %>
+ <%= f.submit "Update" %>
+<% end -%>
@@ -0,0 +1,25 @@
+<h1>Invitations</h1>
+<p><%= link_to 'User Administration', admin_users_path %> |
+<%= link_to 'Pending Beta Users', admin_invite_actions_path %></p>
+<h2>Add invitations to all users</h2>
+
+<% form_tag url_for(:controller => 'invite_actions', :action => 'create') do -%>
+Add number of invites to users: <%= text_field_tag 'add_invites' %>
+ <%= submit_tag 'Update invite limits' %>
+<% end -%>
+
+<h2>Remove invitations from all users</h2>
+
+<% form_tag url_for(:action => 'create') do -%>
+Reset all invitation limits to zero. Users with invitation codes will still be able to sign up and have the default number of invitations availabe, unless you also set new_user_invite_limit to zero in the config file.<br/>
+ Check for confirmation: <%= check_box_tag 'remove_invites' %> Remove all invites?<br/>
+ <%= submit_tag 'Reset invite limits' %>
+<% end -%>
+
+<h2>Send invitations to pending beta users</h2>
+
+<% form_tag url_for(:controller => 'mailings', :action => 'create') do -%>
+Send invitations to users who have signed up on the front page for the private beta. Recommended maximum of 20 (enforced in the find) sent emails at a time, if you consistantly use that many or need more you should implement the ar_mailer gem or a background process.<br/>
+ Number of invitations to send (max 20): <%= text_field_tag 'limit' %><br/>
+ <%= submit_tag 'Send invitations' %>
+<% end -%>
@@ -1,6 +1,7 @@
<h1>Roles for <%=h @user.login %></h1>
<% form_for @user, :url => { :action => "update", :id => @user } do |f| -%>
-<%= f.hidden_field :created_at %>
+<%= hidden_field_tag "user[created_at]", @user.created_at %>
+
<ul>
<% for role in @roles %>
<li><%= check_box_tag "user[role_ids][]", role.id, @user.roles.include?(role) %> <%= role.name %></li>
@@ -16,6 +16,9 @@
<% end %>
</td>
<td><%= link_to 'edit roles', admin_user_roles_path(user) %></td>
+ <% if_in_beta? do -%>
+ <td><%=h user.invitation_limit %> <%= link_to 'edit', edit_admin_invite_path(user) %></td>
+ <% end -%>
</tr>
@@ -1,11 +1,18 @@
<h1>All Users</h1>
+<% if_in_beta? do -%>
+ <p><%= link_to 'Manage Beta Invitations for All Users', admin_invites_path %> |
+<%= link_to 'Pending Beta Users', admin_invite_actions_path %></p>
+<% end -%>
<table>
<tr>
<th>Login</th>
<th>Email</th>
<th>Active?</th>
<th>Enabled?</th>
<th>Roles</th>
+ <% if_in_beta? do -%>
+ <th>Invitations</th>
+ <% end -%>
</tr>
<%= render :partial => 'user', :collection => @users %>
</table>
@@ -1,5 +1,5 @@
<h1>Log in with OpenID</h1>
-
+<p><%= link_to "Login with Username and Password", login_path %></p>
<% form_tag session_path do -%>
<%= hidden_field_tag 'openid', true %>
@@ -1,5 +1,6 @@
<% content_for :side do -%>
<% unless logged_in? -%><% if_in_beta? do -%>
+ <%= error_messages_for :invitation %>
<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:" %>
@@ -1,7 +1,7 @@
<h1>Log in with your account</h1>
-
+<p><%= link_to "Login with OpenID", login_with_openid_path %></p>
<% form_tag session_path do -%>
-<p><%= label_tag 'login' %><br />
+<p><%= label_tag 'login', "Username" %><br />
<%= text_field_tag 'login', @login %></p>
<p><%= label_tag 'password' %><br/>
@@ -1,6 +1,9 @@
<% if logged_in? -%>
<div id="user-bar-greeting">Logged in as: <%= link_to_current_user :content_method => :login %> |
<%= link_to 'My Account', user_profile_path(current_user), { :title => "My account" } %> |
+ <% if_invites_available? do -%>
+ <%= link_to "Invite Friends (#{current_user.invitation_limit})", new_user_invitation_path %> |
+ <% end -%>
<%= link_to "Log Out", logout_path, { :title => "Log out" } %> </div>
<% else -%>
<div id="user-bar-greeting">Log in with:
@@ -1,5 +1,5 @@
<h1>Resend activation</h1>
-<% form_tag url_for(:action => 'update') do %>
+<% form_tag user_activations_path do %>
What is the email address used to create your account?<br />
<%= text_field_tag :email, "", :size => 50 %><br />
<%= submit_tag 'Resend activation code' %>
@@ -1,5 +1,9 @@
<h1>My Profile: <%=h @user.login %> <%= link_to '(edit)', edit_user_profile_path(current_user) %></h1>
-<p>Joined on: <%= @user.created_at.to_s(:long) %></p>
+<% if_invites_available? do -%>
+ <p>You have <%=h @user.invitation_limit %> invitations left. <%= link_to "Invite friends to join",
+ new_user_invitation_path %>.</p>
+<% end -%>
+<p>Joined on: <%=h @user.created_at.to_s(:long) %></p>
<p>Name: <%=h @user.name %></p>
<p>Username: <%=h @user.login %></p>
<p>Email: <%=h @user.email %></p>
View
@@ -8,6 +8,7 @@ development:
admin_email: yourpersonalemail@example.com
in_beta: true
new_user_invite_limit: 5
+ max_user_invite_limit: 100
rest_auth:
site_key: e587f9d09baa59c920b9ee97ac70f58b3c51356c
stretches: 10
@@ -16,7 +17,7 @@ development:
port: 25
domain: yourapplication.com
authentication: :login
- user_name: emailaccount@yourapplication.com
+ user_name: email_account_username_or_address@yourapplication.com
password: emailaccountpassword
sender: donotreply@yourapplication.com
recaptcha:
Oops, something went wrong.

0 comments on commit 25c338e

Please sign in to comment.