tonkapark-clearance 0.6.9.1 → 0.6.9.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.textile +4 -0
- data/README.textile +12 -13
- data/Rakefile +3 -3
- data/app/controllers/clearance/invitations_controller.rb +28 -0
- data/app/controllers/clearance/passwords_controller.rb +1 -1
- data/app/controllers/clearance/sessions_controller.rb +1 -1
- data/app/controllers/clearance/users_controller.rb +4 -3
- data/app/models/clearance_mailer.rb +25 -3
- data/app/views/clearance_mailer/invitation.html.erb +3 -0
- data/app/views/invitations/index.html.erb +20 -0
- data/app/views/invitations/new.html.erb +15 -0
- data/app/views/passwords/edit.html.erb +14 -12
- data/app/views/passwords/new.html.erb +10 -9
- data/app/views/sessions/new.html.erb +1 -26
- data/app/views/users/_user.html.erb +28 -0
- data/app/views/users/new.html.erb +3 -5
- data/clearance.gemspec +12 -3
- data/config/clearance_routes.rb +10 -4
- data/generators/clearance/clearance_generator.rb +30 -3
- data/generators/clearance/templates/README +17 -17
- data/generators/clearance/templates/invitation.rb +4 -0
- data/generators/clearance/templates/migrations/create_invitations.rb +20 -0
- data/generators/clearance/templates/migrations/create_users.rb +2 -0
- data/generators/clearance/templates/migrations/update_invitations.rb +40 -0
- data/generators/clearance/templates/migrations/update_users.rb +3 -1
- data/generators/clearance/templates/user.rb +1 -0
- data/lib/clearance/authentication.rb +1 -1
- data/lib/clearance/invitation.rb +87 -0
- data/lib/clearance/user.rb +26 -2
- data/lib/clearance.rb +1 -0
- metadata +12 -3
- data/app/views/users/_form.html.erb +0 -13
data/CHANGELOG.textile
CHANGED
data/README.textile
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
h1. Clearance
|
2
2
|
|
3
|
-
Rails authentication with email & password
|
3
|
+
Rails authentication with email & password, *and invitations*.
|
4
4
|
|
5
5
|
"We have clearance, Clarence.":http://www.youtube.com/v/mNRXJEE3Nz8
|
6
6
|
|
@@ -15,10 +15,10 @@ Clearance is a Rails engine. It works with versions of Rails greater than 2.3.
|
|
15
15
|
In config/environment.rb:
|
16
16
|
|
17
17
|
<pre>
|
18
|
-
config.gem "
|
18
|
+
config.gem "tonkapark-clearance",
|
19
19
|
:lib => 'clearance',
|
20
20
|
:source => 'http://gems.github.com',
|
21
|
-
:version => '0.6.9'
|
21
|
+
:version => '0.6.9.2'
|
22
22
|
</pre>
|
23
23
|
|
24
24
|
Vendor the gem:
|
@@ -41,17 +41,16 @@ Run the migration:
|
|
41
41
|
@rake db:migrate@
|
42
42
|
|
43
43
|
Define a HOST constant in your environment files.
|
44
|
-
In config/
|
45
|
-
|
46
|
-
@HOST = "localhost"@
|
47
|
-
|
48
|
-
In production.rb it must be the actual host your application is deployed to.
|
49
|
-
The constant is used by mailers to generate URLs in emails.
|
50
|
-
|
51
|
-
In config/environment.rb:
|
52
|
-
|
53
|
-
@DO_NOT_REPLY = "donotreply@example.com"@
|
44
|
+
In config/clearance.yml:
|
54
45
|
|
46
|
+
<pre>
|
47
|
+
production:
|
48
|
+
host: you_production_host.com
|
49
|
+
do_not_reply: do_not_reply@you_production_host.com
|
50
|
+
invitation_subject_line: An Invitation
|
51
|
+
invitation_from_email: invitation@you_production_host.com
|
52
|
+
</pre>
|
53
|
+
|
55
54
|
Define root_url to *something* in your config/routes.rb:
|
56
55
|
|
57
56
|
@map.root :controller => 'home'@
|
data/Rakefile
CHANGED
@@ -53,11 +53,11 @@ task :default => ['test:all', 'test:features']
|
|
53
53
|
|
54
54
|
gem_spec = Gem::Specification.new do |gem_spec|
|
55
55
|
gem_spec.name = "clearance"
|
56
|
-
gem_spec.version = "0.6.9.
|
57
|
-
gem_spec.summary = "Rails authentication with email & password."
|
56
|
+
gem_spec.version = "0.6.9.2"
|
57
|
+
gem_spec.summary = "Rails authentication with email & password and invitations."
|
58
58
|
gem_spec.email = "matt@tonkapark.com"
|
59
59
|
gem_spec.homepage = "http://github.com/tonkapark/clearance"
|
60
|
-
gem_spec.description = "Rails authentication with email & password."
|
60
|
+
gem_spec.description = "Rails authentication with email & password and invitations."
|
61
61
|
gem_spec.authors = ["Dan Croak", "Mike Burns", "Jason Morrison",
|
62
62
|
"Joe Ferris", "Eugene Bolshakov", "Nick Quaranto",
|
63
63
|
"Josh Nichols", "Mike Breen", "Marcel Görner",
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class Clearance::InvitationsController < ApplicationController
|
2
|
+
unloadable
|
3
|
+
|
4
|
+
before_filter :redirect_to_root, :only => [:new, :create], :if => :signed_in?
|
5
|
+
|
6
|
+
def new
|
7
|
+
@invitation = ::Invitation.new(params[:invitation])
|
8
|
+
render :template => 'invitations/new'
|
9
|
+
end
|
10
|
+
|
11
|
+
def create
|
12
|
+
@invitation = ::Invitation.new(params[:invitation])
|
13
|
+
@invitation.sender = current_user
|
14
|
+
if @invitation.save
|
15
|
+
if signed_in?
|
16
|
+
::ClearanceMailer.deliver_invitation(@invitation, signup_url(@invitation.token))
|
17
|
+
flash[:notice] = "Thank you, an invitation has been sent."
|
18
|
+
redirect_to root_url
|
19
|
+
else
|
20
|
+
flash[:notice] = "Thank you for your interest, your invitation has been received."
|
21
|
+
redirect_to root_url
|
22
|
+
end
|
23
|
+
else
|
24
|
+
render :action => 'new'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -4,8 +4,9 @@ class Clearance::UsersController < ApplicationController
|
|
4
4
|
before_filter :redirect_to_root, :only => [:new, :create], :if => :signed_in?
|
5
5
|
filter_parameter_logging :password
|
6
6
|
|
7
|
-
def new
|
8
|
-
@user = ::User.new(params[:
|
7
|
+
def new
|
8
|
+
@user = ::User.new(:invitation_token => params[:invitation_token])
|
9
|
+
@user.email = @user.invitation.recipient_email if @user.invitation
|
9
10
|
render :template => 'users/new'
|
10
11
|
end
|
11
12
|
|
@@ -30,6 +31,6 @@ class Clearance::UsersController < ApplicationController
|
|
30
31
|
end
|
31
32
|
|
32
33
|
def url_after_create
|
33
|
-
|
34
|
+
signin_url
|
34
35
|
end
|
35
36
|
end
|
@@ -1,9 +1,22 @@
|
|
1
1
|
class ClearanceMailer < ActionMailer::Base
|
2
2
|
|
3
|
-
|
3
|
+
def self.config_file
|
4
|
+
File.join(RAILS_ROOT, 'config', 'clearance.yml')
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.config
|
8
|
+
YAML.load(File.read(config_file)).with_indifferent_access[RAILS_ENV]
|
9
|
+
end
|
10
|
+
|
11
|
+
def config
|
12
|
+
self.class.config
|
13
|
+
end
|
14
|
+
|
15
|
+
default_url_options[:host] = config[:host]
|
16
|
+
|
4
17
|
|
5
18
|
def change_password(user)
|
6
|
-
from
|
19
|
+
from config[:do_not_reply]
|
7
20
|
recipients user.email
|
8
21
|
subject I18n.t(:change_password,
|
9
22
|
:scope => [:clearance, :models, :clearance_mailer],
|
@@ -12,12 +25,21 @@ class ClearanceMailer < ActionMailer::Base
|
|
12
25
|
end
|
13
26
|
|
14
27
|
def confirmation(user)
|
15
|
-
from
|
28
|
+
from config[:do_not_reply]
|
16
29
|
recipients user.email
|
17
30
|
subject I18n.t(:confirmation,
|
18
31
|
:scope => [:clearance, :models, :clearance_mailer],
|
19
32
|
:default => "Account confirmation")
|
20
33
|
body :user => user
|
21
34
|
end
|
35
|
+
|
36
|
+
def invitation(invitation, url)
|
37
|
+
subject config[:invitation_subject_line]
|
38
|
+
recipients invitation.recipient_email
|
39
|
+
from config[:invitation_from_email]
|
40
|
+
body :invitation => invitation, :signup_url => url
|
41
|
+
invitation.update_attribute(:invited_at, Time.now)
|
42
|
+
end
|
43
|
+
|
22
44
|
|
23
45
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<h2>Invitations</h2>
|
2
|
+
|
3
|
+
<table>
|
4
|
+
<tr>
|
5
|
+
<th>Recipient</th>
|
6
|
+
<th>Invited By</th>
|
7
|
+
<th>Invited At</th>
|
8
|
+
<th>Redeemed At</th>
|
9
|
+
<th></th>
|
10
|
+
</tr>
|
11
|
+
|
12
|
+
<% @invitations.each do |invite| %>
|
13
|
+
<tr>
|
14
|
+
<td><%= invite.recipient_email %></td>
|
15
|
+
<td><%= invite.invited_at %></td>
|
16
|
+
<td><%= invite.redeemed_at %></td>
|
17
|
+
<td><%= link_to "Redeem Link", signup_url(invite.token) %></td>
|
18
|
+
</tr>
|
19
|
+
<% end %>
|
20
|
+
</table>
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<h2>Request Invitation</h2>
|
2
|
+
|
3
|
+
<% form_for @invitation do |f| %>
|
4
|
+
<%= f.error_messages %>
|
5
|
+
<ul>
|
6
|
+
<li>
|
7
|
+
<%= f.label :recipient_email, "Your Email" %><br />
|
8
|
+
<p class="instructions">This email will be used for your account.</p>
|
9
|
+
<%= f.text_field :recipient_email, {:class => "big"} %>
|
10
|
+
</li>
|
11
|
+
</ul>
|
12
|
+
<p><%= f.submit "Submit" %></p>
|
13
|
+
<% end %>
|
14
|
+
|
15
|
+
|
@@ -9,15 +9,17 @@
|
|
9
9
|
<% form_for(:user,
|
10
10
|
:url => user_password_path(@user, :token => @user.token),
|
11
11
|
:html => { :method => :put }) do |form| %>
|
12
|
-
<
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
12
|
+
<ul>
|
13
|
+
<li>
|
14
|
+
<%= form.label :password, "Choose password" %>
|
15
|
+
<%= form.password_field :password, {:class => "big"} %>
|
16
|
+
</li>
|
17
|
+
<li>
|
18
|
+
<%= form.label :password_confirmation, "Confirm password" %>
|
19
|
+
<%= form.password_field :password_confirmation, {:class => "big"} %>
|
20
|
+
</li>
|
21
|
+
<li class="submit">
|
22
|
+
<%= form.submit "Save this password", :disable_with => "Please wait..." %>
|
23
|
+
</li>
|
24
|
+
</ul>
|
25
|
+
<% end %>
|
@@ -1,15 +1,16 @@
|
|
1
1
|
<h2>Change your password</h2>
|
2
|
-
|
3
2
|
<p>
|
4
3
|
We will email you a link to change your password.
|
5
4
|
</p>
|
6
5
|
|
7
6
|
<% form_for :password, :url => passwords_path do |form| %>
|
8
|
-
<
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
7
|
+
<ul>
|
8
|
+
<li>
|
9
|
+
<%= form.label :email, "Email address" %>
|
10
|
+
<%= form.text_field :email, {:class => "big"} %>
|
11
|
+
</li>
|
12
|
+
<li class="submit">
|
13
|
+
<%= form.submit "Reset password", :disable_with => "Please wait..." %>
|
14
|
+
</li>
|
15
|
+
</ul>
|
16
|
+
<% end %>
|
@@ -1,28 +1,3 @@
|
|
1
1
|
<h2>Sign in</h2>
|
2
2
|
|
3
|
-
|
4
|
-
<div class="text_field">
|
5
|
-
<%= form.label :email %>
|
6
|
-
<%= form.text_field :email %>
|
7
|
-
</div>
|
8
|
-
<div class="text_field">
|
9
|
-
<%= form.label :password %>
|
10
|
-
<%= form.password_field :password %>
|
11
|
-
</div>
|
12
|
-
<div class="text_field">
|
13
|
-
<%= form.check_box :remember_me %>
|
14
|
-
<%= form.label :remember_me %>
|
15
|
-
</div>
|
16
|
-
<div class="submit_field">
|
17
|
-
<%= form.submit "Sign in", :disable_with => "Please wait..." %>
|
18
|
-
</div>
|
19
|
-
<% end %>
|
20
|
-
|
21
|
-
<ul>
|
22
|
-
<li>
|
23
|
-
<%= link_to "Sign up", new_user_path %>
|
24
|
-
</li>
|
25
|
-
<li>
|
26
|
-
<%= link_to "Forgot password?", new_password_path %>
|
27
|
-
</li>
|
28
|
-
</ul>
|
3
|
+
<%= render :partial => 'sessions/form' %>
|
@@ -0,0 +1,28 @@
|
|
1
|
+
|
2
|
+
<% form_for(@user, :html => {:id => "user_form"}) do |form| %>
|
3
|
+
|
4
|
+
<%= form.error_messages %>
|
5
|
+
|
6
|
+
<ul>
|
7
|
+
<li>
|
8
|
+
<label for="user_email">Email <small>We'll keep it a secret.</small></label>
|
9
|
+
<%= form.text_field :email, {:class => "big"} %>
|
10
|
+
</li>
|
11
|
+
|
12
|
+
<li>
|
13
|
+
<%= form.label :password %>
|
14
|
+
<%= form.password_field :password, {:class => "big"} %>
|
15
|
+
</li>
|
16
|
+
|
17
|
+
<li>
|
18
|
+
<%= form.label :password_confirmation, "Confirm password" %>
|
19
|
+
<%= form.password_field :password_confirmation, {:class => "big"} %>
|
20
|
+
</li>
|
21
|
+
|
22
|
+
<% if @user.new_record? %>
|
23
|
+
<%= form.hidden_field :invitation_token %>
|
24
|
+
<% end %>
|
25
|
+
|
26
|
+
<li class="submit"><%= form.submit button_name, :disable_with => 'Please wait...' %> | <% if @user.new_record? %><%= link_to 'Cancel', root_url %><% else %><%= link_to 'Sign In', signin_path %><% end %></li>
|
27
|
+
</ul>
|
28
|
+
<% end %>
|
@@ -1,6 +1,4 @@
|
|
1
|
-
<h2>
|
1
|
+
<h2>Signup</h2>
|
2
|
+
|
3
|
+
<%= render :partial => 'users/user', :locals => {:button_name => "Create my account"} %>
|
2
4
|
|
3
|
-
<% form_for @user do |form| %>
|
4
|
-
<%= render :partial => '/users/form', :object => form %>
|
5
|
-
<%= form.submit 'Sign up', :disable_with => 'Please wait...' %>
|
6
|
-
<% end %>
|
data/clearance.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: clearance
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.9.
|
4
|
+
version: 0.6.9.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dan Croak
|
@@ -28,7 +28,7 @@ date: 2009-07-16 00:00:00 -05:00
|
|
28
28
|
default_executable:
|
29
29
|
dependencies: []
|
30
30
|
|
31
|
-
description: Rails authentication with email & password.
|
31
|
+
description: Rails authentication with email & password and invitations.
|
32
32
|
email: matt@tonkapark.com
|
33
33
|
executables: []
|
34
34
|
|
@@ -53,6 +53,7 @@ files:
|
|
53
53
|
- app/controllers
|
54
54
|
- app/controllers/clearance
|
55
55
|
- app/controllers/clearance/confirmations_controller.rb
|
56
|
+
- app/controllers/clearance/invitations_controller.rb
|
56
57
|
- app/controllers/clearance/passwords_controller.rb
|
57
58
|
- app/controllers/clearance/sessions_controller.rb
|
58
59
|
- app/controllers/clearance/users_controller.rb
|
@@ -62,6 +63,7 @@ files:
|
|
62
63
|
- app/views/clearance_mailer
|
63
64
|
- app/views/clearance_mailer/change_password.html.erb
|
64
65
|
- app/views/clearance_mailer/confirmation.html.erb
|
66
|
+
- app/views/clearance_mailer/invitation.html.erb
|
65
67
|
- app/views/passwords
|
66
68
|
- app/views/passwords/edit.html.erb
|
67
69
|
- app/views/passwords/new.html.erb
|
@@ -69,7 +71,10 @@ files:
|
|
69
71
|
- app/views/sessions/new.html.erb
|
70
72
|
- app/views/users
|
71
73
|
- app/views/users/new.html.erb
|
72
|
-
- app/views/users/
|
74
|
+
- app/views/users/_user.html.erb
|
75
|
+
- app/views/invitations
|
76
|
+
- app/views/invitations/new.html.erb
|
77
|
+
- app/views/invitations/index.html.erb
|
73
78
|
- config/clearance_routes.rb
|
74
79
|
- generators/clearance
|
75
80
|
- generators/clearance/clearance_generator.rb
|
@@ -81,8 +86,11 @@ files:
|
|
81
86
|
- generators/clearance/templates/migrations
|
82
87
|
- generators/clearance/templates/migrations/create_users.rb
|
83
88
|
- generators/clearance/templates/migrations/update_users.rb
|
89
|
+
- generators/clearance/templates/migrations/create_invitations.rb
|
90
|
+
- generators/clearance/templates/migrations/update_invitations.rb
|
84
91
|
- generators/clearance/templates/README
|
85
92
|
- generators/clearance/templates/user.rb
|
93
|
+
- generators/clearance/templates/invitation.rb
|
86
94
|
- generators/clearance/USAGE
|
87
95
|
- generators/clearance_features
|
88
96
|
- generators/clearance_features/clearance_features_generator.rb
|
@@ -104,6 +112,7 @@ files:
|
|
104
112
|
- lib/clearance/extensions/errors.rb
|
105
113
|
- lib/clearance/extensions/rescue.rb
|
106
114
|
- lib/clearance/extensions/routes.rb
|
115
|
+
- lib/clearance/invitation.rb
|
107
116
|
- lib/clearance/user.rb
|
108
117
|
- lib/clearance.rb
|
109
118
|
- shoulda_macros/clearance.rb
|
data/config/clearance_routes.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
ActionController::Routing::Routes.draw do |map|
|
2
2
|
map.resources :passwords,
|
3
3
|
:controller => 'clearance/passwords',
|
4
|
-
:only => [:
|
4
|
+
:only => [:create]
|
5
5
|
|
6
6
|
map.resource :session,
|
7
7
|
:controller => 'clearance/sessions',
|
@@ -17,13 +17,19 @@ ActionController::Routing::Routes.draw do |map|
|
|
17
17
|
:only => [:new, :create]
|
18
18
|
end
|
19
19
|
|
20
|
-
map.
|
20
|
+
map.resources :invitations,
|
21
|
+
:controller => 'clearance/invitations',
|
22
|
+
:only => [:new, :create]
|
23
|
+
|
24
|
+
map.recover_password '/recover_password', :controller =>'clearance/passwords', :action => 'new'
|
25
|
+
|
26
|
+
map.signup '/signup/:invitation_token',
|
21
27
|
:controller => 'clearance/users',
|
22
28
|
:action => 'new'
|
23
|
-
map.
|
29
|
+
map.signin 'login',
|
24
30
|
:controller => 'clearance/sessions',
|
25
31
|
:action => 'new'
|
26
|
-
map.
|
32
|
+
map.logout 'logout',
|
27
33
|
:controller => 'clearance/sessions',
|
28
34
|
:action => 'destroy',
|
29
35
|
:method => :delete
|
@@ -12,6 +12,7 @@ class ClearanceGenerator < Rails::Generator::Base
|
|
12
12
|
user_model = "app/models/user.rb"
|
13
13
|
if File.exists?(user_model)
|
14
14
|
m.insert_into user_model, "include Clearance::User"
|
15
|
+
m.insert_into user_model, "belongs_to :invitation"
|
15
16
|
else
|
16
17
|
m.directory File.join("app", "models")
|
17
18
|
m.file "user.rb", user_model
|
@@ -20,17 +21,35 @@ class ClearanceGenerator < Rails::Generator::Base
|
|
20
21
|
m.directory File.join("test", "factories")
|
21
22
|
m.file "factories.rb", "test/factories/clearance.rb"
|
22
23
|
|
23
|
-
m.migration_template "migrations/#{
|
24
|
+
m.migration_template "migrations/#{user_migration_name}.rb",
|
24
25
|
'db/migrate',
|
25
|
-
:migration_file_name => "clearance_#{
|
26
|
+
:migration_file_name => "clearance_#{user_migration_name}"
|
26
27
|
|
28
|
+
m.directory File.join("config")
|
29
|
+
m.file "clearance.yml", 'config/clearance.yml'
|
30
|
+
|
31
|
+
invitation_model = "app/models/invitation.rb"
|
32
|
+
if File.exists?(invitation_model)
|
33
|
+
m.insert_into invitation_model, "include Clearance::Invitation"
|
34
|
+
m.insert_into invitation_model, "belongs_to :sender, :class_name => 'User', :foreign_key => :sender_id"
|
35
|
+
else
|
36
|
+
m.directory File.join("app", "models")
|
37
|
+
m.file "invitation.rb", invitation_model
|
38
|
+
end
|
39
|
+
|
40
|
+
m.migration_template "migrations/#{invitation_migration_name}.rb",
|
41
|
+
'db/migrate',
|
42
|
+
:migration_file_name => "clearance_#{invitation_migration_name}"
|
43
|
+
|
44
|
+
|
45
|
+
|
27
46
|
m.readme "README"
|
28
47
|
end
|
29
48
|
end
|
30
49
|
|
31
50
|
private
|
32
51
|
|
33
|
-
def
|
52
|
+
def user_migration_name
|
34
53
|
if ActiveRecord::Base.connection.table_exists?(:users)
|
35
54
|
'update_users'
|
36
55
|
else
|
@@ -38,4 +57,12 @@ class ClearanceGenerator < Rails::Generator::Base
|
|
38
57
|
end
|
39
58
|
end
|
40
59
|
|
60
|
+
def invitation_migration_name
|
61
|
+
if ActiveRecord::Base.connection.table_exists?(:invitations)
|
62
|
+
'update_invitations'
|
63
|
+
else
|
64
|
+
'create_invitations'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
41
68
|
end
|
@@ -1,22 +1,22 @@
|
|
1
1
|
|
2
2
|
*******************************************************************************
|
3
|
-
|
3
|
+
|
4
4
|
Ok, enough fancy automatic stuff. Time for some old school monkey copy-pasting.
|
5
|
-
|
6
|
-
1.
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
5
|
+
|
6
|
+
1. A a clearance.yml config file has been created in your config/ directory.
|
7
|
+
|
8
|
+
Use it to configure the host name and sender of clearance generated emails.
|
9
|
+
In production it must be the actual host your application is deployed to.
|
10
|
+
The config is used by mailers to generate URLs in emails.
|
11
|
+
|
12
|
+
It can look like this:
|
13
|
+
|
14
|
+
production:
|
15
|
+
host: you_production_host.com
|
16
|
+
do_not_reply: do_not_reply@you_production_host.com
|
17
|
+
|
18
|
+
2. Define root_url to *something* in your config/routes.rb:
|
19
|
+
|
20
20
|
map.root :controller => 'home'
|
21
|
-
|
21
|
+
|
22
22
|
*******************************************************************************
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class ClearanceCreateInvitations < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table(:invitations) do |t|
|
4
|
+
t.integer :sender_id
|
5
|
+
t.string :recipient_email
|
6
|
+
t.string :token
|
7
|
+
t.datetime :invited_at
|
8
|
+
t.datetime :redeemed_at
|
9
|
+
t.timestamps
|
10
|
+
end
|
11
|
+
|
12
|
+
add_index :invitations, [:id, :token]
|
13
|
+
add_index :invitations, :recipient_email
|
14
|
+
add_index :invitations, :token
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.down
|
18
|
+
drop_table :invitations
|
19
|
+
end
|
20
|
+
end
|
@@ -7,6 +7,8 @@ class ClearanceCreateUsers < ActiveRecord::Migration
|
|
7
7
|
t.string :token, :limit => 128
|
8
8
|
t.datetime :token_expires_at
|
9
9
|
t.boolean :email_confirmed, :default => false, :null => false
|
10
|
+
t.integer :invitation_id
|
11
|
+
t.integer :invitation_limit
|
10
12
|
t.timestamps
|
11
13
|
end
|
12
14
|
|
@@ -0,0 +1,40 @@
|
|
1
|
+
class ClearanceUpdateUsers < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
<%
|
4
|
+
existing_columns = ActiveRecord::Base.connection.columns(:users).collect { |each| each.name }
|
5
|
+
columns = [
|
6
|
+
[:sender_id, 't.integer :sender_id'],
|
7
|
+
[:recipient_email, 't.string :recipient_email, :limit => 128'],
|
8
|
+
[:token, 't.string :token, :limit => 128'],
|
9
|
+
[:invited_at, 't.datetime :invited_at'],
|
10
|
+
[:redeemed_at, 't.datetime :redeemed_at']
|
11
|
+
].delete_if {|c| existing_columns.include?(c.first.to_s)}
|
12
|
+
-%>
|
13
|
+
change_table(:users) do |t|
|
14
|
+
<% columns.each do |c| -%>
|
15
|
+
<%= c.last %>
|
16
|
+
<% end -%>
|
17
|
+
end
|
18
|
+
|
19
|
+
<%
|
20
|
+
existing_indexes = ActiveRecord::Base.connection.indexes(:users)
|
21
|
+
index_names = existing_indexes.collect { |each| each.name }
|
22
|
+
new_indexes = [
|
23
|
+
[:index_invitations_on_id_and_token, 'add_index :invitations, [:id, :token]'],
|
24
|
+
[:index_invitations_on_email, 'add_index :invitations, :recipient_email'],
|
25
|
+
[:index_invitations_on_token, 'add_index :invitations, :token']
|
26
|
+
].delete_if { |each| index_names.include?(each.first.to_s) }
|
27
|
+
-%>
|
28
|
+
<% new_indexes.each do |each| -%>
|
29
|
+
<%= each.last %>
|
30
|
+
<% end -%>
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.down
|
34
|
+
change_table(:invitations) do |t|
|
35
|
+
<% unless columns.empty? -%>
|
36
|
+
t.remove <%= columns.collect { |each| ":#{each.first}" }.join(',') %>
|
37
|
+
<% end -%>
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -8,7 +8,9 @@ class ClearanceUpdateUsers < ActiveRecord::Migration
|
|
8
8
|
[:salt, 't.string :salt, :limit => 128'],
|
9
9
|
[:token, 't.string :token, :limit => 128'],
|
10
10
|
[:token_expires_at, 't.datetime :token_expires_at'],
|
11
|
-
[:email_confirmed, 't.boolean :email_confirmed, :default => false, :null => false']
|
11
|
+
[:email_confirmed, 't.boolean :email_confirmed, :default => false, :null => false'],
|
12
|
+
[:invitation_id, 't.integer :invitation_id'],
|
13
|
+
[:invitation_limit, 't.integer :invitation_limit']
|
12
14
|
].delete_if {|c| existing_columns.include?(c.first.to_s)}
|
13
15
|
-%>
|
14
16
|
change_table(:users) do |t|
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Clearance
|
2
|
+
module Invitation
|
3
|
+
|
4
|
+
def self.included(model)
|
5
|
+
model.extend(ClassMethods)
|
6
|
+
|
7
|
+
model.send(:include, InstanceMethods)
|
8
|
+
model.send(:include, AttrAccessible)
|
9
|
+
model.send(:include, AttrAccessor)
|
10
|
+
model.send(:include, Validations)
|
11
|
+
model.send(:include, Callbacks)
|
12
|
+
end
|
13
|
+
|
14
|
+
module AttrAccessible
|
15
|
+
def self.included(model)
|
16
|
+
model.class_eval do
|
17
|
+
attr_accessible :recipient_email
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module AttrAccessor
|
23
|
+
def self.included(model)
|
24
|
+
model.class_eval do
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module Validations
|
31
|
+
def self.included(model)
|
32
|
+
model.class_eval do
|
33
|
+
validates_presence_of :recipient_email
|
34
|
+
validates_format_of :recipient_email, :with => %r{.+@.+\..+}
|
35
|
+
|
36
|
+
validate :recipient_is_not_registered
|
37
|
+
validate :sender_has_invitations, :if => :sender
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
module Callbacks
|
43
|
+
def self.included(model)
|
44
|
+
model.class_eval do
|
45
|
+
before_create :generate_token
|
46
|
+
before_create :decrement_sender_count, :if => :sender
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
module InstanceMethods
|
52
|
+
|
53
|
+
def redeemed!
|
54
|
+
self.redeemed_at = Time.now.utc
|
55
|
+
self.save!
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.find_redeemable(invitation_code)
|
59
|
+
self.find(:first, :conditions => {:redeemed_at => nil, :token => invitation_code})
|
60
|
+
end
|
61
|
+
|
62
|
+
protected
|
63
|
+
|
64
|
+
def recipient_is_not_registered
|
65
|
+
errors.add :recipient_email, 'is already registered' if ::User.find_by_email(recipient_email)
|
66
|
+
end
|
67
|
+
|
68
|
+
def sender_has_invitations
|
69
|
+
unless sender.invitation_limit > 0
|
70
|
+
errors.add_to_base 'You have reached your limit of invitations to send.'
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def generate_token
|
75
|
+
self.token = Digest::SHA1.hexdigest([Time.now, rand].join)
|
76
|
+
end
|
77
|
+
|
78
|
+
def decrement_sender_count
|
79
|
+
sender.decrement! :invitation_limit
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
module ClassMethods
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
data/lib/clearance/user.rb
CHANGED
@@ -16,7 +16,7 @@ module Clearance
|
|
16
16
|
module AttrAccessible
|
17
17
|
def self.included(model)
|
18
18
|
model.class_eval do
|
19
|
-
attr_accessible :email, :password, :password_confirmation
|
19
|
+
attr_accessible :email, :password, :password_confirmation, :invitation_token
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
@@ -38,6 +38,9 @@ module Clearance
|
|
38
38
|
|
39
39
|
validates_presence_of :password, :if => :password_required?
|
40
40
|
validates_confirmation_of :password, :if => :password_required?
|
41
|
+
|
42
|
+
validates_presence_of :invitation_id, :message => 'is required', :if => Proc.new { |u| u.new_record?}
|
43
|
+
validates_uniqueness_of :invitation_id, :if => Proc.new { |u| u.new_record?}
|
41
44
|
end
|
42
45
|
end
|
43
46
|
end
|
@@ -45,7 +48,8 @@ module Clearance
|
|
45
48
|
module Callbacks
|
46
49
|
def self.included(model)
|
47
50
|
model.class_eval do
|
48
|
-
before_save :initialize_salt, :encrypt_password, :initialize_token
|
51
|
+
before_save :initialize_salt, :encrypt_password, :initialize_token, :downcase_email
|
52
|
+
before_create :set_invitation_limit, :redeemed
|
49
53
|
end
|
50
54
|
end
|
51
55
|
end
|
@@ -89,6 +93,14 @@ module Clearance
|
|
89
93
|
clear_token if valid?
|
90
94
|
save
|
91
95
|
end
|
96
|
+
|
97
|
+
def invitation_token
|
98
|
+
invitation.token if invitation
|
99
|
+
end
|
100
|
+
|
101
|
+
def invitation_token=(token)
|
102
|
+
self.invitation = ::Invitation.find_by_token(token)
|
103
|
+
end
|
92
104
|
|
93
105
|
protected
|
94
106
|
|
@@ -130,6 +142,18 @@ module Clearance
|
|
130
142
|
self.token = encrypt("--#{token_expires_at}--#{password}--")
|
131
143
|
save(false)
|
132
144
|
end
|
145
|
+
|
146
|
+
def downcase_email
|
147
|
+
self.email = email.to_s.downcase
|
148
|
+
end
|
149
|
+
|
150
|
+
def set_invitation_limit
|
151
|
+
self.invitation_limit = 5
|
152
|
+
end
|
153
|
+
|
154
|
+
def redeemed
|
155
|
+
invitation.redeemed! if invitation
|
156
|
+
end
|
133
157
|
end
|
134
158
|
|
135
159
|
module ClassMethods
|
data/lib/clearance.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tonkapark-clearance
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.9.
|
4
|
+
version: 0.6.9.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dan Croak
|
@@ -28,7 +28,7 @@ date: 2009-07-15 22:00:00 -07:00
|
|
28
28
|
default_executable:
|
29
29
|
dependencies: []
|
30
30
|
|
31
|
-
description: Rails authentication with email & password.
|
31
|
+
description: Rails authentication with email & password and invitations.
|
32
32
|
email: matt@tonkapark.com
|
33
33
|
executables: []
|
34
34
|
|
@@ -53,6 +53,7 @@ files:
|
|
53
53
|
- app/controllers
|
54
54
|
- app/controllers/clearance
|
55
55
|
- app/controllers/clearance/confirmations_controller.rb
|
56
|
+
- app/controllers/clearance/invitations_controller.rb
|
56
57
|
- app/controllers/clearance/passwords_controller.rb
|
57
58
|
- app/controllers/clearance/sessions_controller.rb
|
58
59
|
- app/controllers/clearance/users_controller.rb
|
@@ -62,6 +63,7 @@ files:
|
|
62
63
|
- app/views/clearance_mailer
|
63
64
|
- app/views/clearance_mailer/change_password.html.erb
|
64
65
|
- app/views/clearance_mailer/confirmation.html.erb
|
66
|
+
- app/views/clearance_mailer/invitation.html.erb
|
65
67
|
- app/views/passwords
|
66
68
|
- app/views/passwords/edit.html.erb
|
67
69
|
- app/views/passwords/new.html.erb
|
@@ -69,7 +71,10 @@ files:
|
|
69
71
|
- app/views/sessions/new.html.erb
|
70
72
|
- app/views/users
|
71
73
|
- app/views/users/new.html.erb
|
72
|
-
- app/views/users/
|
74
|
+
- app/views/users/_user.html.erb
|
75
|
+
- app/views/invitations
|
76
|
+
- app/views/invitations/new.html.erb
|
77
|
+
- app/views/invitations/index.html.erb
|
73
78
|
- config/clearance_routes.rb
|
74
79
|
- generators/clearance
|
75
80
|
- generators/clearance/clearance_generator.rb
|
@@ -81,8 +86,11 @@ files:
|
|
81
86
|
- generators/clearance/templates/migrations
|
82
87
|
- generators/clearance/templates/migrations/create_users.rb
|
83
88
|
- generators/clearance/templates/migrations/update_users.rb
|
89
|
+
- generators/clearance/templates/migrations/create_invitations.rb
|
90
|
+
- generators/clearance/templates/migrations/update_invitations.rb
|
84
91
|
- generators/clearance/templates/README
|
85
92
|
- generators/clearance/templates/user.rb
|
93
|
+
- generators/clearance/templates/invitation.rb
|
86
94
|
- generators/clearance/USAGE
|
87
95
|
- generators/clearance_features
|
88
96
|
- generators/clearance_features/clearance_features_generator.rb
|
@@ -104,6 +112,7 @@ files:
|
|
104
112
|
- lib/clearance/extensions/errors.rb
|
105
113
|
- lib/clearance/extensions/rescue.rb
|
106
114
|
- lib/clearance/extensions/routes.rb
|
115
|
+
- lib/clearance/invitation.rb
|
107
116
|
- lib/clearance/user.rb
|
108
117
|
- lib/clearance.rb
|
109
118
|
- shoulda_macros/clearance.rb
|
@@ -1,13 +0,0 @@
|
|
1
|
-
<%= form.error_messages %>
|
2
|
-
<div class="text_field">
|
3
|
-
<%= form.label :email %>
|
4
|
-
<%= form.text_field :email %>
|
5
|
-
</div>
|
6
|
-
<div class="password_field">
|
7
|
-
<%= form.label :password %>
|
8
|
-
<%= form.password_field :password %>
|
9
|
-
</div>
|
10
|
-
<div class="password_field">
|
11
|
-
<%= form.label :password_confirmation, "Confirm password" %>
|
12
|
-
<%= form.password_field :password_confirmation %>
|
13
|
-
</div>
|