devise_invitable 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +29 -10
- data/app/controllers/devise/invitations_controller.rb +2 -2
- data/app/views/devise/invitations/edit.html.erb +2 -2
- data/app/views/devise/invitations/new.html.erb +6 -6
- data/config/locales/en.yml +6 -0
- data/lib/devise_invitable/controllers/registrations.rb +39 -0
- data/lib/devise_invitable/controllers/registrations.rb~ +36 -0
- data/lib/devise_invitable/controllers/url_helpers.rb +1 -1
- data/lib/devise_invitable/mailer.rb +1 -1
- data/lib/devise_invitable/model.rb +111 -22
- data/lib/devise_invitable/model.rb~ +29 -15
- data/lib/devise_invitable/rails.rb +3 -3
- data/lib/devise_invitable/routes.rb +2 -2
- data/lib/devise_invitable/version.rb +1 -1
- data/lib/devise_invitable.rb +13 -6
- data/lib/generators/devise_invitable/install_generator.rb +12 -10
- data/lib/generators/devise_invitable/templates/simple_form_for/invitations/edit.html.erb +11 -0
- data/lib/generators/devise_invitable/templates/simple_form_for/invitations/new.html.erb +11 -0
- data/lib/generators/devise_invitable/views_generator.rb +27 -5
- metadata +35 -16
data/README.rdoc
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
= DeviseInvitable
|
|
2
|
+
{<img src="http://travis-ci.org/scambra/devise_invitable.png"/>}[http://travis-ci.org/scambra/devise_invitable]
|
|
2
3
|
|
|
3
4
|
It adds support to devise[http://github.com/plataformatec/devise] for send invitations by email (it requires to be authenticated) and accept the invitation setting the password.
|
|
4
5
|
|
|
5
6
|
DeviseInvitable currently only support Rails 3, if you want to use it with Rails 2.3 you must install version {0.2.3}[http://rubygems.org/gems/devise_invitable/versions/0.2.3]
|
|
6
7
|
|
|
7
|
-
== Installation
|
|
8
|
+
== Installation
|
|
8
9
|
|
|
9
10
|
Install DeviseInvitable gem, it will also install dependencies (such as devise and warden):
|
|
10
11
|
|
|
@@ -12,8 +13,8 @@ Install DeviseInvitable gem, it will also install dependencies (such as devise a
|
|
|
12
13
|
|
|
13
14
|
Add DeviseInvitable to your Gemfile (and Devise if you weren't using them):
|
|
14
15
|
|
|
15
|
-
gem 'devise', '
|
|
16
|
-
gem 'devise_invitable', '~> 0.
|
|
16
|
+
gem 'devise', '>= 2.0.0'
|
|
17
|
+
gem 'devise_invitable', '~> 1.0.0'
|
|
17
18
|
|
|
18
19
|
=== Automatic installation
|
|
19
20
|
|
|
@@ -50,7 +51,7 @@ Add t.invitable to your Devise model migration:
|
|
|
50
51
|
t.string :invited_by_type
|
|
51
52
|
...
|
|
52
53
|
end
|
|
53
|
-
add_index :users, :invitation_token
|
|
54
|
+
add_index :users, :invitation_token, :unique => true
|
|
54
55
|
|
|
55
56
|
or for a model that already exists, define a migration to add DeviseInvitable to your model:
|
|
56
57
|
|
|
@@ -65,9 +66,9 @@ or for a model that already exists, define a migration to add DeviseInvitable to
|
|
|
65
66
|
end
|
|
66
67
|
|
|
67
68
|
# Allow null encrypted_password
|
|
68
|
-
|
|
69
|
+
change_column :users, :encrypted_password, :string, :null => true
|
|
69
70
|
# Allow null password_salt (add it if you are using Devise's encryptable module)
|
|
70
|
-
|
|
71
|
+
change_column :users, :password_salt, :string, :null => true
|
|
71
72
|
|
|
72
73
|
== Model configuration
|
|
73
74
|
|
|
@@ -89,12 +90,14 @@ or directly as parameters to the <tt>devise</tt> method:
|
|
|
89
90
|
|
|
90
91
|
* invitation_limit: The number of invitations users can send. The default value of nil means users can send as many invites as they want. A setting of 0 means they can't send invitations. A setting n > 0 means they can send n invitations.
|
|
91
92
|
|
|
92
|
-
* invite_key: The key to be used to check existing users when sending an invitation.
|
|
93
|
+
* invite_key: The key to be used to check existing users when sending an invitation. You can use multiple keys. This value must be a hash with the invite key as hash keys, and regexp to validate format as values. If you don't to validate the key you can set nil as validation format. The default value is looking for users by email and validating with Devise.email_regexp {:email => Devise.email_regexp}.
|
|
93
94
|
|
|
94
95
|
* validate_on_invite: force a record to be valid before being actually invited.
|
|
95
96
|
|
|
96
97
|
* resend_invitation: resend invitation if user with invited status is invited again. Enabled by default.
|
|
97
98
|
|
|
99
|
+
* invited_by_class_name: The class name of the inviting model. If this is nil, polymorphic association is used.
|
|
100
|
+
|
|
98
101
|
For more details, see <tt>config/initializers/devise.rb</tt> (after you invoked the "devise_invitable:install" generator described above).
|
|
99
102
|
|
|
100
103
|
== Configuring views
|
|
@@ -152,6 +155,15 @@ You can add :skip_invitation to attributes hash if skip_invitation is added to a
|
|
|
152
155
|
User.invite!(:email => "new_user@example.com", :name => "John Doe", :skip_invitation => true)
|
|
153
156
|
# => the record will be created, but the invitation email will not be sent
|
|
154
157
|
|
|
158
|
+
You can send an invitation to an existing user if your workflow creates them separately:
|
|
159
|
+
|
|
160
|
+
user = User.find(42)
|
|
161
|
+
user.invite!(current_user) # current user is optional to set the invited_by attribute
|
|
162
|
+
|
|
163
|
+
You can also set <tt>invited_by</tt> when using the <tt>invite!</tt> class method:
|
|
164
|
+
|
|
165
|
+
User.invite!({:email => "new_user@example.com"}, current_user) # current_user will be set as invited_by
|
|
166
|
+
|
|
155
167
|
=== Accept an invitation
|
|
156
168
|
|
|
157
169
|
To accept an invitation with a token use the <tt>accept_invitation!</tt> class method. <tt>:invitation_token</tt> must be present in the parameters hash. You can also include other attributes in the hash.
|
|
@@ -165,11 +177,18 @@ A callback event is fired before and after an invitation is accepted (User#accep
|
|
|
165
177
|
after_invitation_accepted :email_invited_by
|
|
166
178
|
|
|
167
179
|
def email_invited_by
|
|
168
|
-
# ...
|
|
180
|
+
# ...
|
|
169
181
|
end
|
|
170
182
|
|
|
171
183
|
The callbacks support all options and arguments available to the standard callbacks provided by AR.
|
|
172
184
|
|
|
185
|
+
=== Scopes
|
|
186
|
+
|
|
187
|
+
A pair of scopes to find those users that have accepted, and those that have not accepted, invitations are defined:
|
|
188
|
+
|
|
189
|
+
User.invitation_accepted # => returns all Users for whom the invitation_accepted_at attribute is not nil
|
|
190
|
+
User.invitation_not_accepted # => returns all Users for whom the invitation_accepted_at attribute is nil
|
|
191
|
+
|
|
173
192
|
== Integration in a Rails application
|
|
174
193
|
|
|
175
194
|
Since the invitations controller take care of all the creation/acceptation of an invitation, in most cases you wouldn't call the <tt>invite!</tt> and <tt>accept_invitation!</tt> methods directly.
|
|
@@ -262,7 +281,7 @@ http://github.com/scambra/devise_invitable/contributors
|
|
|
262
281
|
Special thanks to rymai[http://github.com/rymai] for the Rails 3 support, his fork was a great help.
|
|
263
282
|
|
|
264
283
|
== Note on Patches/Pull Requests
|
|
265
|
-
|
|
284
|
+
|
|
266
285
|
* Fork the project.
|
|
267
286
|
* Make your feature addition or bug fix.
|
|
268
287
|
* Add tests for it. This is important so I don't break it in a future version unintentionally.
|
|
@@ -271,4 +290,4 @@ Special thanks to rymai[http://github.com/rymai] for the Rails 3 support, his fo
|
|
|
271
290
|
|
|
272
291
|
== Copyright
|
|
273
292
|
|
|
274
|
-
Copyright (c)
|
|
293
|
+
Copyright (c) 2012 Sergio Cambra. See LICENSE for details.
|
|
@@ -13,7 +13,7 @@ class Devise::InvitationsController < DeviseController
|
|
|
13
13
|
|
|
14
14
|
# POST /resource/invitation
|
|
15
15
|
def create
|
|
16
|
-
self.resource = resource_class.invite!(
|
|
16
|
+
self.resource = resource_class.invite!(resource_params, current_inviter)
|
|
17
17
|
|
|
18
18
|
if resource.errors.empty?
|
|
19
19
|
set_flash_message :notice, :send_instructions, :email => self.resource.email
|
|
@@ -35,7 +35,7 @@ class Devise::InvitationsController < DeviseController
|
|
|
35
35
|
|
|
36
36
|
# PUT /resource/invitation
|
|
37
37
|
def update
|
|
38
|
-
self.resource = resource_class.accept_invitation!(
|
|
38
|
+
self.resource = resource_class.accept_invitation!(resource_params)
|
|
39
39
|
|
|
40
40
|
if resource.errors.empty?
|
|
41
41
|
set_flash_message :notice, :updated
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<h2
|
|
1
|
+
<h2><%= t 'devise.invitations.edit.header' %></h2>
|
|
2
2
|
|
|
3
3
|
<%= form_for resource, :as => resource_name, :url => invitation_path(resource_name), :html => { :method => :put } do |f| %>
|
|
4
4
|
<%= devise_error_messages! %>
|
|
@@ -10,5 +10,5 @@
|
|
|
10
10
|
<p><%= f.label :password_confirmation %><br />
|
|
11
11
|
<%= f.password_field :password_confirmation %></p>
|
|
12
12
|
|
|
13
|
-
<p><%= f.submit "
|
|
13
|
+
<p><%= f.submit t("devise.invitations.edit.submit_button") %></p>
|
|
14
14
|
<% end %>
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
<h2
|
|
1
|
+
<h2><%= t "devise.invitations.new.header" %></h2>
|
|
2
2
|
|
|
3
3
|
<%= form_for resource, :as => resource_name, :url => invitation_path(resource_name), :html => {:method => :post} do |f| %>
|
|
4
4
|
<%= devise_error_messages! %>
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
<% resource.class.invite_key_fields.each do |field| -%>
|
|
7
|
+
<p><%= f.label field %><br />
|
|
8
|
+
<%= f.text_field field %></p>
|
|
9
|
+
<% end -%>
|
|
8
10
|
|
|
9
|
-
<p><%= f.submit "
|
|
11
|
+
<p><%= f.submit t("devise.invitations.new.submit_button") %></p>
|
|
10
12
|
<% end %>
|
|
11
|
-
|
|
12
|
-
<%= link_to "Home", after_sign_in_path_for(resource_name) %><br />
|
data/config/locales/en.yml
CHANGED
|
@@ -5,6 +5,12 @@ en:
|
|
|
5
5
|
invitation_token_invalid: 'The invitation token provided is not valid!'
|
|
6
6
|
updated: 'Your password was set successfully. You are now signed in.'
|
|
7
7
|
no_invitations_remaining: "No invitations remaining"
|
|
8
|
+
new:
|
|
9
|
+
header: "Send invitation"
|
|
10
|
+
submit_button: "Send an invitation"
|
|
11
|
+
edit:
|
|
12
|
+
header: "Set your password"
|
|
13
|
+
submit_button: "Set my password"
|
|
8
14
|
mailer:
|
|
9
15
|
invitation_instructions:
|
|
10
16
|
subject: 'Invitation instructions'
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module DeviseInvitable::Controllers::Registrations
|
|
2
|
+
def self.included(controller)
|
|
3
|
+
controller.send :around_filter, :keep_invitation_info, :only => :create
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
protected
|
|
7
|
+
|
|
8
|
+
def destroy_if_previously_invited
|
|
9
|
+
hash = params[resource_name]
|
|
10
|
+
if hash && hash[:email]
|
|
11
|
+
resource = resource_class.where(:email => hash[:email], :encrypted_password => '').first
|
|
12
|
+
if resource
|
|
13
|
+
@invitation_info = Hash[resource.invitation_fields.map {|field|
|
|
14
|
+
[field, resource[field]]
|
|
15
|
+
}]
|
|
16
|
+
resource.destroy
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def keep_invitation_info
|
|
22
|
+
resource_invitable = resource_class.devise_modules.include?(:invitable)
|
|
23
|
+
destroy_if_previously_invited if resource_invitable
|
|
24
|
+
yield
|
|
25
|
+
reset_invitation_info if resource_invitable
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def reset_invitation_info
|
|
29
|
+
# Restore info about the last invitation (for later reference)
|
|
30
|
+
# Reset the invitation_info only, if invited_by_id is still nil at this stage:
|
|
31
|
+
resource = resource_class.where(:email => params[resource_name][:email], :invited_by_id => nil).first
|
|
32
|
+
if resource && @invitation_info
|
|
33
|
+
resource.invitation_fields.each do |field|
|
|
34
|
+
resource[field] = @invitation_info[field]
|
|
35
|
+
end
|
|
36
|
+
resource.save!
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module DeviseInvitable::Controllers::Registrations
|
|
2
|
+
def self.included(controller)
|
|
3
|
+
controller.send :around_filter, :destroy_if_previously_invited, :only => :create
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
protected
|
|
7
|
+
|
|
8
|
+
def destroy_if_previously_invited
|
|
9
|
+
invitation_info = {}
|
|
10
|
+
|
|
11
|
+
hash = params[resource_name]
|
|
12
|
+
if resource_class.modules.include?(:invitable) && hash && hash[:email]
|
|
13
|
+
resource = resource_class.where(:email => hash[:email], :encrypted_password => '').first
|
|
14
|
+
if resource
|
|
15
|
+
invitation_info[:invitation_sent_at] = resource[:invitation_sent_at]
|
|
16
|
+
invitation_info[:invited_by_id] = resource[:invited_by_id]
|
|
17
|
+
invitation_info[:invited_by_type] = resource[:invited_by_type]
|
|
18
|
+
resource.destroy
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# execute the action (create)
|
|
23
|
+
yield
|
|
24
|
+
# Note that the after_filter is executed at THIS position !
|
|
25
|
+
|
|
26
|
+
# Restore info about the last invitation (for later reference)
|
|
27
|
+
# Reset the invitation_info only, if invited_by_id is still nil at this stage:
|
|
28
|
+
resource = resource_class.where(:email => hash[:email], :invited_by_id => nil).first
|
|
29
|
+
if resource
|
|
30
|
+
resource[:invitation_sent_at] = invitation_info[:invitation_sent_at]
|
|
31
|
+
resource[:invited_by_id] = invitation_info[:invited_by_id]
|
|
32
|
+
resource[:invited_by_type] = invitation_info[:invited_by_type]
|
|
33
|
+
resource.save!
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require 'active_support/deprecation'
|
|
2
|
+
|
|
1
3
|
module Devise
|
|
2
4
|
module Models
|
|
3
5
|
# Invitable is responsible for sending invitation emails.
|
|
@@ -13,7 +15,7 @@ module Devise
|
|
|
13
15
|
#
|
|
14
16
|
# Examples:
|
|
15
17
|
#
|
|
16
|
-
# User.find(1).
|
|
18
|
+
# User.find(1).invited_to_sign_up? # => true/false
|
|
17
19
|
# User.invite!(:email => 'someone@example.com') # => send invitation
|
|
18
20
|
# User.accept_invitation!(:invitation_token => '...') # => accept invitation with a token
|
|
19
21
|
# User.find(1).accept_invitation! # => accept invitation
|
|
@@ -22,40 +24,102 @@ module Devise
|
|
|
22
24
|
extend ActiveSupport::Concern
|
|
23
25
|
|
|
24
26
|
attr_accessor :skip_invitation
|
|
27
|
+
attr_accessor :completing_invite
|
|
25
28
|
|
|
26
29
|
included do
|
|
27
|
-
include ::DeviseInvitable::Inviter
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
include ::DeviseInvitable::Inviter
|
|
31
|
+
if Devise.invited_by_class_name
|
|
32
|
+
belongs_to :invited_by, :class_name => Devise.invited_by_class_name
|
|
33
|
+
else
|
|
34
|
+
belongs_to :invited_by, :polymorphic => true
|
|
35
|
+
end
|
|
36
|
+
|
|
30
37
|
include ActiveSupport::Callbacks
|
|
31
38
|
define_callbacks :invitation_accepted
|
|
32
|
-
|
|
39
|
+
|
|
33
40
|
attr_writer :skip_password
|
|
41
|
+
|
|
42
|
+
scope :invitation_not_accepted, where(:invitation_accepted_at => nil)
|
|
43
|
+
if defined?(Mongoid) && self < Mongoid::Document
|
|
44
|
+
scope :invitation_accepted, where(:invitation_accepted_at.ne => nil)
|
|
45
|
+
else
|
|
46
|
+
scope :invitation_accepted, where(arel_table[:invitation_accepted_at].not_eq(nil))
|
|
47
|
+
|
|
48
|
+
[:before_invitation_accepted, :after_invitation_accepted].each do |callback_method|
|
|
49
|
+
send callback_method do
|
|
50
|
+
notify_observers callback_method
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def self.required_fields(klass)
|
|
57
|
+
fields = [:invitation_token, :invitation_sent_at, :invitation_accepted_at,
|
|
58
|
+
:invitation_limit, :invited_by_id, :invited_by_type]
|
|
59
|
+
if Devise.invited_by_class_name
|
|
60
|
+
fields -= :invited_by_type
|
|
61
|
+
end
|
|
62
|
+
fields
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def invitation_fields
|
|
66
|
+
fields = [:invitation_sent_at, :invited_by_id, :invited_by_type]
|
|
67
|
+
if Devise.invited_by_class_name
|
|
68
|
+
fields -= :invited_by_type
|
|
69
|
+
end
|
|
70
|
+
fields
|
|
34
71
|
end
|
|
35
72
|
|
|
36
73
|
# Accept an invitation by clearing invitation token and and setting invitation_accepted_at
|
|
37
74
|
# Confirms it if model is confirmable
|
|
38
75
|
def accept_invitation!
|
|
39
|
-
|
|
76
|
+
self.invitation_accepted_at = Time.now.utc
|
|
77
|
+
if self.invited_to_sign_up? && self.valid?
|
|
40
78
|
run_callbacks :invitation_accepted do
|
|
41
79
|
self.invitation_token = nil
|
|
42
|
-
self.invitation_accepted_at = Time.now.utc if respond_to? :"invitation_accepted_at="
|
|
43
80
|
self.save(:validate => false)
|
|
44
81
|
end
|
|
45
82
|
end
|
|
46
83
|
end
|
|
47
84
|
|
|
85
|
+
# Verifies whether a user has accepted an invitation (or is accepting it), or was never invited
|
|
86
|
+
def accepting_or_not_invited?
|
|
87
|
+
ActiveSupport::Deprecation.warn "accepting_or_not_invited? is deprecated and will be removed from DeviseInvitable 1.1.0 (use accepted_or_not_invited? instead)"
|
|
88
|
+
accepted_or_not_invited?
|
|
89
|
+
end
|
|
90
|
+
|
|
48
91
|
# Verifies whether a user has been invited or not
|
|
49
|
-
def
|
|
92
|
+
def invited_to_sign_up?
|
|
50
93
|
persisted? && invitation_token.present?
|
|
51
94
|
end
|
|
52
95
|
|
|
96
|
+
# Verifies whether a user accepted an invitation (or is accepting it)
|
|
97
|
+
def invitation_accepted?
|
|
98
|
+
invitation_accepted_at.present?
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Verifies whether a user has accepted an invitation (or is accepting it), or was never invited
|
|
102
|
+
def accepted_or_not_invited?
|
|
103
|
+
invitation_accepted? || !invited_to_sign_up?
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def invited?
|
|
107
|
+
ActiveSupport::Deprecation.warn "invited? is deprecated and will be removed from DeviseInvitable 1.1.0 (use invited_to_sign_up? instead)"
|
|
108
|
+
invited_to_sign_up?
|
|
109
|
+
end
|
|
110
|
+
|
|
53
111
|
# Reset invitation token and send invitation again
|
|
54
|
-
def invite!
|
|
55
|
-
was_invited =
|
|
112
|
+
def invite!(invited_by = nil)
|
|
113
|
+
was_invited = invited_to_sign_up?
|
|
56
114
|
self.skip_confirmation! if self.new_record? && self.respond_to?(:skip_confirmation!)
|
|
57
115
|
generate_invitation_token if self.invitation_token.nil?
|
|
58
116
|
self.invitation_sent_at = Time.now.utc
|
|
117
|
+
self.invited_by = invited_by if invited_by
|
|
118
|
+
|
|
119
|
+
# Call these before_validate methods since we aren't validating on save
|
|
120
|
+
self.downcase_keys if self.new_record? && self.respond_to?(:downcase_keys)
|
|
121
|
+
self.strip_whitespace if self.new_record? && self.respond_to?(:strip_whitespace)
|
|
122
|
+
|
|
59
123
|
if save(:validate => false)
|
|
60
124
|
self.invited_by.decrement_invitation_limit! if !was_invited and self.invited_by.present?
|
|
61
125
|
deliver_invitation unless @skip_invitation
|
|
@@ -66,19 +130,26 @@ module Devise
|
|
|
66
130
|
# invited, we need to calculate if the invitation time has not expired
|
|
67
131
|
# for this user, in other words, if the invitation is still valid.
|
|
68
132
|
def valid_invitation?
|
|
69
|
-
|
|
133
|
+
invited_to_sign_up? && invitation_period_valid?
|
|
70
134
|
end
|
|
71
135
|
|
|
72
136
|
# Only verify password when is not invited
|
|
73
137
|
def valid_password?(password)
|
|
74
|
-
super unless
|
|
138
|
+
super unless invited_to_sign_up?
|
|
75
139
|
end
|
|
76
|
-
|
|
140
|
+
|
|
77
141
|
def reset_password!(new_password, new_password_confirmation)
|
|
78
142
|
super
|
|
79
143
|
accept_invitation!
|
|
80
144
|
end
|
|
81
145
|
|
|
146
|
+
def invite_key_valid?
|
|
147
|
+
return true unless self.class.invite_key.is_a? Hash # FIXME: remove this line when deprecation is removed
|
|
148
|
+
self.class.invite_key.all? do |key, regexp|
|
|
149
|
+
regexp.nil? || self.send(key).try(:match, regexp)
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
82
153
|
protected
|
|
83
154
|
# Overriding the method in Devise's :validatable module so password is not required on inviting
|
|
84
155
|
def password_required?
|
|
@@ -87,7 +158,7 @@ module Devise
|
|
|
87
158
|
|
|
88
159
|
# Deliver the invitation email
|
|
89
160
|
def deliver_invitation
|
|
90
|
-
|
|
161
|
+
send_devise_notification(:invitation_instructions)
|
|
91
162
|
end
|
|
92
163
|
|
|
93
164
|
# Checks if the invitation for the user is within the limit time.
|
|
@@ -120,6 +191,16 @@ module Devise
|
|
|
120
191
|
end
|
|
121
192
|
|
|
122
193
|
module ClassMethods
|
|
194
|
+
# Return fields to invite
|
|
195
|
+
def invite_key_fields
|
|
196
|
+
if invite_key.is_a? Hash
|
|
197
|
+
invite_key.keys
|
|
198
|
+
else
|
|
199
|
+
ActiveSupport::Deprecation.warn("invite_key should be a hash like {#{invite_key.inspect} => /.../}")
|
|
200
|
+
Array(invite_key)
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
123
204
|
# Attempt to find a user by it's email. If a record is not found, create a new
|
|
124
205
|
# user and send invitation to it. If user is found, returns the user with an
|
|
125
206
|
# email already exists error.
|
|
@@ -127,16 +208,24 @@ module Devise
|
|
|
127
208
|
# resend_invitation is set to false
|
|
128
209
|
# Attributes must contain the user email, other attributes will be set in the record
|
|
129
210
|
def _invite(attributes={}, invited_by=nil, &block)
|
|
130
|
-
|
|
211
|
+
invite_key_array = invite_key_fields
|
|
212
|
+
attributes_hash = {}
|
|
213
|
+
invite_key_array.each do |k,v|
|
|
214
|
+
attributes_hash[k] = attributes.delete(k)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
invitable = find_or_initialize_with_errors(invite_key_array, attributes_hash)
|
|
131
218
|
invitable.assign_attributes(attributes, :as => inviter_role(invited_by))
|
|
132
219
|
invitable.invited_by = invited_by
|
|
133
220
|
|
|
134
221
|
invitable.skip_password = true
|
|
135
222
|
invitable.valid? if self.validate_on_invite
|
|
136
223
|
if invitable.new_record?
|
|
137
|
-
invitable.errors.clear if !self.validate_on_invite and invitable.
|
|
138
|
-
|
|
139
|
-
|
|
224
|
+
invitable.errors.clear if !self.validate_on_invite and invitable.invite_key_valid?
|
|
225
|
+
elsif !invitable.invited_to_sign_up? || !self.resend_invitation
|
|
226
|
+
invite_key_array.each do |key|
|
|
227
|
+
invitable.errors.add(key, :taken)
|
|
228
|
+
end
|
|
140
229
|
end
|
|
141
230
|
|
|
142
231
|
if invitable.errors.empty?
|
|
@@ -145,7 +234,7 @@ module Devise
|
|
|
145
234
|
end
|
|
146
235
|
[invitable, mail]
|
|
147
236
|
end
|
|
148
|
-
|
|
237
|
+
|
|
149
238
|
# Override this method if the invitable is using Mass Assignment Security
|
|
150
239
|
# and the inviter has a non-default role.
|
|
151
240
|
def inviter_role(inviter)
|
|
@@ -181,16 +270,16 @@ module Devise
|
|
|
181
270
|
def invitation_token
|
|
182
271
|
generate_token(:invitation_token)
|
|
183
272
|
end
|
|
184
|
-
|
|
273
|
+
|
|
185
274
|
# Callback convenience methods
|
|
186
275
|
def before_invitation_accepted(*args, &blk)
|
|
187
276
|
set_callback(:invitation_accepted, :before, *args, &blk)
|
|
188
277
|
end
|
|
189
|
-
|
|
278
|
+
|
|
190
279
|
def after_invitation_accepted(*args, &blk)
|
|
191
280
|
set_callback(:invitation_accepted, :after, *args, &blk)
|
|
192
281
|
end
|
|
193
|
-
|
|
282
|
+
|
|
194
283
|
|
|
195
284
|
Devise::Models.config(self, :invite_for)
|
|
196
285
|
Devise::Models.config(self, :validate_on_invite)
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require 'active_support/deprecation'
|
|
2
|
+
|
|
1
3
|
module Devise
|
|
2
4
|
module Models
|
|
3
5
|
# Invitable is responsible for sending invitation emails.
|
|
@@ -13,7 +15,7 @@ module Devise
|
|
|
13
15
|
#
|
|
14
16
|
# Examples:
|
|
15
17
|
#
|
|
16
|
-
# User.find(1).
|
|
18
|
+
# User.find(1).invited_to_sign_up? # => true/false
|
|
17
19
|
# User.invite!(:email => 'someone@example.com') # => send invitation
|
|
18
20
|
# User.accept_invitation!(:invitation_token => '...') # => accept invitation with a token
|
|
19
21
|
# User.find(1).accept_invitation! # => accept invitation
|
|
@@ -22,6 +24,7 @@ module Devise
|
|
|
22
24
|
extend ActiveSupport::Concern
|
|
23
25
|
|
|
24
26
|
attr_accessor :skip_invitation
|
|
27
|
+
attr_accessor :completing_invite
|
|
25
28
|
|
|
26
29
|
included do
|
|
27
30
|
include ::DeviseInvitable::Inviter
|
|
@@ -36,26 +39,43 @@ module Devise
|
|
|
36
39
|
# Accept an invitation by clearing invitation token and and setting invitation_accepted_at
|
|
37
40
|
# Confirms it if model is confirmable
|
|
38
41
|
def accept_invitation!
|
|
39
|
-
|
|
42
|
+
self.completing_invite = true
|
|
43
|
+
if self.invited_to_sign_up? && self.valid?
|
|
40
44
|
run_callbacks :invitation_accepted do
|
|
41
45
|
self.invitation_token = nil
|
|
42
46
|
self.invitation_accepted_at = Time.now.utc if respond_to? :"invitation_accepted_at="
|
|
47
|
+
self.completing_invite = false
|
|
43
48
|
self.save(:validate => false)
|
|
44
49
|
end
|
|
45
50
|
end
|
|
46
51
|
end
|
|
47
52
|
|
|
53
|
+
# Verifies whether a user has accepted an invite, was never invited, or is in the process of accepting an invitation, or not
|
|
54
|
+
def accepting_or_not_invited?
|
|
55
|
+
!!completing_invite || invited_to_sign_up?
|
|
56
|
+
end
|
|
57
|
+
|
|
48
58
|
# Verifies whether a user has been invited or not
|
|
49
|
-
def
|
|
59
|
+
def invited_to_sign_up?
|
|
50
60
|
persisted? && invitation_token.present?
|
|
51
61
|
end
|
|
62
|
+
|
|
63
|
+
def invited?
|
|
64
|
+
invited_to_sign_up?
|
|
65
|
+
end
|
|
66
|
+
deprecate :invited?
|
|
52
67
|
|
|
53
68
|
# Reset invitation token and send invitation again
|
|
54
69
|
def invite!
|
|
55
|
-
was_invited =
|
|
70
|
+
was_invited = invited_to_sign_up?
|
|
56
71
|
self.skip_confirmation! if self.new_record? && self.respond_to?(:skip_confirmation!)
|
|
57
72
|
generate_invitation_token if self.invitation_token.nil?
|
|
58
73
|
self.invitation_sent_at = Time.now.utc
|
|
74
|
+
|
|
75
|
+
# Call these before_validate methods since we aren't validating on save
|
|
76
|
+
self.downcase_keys if self.new_record? && self.respond_to?(:downcase_keys)
|
|
77
|
+
self.strip_whitespace if self.new_record? && self.respond_to?(:strip_whitespace)
|
|
78
|
+
|
|
59
79
|
if save(:validate => false)
|
|
60
80
|
self.invited_by.decrement_invitation_limit! if !was_invited and self.invited_by.present?
|
|
61
81
|
deliver_invitation unless @skip_invitation
|
|
@@ -66,19 +86,19 @@ module Devise
|
|
|
66
86
|
# invited, we need to calculate if the invitation time has not expired
|
|
67
87
|
# for this user, in other words, if the invitation is still valid.
|
|
68
88
|
def valid_invitation?
|
|
69
|
-
|
|
89
|
+
invited_to_sign_up? && invitation_period_valid?
|
|
70
90
|
end
|
|
71
91
|
|
|
72
92
|
# Only verify password when is not invited
|
|
73
93
|
def valid_password?(password)
|
|
74
|
-
super unless
|
|
94
|
+
super unless invited_to_sign_up?
|
|
75
95
|
end
|
|
76
|
-
|
|
96
|
+
|
|
77
97
|
def reset_password!(new_password, new_password_confirmation)
|
|
78
98
|
super
|
|
79
99
|
accept_invitation!
|
|
80
100
|
end
|
|
81
|
-
|
|
101
|
+
|
|
82
102
|
protected
|
|
83
103
|
# Overriding the method in Devise's :validatable module so password is not required on inviting
|
|
84
104
|
def password_required?
|
|
@@ -90,12 +110,6 @@ module Devise
|
|
|
90
110
|
::Devise.mailer.invitation_instructions(self).deliver
|
|
91
111
|
end
|
|
92
112
|
|
|
93
|
-
# Clear invitation token when reset password token is cleared too
|
|
94
|
-
def clear_reset_password_token
|
|
95
|
-
self.invitation_token = nil if invited?
|
|
96
|
-
super
|
|
97
|
-
end
|
|
98
|
-
|
|
99
113
|
# Checks if the invitation for the user is within the limit time.
|
|
100
114
|
# We do this by calculating if the difference between today and the
|
|
101
115
|
# invitation sent date does not exceed the invite for time configured.
|
|
@@ -142,7 +156,7 @@ module Devise
|
|
|
142
156
|
if invitable.new_record?
|
|
143
157
|
invitable.errors.clear if !self.validate_on_invite and invitable.email.try(:match, Devise.email_regexp)
|
|
144
158
|
else
|
|
145
|
-
invitable.errors.add(invite_key, :taken) unless invitable.
|
|
159
|
+
invitable.errors.add(invite_key, :taken) unless invitable.invited_to_sign_up? && self.resend_invitation
|
|
146
160
|
end
|
|
147
161
|
|
|
148
162
|
if invitable.errors.empty?
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
module DeviseInvitable
|
|
2
2
|
class Engine < ::Rails::Engine
|
|
3
|
-
|
|
3
|
+
|
|
4
4
|
ActiveSupport.on_load(:action_controller) { include DeviseInvitable::Controllers::UrlHelpers }
|
|
5
5
|
ActiveSupport.on_load(:action_view) { include DeviseInvitable::Controllers::UrlHelpers }
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
# We use to_prepare instead of after_initialize here because Devise is a Rails engine; its
|
|
8
8
|
# mailer is reloaded like the rest of the user's app. Got to make sure that our mailer methods
|
|
9
9
|
# are included each time Devise::Mailer is (re)loaded.
|
|
10
10
|
config.to_prepare do
|
|
11
11
|
require 'devise/mailer'
|
|
12
12
|
Devise::Mailer.send :include, DeviseInvitable::Mailer
|
|
13
|
+
Devise::RegistrationsController.send :include, DeviseInvitable::Controllers::Registrations
|
|
13
14
|
end
|
|
14
|
-
|
|
15
15
|
end
|
|
16
16
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
module ActionDispatch::Routing
|
|
2
2
|
class Mapper
|
|
3
|
-
|
|
3
|
+
|
|
4
4
|
protected
|
|
5
5
|
def devise_invitation(mapping, controllers)
|
|
6
6
|
resource :invitation, :only => [:new, :create, :update],
|
|
@@ -8,6 +8,6 @@ module ActionDispatch::Routing
|
|
|
8
8
|
get :edit, :path => mapping.path_names[:accept], :as => :accept
|
|
9
9
|
end
|
|
10
10
|
end
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
end
|
|
13
13
|
end
|
data/lib/devise_invitable.rb
CHANGED
|
@@ -7,11 +7,12 @@ end
|
|
|
7
7
|
require 'devise_invitable/mailer'
|
|
8
8
|
require 'devise_invitable/routes'
|
|
9
9
|
require 'devise_invitable/controllers/url_helpers'
|
|
10
|
+
require 'devise_invitable/controllers/registrations'
|
|
10
11
|
require 'devise_invitable/controllers/helpers'
|
|
11
12
|
require 'devise_invitable/rails'
|
|
12
13
|
|
|
13
14
|
module Devise
|
|
14
|
-
# Public: Validity period of the invitation token (default: 0). If
|
|
15
|
+
# Public: Validity period of the invitation token (default: 0). If
|
|
15
16
|
# invite_for is 0 or nil, the invitation will never expire.
|
|
16
17
|
# Set invite_for in the Devise configuration file (in config/initializers/devise.rb).
|
|
17
18
|
#
|
|
@@ -19,7 +20,7 @@ module Devise
|
|
|
19
20
|
mattr_accessor :invite_for
|
|
20
21
|
@@invite_for = 0
|
|
21
22
|
|
|
22
|
-
# Public: Flag that force a record to be valid before being actually invited
|
|
23
|
+
# Public: Flag that force a record to be valid before being actually invited
|
|
23
24
|
# (default: false).
|
|
24
25
|
#
|
|
25
26
|
# Examples (in config/initializers/devise.rb)
|
|
@@ -35,14 +36,15 @@ module Devise
|
|
|
35
36
|
# config.invitation_limit = nil
|
|
36
37
|
mattr_accessor :invitation_limit
|
|
37
38
|
@@invitation_limit = nil
|
|
38
|
-
|
|
39
|
-
# Public: The key to be used to check existing users when sending an invitation
|
|
39
|
+
|
|
40
|
+
# Public: The key to be used to check existing users when sending an invitation,
|
|
41
|
+
# and the regexp used to test it when validate_on_invite is not set.
|
|
40
42
|
#
|
|
41
43
|
# Examples (in config/initializers/devise.rb)
|
|
42
44
|
#
|
|
43
|
-
# config.invite_key = :email
|
|
45
|
+
# config.invite_key = {:email => /\A[^@]+@[^@]+\z/}
|
|
44
46
|
mattr_accessor :invite_key
|
|
45
|
-
@@invite_key = :email
|
|
47
|
+
@@invite_key = {:email => Devise.email_regexp}
|
|
46
48
|
|
|
47
49
|
# Public: Resend invitation if user with invited status is invited again
|
|
48
50
|
# (default: true)
|
|
@@ -52,6 +54,11 @@ module Devise
|
|
|
52
54
|
# config.resend_invitation = false
|
|
53
55
|
mattr_accessor :resend_invitation
|
|
54
56
|
@@resend_invitation = true
|
|
57
|
+
|
|
58
|
+
# Public: The class name of the inviting model. If this is nil,
|
|
59
|
+
# the #invited_by association is declared to be polymorphic. (default: nil)
|
|
60
|
+
mattr_accessor :invited_by_class_name
|
|
61
|
+
@@invited_by_class_name = nil
|
|
55
62
|
end
|
|
56
63
|
|
|
57
64
|
Devise.add_module :invitable, :controller => :invitations, :model => 'devise_invitable/model', :route => :invitation
|
|
@@ -3,16 +3,16 @@ module DeviseInvitable
|
|
|
3
3
|
class InstallGenerator < Rails::Generators::Base
|
|
4
4
|
source_root File.expand_path("../../templates", __FILE__)
|
|
5
5
|
desc "Add DeviseInvitable config variables to the Devise initializer and copy DeviseInvitable locale files to your application."
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
# def devise_install
|
|
8
8
|
# invoke "devise:install"
|
|
9
9
|
# end
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
def add_config_options_to_initializer
|
|
12
12
|
devise_initializer_path = "config/initializers/devise.rb"
|
|
13
13
|
if File.exist?(devise_initializer_path)
|
|
14
14
|
old_content = File.read(devise_initializer_path)
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
if old_content.match(Regexp.new(/^\s# ==> Configuration for :invitable\n/))
|
|
17
17
|
false
|
|
18
18
|
else
|
|
@@ -23,18 +23,20 @@ module DeviseInvitable
|
|
|
23
23
|
# this period, the invited resource won't be able to accept the invitation.
|
|
24
24
|
# When invite_for is 0 (the default), the invitation won't expire.
|
|
25
25
|
# config.invite_for = 2.weeks
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
# Number of invitations users can send.
|
|
28
28
|
# If invitation_limit is nil, users can send unlimited invitations.
|
|
29
29
|
# If invitation_limit is 0, users can't send invitations.
|
|
30
30
|
# If invitation_limit n > 0, users can send n invitations.
|
|
31
31
|
# Default: nil
|
|
32
32
|
# config.invitation_limit = 5
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
# The key to be used to check existing users when sending an invitation
|
|
35
|
-
#
|
|
36
|
-
|
|
37
|
-
#
|
|
35
|
+
# and the regexp used to test it when validate_on_invite is not set.
|
|
36
|
+
# config.invite_key = {:email => /\A[^@]+@[^@]+\z/}
|
|
37
|
+
# config.invite_key = {:email => /\A[^@]+@[^@]+\z/, :username => nil}
|
|
38
|
+
|
|
39
|
+
# Flag that force a record to be valid before being actually invited
|
|
38
40
|
# Default: false
|
|
39
41
|
# config.validate_on_invite = true
|
|
40
42
|
|
|
@@ -43,11 +45,11 @@ CONTENT
|
|
|
43
45
|
end
|
|
44
46
|
end
|
|
45
47
|
end
|
|
46
|
-
|
|
48
|
+
|
|
47
49
|
def copy_locale
|
|
48
50
|
copy_file "../../../config/locales/en.yml", "config/locales/devise_invitable.en.yml"
|
|
49
51
|
end
|
|
50
|
-
|
|
52
|
+
|
|
51
53
|
end
|
|
52
54
|
end
|
|
53
55
|
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<h2><%= t 'devise.invitations.edit.header' %></h2>
|
|
2
|
+
|
|
3
|
+
<%= simple_form_for resource, :as => resource_name, :url => invitation_path(resource_name), :html => { :method => :put } do |f| %>
|
|
4
|
+
<%= devise_error_messages! %>
|
|
5
|
+
<%= f.hidden_field :invitation_token %>
|
|
6
|
+
|
|
7
|
+
<%= f.input :password %>
|
|
8
|
+
<%= f.input :password_confirmation %>
|
|
9
|
+
|
|
10
|
+
<%= f.submit t("devise.invitations.edit.submit_button") %>
|
|
11
|
+
<% end %>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<h2><%= t "devise.invitations.new.header" %></h2>
|
|
2
|
+
|
|
3
|
+
<%= simple_form_for resource, :as => resource_name, :url => invitation_path(resource_name), :html => {:method => :post} do |f| %>
|
|
4
|
+
<%= devise_error_messages! %>
|
|
5
|
+
|
|
6
|
+
<% resource.class.invite_key_fields.each do |field| -%>
|
|
7
|
+
<%= f.input field %>
|
|
8
|
+
<% end -%>
|
|
9
|
+
|
|
10
|
+
<%= f.submit t("devise.invitations.new.submit_button") %>
|
|
11
|
+
<% end %>
|
|
@@ -2,18 +2,40 @@ require 'generators/devise/views_generator'
|
|
|
2
2
|
|
|
3
3
|
module DeviseInvitable
|
|
4
4
|
module Generators
|
|
5
|
-
class
|
|
6
|
-
|
|
5
|
+
class InvitationViewsGenerator < Rails::Generators::Base
|
|
6
|
+
include ::Devise::Generators::ViewPathTemplates
|
|
7
|
+
|
|
8
|
+
def copy_views
|
|
9
|
+
view_directory :invitations
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class SimpleFormForGenerator < InvitationViewsGenerator
|
|
14
|
+
source_root File.expand_path("../templates/simple_form_for", __FILE__)
|
|
15
|
+
end
|
|
7
16
|
|
|
8
|
-
|
|
9
|
-
|
|
17
|
+
class FormForGenerator < InvitationViewsGenerator
|
|
18
|
+
source_root File.expand_path("../../../../app/views/devise", __FILE__)
|
|
19
|
+
end
|
|
10
20
|
|
|
21
|
+
class MailerViewsGenerator < Rails::Generators::Base
|
|
11
22
|
include ::Devise::Generators::ViewPathTemplates
|
|
12
23
|
source_root File.expand_path("../../../../app/views/devise", __FILE__)
|
|
24
|
+
desc "Copies Devise mail erb views to your application."
|
|
25
|
+
hide!
|
|
26
|
+
|
|
13
27
|
def copy_views
|
|
14
|
-
view_directory :invitations
|
|
15
28
|
view_directory :mailer
|
|
16
29
|
end
|
|
17
30
|
end
|
|
31
|
+
|
|
32
|
+
class ViewsGenerator < Rails::Generators::Base
|
|
33
|
+
desc 'Copies all DeviseInvitable views to your application.'
|
|
34
|
+
argument :scope, :required => false, :default => nil, :desc => "The scope to copy views to"
|
|
35
|
+
|
|
36
|
+
invoke MailerViewsGenerator
|
|
37
|
+
|
|
38
|
+
hook_for :form_builder, :aliases => "-b", :desc => "Form builder to be used", :default => defined?(SimpleForm) ? "simple_form_for" : "form_for"
|
|
39
|
+
end
|
|
18
40
|
end
|
|
19
41
|
end
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: devise_invitable
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
hash:
|
|
4
|
+
hash: 19
|
|
5
5
|
prerelease:
|
|
6
6
|
segments:
|
|
7
7
|
- 1
|
|
8
|
+
- 1
|
|
8
9
|
- 0
|
|
9
|
-
|
|
10
|
-
version: 1.0.0
|
|
10
|
+
version: 1.1.0
|
|
11
11
|
platform: ruby
|
|
12
12
|
authors:
|
|
13
13
|
- Sergio Cambra
|
|
@@ -15,7 +15,7 @@ autorequire:
|
|
|
15
15
|
bindir: bin
|
|
16
16
|
cert_chain: []
|
|
17
17
|
|
|
18
|
-
date: 2012-
|
|
18
|
+
date: 2012-08-20 00:00:00 Z
|
|
19
19
|
dependencies:
|
|
20
20
|
- !ruby/object:Gem::Dependency
|
|
21
21
|
name: bundler
|
|
@@ -23,18 +23,18 @@ dependencies:
|
|
|
23
23
|
requirement: &id001 !ruby/object:Gem::Requirement
|
|
24
24
|
none: false
|
|
25
25
|
requirements:
|
|
26
|
-
- -
|
|
26
|
+
- - ">="
|
|
27
27
|
- !ruby/object:Gem::Version
|
|
28
|
-
hash:
|
|
28
|
+
hash: 19
|
|
29
29
|
segments:
|
|
30
30
|
- 1
|
|
31
|
+
- 1
|
|
31
32
|
- 0
|
|
32
|
-
|
|
33
|
-
version: 1.0.7
|
|
33
|
+
version: 1.1.0
|
|
34
34
|
type: :development
|
|
35
35
|
version_requirements: *id001
|
|
36
36
|
- !ruby/object:Gem::Dependency
|
|
37
|
-
name:
|
|
37
|
+
name: railties
|
|
38
38
|
prerelease: false
|
|
39
39
|
requirement: &id002 !ruby/object:Gem::Requirement
|
|
40
40
|
none: false
|
|
@@ -49,21 +49,36 @@ dependencies:
|
|
|
49
49
|
type: :runtime
|
|
50
50
|
version_requirements: *id002
|
|
51
51
|
- !ruby/object:Gem::Dependency
|
|
52
|
-
name:
|
|
52
|
+
name: actionmailer
|
|
53
53
|
prerelease: false
|
|
54
54
|
requirement: &id003 !ruby/object:Gem::Requirement
|
|
55
55
|
none: false
|
|
56
56
|
requirements:
|
|
57
57
|
- - ~>
|
|
58
58
|
- !ruby/object:Gem::Version
|
|
59
|
-
hash:
|
|
59
|
+
hash: 7
|
|
60
60
|
segments:
|
|
61
|
-
-
|
|
62
|
-
- 0
|
|
61
|
+
- 3
|
|
63
62
|
- 0
|
|
64
|
-
version:
|
|
63
|
+
version: "3.0"
|
|
65
64
|
type: :runtime
|
|
66
65
|
version_requirements: *id003
|
|
66
|
+
- !ruby/object:Gem::Dependency
|
|
67
|
+
name: devise
|
|
68
|
+
prerelease: false
|
|
69
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
|
70
|
+
none: false
|
|
71
|
+
requirements:
|
|
72
|
+
- - ">="
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
hash: 11
|
|
75
|
+
segments:
|
|
76
|
+
- 2
|
|
77
|
+
- 1
|
|
78
|
+
- 0
|
|
79
|
+
version: 2.1.0
|
|
80
|
+
type: :runtime
|
|
81
|
+
version_requirements: *id004
|
|
67
82
|
description: It adds support for send invitations by email (it requires to be authenticated) and accept the invitation by setting a password.
|
|
68
83
|
email:
|
|
69
84
|
- sergio@entrecables.com
|
|
@@ -84,9 +99,11 @@ files:
|
|
|
84
99
|
- lib/devise_invitable/model.rb
|
|
85
100
|
- lib/devise_invitable/rails.rb
|
|
86
101
|
- lib/devise_invitable/routes.rb
|
|
102
|
+
- lib/devise_invitable/version.rb
|
|
87
103
|
- lib/devise_invitable/controllers/helpers.rb
|
|
88
104
|
- lib/devise_invitable/controllers/url_helpers.rb
|
|
89
|
-
- lib/devise_invitable/
|
|
105
|
+
- lib/devise_invitable/controllers/registrations.rb
|
|
106
|
+
- lib/devise_invitable/controllers/registrations.rb~
|
|
90
107
|
- lib/devise_invitable/inviter.rb
|
|
91
108
|
- lib/devise_invitable/model.rb~
|
|
92
109
|
- lib/generators/active_record/devise_invitable_generator.rb
|
|
@@ -94,6 +111,8 @@ files:
|
|
|
94
111
|
- lib/generators/devise_invitable/views_generator.rb
|
|
95
112
|
- lib/generators/devise_invitable/devise_invitable_generator.rb
|
|
96
113
|
- lib/generators/devise_invitable/install_generator.rb
|
|
114
|
+
- lib/generators/devise_invitable/templates/simple_form_for/invitations/edit.html.erb
|
|
115
|
+
- lib/generators/devise_invitable/templates/simple_form_for/invitations/new.html.erb
|
|
97
116
|
- lib/generators/mongoid/devise_invitable_generator.rb
|
|
98
117
|
- LICENSE
|
|
99
118
|
- README.rdoc
|
|
@@ -132,7 +151,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
132
151
|
requirements: []
|
|
133
152
|
|
|
134
153
|
rubyforge_project:
|
|
135
|
-
rubygems_version: 1.8.
|
|
154
|
+
rubygems_version: 1.8.23
|
|
136
155
|
signing_key:
|
|
137
156
|
specification_version: 3
|
|
138
157
|
summary: An invitation strategy for Devise
|