devise_invitable 1.5.3 → 1.6.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.
- checksums.yaml +4 -4
- data/CHANGELOG +26 -0
- data/README.rdoc +29 -18
- data/app/controllers/devise/invitations_controller.rb +18 -5
- data/app/views/devise/invitations/edit.html.erb +2 -0
- data/app/views/devise/mailer/invitation_instructions.html.erb +4 -0
- data/app/views/devise/mailer/invitation_instructions.text.erb +11 -0
- data/config/locales/en.yml +8 -1
- data/lib/devise_invitable/controllers/helpers.rb +0 -1
- data/lib/devise_invitable/mapping.rb +3 -7
- data/lib/devise_invitable/model.rb +76 -26
- data/lib/devise_invitable/parameter_sanitizer.rb +27 -19
- data/lib/devise_invitable/rails.rb +2 -2
- data/lib/devise_invitable/version.rb +1 -1
- data/lib/devise_invitable.rb +7 -1
- data/lib/generators/devise_invitable/install_generator.rb +2 -2
- data/test/mailers/invitation_mail_test.rb +45 -5
- data/test/model_tests_helper.rb +1 -0
- data/test/models/invitable_test.rb +119 -61
- data/test/rails_app/app/models/user.rb +8 -3
- data/test/rails_app/config/application.rb +5 -1
- data/test/rails_app/config/database.yml +2 -1
- data/test/rails_app/config/environments/test.rb +2 -0
- data/test/rails_app/config/locales/en.yml +3 -0
- data/test/rails_app/db/migrate/20100401102949_create_tables.rb +1 -1
- data/test/test_helper.rb +4 -4
- metadata +14 -19
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1c29d1de12fe88af51d1a7e23efd9829db040271
|
|
4
|
+
data.tar.gz: e4ea042b56eac21a082f284d66b2f0d129555a83
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d11eadb4b2f24ca4500a85fbb7c0636c7cca291f2b43dd9af7321bce5a88942e286fe2412ad4a51ae1556cf4441f7244e8910ccac189751a8b073a54510a1335
|
|
7
|
+
data.tar.gz: a1e51d0f15d30630bbf44f04d1ef7fcabc279ea405f931a0ef390939bfd5ce71d3dc117a178f927d081634344aba03eb96904f04199bbf53558b05d3f6d18351
|
data/CHANGELOG
CHANGED
|
@@ -1,3 +1,29 @@
|
|
|
1
|
+
= 1.6.0
|
|
2
|
+
|
|
3
|
+
- Support devise 4.0 and rails 5.0
|
|
4
|
+
- Add before/after invitation_created callbacks
|
|
5
|
+
- Fix invitation_due_at when invite_for is 0
|
|
6
|
+
- Add plain text mailer template
|
|
7
|
+
- Ruby 1.9 not supported anymore
|
|
8
|
+
- Adds :require_password_on_accepting config option, and ensure invitation is not accepted if password is required and removed from form
|
|
9
|
+
|
|
10
|
+
= 1.5.5
|
|
11
|
+
|
|
12
|
+
- Add optional options hash to invite! methods, they will be used for send_devise_notification call
|
|
13
|
+
|
|
14
|
+
= 1.5.4
|
|
15
|
+
|
|
16
|
+
- Ensure that all invited user passwords conform to a format
|
|
17
|
+
- Call set_minimum_password_length (if exists) on accept invitation as devise does
|
|
18
|
+
- Controllers inheriting from Devise::InvitationsController will now use 'devise.invitations' translations
|
|
19
|
+
when using Devise >= 3.5. See https://github.com/plataformatec/devise/pull/3407 for more details.
|
|
20
|
+
- Add invitation due date to mailer
|
|
21
|
+
|
|
22
|
+
= 1.5.3
|
|
23
|
+
|
|
24
|
+
- Fix #585, avoid generating new password if there already is a encrypted one
|
|
25
|
+
- Give error if trying to register with a registered email
|
|
26
|
+
|
|
1
27
|
= 1.5.2
|
|
2
28
|
|
|
3
29
|
- Fix #571, accept invitation when password changes only if reset_password_token was present
|
data/README.rdoc
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
= DeviseInvitable
|
|
2
|
-
{<img src="https://travis-ci.org/scambra/devise_invitable.png"/>}[https://travis-ci.org/scambra/devise_invitable] {<img src="https://codeclimate.com/github/scambra/devise_invitable/badges/gpa.svg"/}[https://codeclimate.com/github/scambra/devise_invitable]
|
|
2
|
+
{<img src="https://badge.fury.io/rb/devise_invitable.svg"/>}[http://badge.fury.io/rb/devise_invitable] {<img src="https://travis-ci.org/scambra/devise_invitable.png"/>}[https://travis-ci.org/scambra/devise_invitable] {<img src="https://codeclimate.com/github/scambra/devise_invitable/badges/gpa.svg"/}[https://codeclimate.com/github/scambra/devise_invitable]
|
|
3
3
|
|
|
4
4
|
It adds support to devise[https://github.com/plataformatec/devise] for sending invitations by email (it requires to be authenticated) and accept the invitation setting the password.
|
|
5
5
|
|
|
6
6
|
DeviseInvitable currently supports Rails 3 and 4, if you want to use it with Rails 2.3 you must install version {0.2.3}[https://rubygems.org/gems/devise_invitable/versions/0.2.3]
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
It works with Devise >= 3.2
|
|
9
|
+
If you want to use devise 3.0.x, you must use 1.2.1 or lower
|
|
10
|
+
If you want to use devise 3.1.x, you must use 1.3.2 or lower
|
|
9
11
|
|
|
10
12
|
== Installation
|
|
11
13
|
|
|
@@ -130,12 +132,16 @@ or directly as parameters to the <tt>devise</tt> method:
|
|
|
130
132
|
|
|
131
133
|
* resend_invitation: resend invitation if user with invited status is invited again. Enabled by default.
|
|
132
134
|
|
|
133
|
-
* invited_by_class_name:
|
|
135
|
+
* invited_by_class_name: the class name of the inviting model. If this is nil, polymorphic association is used.
|
|
134
136
|
|
|
135
|
-
* invited_by_foreign_key:
|
|
137
|
+
* invited_by_foreign_key: the foreign key to the inviting model (only used if invited_by_class_name is set, otherwise :invited_by_id)
|
|
138
|
+
|
|
139
|
+
* invited_by_counter_cache: the column name used for counter_cache column. If this is nil (default value), the invited_by association is declared without counter_cache.
|
|
136
140
|
|
|
137
141
|
* allow_insecure_sign_in_after_accept: automatically sign in the user after they set a password. Enabled by default.
|
|
138
142
|
|
|
143
|
+
* require_password_on_accepting: require password when user accepts the invitation. Enabled by default. Disable if you don't want to ask or enforce to set password while accepting, because is set when user is invited or it will be set later.
|
|
144
|
+
|
|
139
145
|
For more details, see <tt>config/initializers/devise.rb</tt> (after you invoked the "devise_invitable:install" generator described above).
|
|
140
146
|
|
|
141
147
|
== Configuring views
|
|
@@ -160,7 +166,7 @@ To change the controller's behavior, create a controller that inherits from <tt>
|
|
|
160
166
|
|
|
161
167
|
class Users::InvitationsController < Devise::InvitationsController
|
|
162
168
|
def update
|
|
163
|
-
if
|
|
169
|
+
if some_condition
|
|
164
170
|
redirect_to root_path
|
|
165
171
|
else
|
|
166
172
|
super
|
|
@@ -174,7 +180,7 @@ Now just tell Devise that you want to use your controller, the controller above
|
|
|
174
180
|
|
|
175
181
|
be sure that you generate the views and put them into the controller that you generated, so for this example it would be:
|
|
176
182
|
|
|
177
|
-
rails generate devise_invitable:views users
|
|
183
|
+
rails generate devise_invitable:views users
|
|
178
184
|
|
|
179
185
|
To change behaviour of inviting or accepting users, you can simply override two methods:
|
|
180
186
|
|
|
@@ -185,7 +191,7 @@ To change behaviour of inviting or accepting users, you can simply override two
|
|
|
185
191
|
# should return an instance of resource class
|
|
186
192
|
def invite_resource
|
|
187
193
|
## skip sending emails on invite
|
|
188
|
-
|
|
194
|
+
super do |u|
|
|
189
195
|
u.skip_invitation = true
|
|
190
196
|
end
|
|
191
197
|
end
|
|
@@ -247,25 +253,25 @@ the value is temporarily available when you invite a user and will be decrypted
|
|
|
247
253
|
|
|
248
254
|
accept_user_invitation_url(:invitation_token => user.raw_invitation_token)
|
|
249
255
|
|
|
250
|
-
When skip_invitation is used, you must also then set the invitation_sent_at field when the user is sent their
|
|
251
|
-
token. Failure to do so will yield
|
|
256
|
+
When <tt>skip_invitation</tt> is used, you must also then set the <tt>invitation_sent_at</tt> field when the user is sent their
|
|
257
|
+
token. Failure to do so will yield <tt>Invalid invitation token</tt> error when the user attempts to accept the invite.
|
|
252
258
|
You can set it like so:
|
|
253
259
|
|
|
254
260
|
user.deliver_invitation
|
|
255
261
|
|
|
256
|
-
You can add
|
|
262
|
+
You can add <tt>:skip_invitation</tt> to attributes hash if <tt>skip_invitation</tt> is added to <tt>attr_accessible</tt>.
|
|
257
263
|
|
|
258
264
|
User.invite!(:email => "new_user@example.com", :name => "John Doe", :skip_invitation => true)
|
|
259
265
|
# => the record will be created, but the invitation email will not be sent
|
|
260
266
|
|
|
261
|
-
|
|
267
|
+
<tt>skip_invitation</tt> skips sending the email, but sets <tt>invitation_token</tt>, so <tt>invited_to_sign_up?</tt> on the
|
|
262
268
|
resulting user returns true.
|
|
263
269
|
|
|
264
|
-
To check if a
|
|
270
|
+
To check if a particular user is created by invitation, irrespective to state of invitation one can use <tt>created_by_invite?</tt>
|
|
265
271
|
|
|
266
272
|
**Warning**
|
|
267
273
|
|
|
268
|
-
When using skip_invitation you must send the email with the user object instance that generated the tokens, as
|
|
274
|
+
When using skip_invitation you must send the email with the user object instance that generated the tokens, as
|
|
269
275
|
user.raw_invitation_token is available only to the instance and is not persisted in the database.
|
|
270
276
|
|
|
271
277
|
You can send an invitation to an existing user if your workflow creates them separately:
|
|
@@ -287,12 +293,17 @@ To accept an invitation with a token use the <tt>accept_invitation!</tt> class m
|
|
|
287
293
|
|
|
288
294
|
=== Callbacks
|
|
289
295
|
|
|
290
|
-
A callback event is fired before and after an invitation is accepted (User#accept_invitation!). For example, in your resource model you can add:
|
|
296
|
+
A callback event is fired before and after an invitation is created (User#invite!) or accepted (User#accept_invitation!). For example, in your resource model you can add:
|
|
291
297
|
|
|
298
|
+
before_invitation_created :email_admins
|
|
292
299
|
after_invitation_accepted :email_invited_by
|
|
293
300
|
|
|
301
|
+
def email_admins
|
|
302
|
+
# ...
|
|
303
|
+
end
|
|
304
|
+
|
|
294
305
|
def email_invited_by
|
|
295
|
-
|
|
306
|
+
# ...
|
|
296
307
|
end
|
|
297
308
|
|
|
298
309
|
The callbacks support all options and arguments available to the standard callbacks provided by AR.
|
|
@@ -303,7 +314,7 @@ A pair of scopes to find those users that have accepted, and those that have not
|
|
|
303
314
|
|
|
304
315
|
User.invitation_accepted # => returns all Users for whom the invitation_accepted_at attribute is not nil
|
|
305
316
|
User.invitation_not_accepted # => returns all Users for whom the invitation_accepted_at attribute is nil
|
|
306
|
-
User.created_by_invite # => returns all Users who are created by invitations, irrespective to invitation status
|
|
317
|
+
User.created_by_invite # => returns all Users who are created by invitations, irrespective to invitation status
|
|
307
318
|
|
|
308
319
|
== Integration in a Rails application
|
|
309
320
|
|
|
@@ -398,9 +409,9 @@ https://github.com/scambra/devise_invitable/wiki
|
|
|
398
409
|
|
|
399
410
|
== Testing
|
|
400
411
|
|
|
401
|
-
To test DeviseInvitable for the ActiveRecord ORM with RVM, Ruby
|
|
412
|
+
To test DeviseInvitable for the ActiveRecord ORM with RVM, Ruby 2.0.0:
|
|
402
413
|
|
|
403
|
-
rvm use
|
|
414
|
+
rvm use 2.0.0
|
|
404
415
|
rvm gemset create devise_invitable
|
|
405
416
|
rvm gemset use devise_invitable
|
|
406
417
|
gem install bundler
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
class Devise::InvitationsController < DeviseController
|
|
2
|
+
if Rails::VERSION::MAJOR >= 5
|
|
3
|
+
prepend_before_action :authenticate_inviter!, :only => [:new, :create]
|
|
4
|
+
prepend_before_action :has_invitations_left?, :only => [:create]
|
|
5
|
+
prepend_before_action :require_no_authentication, :only => [:edit, :update, :destroy]
|
|
6
|
+
prepend_before_action :resource_from_invitation_token, :only => [:edit, :destroy]
|
|
7
|
+
else
|
|
8
|
+
prepend_before_filter :authenticate_inviter!, :only => [:new, :create]
|
|
9
|
+
prepend_before_filter :has_invitations_left?, :only => [:create]
|
|
10
|
+
prepend_before_filter :require_no_authentication, :only => [:edit, :update, :destroy]
|
|
11
|
+
prepend_before_filter :resource_from_invitation_token, :only => [:edit, :destroy]
|
|
12
|
+
end
|
|
2
13
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
prepend_before_filter :resource_from_invitation_token, :only => [:edit, :destroy]
|
|
7
|
-
helper_method :after_sign_in_path_for
|
|
14
|
+
if respond_to? :helper_method
|
|
15
|
+
helper_method :after_sign_in_path_for
|
|
16
|
+
end
|
|
8
17
|
|
|
9
18
|
# GET /resource/invitation/new
|
|
10
19
|
def new
|
|
@@ -31,6 +40,7 @@ class Devise::InvitationsController < DeviseController
|
|
|
31
40
|
|
|
32
41
|
# GET /resource/invitation/accept?invitation_token=abcdef
|
|
33
42
|
def edit
|
|
43
|
+
set_minimum_password_length if respond_to? :set_minimum_password_length
|
|
34
44
|
resource.invitation_token = params[:invitation_token]
|
|
35
45
|
render :edit
|
|
36
46
|
end
|
|
@@ -103,5 +113,8 @@ class Devise::InvitationsController < DeviseController
|
|
|
103
113
|
devise_parameter_sanitizer.sanitize(:accept_invitation)
|
|
104
114
|
end
|
|
105
115
|
|
|
116
|
+
def translation_scope
|
|
117
|
+
'devise.invitations'
|
|
118
|
+
end
|
|
106
119
|
end
|
|
107
120
|
|
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
<%= devise_error_messages! %>
|
|
5
5
|
<%= f.hidden_field :invitation_token %>
|
|
6
6
|
|
|
7
|
+
<% if f.object.class.require_password_on_accepting %>
|
|
7
8
|
<p><%= f.label :password %><br />
|
|
8
9
|
<%= f.password_field :password %></p>
|
|
9
10
|
|
|
10
11
|
<p><%= f.label :password_confirmation %><br />
|
|
11
12
|
<%= f.password_field :password_confirmation %></p>
|
|
13
|
+
<% end %>
|
|
12
14
|
|
|
13
15
|
<p><%= f.submit t("devise.invitations.edit.submit_button") %></p>
|
|
14
16
|
<% end %>
|
|
@@ -4,4 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
<p><%= link_to t("devise.mailer.invitation_instructions.accept"), accept_invitation_url(@resource, :invitation_token => @token) %></p>
|
|
6
6
|
|
|
7
|
+
<% if @resource.invitation_due_at %>
|
|
8
|
+
<p><%= t("devise.mailer.invitation_instructions.accept_until", due_date: l(@resource.invitation_due_at, format: :'devise.mailer.invitation_instructions.accept_until_format')) %></p>
|
|
9
|
+
<% end %>
|
|
10
|
+
|
|
7
11
|
<p><%= t("devise.mailer.invitation_instructions.ignore").html_safe %></p>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<%= t("devise.mailer.invitation_instructions.hello", email: @resource.email) %>
|
|
2
|
+
|
|
3
|
+
<%= t("devise.mailer.invitation_instructions.someone_invited_you", url: root_url) %>
|
|
4
|
+
|
|
5
|
+
<%= accept_invitation_url(@resource, :invitation_token => @token) %>
|
|
6
|
+
|
|
7
|
+
<% if @resource.invitation_due_at %>
|
|
8
|
+
<%= t("devise.mailer.invitation_instructions.accept_until", due_date: l(@resource.invitation_due_at, format: :'devise.mailer.invitation_instructions.accept_until_format')) %>
|
|
9
|
+
<% end %>
|
|
10
|
+
|
|
11
|
+
<%= strip_tags t("devise.mailer.invitation_instructions.ignore") %>
|
data/config/locales/en.yml
CHANGED
|
@@ -21,4 +21,11 @@ en:
|
|
|
21
21
|
hello: "Hello %{email}"
|
|
22
22
|
someone_invited_you: "Someone has invited you to %{url}, you can accept it through the link below."
|
|
23
23
|
accept: "Accept invitation"
|
|
24
|
-
|
|
24
|
+
accept_until: "This invitation will be due in %{due_date}."
|
|
25
|
+
ignore: "If you don't want to accept the invitation, please ignore this email.<br />\nYour account won't be created until you access the link above and set your password."
|
|
26
|
+
time:
|
|
27
|
+
formats:
|
|
28
|
+
devise:
|
|
29
|
+
mailer:
|
|
30
|
+
invitation_instructions:
|
|
31
|
+
accept_until_format: "%B %d, %Y %I:%M %p"
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
module DeviseInvitable
|
|
2
2
|
module Mapping
|
|
3
|
-
def self.included(base)
|
|
4
|
-
base.alias_method_chain :default_controllers, :invitable
|
|
5
|
-
end
|
|
6
|
-
|
|
7
3
|
private
|
|
8
|
-
def
|
|
4
|
+
def default_controllers(options)
|
|
9
5
|
options[:controllers] ||= {}
|
|
10
6
|
options[:controllers][:registrations] ||= "devise_invitable/registrations"
|
|
11
|
-
|
|
7
|
+
super
|
|
12
8
|
end
|
|
13
9
|
end
|
|
14
|
-
end
|
|
10
|
+
end
|
|
@@ -34,16 +34,18 @@ module Devise
|
|
|
34
34
|
else
|
|
35
35
|
{:polymorphic => true}
|
|
36
36
|
end
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
if fk = Devise.invited_by_foreign_key
|
|
38
|
+
belongs_to_options[:foreign_key] = fk
|
|
39
|
+
end
|
|
40
40
|
if defined?(ActiveRecord) && defined?(ActiveRecord::Base) && self < ActiveRecord::Base
|
|
41
41
|
counter_cache = Devise.invited_by_counter_cache
|
|
42
42
|
belongs_to_options.merge! :counter_cache => counter_cache if counter_cache
|
|
43
|
+
belongs_to_options.merge! :optional => true if ActiveRecord::VERSION::MAJOR >= 5
|
|
43
44
|
end
|
|
44
45
|
belongs_to :invited_by, belongs_to_options
|
|
45
46
|
|
|
46
47
|
include ActiveSupport::Callbacks
|
|
48
|
+
define_callbacks :invitation_created
|
|
47
49
|
define_callbacks :invitation_accepted
|
|
48
50
|
|
|
49
51
|
scope :no_active_invitation, lambda { where(:invitation_token => nil) }
|
|
@@ -56,7 +58,14 @@ module Devise
|
|
|
56
58
|
scope :invitation_not_accepted, lambda { where(arel_table[:invitation_token].not_eq(nil)).where(:invitation_accepted_at => nil) }
|
|
57
59
|
scope :invitation_accepted, lambda { where(arel_table[:invitation_accepted_at].not_eq(nil)) }
|
|
58
60
|
|
|
59
|
-
|
|
61
|
+
callbacks = [
|
|
62
|
+
:before_invitation_created,
|
|
63
|
+
:after_invitation_created,
|
|
64
|
+
:before_invitation_accepted,
|
|
65
|
+
:after_invitation_accepted,
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
callbacks.each do |callback_method|
|
|
60
69
|
send callback_method
|
|
61
70
|
end
|
|
62
71
|
end
|
|
@@ -79,6 +88,7 @@ module Devise
|
|
|
79
88
|
# Accept an invitation by clearing invitation token and and setting invitation_accepted_at
|
|
80
89
|
# Saves the model and confirms it if model is confirmable, running invitation_accepted callbacks
|
|
81
90
|
def accept_invitation!
|
|
91
|
+
@accepting_invitation = true
|
|
82
92
|
if self.invited_to_sign_up? && self.valid?
|
|
83
93
|
run_callbacks :invitation_accepted do
|
|
84
94
|
self.accept_invitation
|
|
@@ -98,6 +108,11 @@ module Devise
|
|
|
98
108
|
persisted? && invitation_token.present?
|
|
99
109
|
end
|
|
100
110
|
|
|
111
|
+
# Returns true if accept_invitation! was called
|
|
112
|
+
def accepting_invitation?
|
|
113
|
+
@accepting_invitation
|
|
114
|
+
end
|
|
115
|
+
|
|
101
116
|
# Verifies whether a user accepted an invitation (or is accepting it)
|
|
102
117
|
def invitation_accepted?
|
|
103
118
|
invitation_accepted_at.present?
|
|
@@ -109,8 +124,8 @@ module Devise
|
|
|
109
124
|
end
|
|
110
125
|
|
|
111
126
|
# Reset invitation token and send invitation again
|
|
112
|
-
def invite!(invited_by = nil)
|
|
113
|
-
# This is an order-dependant assignment, this can't be moved
|
|
127
|
+
def invite!(invited_by = nil, options = {})
|
|
128
|
+
# This is an order-dependant assignment, this can't be moved
|
|
114
129
|
was_invited = invited_to_sign_up?
|
|
115
130
|
|
|
116
131
|
# Required to workaround confirmable model's confirmation_required? method
|
|
@@ -121,17 +136,20 @@ module Devise
|
|
|
121
136
|
|
|
122
137
|
yield self if block_given?
|
|
123
138
|
generate_invitation_token if no_token_present_or_skip_invitation?
|
|
124
|
-
self.invitation_created_at = Time.now.utc
|
|
125
|
-
self.invitation_sent_at = self.invitation_created_at unless skip_invitation
|
|
126
|
-
self.invited_by = invited_by if invited_by
|
|
127
139
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
140
|
+
run_callbacks :invitation_created do
|
|
141
|
+
self.invitation_created_at = Time.now.utc
|
|
142
|
+
self.invitation_sent_at = self.invitation_created_at unless skip_invitation
|
|
143
|
+
self.invited_by = invited_by if invited_by
|
|
144
|
+
|
|
145
|
+
# Call these before_validate methods since we aren't validating on save
|
|
146
|
+
self.downcase_keys if new_record_and_responds_to?(:downcase_keys)
|
|
147
|
+
self.strip_whitespace if new_record_and_responds_to?(:strip_whitespace)
|
|
131
148
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
149
|
+
if save(:validate => false)
|
|
150
|
+
self.invited_by.decrement_invitation_limit! if !was_invited and self.invited_by.present?
|
|
151
|
+
deliver_invitation(options) unless skip_invitation
|
|
152
|
+
end
|
|
135
153
|
end
|
|
136
154
|
end
|
|
137
155
|
|
|
@@ -144,7 +162,12 @@ module Devise
|
|
|
144
162
|
|
|
145
163
|
# Only verify password when is not invited
|
|
146
164
|
def valid_password?(password)
|
|
147
|
-
super unless block_from_invitation?
|
|
165
|
+
super unless !accepting_invitation? && block_from_invitation?
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Enforce password when invitation is being accepted
|
|
169
|
+
def password_required?
|
|
170
|
+
(accepting_invitation? && self.class.require_password_on_accepting) || super
|
|
148
171
|
end
|
|
149
172
|
|
|
150
173
|
def unauthenticated_message
|
|
@@ -164,10 +187,10 @@ module Devise
|
|
|
164
187
|
end
|
|
165
188
|
|
|
166
189
|
# Deliver the invitation email
|
|
167
|
-
def deliver_invitation
|
|
190
|
+
def deliver_invitation(options = {})
|
|
168
191
|
generate_invitation_token! unless @raw_invitation_token
|
|
169
192
|
self.update_attribute :invitation_sent_at, Time.now.utc unless self.invitation_sent_at
|
|
170
|
-
send_devise_notification(:invitation_instructions, @raw_invitation_token)
|
|
193
|
+
send_devise_notification(:invitation_instructions, @raw_invitation_token, options)
|
|
171
194
|
end
|
|
172
195
|
|
|
173
196
|
# provide alias to the encrypted invitation_token stored by devise
|
|
@@ -179,6 +202,14 @@ module Devise
|
|
|
179
202
|
respond_to?(:confirmation_required?, true) && confirmation_required?
|
|
180
203
|
end
|
|
181
204
|
|
|
205
|
+
def invitation_due_at
|
|
206
|
+
return nil if (self.class.invite_for == 0 || self.class.invite_for.nil?)
|
|
207
|
+
#return nil unless self.class.invite_for
|
|
208
|
+
|
|
209
|
+
time = self.invitation_created_at || self.invitation_sent_at
|
|
210
|
+
time + self.class.invite_for
|
|
211
|
+
end
|
|
212
|
+
|
|
182
213
|
protected
|
|
183
214
|
|
|
184
215
|
def block_from_invitation?
|
|
@@ -242,7 +273,7 @@ module Devise
|
|
|
242
273
|
# email is resent unless resend_invitation is set to false.
|
|
243
274
|
# Attributes must contain the user's email, other attributes will be
|
|
244
275
|
# set in the record
|
|
245
|
-
def _invite(attributes={}, invited_by=nil, &block)
|
|
276
|
+
def _invite(attributes={}, invited_by=nil, options = {}, &block)
|
|
246
277
|
invite_key_array = invite_key_fields
|
|
247
278
|
attributes_hash = {}
|
|
248
279
|
invite_key_array.each do |k,v|
|
|
@@ -255,7 +286,7 @@ module Devise
|
|
|
255
286
|
invitable.assign_attributes(attributes)
|
|
256
287
|
invitable.invited_by = invited_by
|
|
257
288
|
unless invitable.password || invitable.encrypted_password.present?
|
|
258
|
-
invitable.password =
|
|
289
|
+
invitable.password = random_password
|
|
259
290
|
end
|
|
260
291
|
|
|
261
292
|
invitable.valid? if self.validate_on_invite
|
|
@@ -268,16 +299,16 @@ module Devise
|
|
|
268
299
|
end
|
|
269
300
|
|
|
270
301
|
yield invitable if block_given?
|
|
271
|
-
mail = invitable.invite! if invitable.errors.empty?
|
|
302
|
+
mail = invitable.invite!(nil, options) if invitable.errors.empty?
|
|
272
303
|
[invitable, mail]
|
|
273
304
|
end
|
|
274
305
|
|
|
275
|
-
def invite!(attributes={}, invited_by=nil, &block)
|
|
276
|
-
_invite(attributes.with_indifferent_access, invited_by, &block).first
|
|
306
|
+
def invite!(attributes={}, invited_by=nil, options = {}, &block)
|
|
307
|
+
_invite(attributes.with_indifferent_access, invited_by, options, &block).first
|
|
277
308
|
end
|
|
278
309
|
|
|
279
|
-
def invite_mail!(attributes={}, invited_by=nil, &block)
|
|
280
|
-
_invite(attributes, invited_by, &block).last
|
|
310
|
+
def invite_mail!(attributes={}, invited_by=nil, options = {}, &block)
|
|
311
|
+
_invite(attributes, invited_by, options, &block).last
|
|
281
312
|
end
|
|
282
313
|
|
|
283
314
|
# Attempt to find a user by it's invitation_token to set it's password.
|
|
@@ -304,6 +335,14 @@ module Devise
|
|
|
304
335
|
end
|
|
305
336
|
|
|
306
337
|
# Callback convenience methods
|
|
338
|
+
def before_invitation_created(*args, &blk)
|
|
339
|
+
set_callback(:invitation_created, :before, *args, &blk)
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
def after_invitation_created(*args, &blk)
|
|
343
|
+
set_callback(:invitation_created, :after, *args, &blk)
|
|
344
|
+
end
|
|
345
|
+
|
|
307
346
|
def before_invitation_accepted(*args, &blk)
|
|
308
347
|
set_callback(:invitation_accepted, :before, *args, &blk)
|
|
309
348
|
end
|
|
@@ -319,9 +358,20 @@ module Devise
|
|
|
319
358
|
Devise::Models.config(self, :invite_key)
|
|
320
359
|
Devise::Models.config(self, :resend_invitation)
|
|
321
360
|
Devise::Models.config(self, :allow_insecure_sign_in_after_accept)
|
|
361
|
+
Devise::Models.config(self, :require_password_on_accepting)
|
|
362
|
+
|
|
363
|
+
private
|
|
364
|
+
|
|
365
|
+
# The random password, as set after an invitation, must conform
|
|
366
|
+
# to any password format validation rules of the application.
|
|
367
|
+
# This default fixes the most common scenarios: Passwords must contain
|
|
368
|
+
# lower + upper case, a digit and a symbol.
|
|
369
|
+
# For more unusual rules, this method can be overridden.
|
|
370
|
+
def random_password
|
|
371
|
+
"aA1!" + Devise.friendly_token[0, 20]
|
|
372
|
+
end
|
|
322
373
|
|
|
323
374
|
end
|
|
324
375
|
end
|
|
325
376
|
end
|
|
326
377
|
end
|
|
327
|
-
|
|
@@ -1,29 +1,37 @@
|
|
|
1
1
|
module DeviseInvitable
|
|
2
2
|
module ParameterSanitizer
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def accept_invitation
|
|
8
|
-
permit self.for(:accept_invitation)
|
|
9
|
-
end
|
|
3
|
+
if defined?(Devise::BaseSanitizer)
|
|
4
|
+
def invite
|
|
5
|
+
permit self.for(:invite)
|
|
6
|
+
end
|
|
10
7
|
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
def accept_invitation
|
|
9
|
+
permit self.for(:accept_invitation)
|
|
10
|
+
end
|
|
13
11
|
end
|
|
14
12
|
|
|
15
13
|
private
|
|
16
|
-
def permit(keys)
|
|
17
|
-
default_params.permit(*Array(keys))
|
|
18
|
-
end
|
|
19
14
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
15
|
+
if defined?(Devise::BaseSanitizer)
|
|
16
|
+
def permit(keys)
|
|
17
|
+
default_params.permit(*Array(keys))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def attributes_for(kind)
|
|
21
|
+
case kind
|
|
22
|
+
when :invite
|
|
23
|
+
resource_class.respond_to?(:invite_key_fields) ? resource_class.invite_key_fields : []
|
|
24
|
+
when :accept_invitation
|
|
25
|
+
[:password, :password_confirmation, :invitation_token]
|
|
26
|
+
else
|
|
27
|
+
super
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
else
|
|
31
|
+
def initialize(resource_class, resource_name, params)
|
|
32
|
+
super
|
|
33
|
+
permit(:invite, keys: (resource_class.respond_to?(:invite_key_fields) ? resource_class.invite_key_fields : []) )
|
|
34
|
+
permit(:accept_invitation, keys: [:password, :password_confirmation, :invitation_token] )
|
|
27
35
|
end
|
|
28
36
|
end
|
|
29
37
|
end
|
|
@@ -16,8 +16,8 @@ module DeviseInvitable
|
|
|
16
16
|
end
|
|
17
17
|
# extend mapping with after_initialize because it's not reloaded
|
|
18
18
|
config.after_initialize do
|
|
19
|
-
Devise::Mapping.send :
|
|
20
|
-
Devise::ParameterSanitizer.send :
|
|
19
|
+
Devise::Mapping.send :prepend, DeviseInvitable::Mapping
|
|
20
|
+
Devise::ParameterSanitizer.send :prepend, DeviseInvitable::ParameterSanitizer
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
23
|
end
|
data/lib/devise_invitable.rb
CHANGED
|
@@ -72,10 +72,16 @@ module Devise
|
|
|
72
72
|
mattr_accessor :invited_by_counter_cache
|
|
73
73
|
@@invited_by_counter_cache = nil
|
|
74
74
|
|
|
75
|
-
# Public: Auto-login after the user accepts the
|
|
75
|
+
# Public: Auto-login after the user accepts the invitation. If this is false,
|
|
76
76
|
# the user will need to manually log in after accepting the invite (default: true).
|
|
77
77
|
mattr_accessor :allow_insecure_sign_in_after_accept
|
|
78
78
|
@@allow_insecure_sign_in_after_accept = true
|
|
79
|
+
|
|
80
|
+
# Public: Require password when user accepts the invitation. Set to false if
|
|
81
|
+
# you don't want to ask or enforce to set password while accepting, because is
|
|
82
|
+
# set when user is invited or it will be set later (default: true).
|
|
83
|
+
mattr_accessor :require_password_on_accepting
|
|
84
|
+
@@require_password_on_accepting = true
|
|
79
85
|
end
|
|
80
86
|
|
|
81
87
|
Devise.add_module :invitable, :controller => :invitations, :model => 'devise_invitable/model', :route => {:invitation => [nil, :new, :accept]}
|
|
@@ -63,8 +63,8 @@ module DeviseInvitable
|
|
|
63
63
|
|
|
64
64
|
# Auto-login after the user accepts the invite. If this is false,
|
|
65
65
|
# the user will need to manually log in after accepting the invite.
|
|
66
|
-
# Default:
|
|
67
|
-
# config.allow_insecure_sign_in_after_accept =
|
|
66
|
+
# Default: true
|
|
67
|
+
# config.allow_insecure_sign_in_after_accept = false
|
|
68
68
|
|
|
69
69
|
CONTENT
|
|
70
70
|
end
|