devise_invitable 1.0.1 → 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 +21 -3
- 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 +18 -15
- data/lib/devise_invitable/controllers/registrations.rb~ +36 -0
- data/lib/devise_invitable/mailer.rb +1 -1
- data/lib/devise_invitable/model.rb +108 -24
- data/lib/devise_invitable/model.rb~ +224 -0
- data/lib/devise_invitable/routes.rb +2 -2
- data/lib/devise_invitable/version.rb +1 -1
- data/lib/devise_invitable.rb +9 -3
- 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 +37 -18
data/README.rdoc
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
= DeviseInvitable
|
|
2
|
-
{<img src="
|
|
2
|
+
{<img src="http://travis-ci.org/scambra/devise_invitable.png"/>}[http://travis-ci.org/scambra/devise_invitable]
|
|
3
3
|
|
|
4
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.
|
|
5
5
|
|
|
@@ -90,12 +90,14 @@ or directly as parameters to the <tt>devise</tt> method:
|
|
|
90
90
|
|
|
91
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.
|
|
92
92
|
|
|
93
|
-
* 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}.
|
|
94
94
|
|
|
95
95
|
* validate_on_invite: force a record to be valid before being actually invited.
|
|
96
96
|
|
|
97
97
|
* resend_invitation: resend invitation if user with invited status is invited again. Enabled by default.
|
|
98
98
|
|
|
99
|
+
* invited_by_class_name: The class name of the inviting model. If this is nil, polymorphic association is used.
|
|
100
|
+
|
|
99
101
|
For more details, see <tt>config/initializers/devise.rb</tt> (after you invoked the "devise_invitable:install" generator described above).
|
|
100
102
|
|
|
101
103
|
== Configuring views
|
|
@@ -153,6 +155,15 @@ You can add :skip_invitation to attributes hash if skip_invitation is added to a
|
|
|
153
155
|
User.invite!(:email => "new_user@example.com", :name => "John Doe", :skip_invitation => true)
|
|
154
156
|
# => the record will be created, but the invitation email will not be sent
|
|
155
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
|
+
|
|
156
167
|
=== Accept an invitation
|
|
157
168
|
|
|
158
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.
|
|
@@ -171,6 +182,13 @@ A callback event is fired before and after an invitation is accepted (User#accep
|
|
|
171
182
|
|
|
172
183
|
The callbacks support all options and arguments available to the standard callbacks provided by AR.
|
|
173
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
|
+
|
|
174
192
|
== Integration in a Rails application
|
|
175
193
|
|
|
176
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.
|
|
@@ -272,4 +290,4 @@ Special thanks to rymai[http://github.com/rymai] for the Rails 3 support, his fo
|
|
|
272
290
|
|
|
273
291
|
== Copyright
|
|
274
292
|
|
|
275
|
-
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'
|
|
@@ -1,36 +1,39 @@
|
|
|
1
1
|
module DeviseInvitable::Controllers::Registrations
|
|
2
2
|
def self.included(controller)
|
|
3
|
-
controller.send :around_filter, :
|
|
3
|
+
controller.send :around_filter, :keep_invitation_info, :only => :create
|
|
4
4
|
end
|
|
5
5
|
|
|
6
6
|
protected
|
|
7
7
|
|
|
8
8
|
def destroy_if_previously_invited
|
|
9
|
-
invitation_info = {}
|
|
10
|
-
|
|
11
9
|
hash = params[resource_name]
|
|
12
10
|
if hash && hash[:email]
|
|
13
|
-
resource = resource_class.
|
|
11
|
+
resource = resource_class.where(:email => hash[:email], :encrypted_password => '').first
|
|
14
12
|
if resource
|
|
15
|
-
invitation_info
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
@invitation_info = Hash[resource.invitation_fields.map {|field|
|
|
14
|
+
[field, resource[field]]
|
|
15
|
+
}]
|
|
18
16
|
resource.destroy
|
|
19
17
|
end
|
|
20
18
|
end
|
|
19
|
+
end
|
|
21
20
|
|
|
22
|
-
|
|
21
|
+
def keep_invitation_info
|
|
22
|
+
resource_invitable = resource_class.devise_modules.include?(:invitable)
|
|
23
|
+
destroy_if_previously_invited if resource_invitable
|
|
23
24
|
yield
|
|
24
|
-
|
|
25
|
+
reset_invitation_info if resource_invitable
|
|
26
|
+
end
|
|
25
27
|
|
|
28
|
+
def reset_invitation_info
|
|
26
29
|
# Restore info about the last invitation (for later reference)
|
|
27
30
|
# Reset the invitation_info only, if invited_by_id is still nil at this stage:
|
|
28
|
-
resource = resource_class.
|
|
29
|
-
if resource
|
|
30
|
-
resource
|
|
31
|
-
|
|
32
|
-
|
|
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
|
|
33
36
|
resource.save!
|
|
34
37
|
end
|
|
35
38
|
end
|
|
36
|
-
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,45 +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
|
|
59
|
-
|
|
117
|
+
self.invited_by = invited_by if invited_by
|
|
118
|
+
|
|
60
119
|
# Call these before_validate methods since we aren't validating on save
|
|
61
120
|
self.downcase_keys if self.new_record? && self.respond_to?(:downcase_keys)
|
|
62
121
|
self.strip_whitespace if self.new_record? && self.respond_to?(:strip_whitespace)
|
|
63
|
-
|
|
122
|
+
|
|
64
123
|
if save(:validate => false)
|
|
65
124
|
self.invited_by.decrement_invitation_limit! if !was_invited and self.invited_by.present?
|
|
66
125
|
deliver_invitation unless @skip_invitation
|
|
@@ -71,19 +130,26 @@ module Devise
|
|
|
71
130
|
# invited, we need to calculate if the invitation time has not expired
|
|
72
131
|
# for this user, in other words, if the invitation is still valid.
|
|
73
132
|
def valid_invitation?
|
|
74
|
-
|
|
133
|
+
invited_to_sign_up? && invitation_period_valid?
|
|
75
134
|
end
|
|
76
135
|
|
|
77
136
|
# Only verify password when is not invited
|
|
78
137
|
def valid_password?(password)
|
|
79
|
-
super unless
|
|
138
|
+
super unless invited_to_sign_up?
|
|
80
139
|
end
|
|
81
|
-
|
|
140
|
+
|
|
82
141
|
def reset_password!(new_password, new_password_confirmation)
|
|
83
142
|
super
|
|
84
143
|
accept_invitation!
|
|
85
144
|
end
|
|
86
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
|
+
|
|
87
153
|
protected
|
|
88
154
|
# Overriding the method in Devise's :validatable module so password is not required on inviting
|
|
89
155
|
def password_required?
|
|
@@ -92,7 +158,7 @@ module Devise
|
|
|
92
158
|
|
|
93
159
|
# Deliver the invitation email
|
|
94
160
|
def deliver_invitation
|
|
95
|
-
|
|
161
|
+
send_devise_notification(:invitation_instructions)
|
|
96
162
|
end
|
|
97
163
|
|
|
98
164
|
# Checks if the invitation for the user is within the limit time.
|
|
@@ -125,6 +191,16 @@ module Devise
|
|
|
125
191
|
end
|
|
126
192
|
|
|
127
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
|
+
|
|
128
204
|
# Attempt to find a user by it's email. If a record is not found, create a new
|
|
129
205
|
# user and send invitation to it. If user is found, returns the user with an
|
|
130
206
|
# email already exists error.
|
|
@@ -132,16 +208,24 @@ module Devise
|
|
|
132
208
|
# resend_invitation is set to false
|
|
133
209
|
# Attributes must contain the user email, other attributes will be set in the record
|
|
134
210
|
def _invite(attributes={}, invited_by=nil, &block)
|
|
135
|
-
|
|
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)
|
|
136
218
|
invitable.assign_attributes(attributes, :as => inviter_role(invited_by))
|
|
137
219
|
invitable.invited_by = invited_by
|
|
138
220
|
|
|
139
221
|
invitable.skip_password = true
|
|
140
222
|
invitable.valid? if self.validate_on_invite
|
|
141
223
|
if invitable.new_record?
|
|
142
|
-
invitable.errors.clear if !self.validate_on_invite and invitable.
|
|
143
|
-
|
|
144
|
-
|
|
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
|
|
145
229
|
end
|
|
146
230
|
|
|
147
231
|
if invitable.errors.empty?
|
|
@@ -150,7 +234,7 @@ module Devise
|
|
|
150
234
|
end
|
|
151
235
|
[invitable, mail]
|
|
152
236
|
end
|
|
153
|
-
|
|
237
|
+
|
|
154
238
|
# Override this method if the invitable is using Mass Assignment Security
|
|
155
239
|
# and the inviter has a non-default role.
|
|
156
240
|
def inviter_role(inviter)
|
|
@@ -186,16 +270,16 @@ module Devise
|
|
|
186
270
|
def invitation_token
|
|
187
271
|
generate_token(:invitation_token)
|
|
188
272
|
end
|
|
189
|
-
|
|
273
|
+
|
|
190
274
|
# Callback convenience methods
|
|
191
275
|
def before_invitation_accepted(*args, &blk)
|
|
192
276
|
set_callback(:invitation_accepted, :before, *args, &blk)
|
|
193
277
|
end
|
|
194
|
-
|
|
278
|
+
|
|
195
279
|
def after_invitation_accepted(*args, &blk)
|
|
196
280
|
set_callback(:invitation_accepted, :after, *args, &blk)
|
|
197
281
|
end
|
|
198
|
-
|
|
282
|
+
|
|
199
283
|
|
|
200
284
|
Devise::Models.config(self, :invite_for)
|
|
201
285
|
Devise::Models.config(self, :validate_on_invite)
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
require 'active_support/deprecation'
|
|
2
|
+
|
|
3
|
+
module Devise
|
|
4
|
+
module Models
|
|
5
|
+
# Invitable is responsible for sending invitation emails.
|
|
6
|
+
# When an invitation is sent to an email address, an account is created for it.
|
|
7
|
+
# Invitation email contains a link allowing the user to accept the invitation
|
|
8
|
+
# by setting a password (as reset password from Devise's recoverable module).
|
|
9
|
+
#
|
|
10
|
+
# Configuration:
|
|
11
|
+
#
|
|
12
|
+
# invite_for: The period the generated invitation token is valid, after
|
|
13
|
+
# this period, the invited resource won't be able to accept the invitation.
|
|
14
|
+
# When invite_for is 0 (the default), the invitation won't expire.
|
|
15
|
+
#
|
|
16
|
+
# Examples:
|
|
17
|
+
#
|
|
18
|
+
# User.find(1).invited_to_sign_up? # => true/false
|
|
19
|
+
# User.invite!(:email => 'someone@example.com') # => send invitation
|
|
20
|
+
# User.accept_invitation!(:invitation_token => '...') # => accept invitation with a token
|
|
21
|
+
# User.find(1).accept_invitation! # => accept invitation
|
|
22
|
+
# User.find(1).invite! # => reset invitation status and send invitation again
|
|
23
|
+
module Invitable
|
|
24
|
+
extend ActiveSupport::Concern
|
|
25
|
+
|
|
26
|
+
attr_accessor :skip_invitation
|
|
27
|
+
attr_accessor :completing_invite
|
|
28
|
+
|
|
29
|
+
included do
|
|
30
|
+
include ::DeviseInvitable::Inviter
|
|
31
|
+
belongs_to :invited_by, :polymorphic => true
|
|
32
|
+
|
|
33
|
+
include ActiveSupport::Callbacks
|
|
34
|
+
define_callbacks :invitation_accepted
|
|
35
|
+
|
|
36
|
+
attr_writer :skip_password
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Accept an invitation by clearing invitation token and and setting invitation_accepted_at
|
|
40
|
+
# Confirms it if model is confirmable
|
|
41
|
+
def accept_invitation!
|
|
42
|
+
self.completing_invite = true
|
|
43
|
+
if self.invited_to_sign_up? && self.valid?
|
|
44
|
+
run_callbacks :invitation_accepted do
|
|
45
|
+
self.invitation_token = nil
|
|
46
|
+
self.invitation_accepted_at = Time.now.utc if respond_to? :"invitation_accepted_at="
|
|
47
|
+
self.completing_invite = false
|
|
48
|
+
self.save(:validate => false)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
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
|
+
|
|
58
|
+
# Verifies whether a user has been invited or not
|
|
59
|
+
def invited_to_sign_up?
|
|
60
|
+
persisted? && invitation_token.present?
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def invited?
|
|
64
|
+
invited_to_sign_up?
|
|
65
|
+
end
|
|
66
|
+
deprecate :invited?
|
|
67
|
+
|
|
68
|
+
# Reset invitation token and send invitation again
|
|
69
|
+
def invite!
|
|
70
|
+
was_invited = invited_to_sign_up?
|
|
71
|
+
self.skip_confirmation! if self.new_record? && self.respond_to?(:skip_confirmation!)
|
|
72
|
+
generate_invitation_token if self.invitation_token.nil?
|
|
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
|
+
|
|
79
|
+
if save(:validate => false)
|
|
80
|
+
self.invited_by.decrement_invitation_limit! if !was_invited and self.invited_by.present?
|
|
81
|
+
deliver_invitation unless @skip_invitation
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Verify whether a invitation is active or not. If the user has been
|
|
86
|
+
# invited, we need to calculate if the invitation time has not expired
|
|
87
|
+
# for this user, in other words, if the invitation is still valid.
|
|
88
|
+
def valid_invitation?
|
|
89
|
+
invited_to_sign_up? && invitation_period_valid?
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Only verify password when is not invited
|
|
93
|
+
def valid_password?(password)
|
|
94
|
+
super unless invited_to_sign_up?
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def reset_password!(new_password, new_password_confirmation)
|
|
98
|
+
super
|
|
99
|
+
accept_invitation!
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
protected
|
|
103
|
+
# Overriding the method in Devise's :validatable module so password is not required on inviting
|
|
104
|
+
def password_required?
|
|
105
|
+
!@skip_password && super
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Deliver the invitation email
|
|
109
|
+
def deliver_invitation
|
|
110
|
+
::Devise.mailer.invitation_instructions(self).deliver
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Checks if the invitation for the user is within the limit time.
|
|
114
|
+
# We do this by calculating if the difference between today and the
|
|
115
|
+
# invitation sent date does not exceed the invite for time configured.
|
|
116
|
+
# Invite_for is a model configuration, must always be an integer value.
|
|
117
|
+
#
|
|
118
|
+
# Example:
|
|
119
|
+
#
|
|
120
|
+
# # invite_for = 1.day and invitation_sent_at = today
|
|
121
|
+
# invitation_period_valid? # returns true
|
|
122
|
+
#
|
|
123
|
+
# # invite_for = 5.days and invitation_sent_at = 4.days.ago
|
|
124
|
+
# invitation_period_valid? # returns true
|
|
125
|
+
#
|
|
126
|
+
# # invite_for = 5.days and invitation_sent_at = 5.days.ago
|
|
127
|
+
# invitation_period_valid? # returns false
|
|
128
|
+
#
|
|
129
|
+
# # invite_for = nil
|
|
130
|
+
# invitation_period_valid? # will always return true
|
|
131
|
+
#
|
|
132
|
+
def invitation_period_valid?
|
|
133
|
+
invitation_sent_at && (self.class.invite_for.to_i.zero? || invitation_sent_at.utc >= self.class.invite_for.ago)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Generates a new random token for invitation, and stores the time
|
|
137
|
+
# this token is being generated
|
|
138
|
+
def generate_invitation_token
|
|
139
|
+
self.invitation_token = self.class.invitation_token
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
module ClassMethods
|
|
143
|
+
# Attempt to find a user by it's email. If a record is not found, create a new
|
|
144
|
+
# user and send invitation to it. If user is found, returns the user with an
|
|
145
|
+
# email already exists error.
|
|
146
|
+
# If user is found and still have pending invitation, email is resend unless
|
|
147
|
+
# resend_invitation is set to false
|
|
148
|
+
# Attributes must contain the user email, other attributes will be set in the record
|
|
149
|
+
def _invite(attributes={}, invited_by=nil, &block)
|
|
150
|
+
invitable = find_or_initialize_with_error_by(invite_key, attributes.delete(invite_key))
|
|
151
|
+
invitable.assign_attributes(attributes, :as => inviter_role(invited_by))
|
|
152
|
+
invitable.invited_by = invited_by
|
|
153
|
+
|
|
154
|
+
invitable.skip_password = true
|
|
155
|
+
invitable.valid? if self.validate_on_invite
|
|
156
|
+
if invitable.new_record?
|
|
157
|
+
invitable.errors.clear if !self.validate_on_invite and invitable.email.try(:match, Devise.email_regexp)
|
|
158
|
+
else
|
|
159
|
+
invitable.errors.add(invite_key, :taken) unless invitable.invited_to_sign_up? && self.resend_invitation
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
if invitable.errors.empty?
|
|
163
|
+
yield invitable if block_given?
|
|
164
|
+
mail = invitable.invite!
|
|
165
|
+
end
|
|
166
|
+
[invitable, mail]
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Override this method if the invitable is using Mass Assignment Security
|
|
170
|
+
# and the inviter has a non-default role.
|
|
171
|
+
def inviter_role(inviter)
|
|
172
|
+
:default
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def invite!(attributes={}, invited_by=nil, &block)
|
|
176
|
+
invitable, mail = _invite(attributes, invited_by, &block)
|
|
177
|
+
invitable
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def invite_mail!(attributes={}, invited_by=nil, &block)
|
|
181
|
+
invitable, mail = _invite(attributes, invited_by, &block)
|
|
182
|
+
mail
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Attempt to find a user by it's invitation_token to set it's password.
|
|
186
|
+
# If a user is found, reset it's password and automatically try saving
|
|
187
|
+
# the record. If not user is found, returns a new user containing an
|
|
188
|
+
# error in invitation_token attribute.
|
|
189
|
+
# Attributes must contain invitation_token, password and confirmation
|
|
190
|
+
def accept_invitation!(attributes={})
|
|
191
|
+
invitable = find_or_initialize_with_error_by(:invitation_token, attributes.delete(:invitation_token))
|
|
192
|
+
invitable.errors.add(:invitation_token, :invalid) if invitable.invitation_token && invitable.persisted? && !invitable.valid_invitation?
|
|
193
|
+
if invitable.errors.empty?
|
|
194
|
+
invitable.attributes = attributes
|
|
195
|
+
invitable.accept_invitation!
|
|
196
|
+
end
|
|
197
|
+
invitable
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Generate a token checking if one does not already exist in the database.
|
|
201
|
+
def invitation_token
|
|
202
|
+
generate_token(:invitation_token)
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# Callback convenience methods
|
|
206
|
+
def before_invitation_accepted(*args, &blk)
|
|
207
|
+
set_callback(:invitation_accepted, :before, *args, &blk)
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def after_invitation_accepted(*args, &blk)
|
|
211
|
+
set_callback(:invitation_accepted, :after, *args, &blk)
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
Devise::Models.config(self, :invite_for)
|
|
216
|
+
Devise::Models.config(self, :validate_on_invite)
|
|
217
|
+
Devise::Models.config(self, :invitation_limit)
|
|
218
|
+
Devise::Models.config(self, :invite_key)
|
|
219
|
+
Devise::Models.config(self, :resend_invitation)
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
|
|
@@ -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
|
@@ -37,13 +37,14 @@ module Devise
|
|
|
37
37
|
mattr_accessor :invitation_limit
|
|
38
38
|
@@invitation_limit = nil
|
|
39
39
|
|
|
40
|
-
# Public: The key to be used to check existing users when sending an invitation
|
|
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.
|
|
41
42
|
#
|
|
42
43
|
# Examples (in config/initializers/devise.rb)
|
|
43
44
|
#
|
|
44
|
-
# config.invite_key = :email
|
|
45
|
+
# config.invite_key = {:email => /\A[^@]+@[^@]+\z/}
|
|
45
46
|
mattr_accessor :invite_key
|
|
46
|
-
@@invite_key = :email
|
|
47
|
+
@@invite_key = {:email => Devise.email_regexp}
|
|
47
48
|
|
|
48
49
|
# Public: Resend invitation if user with invited status is invited again
|
|
49
50
|
# (default: true)
|
|
@@ -53,6 +54,11 @@ module Devise
|
|
|
53
54
|
# config.resend_invitation = false
|
|
54
55
|
mattr_accessor :resend_invitation
|
|
55
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
|
|
56
62
|
end
|
|
57
63
|
|
|
58
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
|
-
- 0
|
|
9
8
|
- 1
|
|
10
|
-
|
|
9
|
+
- 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,7 +23,7 @@ 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
28
|
hash: 19
|
|
29
29
|
segments:
|
|
@@ -34,7 +34,7 @@ dependencies:
|
|
|
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
|
+
none: false
|
|
56
|
+
requirements:
|
|
57
|
+
- - ~>
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
hash: 7
|
|
60
|
+
segments:
|
|
61
|
+
- 3
|
|
62
|
+
- 0
|
|
63
|
+
version: "3.0"
|
|
64
|
+
type: :runtime
|
|
65
|
+
version_requirements: *id003
|
|
66
|
+
- !ruby/object:Gem::Dependency
|
|
67
|
+
name: devise
|
|
68
|
+
prerelease: false
|
|
69
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
|
55
70
|
none: false
|
|
56
71
|
requirements:
|
|
57
72
|
- - ">="
|
|
58
73
|
- !ruby/object:Gem::Version
|
|
59
|
-
hash:
|
|
74
|
+
hash: 11
|
|
60
75
|
segments:
|
|
61
76
|
- 2
|
|
77
|
+
- 1
|
|
62
78
|
- 0
|
|
63
|
-
|
|
64
|
-
version: 2.0.0
|
|
79
|
+
version: 2.1.0
|
|
65
80
|
type: :runtime
|
|
66
|
-
version_requirements: *
|
|
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
|
|
@@ -79,21 +94,25 @@ files:
|
|
|
79
94
|
- app/views/devise/invitations/new.html.erb
|
|
80
95
|
- app/views/devise/mailer/invitation_instructions.html.erb
|
|
81
96
|
- config/locales/en.yml
|
|
82
|
-
- lib/devise_invitable
|
|
83
|
-
- lib/devise_invitable/controllers/registrations.rb
|
|
84
|
-
- lib/devise_invitable/controllers/url_helpers.rb
|
|
85
|
-
- lib/devise_invitable/inviter.rb
|
|
97
|
+
- lib/devise_invitable.rb
|
|
86
98
|
- lib/devise_invitable/mailer.rb
|
|
87
99
|
- lib/devise_invitable/model.rb
|
|
88
100
|
- lib/devise_invitable/rails.rb
|
|
89
101
|
- lib/devise_invitable/routes.rb
|
|
90
102
|
- lib/devise_invitable/version.rb
|
|
91
|
-
- lib/devise_invitable.rb
|
|
103
|
+
- lib/devise_invitable/controllers/helpers.rb
|
|
104
|
+
- lib/devise_invitable/controllers/url_helpers.rb
|
|
105
|
+
- lib/devise_invitable/controllers/registrations.rb
|
|
106
|
+
- lib/devise_invitable/controllers/registrations.rb~
|
|
107
|
+
- lib/devise_invitable/inviter.rb
|
|
108
|
+
- lib/devise_invitable/model.rb~
|
|
92
109
|
- lib/generators/active_record/devise_invitable_generator.rb
|
|
93
110
|
- lib/generators/active_record/templates/migration.rb
|
|
111
|
+
- lib/generators/devise_invitable/views_generator.rb
|
|
94
112
|
- lib/generators/devise_invitable/devise_invitable_generator.rb
|
|
95
113
|
- lib/generators/devise_invitable/install_generator.rb
|
|
96
|
-
- lib/generators/devise_invitable/
|
|
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
|