devise_invitable 1.4.0 → 1.5.2
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 +60 -0
- data/README.rdoc +21 -12
- data/app/controllers/devise/invitations_controller.rb +21 -10
- data/app/controllers/devise_invitable/registrations_controller.rb +2 -2
- data/config/locales/en.yml +10 -9
- data/lib/devise_invitable/model.rb +43 -25
- data/lib/devise_invitable/parameter_sanitizer.rb +1 -1
- data/lib/devise_invitable/version.rb +1 -1
- data/lib/devise_invitable.rb +10 -0
- data/lib/generators/active_record/templates/migration.rb +3 -13
- data/lib/generators/devise_invitable/install_generator.rb +9 -0
- data/lib/generators/devise_invitable/templates/simple_form_for/invitations/edit.html.erb +1 -1
- data/lib/generators/devise_invitable/templates/simple_form_for/invitations/new.html.erb +1 -1
- data/test/functional/controller_helpers_test.rb +3 -3
- data/test/functional/registrations_controller_test.rb +4 -2
- data/test/integration/invitation_remove_test.rb +1 -1
- data/test/integration/invitation_test.rb +51 -6
- data/test/models/invitable_test.rb +32 -8
- data/test/rails_app/app/controllers/application_controller.rb +7 -0
- data/test/rails_app/app/controllers/free_invitations_controller.rb +6 -0
- data/test/rails_app/app/views/devise/sessions/new.html.erb +17 -0
- data/test/rails_app/config/initializers/devise.rb +9 -0
- metadata +8 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 21840c71f560017345f75d9152e44b295ccb8043
|
|
4
|
+
data.tar.gz: 8470539a88ef73abb3ef396a4c043e9405eecd57
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 451e491d4a3a8135bc4d1447d1b9035624f2be5757f03544fe1bf8925e0b08df1317a57ec93cb20f293e5dc77397e3c1ba6a6e854f1b8a7646804ce6b43258fc
|
|
7
|
+
data.tar.gz: ee5f07eb0c08aca5310d36b42eb7fa20fee2bbc657a101a79981c5072e6da3593a813ca79bb08ef05d3494db65e228017f692dc96645c1e62b9ff3c4f189e4f8
|
data/CHANGELOG
CHANGED
|
@@ -1,9 +1,69 @@
|
|
|
1
|
+
= 1.5.2
|
|
2
|
+
|
|
3
|
+
- Fix #571, accept invitation when password changes only if reset_password_token was present
|
|
4
|
+
- Add support for setting invited_by foreign key
|
|
5
|
+
- Set random initial password for invited users
|
|
6
|
+
- Don't override password while User.invite!
|
|
7
|
+
|
|
8
|
+
= 1.5.1
|
|
9
|
+
|
|
10
|
+
- Fix #562 Avoid using after_password_reset
|
|
11
|
+
- Fix #564
|
|
12
|
+
|
|
13
|
+
Compare: https://github.com/scambra/devise_invitable/compare/v1.5.0...v1.5.1
|
|
14
|
+
|
|
15
|
+
= 1.5.0
|
|
16
|
+
|
|
17
|
+
Override valid_password? and unauthenticated_message instead of active_for_authentication? and inactive_message, active_for_authentication? doesn't work for default behavior of invited users without password
|
|
18
|
+
|
|
19
|
+
- Get list & check for user(s) created by invite irrespective of status
|
|
20
|
+
- Update simple_form template's hash syntax
|
|
21
|
+
- Update migration template's hash syntax
|
|
22
|
+
- Check if after_password_reset is defined
|
|
23
|
+
|
|
24
|
+
Compare: https://github.com/scambra/devise_invitable/compare/v1.4.2...v1.5.0
|
|
25
|
+
|
|
26
|
+
= 1.4.2
|
|
27
|
+
|
|
28
|
+
- Add option to allow controlling of auto sign in functionality for security
|
|
29
|
+
- Add intermediate method in active_for_authentication? for more flexibility
|
|
30
|
+
- Add intermediate block_from_invitation? method for more flexibility
|
|
31
|
+
- Fix undefined method `invite_key_fields'
|
|
32
|
+
- Fix override valid_password? instead of active_for_authentication? (fixes #541)
|
|
33
|
+
|
|
34
|
+
Compare: https://github.com/scambra/devise_invitable/compare/v1.4.1...v1.4.2
|
|
35
|
+
|
|
36
|
+
= 1.4.1
|
|
37
|
+
|
|
38
|
+
- Begin testing against devise 3.4
|
|
39
|
+
- Use current_inviter to get redirect path after invite
|
|
40
|
+
(https://github.com/scambra/devise_invitable/pull/523)
|
|
41
|
+
|
|
42
|
+
Compare: https://github.com/scambra/devise_invitable/compare/v1.4.0...v1.4.1
|
|
43
|
+
|
|
1
44
|
= 1.4.0
|
|
45
|
+
|
|
2
46
|
Override active_for_authentication? and inactive_message instead of valid_password?
|
|
3
47
|
To use counter_cache, invited_by_counter_cache must be set, no more checking of invitations_count to enable counter cache
|
|
4
48
|
|
|
49
|
+
Compare: https://github.com/scambra/devise_invitable/compare/v1.3.6...v1.4.0
|
|
50
|
+
|
|
51
|
+
= 1.3.6
|
|
52
|
+
|
|
53
|
+
- Regenerate invitation token each time even if "skip_invitation" was true
|
|
54
|
+
- Add passing a block to instance #invite! method
|
|
55
|
+
- Improvements to tests
|
|
56
|
+
|
|
57
|
+
Compare: https://github.com/scambra/devise_invitable/compare/v1.3.5...v1.3.6
|
|
58
|
+
|
|
59
|
+
= 1.3.5
|
|
60
|
+
|
|
61
|
+
No notes yet, contributions welcome.
|
|
62
|
+
|
|
5
63
|
= 1.3.0
|
|
64
|
+
|
|
6
65
|
Now devise 3.1 compatible, @token must be used instead of @resource.invitation_token in mail views
|
|
7
66
|
|
|
8
67
|
= 1.2.0
|
|
68
|
+
|
|
9
69
|
Add invitation_created_at column which is set when invitation is created even when sending is skipped. This new field is used to check invitation period valid
|
data/README.rdoc
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
= DeviseInvitable
|
|
2
|
-
{<img src="https://travis-ci.org/scambra/devise_invitable.png"/>}[
|
|
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]
|
|
3
3
|
|
|
4
|
-
It adds support to devise[
|
|
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
|
-
DeviseInvitable currently supports Rails 3 and 4, if you want to use it with Rails 2.3 you must install version {0.2.3}[
|
|
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
|
If you want to use devise 3.0.x, you must use 1.2.1, newer versions require devise >= 3.1.0
|
|
9
9
|
|
|
@@ -28,7 +28,7 @@ When you are done, you are ready to add DeviseInvitable to any of your Devise mo
|
|
|
28
28
|
|
|
29
29
|
rails generate devise_invitable MODEL
|
|
30
30
|
|
|
31
|
-
Replace MODEL by the class name you want to add DeviseInvitable, like User, Admin, etc. This will add the :invitable flag to your model's Devise modules. The generator will also create a migration file (if your ORM
|
|
31
|
+
Replace MODEL by the class name you want to add DeviseInvitable, like User, Admin, etc. This will add the :invitable flag to your model's Devise modules. The generator will also create a migration file (if your ORM supports them). Continue reading this file to understand exactly what the generator produces and how to use it.
|
|
32
32
|
|
|
33
33
|
=== Manual installation
|
|
34
34
|
|
|
@@ -132,6 +132,10 @@ or directly as parameters to the <tt>devise</tt> method:
|
|
|
132
132
|
|
|
133
133
|
* invited_by_class_name: The class name of the inviting model. If this is nil, polymorphic association is used.
|
|
134
134
|
|
|
135
|
+
* invited_by_foreign_key: The foreign key to the inviting model (only used if invited_by_class_name is set, otherwise :invited_by_id)
|
|
136
|
+
|
|
137
|
+
* allow_insecure_sign_in_after_accept: automatically sign in the user after they set a password. Enabled by default.
|
|
138
|
+
|
|
135
139
|
For more details, see <tt>config/initializers/devise.rb</tt> (after you invoked the "devise_invitable:install" generator described above).
|
|
136
140
|
|
|
137
141
|
== Configuring views
|
|
@@ -148,11 +152,11 @@ Then turn scoped views on in config/initializers/devise.rb:
|
|
|
148
152
|
|
|
149
153
|
config.scoped_views = true
|
|
150
154
|
|
|
151
|
-
Please refer to {Devise's README}[
|
|
155
|
+
Please refer to {Devise's README}[https://github.com/plataformatec/devise] for more information about views.
|
|
152
156
|
|
|
153
157
|
== Configuring controllers
|
|
154
158
|
|
|
155
|
-
To change the controller's behavior, create a controller that inherits from <tt>Devise::InvitationsController</tt>. The available methods are: new, create, edit, and update. You should read the {original controllers source}[https://
|
|
159
|
+
To change the controller's behavior, create a controller that inherits from <tt>Devise::InvitationsController</tt>. The available methods are: new, create, edit, and update. You should read the {original controllers source}[https://github.com/scambra/devise_invitable/blob/master/app/controllers/devise/invitations_controller.rb] before editing any of these actions. Your controller might now look something like this:
|
|
156
160
|
|
|
157
161
|
class Users::InvitationsController < Devise::InvitationsController
|
|
158
162
|
def update
|
|
@@ -198,7 +202,7 @@ To change behaviour of inviting or accepting users, you can simply override two
|
|
|
198
202
|
|
|
199
203
|
== Strong Parameters
|
|
200
204
|
|
|
201
|
-
When you customize your own views, you may end up adding new attributes to forms. Rails 4 moved the parameter sanitization from the model to the controller, causing DeviseInvitable to handle this concern at the controller as well. Read about it in {devise README}[
|
|
205
|
+
When you customize your own views, you may end up adding new attributes to forms. Rails 4 moved the parameter sanitization from the model to the controller, causing DeviseInvitable to handle this concern at the controller as well. Read about it in {devise README}[https://github.com/plataformatec/devise#strong-parameters]
|
|
202
206
|
|
|
203
207
|
There are just two actions in DeviseInvitable that allows any set of parameters to be passed down to the model, therefore requiring sanitization. Their names and the permited parameters by default are:
|
|
204
208
|
|
|
@@ -254,9 +258,11 @@ You can add :skip_invitation to attributes hash if skip_invitation is added to a
|
|
|
254
258
|
User.invite!(:email => "new_user@example.com", :name => "John Doe", :skip_invitation => true)
|
|
255
259
|
# => the record will be created, but the invitation email will not be sent
|
|
256
260
|
|
|
257
|
-
Skip_invitation skips sending the email, but sets invitation_token, so invited_to_sign_up
|
|
261
|
+
Skip_invitation skips sending the email, but sets invitation_token, so <tt>invited_to_sign_up?</tt> on the
|
|
258
262
|
resulting user returns true.
|
|
259
263
|
|
|
264
|
+
To check if a perticular user is created by invitation, irrespective to state of invitation one can use <tt>created_by_invite?</tt>
|
|
265
|
+
|
|
260
266
|
**Warning**
|
|
261
267
|
|
|
262
268
|
When using skip_invitation you must send the email with the user object instance that generated the tokens, as
|
|
@@ -295,8 +301,9 @@ The callbacks support all options and arguments available to the standard callba
|
|
|
295
301
|
|
|
296
302
|
A pair of scopes to find those users that have accepted, and those that have not accepted, invitations are defined:
|
|
297
303
|
|
|
298
|
-
User.invitation_accepted
|
|
304
|
+
User.invitation_accepted # => returns all Users for whom the invitation_accepted_at attribute is not nil
|
|
299
305
|
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
|
|
300
307
|
|
|
301
308
|
== Integration in a Rails application
|
|
302
309
|
|
|
@@ -305,7 +312,7 @@ Instead, in your views, put a link to <tt>new_user_invitation_path</tt> or <tt>n
|
|
|
305
312
|
|
|
306
313
|
After an invitation is created and sent, the inviter will be redirected to after_invite_path_for(resource_name), which is stored path or the same path as after_sign_in_path_for by default.
|
|
307
314
|
|
|
308
|
-
After an invitation is accepted, the invitee will be redirected to after_accept_path_for(resource), which is the same path as after_sign_in_path_for by default. If you want to override the path, override invitations controller and define after_accept_path_for method. This is useful in the common case that a user is invited to a specific location in your application. More on {Devise's README}[
|
|
315
|
+
After an invitation is accepted, the invitee will be redirected to after_accept_path_for(resource), which is the same path as after_sign_in_path_for by default. If you want to override the path, override invitations controller and define after_accept_path_for method. This is useful in the common case that a user is invited to a specific location in your application. More on {Devise's README}[https://github.com/plataformatec/devise], "Controller filters and helpers" section.
|
|
309
316
|
|
|
310
317
|
The invitation email includes a link to accept the invitation that looks like this: <tt>/users/invitation/accept?invitation_token=abcd123</tt>. When clicked, the invited must set a password in order to accept its invitation. Note that if the invitation_token is not present or not valid, the invited is redirected to after_sign_out_path_for(resource_name).
|
|
311
318
|
|
|
@@ -355,6 +362,7 @@ DeviseInvitable uses flash messages with I18n with the flash keys <tt>:send_inst
|
|
|
355
362
|
send_instructions: 'An invitation email has been sent to %{email}.'
|
|
356
363
|
invitation_token_invalid: 'The invitation token provided is not valid!'
|
|
357
364
|
updated: 'Your password was set successfully. You are now signed in.'
|
|
365
|
+
updated_not_active: 'Your password was set successfully.'
|
|
358
366
|
|
|
359
367
|
You can also create distinct messages based on the resource you've configured using the singular name given in routes:
|
|
360
368
|
|
|
@@ -365,6 +373,7 @@ You can also create distinct messages based on the resource you've configured us
|
|
|
365
373
|
send_instructions: 'A new user invitation has been sent to %{email}.'
|
|
366
374
|
invitation_token_invalid: 'Your invitation token is not valid!'
|
|
367
375
|
updated: 'Welcome on board! You are now signed in.'
|
|
376
|
+
updated_not_active: 'Welcome on board! Sign in to continue.'
|
|
368
377
|
|
|
369
378
|
The DeviseInvitable mailer uses the same pattern as Devise to create mail subject messages:
|
|
370
379
|
|
|
@@ -402,9 +411,9 @@ To test DeviseInvitable for the ActiveRecord ORM with RVM, Ruby 1.9.2, and Rubyg
|
|
|
402
411
|
|
|
403
412
|
Check them all at:
|
|
404
413
|
|
|
405
|
-
|
|
414
|
+
https://github.com/scambra/devise_invitable/contributors
|
|
406
415
|
|
|
407
|
-
Special thanks to rymai[
|
|
416
|
+
Special thanks to rymai[https://github.com/rymai] for the Rails 3 support, his fork was a great help.
|
|
408
417
|
|
|
409
418
|
== Note on Patches/Pull Requests
|
|
410
419
|
|
|
@@ -15,13 +15,15 @@ class Devise::InvitationsController < DeviseController
|
|
|
15
15
|
# POST /resource/invitation
|
|
16
16
|
def create
|
|
17
17
|
self.resource = invite_resource
|
|
18
|
+
resource_invited = resource.errors.empty?
|
|
18
19
|
|
|
19
|
-
if
|
|
20
|
-
|
|
20
|
+
yield resource if block_given?
|
|
21
|
+
|
|
22
|
+
if resource_invited
|
|
21
23
|
if is_flashing_format? && self.resource.invitation_sent_at
|
|
22
24
|
set_flash_message :notice, :send_instructions, :email => self.resource.email
|
|
23
25
|
end
|
|
24
|
-
respond_with resource, :location => after_invite_path_for(
|
|
26
|
+
respond_with resource, :location => after_invite_path_for(current_inviter)
|
|
25
27
|
else
|
|
26
28
|
respond_with_navigational(resource) { render :new }
|
|
27
29
|
end
|
|
@@ -35,15 +37,24 @@ class Devise::InvitationsController < DeviseController
|
|
|
35
37
|
|
|
36
38
|
# PUT /resource/invitation
|
|
37
39
|
def update
|
|
40
|
+
raw_invitation_token = update_resource_params[:invitation_token]
|
|
38
41
|
self.resource = accept_resource
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
invitation_accepted = resource.errors.empty?
|
|
43
|
+
|
|
44
|
+
yield resource if block_given?
|
|
45
|
+
|
|
46
|
+
if invitation_accepted
|
|
47
|
+
if Devise.allow_insecure_sign_in_after_accept
|
|
48
|
+
flash_message = resource.active_for_authentication? ? :updated : :updated_not_active
|
|
49
|
+
set_flash_message :notice, flash_message if is_flashing_format?
|
|
50
|
+
sign_in(resource_name, resource)
|
|
51
|
+
respond_with resource, :location => after_accept_path_for(resource)
|
|
52
|
+
else
|
|
53
|
+
set_flash_message :notice, :updated_not_active if is_flashing_format?
|
|
54
|
+
respond_with resource, :location => new_session_path(resource_name)
|
|
55
|
+
end
|
|
46
56
|
else
|
|
57
|
+
resource.invitation_token = raw_invitation_token
|
|
47
58
|
respond_with_navigational(resource){ render :edit }
|
|
48
59
|
end
|
|
49
60
|
end
|
|
@@ -4,8 +4,8 @@ class DeviseInvitable::RegistrationsController < Devise::RegistrationsController
|
|
|
4
4
|
def build_resource(hash = nil)
|
|
5
5
|
hash ||= resource_params || {}
|
|
6
6
|
if hash[:email]
|
|
7
|
-
self.resource = resource_class.where(:email => hash[:email]
|
|
8
|
-
if self.resource
|
|
7
|
+
self.resource = resource_class.where(:email => hash[:email]).first
|
|
8
|
+
if self.resource and self.resource.invited_to_sign_up?
|
|
9
9
|
self.resource.attributes = hash
|
|
10
10
|
self.resource.send_confirmation_instructions if self.resource.confirmation_required_for_invited?
|
|
11
11
|
self.resource.accept_invitation
|
data/config/locales/en.yml
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
en:
|
|
2
2
|
devise:
|
|
3
3
|
failure:
|
|
4
|
-
invited:
|
|
4
|
+
invited: "You have a pending invitation, accept it to finish creating your account."
|
|
5
5
|
invitations:
|
|
6
|
-
send_instructions:
|
|
7
|
-
invitation_token_invalid:
|
|
8
|
-
updated:
|
|
6
|
+
send_instructions: "An invitation email has been sent to %{email}."
|
|
7
|
+
invitation_token_invalid: "The invitation token provided is not valid!"
|
|
8
|
+
updated: "Your password was set successfully. You are now signed in."
|
|
9
|
+
updated_not_active: "Your password was set successfully."
|
|
9
10
|
no_invitations_remaining: "No invitations remaining"
|
|
10
|
-
invitation_removed:
|
|
11
|
+
invitation_removed: "Your invitation was removed."
|
|
11
12
|
new:
|
|
12
13
|
header: "Send invitation"
|
|
13
14
|
submit_button: "Send an invitation"
|
|
@@ -16,8 +17,8 @@ en:
|
|
|
16
17
|
submit_button: "Set my password"
|
|
17
18
|
mailer:
|
|
18
19
|
invitation_instructions:
|
|
19
|
-
subject:
|
|
20
|
-
hello:
|
|
21
|
-
someone_invited_you:
|
|
22
|
-
accept:
|
|
20
|
+
subject: "Invitation instructions"
|
|
21
|
+
hello: "Hello %{email}"
|
|
22
|
+
someone_invited_you: "Someone has invited you to %{url}, you can accept it through the link below."
|
|
23
|
+
accept: "Accept invitation"
|
|
23
24
|
ignore: "If you don't want to accept the invitation, please ignore this email.<br />Your account won't be created until you access the link above and set your password."
|
|
@@ -34,6 +34,9 @@ module Devise
|
|
|
34
34
|
else
|
|
35
35
|
{:polymorphic => true}
|
|
36
36
|
end
|
|
37
|
+
if fk = Devise.invited_by_foreign_key
|
|
38
|
+
belongs_to_options[:foreign_key] = fk
|
|
39
|
+
end
|
|
37
40
|
if defined?(ActiveRecord) && defined?(ActiveRecord::Base) && self < ActiveRecord::Base
|
|
38
41
|
counter_cache = Devise.invited_by_counter_cache
|
|
39
42
|
belongs_to_options.merge! :counter_cache => counter_cache if counter_cache
|
|
@@ -43,13 +46,13 @@ module Devise
|
|
|
43
46
|
include ActiveSupport::Callbacks
|
|
44
47
|
define_callbacks :invitation_accepted
|
|
45
48
|
|
|
46
|
-
attr_writer :skip_password
|
|
47
|
-
|
|
48
49
|
scope :no_active_invitation, lambda { where(:invitation_token => nil) }
|
|
49
50
|
if defined?(Mongoid) && defined?(Mongoid::Document) && self < Mongoid::Document
|
|
51
|
+
scope :created_by_invite, lambda { where(:invitation_sent_at.ne => nil) }
|
|
50
52
|
scope :invitation_not_accepted, lambda { where(:invitation_accepted_at => nil, :invitation_token.ne => nil) }
|
|
51
53
|
scope :invitation_accepted, lambda { where(:invitation_accepted_at.ne => nil) }
|
|
52
54
|
else
|
|
55
|
+
scope :created_by_invite, lambda { where(arel_table[:invitation_sent_at].not_eq(nil)) }
|
|
53
56
|
scope :invitation_not_accepted, lambda { where(arel_table[:invitation_token].not_eq(nil)).where(:invitation_accepted_at => nil) }
|
|
54
57
|
scope :invitation_accepted, lambda { where(arel_table[:invitation_accepted_at].not_eq(nil)) }
|
|
55
58
|
|
|
@@ -61,7 +64,7 @@ module Devise
|
|
|
61
64
|
|
|
62
65
|
def self.required_fields(klass)
|
|
63
66
|
fields = [:invitation_token, :invitation_created_at, :invitation_sent_at, :invitation_accepted_at,
|
|
64
|
-
:invitation_limit, :invited_by_id, :invited_by_type]
|
|
67
|
+
:invitation_limit, Devise.invited_by_foreign_key || :invited_by_id, :invited_by_type]
|
|
65
68
|
fields << :invitations_count if defined?(ActiveRecord) && self < ActiveRecord::Base
|
|
66
69
|
fields -= [:invited_by_type] if Devise.invited_by_class_name
|
|
67
70
|
fields
|
|
@@ -85,6 +88,11 @@ module Devise
|
|
|
85
88
|
end
|
|
86
89
|
end
|
|
87
90
|
|
|
91
|
+
# Verify wheather a user is created by invitation, irrespective to invitation status
|
|
92
|
+
def created_by_invite?
|
|
93
|
+
invitation_sent_at.present?
|
|
94
|
+
end
|
|
95
|
+
|
|
88
96
|
# Verifies whether a user has been invited or not
|
|
89
97
|
def invited_to_sign_up?
|
|
90
98
|
persisted? && invitation_token.present?
|
|
@@ -102,27 +110,28 @@ module Devise
|
|
|
102
110
|
|
|
103
111
|
# Reset invitation token and send invitation again
|
|
104
112
|
def invite!(invited_by = nil)
|
|
113
|
+
# This is an order-dependant assignment, this can't be moved
|
|
105
114
|
was_invited = invited_to_sign_up?
|
|
106
115
|
|
|
107
116
|
# Required to workaround confirmable model's confirmation_required? method
|
|
108
117
|
# being implemented to check for non-nil value of confirmed_at
|
|
109
|
-
if
|
|
118
|
+
if new_record_and_responds_to?(:confirmation_required?)
|
|
110
119
|
def self.confirmation_required?; false; end
|
|
111
120
|
end
|
|
112
121
|
|
|
113
122
|
yield self if block_given?
|
|
114
|
-
generate_invitation_token if
|
|
123
|
+
generate_invitation_token if no_token_present_or_skip_invitation?
|
|
115
124
|
self.invitation_created_at = Time.now.utc
|
|
116
|
-
self.invitation_sent_at = self.invitation_created_at unless
|
|
125
|
+
self.invitation_sent_at = self.invitation_created_at unless skip_invitation
|
|
117
126
|
self.invited_by = invited_by if invited_by
|
|
118
127
|
|
|
119
128
|
# Call these before_validate methods since we aren't validating on save
|
|
120
|
-
self.downcase_keys if
|
|
121
|
-
self.strip_whitespace if
|
|
129
|
+
self.downcase_keys if new_record_and_responds_to?(:downcase_keys)
|
|
130
|
+
self.strip_whitespace if new_record_and_responds_to?(:strip_whitespace)
|
|
122
131
|
|
|
123
132
|
if save(:validate => false)
|
|
124
133
|
self.invited_by.decrement_invitation_limit! if !was_invited and self.invited_by.present?
|
|
125
|
-
deliver_invitation unless
|
|
134
|
+
deliver_invitation unless skip_invitation
|
|
126
135
|
end
|
|
127
136
|
end
|
|
128
137
|
|
|
@@ -134,17 +143,18 @@ module Devise
|
|
|
134
143
|
end
|
|
135
144
|
|
|
136
145
|
# Only verify password when is not invited
|
|
137
|
-
def
|
|
138
|
-
super unless
|
|
146
|
+
def valid_password?(password)
|
|
147
|
+
super unless block_from_invitation?
|
|
139
148
|
end
|
|
140
149
|
|
|
141
|
-
def
|
|
142
|
-
|
|
150
|
+
def unauthenticated_message
|
|
151
|
+
block_from_invitation? ? :invited : super
|
|
143
152
|
end
|
|
144
153
|
|
|
145
|
-
def
|
|
154
|
+
def clear_reset_password_token
|
|
155
|
+
reset_password_token_present = reset_password_token.present?
|
|
146
156
|
super
|
|
147
|
-
accept_invitation! if invited_to_sign_up?
|
|
157
|
+
accept_invitation! if reset_password_token_present && invited_to_sign_up?
|
|
148
158
|
end
|
|
149
159
|
|
|
150
160
|
def clear_errors_on_valid_keys
|
|
@@ -170,11 +180,10 @@ module Devise
|
|
|
170
180
|
end
|
|
171
181
|
|
|
172
182
|
protected
|
|
173
|
-
# Overriding the method in Devise's :validatable module so password is not required on inviting
|
|
174
|
-
def password_required?
|
|
175
|
-
!@skip_password && super
|
|
176
|
-
end
|
|
177
183
|
|
|
184
|
+
def block_from_invitation?
|
|
185
|
+
invited_to_sign_up?
|
|
186
|
+
end
|
|
178
187
|
|
|
179
188
|
# Checks if the invitation for the user is within the limit time.
|
|
180
189
|
# We do this by calculating if the difference between today and the
|
|
@@ -212,6 +221,14 @@ module Devise
|
|
|
212
221
|
generate_invitation_token && save(:validate => false)
|
|
213
222
|
end
|
|
214
223
|
|
|
224
|
+
def new_record_and_responds_to?(method)
|
|
225
|
+
self.new_record? && self.respond_to?(method, true)
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def no_token_present_or_skip_invitation?
|
|
229
|
+
self.invitation_token.nil? || (!skip_invitation || @raw_invitation_token.nil?)
|
|
230
|
+
end
|
|
231
|
+
|
|
215
232
|
module ClassMethods
|
|
216
233
|
# Return fields to invite
|
|
217
234
|
def invite_key_fields
|
|
@@ -237,8 +254,10 @@ module Devise
|
|
|
237
254
|
invitable = find_or_initialize_with_errors(invite_key_array, attributes_hash)
|
|
238
255
|
invitable.assign_attributes(attributes)
|
|
239
256
|
invitable.invited_by = invited_by
|
|
257
|
+
unless invitable.password
|
|
258
|
+
invitable.password = Devise.friendly_token[0, 20]
|
|
259
|
+
end
|
|
240
260
|
|
|
241
|
-
invitable.skip_password = true
|
|
242
261
|
invitable.valid? if self.validate_on_invite
|
|
243
262
|
if invitable.new_record?
|
|
244
263
|
invitable.clear_errors_on_valid_keys if !self.validate_on_invite
|
|
@@ -254,13 +273,11 @@ module Devise
|
|
|
254
273
|
end
|
|
255
274
|
|
|
256
275
|
def invite!(attributes={}, invited_by=nil, &block)
|
|
257
|
-
|
|
258
|
-
invitable
|
|
276
|
+
_invite(attributes.with_indifferent_access, invited_by, &block).first
|
|
259
277
|
end
|
|
260
278
|
|
|
261
279
|
def invite_mail!(attributes={}, invited_by=nil, &block)
|
|
262
|
-
|
|
263
|
-
mail
|
|
280
|
+
_invite(attributes, invited_by, &block).last
|
|
264
281
|
end
|
|
265
282
|
|
|
266
283
|
# Attempt to find a user by it's invitation_token to set it's password.
|
|
@@ -283,7 +300,6 @@ module Devise
|
|
|
283
300
|
|
|
284
301
|
invitable = find_or_initialize_with_error_by(:invitation_token, invitation_token)
|
|
285
302
|
invitable.errors.add(:invitation_token, :invalid) if invitable.invitation_token && invitable.persisted? && !invitable.valid_invitation?
|
|
286
|
-
invitable.invitation_token = original_token
|
|
287
303
|
invitable unless only_valid && invitable.errors.present?
|
|
288
304
|
end
|
|
289
305
|
|
|
@@ -302,6 +318,8 @@ module Devise
|
|
|
302
318
|
Devise::Models.config(self, :invitation_limit)
|
|
303
319
|
Devise::Models.config(self, :invite_key)
|
|
304
320
|
Devise::Models.config(self, :resend_invitation)
|
|
321
|
+
Devise::Models.config(self, :allow_insecure_sign_in_after_accept)
|
|
322
|
+
|
|
305
323
|
end
|
|
306
324
|
end
|
|
307
325
|
end
|
|
@@ -20,7 +20,7 @@ module DeviseInvitable
|
|
|
20
20
|
def attributes_for_with_invitable(kind)
|
|
21
21
|
case kind
|
|
22
22
|
when :invite
|
|
23
|
-
resource_class.invite_key_fields
|
|
23
|
+
resource_class.respond_to?(:invite_key_fields) ? resource_class.invite_key_fields : []
|
|
24
24
|
when :accept_invitation
|
|
25
25
|
[:password, :password_confirmation, :invitation_token]
|
|
26
26
|
else attributes_for_without_invitable(kind)
|
data/lib/devise_invitable.rb
CHANGED
|
@@ -62,10 +62,20 @@ module Devise
|
|
|
62
62
|
mattr_accessor :invited_by_class_name
|
|
63
63
|
@@invited_by_class_name = nil
|
|
64
64
|
|
|
65
|
+
# Public: The foreign key to the inviting model (if invited_by_class_name is set)
|
|
66
|
+
# (default: :invited_by_id)
|
|
67
|
+
mattr_accessor :invited_by_foreign_key
|
|
68
|
+
@@invited_by_foreign_key = nil
|
|
69
|
+
|
|
65
70
|
# Public: The column name used for counter_cache column. If this is nil,
|
|
66
71
|
# the #invited_by association is declared without counter_cache. (default: nil)
|
|
67
72
|
mattr_accessor :invited_by_counter_cache
|
|
68
73
|
@@invited_by_counter_cache = nil
|
|
74
|
+
|
|
75
|
+
# Public: Auto-login after the user accepts the invite. If this is false,
|
|
76
|
+
# the user will need to manually log in after accepting the invite (default: false).
|
|
77
|
+
mattr_accessor :allow_insecure_sign_in_after_accept
|
|
78
|
+
@@allow_insecure_sign_in_after_accept = true
|
|
69
79
|
end
|
|
70
80
|
|
|
71
81
|
Devise.add_module :invitable, :controller => :invitations, :model => 'devise_invitable/model', :route => {:invitation => [nil, :new, :accept]}
|
|
@@ -6,28 +6,18 @@ class DeviseInvitableAddTo<%= table_name.camelize %> < ActiveRecord::Migration
|
|
|
6
6
|
t.datetime :invitation_sent_at
|
|
7
7
|
t.datetime :invitation_accepted_at
|
|
8
8
|
t.integer :invitation_limit
|
|
9
|
-
t.references :invited_by, :
|
|
9
|
+
t.references :invited_by, polymorphic: true
|
|
10
10
|
t.integer :invitations_count, default: 0
|
|
11
11
|
t.index :invitations_count
|
|
12
|
-
t.index :invitation_token, :
|
|
12
|
+
t.index :invitation_token, unique: true # for invitable
|
|
13
13
|
t.index :invited_by_id
|
|
14
14
|
end
|
|
15
|
-
|
|
16
|
-
# And allow null encrypted_password and password_salt:
|
|
17
|
-
change_column_null :<%= table_name %>, :encrypted_password, true
|
|
18
|
-
<% if class_name.constantize.columns_hash['password_salt'] -%>
|
|
19
|
-
change_column_null :<%= table_name %>, :password_salt, true
|
|
20
|
-
<% end -%>
|
|
21
15
|
end
|
|
22
16
|
|
|
23
17
|
def down
|
|
24
18
|
change_table :<%= table_name %> do |t|
|
|
25
|
-
t.remove_references :invited_by, :
|
|
19
|
+
t.remove_references :invited_by, polymorphic: true
|
|
26
20
|
t.remove :invitations_count, :invitation_limit, :invitation_sent_at, :invitation_accepted_at, :invitation_token, :invitation_created_at
|
|
27
21
|
end
|
|
28
|
-
change_column_null :<%= table_name %>, :encrypted_password, false
|
|
29
|
-
<% if class_name.constantize.columns_hash['password_salt'] -%>
|
|
30
|
-
change_column_null :<%= table_name %>, :password_salt,false
|
|
31
|
-
<% end -%>
|
|
32
22
|
end
|
|
33
23
|
end
|
|
@@ -52,11 +52,20 @@ module DeviseInvitable
|
|
|
52
52
|
# Default: nil
|
|
53
53
|
# config.invited_by_class_name = 'User'
|
|
54
54
|
|
|
55
|
+
# The foreign key to the inviting model (if invited_by_class_name is set)
|
|
56
|
+
# Default: :invited_by_id
|
|
57
|
+
# config.invited_by_foreign_key = :invited_by_id
|
|
58
|
+
|
|
55
59
|
# The column name used for counter_cache column. If this is nil,
|
|
56
60
|
# the #invited_by association is declared without counter_cache.
|
|
57
61
|
# Default: nil
|
|
58
62
|
# config.invited_by_counter_cache = :invitations_count
|
|
59
63
|
|
|
64
|
+
# Auto-login after the user accepts the invite. If this is false,
|
|
65
|
+
# the user will need to manually log in after accepting the invite.
|
|
66
|
+
# Default: false
|
|
67
|
+
# config.allow_insecure_sign_in_after_accept = true
|
|
68
|
+
|
|
60
69
|
CONTENT
|
|
61
70
|
end
|
|
62
71
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<h2><%= t 'devise.invitations.edit.header' %></h2>
|
|
2
2
|
|
|
3
|
-
<%= simple_form_for resource, :
|
|
3
|
+
<%= simple_form_for resource, as: resource_name, url: invitation_path(resource_name), html: { method: :put } do |f| %>
|
|
4
4
|
<%= devise_error_messages! %>
|
|
5
5
|
<%= f.hidden_field :invitation_token %>
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<h2><%= t "devise.invitations.new.header" %></h2>
|
|
2
2
|
|
|
3
|
-
<%= simple_form_for resource, :
|
|
3
|
+
<%= simple_form_for resource, as: resource_name, url: invitation_path(resource_name), html: { method: :post} do |f| %>
|
|
4
4
|
<%= devise_error_messages! %>
|
|
5
5
|
|
|
6
6
|
<% resource.class.invite_key_fields.each do |field| -%>
|
|
@@ -4,11 +4,11 @@ class ControllerHelpersTest < ActionController::TestCase
|
|
|
4
4
|
tests ApplicationController
|
|
5
5
|
|
|
6
6
|
test "after invite path defaults to after sign in path" do
|
|
7
|
-
assert_equal @controller.
|
|
7
|
+
assert_equal @controller.send(:after_sign_in_path_for, :user), @controller.after_invite_path_for(:user)
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
test "after accept path defaults to after sign in path" do
|
|
11
|
-
assert_equal @controller.
|
|
11
|
+
assert_equal @controller.send(:after_sign_in_path_for, :user), @controller.after_accept_path_for(:user)
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
test 'after invite path is customizable from application controller' do
|
|
@@ -36,4 +36,4 @@ class ControllerHelpersTest < ActionController::TestCase
|
|
|
36
36
|
assert Devise::InvitationsController.method_defined? :after_accept_path_for
|
|
37
37
|
assert !Devise::InvitationsController.instance_methods(false).include?(:after_accept_path_for)
|
|
38
38
|
end
|
|
39
|
-
end
|
|
39
|
+
end
|
|
@@ -25,7 +25,9 @@ class DeviseInvitable::RegistrationsControllerTest < ActionController::TestCase
|
|
|
25
25
|
sign_out @issuer
|
|
26
26
|
|
|
27
27
|
@invitee = User.where(:email => invitee_email).first
|
|
28
|
-
|
|
28
|
+
assert_nil @invitee.invitation_accepted_at
|
|
29
|
+
assert_not_nil @invitee.invitation_token
|
|
30
|
+
assert !@invitee.confirmed?
|
|
29
31
|
|
|
30
32
|
# sign_up the invitee
|
|
31
33
|
assert_difference('ActionMailer::Base.deliveries.size') do
|
|
@@ -47,8 +49,8 @@ class DeviseInvitable::RegistrationsControllerTest < ActionController::TestCase
|
|
|
47
49
|
assert_nil @invitee.invitation_token
|
|
48
50
|
assert @invitee.invited_by_id.present?
|
|
49
51
|
assert @invitee.invited_by_type.present?
|
|
50
|
-
assert !@invitee.confirmed?
|
|
51
52
|
assert @invitee.confirmation_token.present?
|
|
53
|
+
assert !@invitee.confirmed?
|
|
52
54
|
end
|
|
53
55
|
|
|
54
56
|
test "not invitable resources can register" do
|
|
@@ -4,7 +4,7 @@ require 'integration_tests_helper'
|
|
|
4
4
|
class InvitationRemoveTest < ActionDispatch::IntegrationTest
|
|
5
5
|
|
|
6
6
|
test 'invited user can choose to remove his account/invite' do
|
|
7
|
-
|
|
7
|
+
User.invite!(:email => "valid@email.com")
|
|
8
8
|
|
|
9
9
|
# remove!
|
|
10
10
|
visit remove_user_invitation_path(:invitation_token => Thread.current[:token])
|
|
@@ -64,6 +64,25 @@ class InvitationTest < ActionDispatch::IntegrationTest
|
|
|
64
64
|
assert_equal root_path, current_path
|
|
65
65
|
end
|
|
66
66
|
|
|
67
|
+
test 'invited user without password should not be able to sign in' do
|
|
68
|
+
user = User.invite!(:email => "valid@email.com")
|
|
69
|
+
user.password = 'test'
|
|
70
|
+
sign_in_as_user user
|
|
71
|
+
|
|
72
|
+
assert_equal new_user_session_path, current_path
|
|
73
|
+
assert page.has_css?('p#alert', :text => 'You have a pending invitation, accept it to finish creating your account.')
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
test 'invited user with password should not be able to sign in' do
|
|
77
|
+
user = User.invite!(:email => "valid@email.com")
|
|
78
|
+
user.password = '987654321'
|
|
79
|
+
user.save
|
|
80
|
+
sign_in_as_user user
|
|
81
|
+
|
|
82
|
+
assert_equal new_user_session_path, current_path
|
|
83
|
+
assert page.has_css?('p#alert', :text => 'You have a pending invitation, accept it to finish creating your account.')
|
|
84
|
+
end
|
|
85
|
+
|
|
67
86
|
test 'not authenticated user with invalid invitation token should not be able to set his password' do
|
|
68
87
|
user = User.invite!(:email => "valid@email.com")
|
|
69
88
|
user.accept_invitation!
|
|
@@ -80,7 +99,7 @@ class InvitationTest < ActionDispatch::IntegrationTest
|
|
|
80
99
|
end
|
|
81
100
|
assert_equal user_invitation_path, current_path
|
|
82
101
|
assert page.has_css?('#error_explanation li', :text => /Password .*doesn\'t match/)
|
|
83
|
-
assert user.
|
|
102
|
+
assert !user.confirmed?
|
|
84
103
|
end
|
|
85
104
|
|
|
86
105
|
test 'not authenticated user with valid data should be able to change his password' do
|
|
@@ -90,6 +109,7 @@ class InvitationTest < ActionDispatch::IntegrationTest
|
|
|
90
109
|
assert_equal root_path, current_path
|
|
91
110
|
assert page.has_css?('p#notice', :text => 'Your password was set successfully. You are now signed in.')
|
|
92
111
|
assert user.reload.valid_password?('987654321')
|
|
112
|
+
assert user.confirmed?
|
|
93
113
|
end
|
|
94
114
|
|
|
95
115
|
test 'after entering invalid data user should still be able to set his password' do
|
|
@@ -99,17 +119,32 @@ class InvitationTest < ActionDispatch::IntegrationTest
|
|
|
99
119
|
end
|
|
100
120
|
assert_equal user_invitation_path, current_path
|
|
101
121
|
assert page.has_css?('#error_explanation')
|
|
102
|
-
assert user.encrypted_password.blank?
|
|
103
122
|
|
|
104
123
|
set_password :visit => false
|
|
105
124
|
assert page.has_css?('p#notice', :text => 'Your password was set successfully. You are now signed in.')
|
|
106
125
|
assert user.reload.valid_password?('987654321')
|
|
107
126
|
end
|
|
108
127
|
|
|
109
|
-
test 'sign in user automatically after setting it\'s password' do
|
|
110
|
-
|
|
128
|
+
test 'sign in user automatically after setting it\'s password if config.allow_insecure_sign_in_after_accept is true' do
|
|
129
|
+
original_option_value = Devise.allow_insecure_sign_in_after_accept
|
|
130
|
+
Devise.allow_insecure_sign_in_after_accept = true
|
|
131
|
+
|
|
132
|
+
User.invite!(:email => "valid@email.com")
|
|
111
133
|
set_password :invitation_token => Thread.current[:token]
|
|
134
|
+
|
|
112
135
|
assert_equal root_path, current_path
|
|
136
|
+
Devise.allow_insecure_sign_in_after_accept = original_option_value
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
test 'does not sign in user automatically after setting it\'s password if config.allow_insecure_sign_in_after_accept is false' do
|
|
140
|
+
original_option_value = Devise.allow_insecure_sign_in_after_accept
|
|
141
|
+
Devise.allow_insecure_sign_in_after_accept = false
|
|
142
|
+
|
|
143
|
+
User.invite!(:email => "valid@email.com")
|
|
144
|
+
set_password :invitation_token => Thread.current[:token]
|
|
145
|
+
|
|
146
|
+
assert_equal new_user_session_path, current_path
|
|
147
|
+
Devise.allow_insecure_sign_in_after_accept = original_option_value
|
|
113
148
|
end
|
|
114
149
|
|
|
115
150
|
test 'clear token and set invitation_accepted_at after recover password instead of accept_invitation' do
|
|
@@ -213,10 +248,20 @@ class InvitationTest < ActionDispatch::IntegrationTest
|
|
|
213
248
|
end
|
|
214
249
|
|
|
215
250
|
test 'authenticated admin should be able to send an admin invitation' do
|
|
216
|
-
|
|
251
|
+
admin = Admin.create(:email => 'admin@test.com', :password => '123456', :password_confirmation => '123456')
|
|
252
|
+
sign_in_as_user admin
|
|
217
253
|
|
|
218
254
|
send_invitation new_admin_path
|
|
219
|
-
assert_equal
|
|
255
|
+
assert_equal edit_admin_registration_path(admin), current_path
|
|
256
|
+
assert page.has_css?('p#notice', :text => 'An invitation email has been sent to user@test.com.')
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
test 'authenticated admin should be redirected to own page after send a free invitation' do
|
|
260
|
+
admin = Admin.create(:email => 'admin@test.com', :password => '123456', :password_confirmation => '123456')
|
|
261
|
+
sign_in_as_user admin
|
|
262
|
+
|
|
263
|
+
send_invitation new_free_invitation_path
|
|
264
|
+
assert_equal edit_admin_registration_path(admin), current_path
|
|
220
265
|
assert page.has_css?('p#notice', :text => 'An invitation email has been sent to user@test.com.')
|
|
221
266
|
end
|
|
222
267
|
end
|
|
@@ -169,7 +169,7 @@ class InvitableTest < ActiveSupport::TestCase
|
|
|
169
169
|
end
|
|
170
170
|
|
|
171
171
|
test 'should set password and password confirmation from params' do
|
|
172
|
-
|
|
172
|
+
User.invite!(:email => "valid@email.com")
|
|
173
173
|
user = User.accept_invitation!(:invitation_token => Thread.current[:token], :password => '123456789', :password_confirmation => '123456789')
|
|
174
174
|
assert user.valid_password?('123456789')
|
|
175
175
|
end
|
|
@@ -181,6 +181,11 @@ class InvitableTest < ActiveSupport::TestCase
|
|
|
181
181
|
assert_not_equal old_encrypted_password, user.encrypted_password
|
|
182
182
|
end
|
|
183
183
|
|
|
184
|
+
test 'should not override password on invite!' do
|
|
185
|
+
user = User.invite!(:email => "valid@email.com", :password => 'password', :password_confirmation => 'password', :skip_invitation => true)
|
|
186
|
+
assert user.valid?
|
|
187
|
+
end
|
|
188
|
+
|
|
184
189
|
test 'should clear invitation token and set invitation_accepted_at while accepting the password' do
|
|
185
190
|
user = User.invite!(:email => "valid@email.com")
|
|
186
191
|
assert user.invitation_token.present?
|
|
@@ -406,7 +411,7 @@ class InvitableTest < ActiveSupport::TestCase
|
|
|
406
411
|
user = new_user(:password => nil, :password_confirmation => nil)
|
|
407
412
|
user.invite!
|
|
408
413
|
|
|
409
|
-
|
|
414
|
+
User.accept_invitation!(
|
|
410
415
|
:invitation_token => Thread.current[:token],
|
|
411
416
|
:password => 'new_password',
|
|
412
417
|
:password_confirmation => 'new_password'
|
|
@@ -431,6 +436,21 @@ class InvitableTest < ActiveSupport::TestCase
|
|
|
431
436
|
user.reload
|
|
432
437
|
assert !user.valid_password?('new_password')
|
|
433
438
|
end
|
|
439
|
+
|
|
440
|
+
test 'should check if created by invitation' do
|
|
441
|
+
user = User.invite!(:email => "valid@email.com")
|
|
442
|
+
assert user.created_by_invite?
|
|
443
|
+
|
|
444
|
+
invited_user = User.accept_invitation!(
|
|
445
|
+
:invitation_token => Thread.current[:token],
|
|
446
|
+
:password => 'new_password',
|
|
447
|
+
:password_confirmation => 'new_password',
|
|
448
|
+
:username => 'a'
|
|
449
|
+
)
|
|
450
|
+
user.reload
|
|
451
|
+
assert user.created_by_invite?
|
|
452
|
+
end
|
|
453
|
+
|
|
434
454
|
|
|
435
455
|
test 'should set other attributes on accepting invitation' do
|
|
436
456
|
user = new_user(:password => nil, :password_confirmation => nil)
|
|
@@ -480,13 +500,13 @@ class InvitableTest < ActiveSupport::TestCase
|
|
|
480
500
|
|
|
481
501
|
test 'should not send an invitation if we want to skip the invitation' do
|
|
482
502
|
assert_no_difference('ActionMailer::Base.deliveries.size') do
|
|
483
|
-
|
|
503
|
+
User.invite!(:email => "valid@email.com", :username => "a"*50, :skip_invitation => true)
|
|
484
504
|
end
|
|
485
505
|
end
|
|
486
506
|
|
|
487
507
|
test 'should not send an invitation if we want to skip the invitation with block' do
|
|
488
508
|
assert_no_difference('ActionMailer::Base.deliveries.size') do
|
|
489
|
-
|
|
509
|
+
User.invite!(:email => "valid@email.com", :username => "a"*50) do |u|
|
|
490
510
|
u.skip_invitation = true
|
|
491
511
|
end
|
|
492
512
|
end
|
|
@@ -606,24 +626,28 @@ class InvitableTest < ActiveSupport::TestCase
|
|
|
606
626
|
end
|
|
607
627
|
|
|
608
628
|
test 'should return instance with errors if invitation_token is nil' do
|
|
609
|
-
|
|
629
|
+
User.create(:email => 'admin@test.com', :password => '123456', :password_confirmation => '123456')
|
|
610
630
|
user = User.accept_invitation!
|
|
611
631
|
assert !user.errors.empty?
|
|
612
632
|
end
|
|
613
633
|
|
|
614
|
-
test "should count accepted and not accepted invitations" do
|
|
634
|
+
test "should count invited, created_by_invite, accepted and not accepted invitations" do
|
|
615
635
|
assert_equal 0, User.invitation_not_accepted.count
|
|
616
636
|
assert_equal 0, User.invitation_accepted.count
|
|
637
|
+
assert_equal 0, User.created_by_invite.count
|
|
617
638
|
|
|
618
639
|
User.invite!(:email => "invalid@email.com")
|
|
640
|
+
User.invite!(:email => "another_invalid@email.com")
|
|
619
641
|
user = User.invite!(:email => "valid@email.com")
|
|
620
642
|
|
|
621
|
-
assert_equal
|
|
643
|
+
assert_equal 3, User.invitation_not_accepted.count
|
|
622
644
|
assert_equal 0, User.invitation_accepted.count
|
|
645
|
+
assert_equal 3, User.created_by_invite.count
|
|
623
646
|
|
|
624
647
|
user.accept_invitation!
|
|
625
|
-
assert_equal
|
|
648
|
+
assert_equal 2, User.invitation_not_accepted.count
|
|
626
649
|
assert_equal 1, User.invitation_accepted.count
|
|
650
|
+
assert_equal 3, User.created_by_invite.count
|
|
627
651
|
end
|
|
628
652
|
|
|
629
653
|
test "should preserve return values of Devise::Recoverable#reset_password!" do
|
|
@@ -3,6 +3,13 @@ class ApplicationController < ActionController::Base
|
|
|
3
3
|
before_filter :configure_permitted_parameters, :if => :devise_controller?
|
|
4
4
|
|
|
5
5
|
protected
|
|
6
|
+
def after_sign_in_path_for(resource)
|
|
7
|
+
if resource.is_a? Admin
|
|
8
|
+
edit_admin_registration_path(resource)
|
|
9
|
+
else
|
|
10
|
+
super
|
|
11
|
+
end
|
|
12
|
+
end
|
|
6
13
|
|
|
7
14
|
def configure_permitted_parameters
|
|
8
15
|
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:email, :password, :bio) } if defined?(ActionController::StrongParameters)
|
|
@@ -3,4 +3,10 @@ class FreeInvitationsController < Devise::InvitationsController
|
|
|
3
3
|
def authenticate_inviter!
|
|
4
4
|
# everyone can invite
|
|
5
5
|
end
|
|
6
|
+
def current_inviter
|
|
7
|
+
current_admin || current_user
|
|
8
|
+
end
|
|
9
|
+
def after_invite_path_for(resource)
|
|
10
|
+
resource ? super : root_path
|
|
11
|
+
end
|
|
6
12
|
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<h2>Log in</h2>
|
|
2
|
+
|
|
3
|
+
<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
|
|
4
|
+
<div><%= f.label :email %><br />
|
|
5
|
+
<%= f.email_field :email, autofocus: true %></div>
|
|
6
|
+
|
|
7
|
+
<div><%= f.label :password %><br />
|
|
8
|
+
<%= f.password_field :password, autocomplete: "off" %></div>
|
|
9
|
+
|
|
10
|
+
<% if devise_mapping.rememberable? -%>
|
|
11
|
+
<div><%= f.check_box :remember_me %> <%= f.label :remember_me %></div>
|
|
12
|
+
<% end -%>
|
|
13
|
+
|
|
14
|
+
<div><%= f.submit "Log in" %></div>
|
|
15
|
+
<% end %>
|
|
16
|
+
|
|
17
|
+
<%= render "devise/shared/links" %>
|
|
@@ -124,11 +124,20 @@ Devise.setup do |config|
|
|
|
124
124
|
# Default: nil
|
|
125
125
|
# config.invited_by_class_name = 'User'
|
|
126
126
|
|
|
127
|
+
# The foreign key to the inviting model (if invited_by_class_name is set)
|
|
128
|
+
# Default: :invited_by_id
|
|
129
|
+
# config.invited_by_foreign_key = :invited_by_id
|
|
130
|
+
|
|
127
131
|
# The column name used for counter_cache column. If this is nil,
|
|
128
132
|
# the #invited_by association is declared without counter_cache.
|
|
129
133
|
# Default: nil
|
|
130
134
|
config.invited_by_counter_cache = :invitations_count
|
|
131
135
|
|
|
136
|
+
# Auto-login after the user accepts the invite. If this is false,
|
|
137
|
+
# the user will need to manually log in after accepting the invite.
|
|
138
|
+
# Default: true
|
|
139
|
+
# config.allow_insecure_sign_in_after_accept = false
|
|
140
|
+
|
|
132
141
|
# ==> Configuration for :confirmable
|
|
133
142
|
# A period that the user is allowed to access the website even without
|
|
134
143
|
# confirming his account. For instance, if set to 2.days, the user will be
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: devise_invitable
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.5.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sergio Cambra
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2015-07-23 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -66,6 +66,9 @@ executables: []
|
|
|
66
66
|
extensions: []
|
|
67
67
|
extra_rdoc_files: []
|
|
68
68
|
files:
|
|
69
|
+
- CHANGELOG
|
|
70
|
+
- LICENSE
|
|
71
|
+
- README.rdoc
|
|
69
72
|
- app/controllers/devise/invitations_controller.rb
|
|
70
73
|
- app/controllers/devise_invitable/registrations_controller.rb
|
|
71
74
|
- app/views/devise/invitations/edit.html.erb
|
|
@@ -90,9 +93,6 @@ files:
|
|
|
90
93
|
- lib/generators/devise_invitable/templates/simple_form_for/invitations/new.html.erb
|
|
91
94
|
- lib/generators/devise_invitable/views_generator.rb
|
|
92
95
|
- lib/generators/mongoid/devise_invitable_generator.rb
|
|
93
|
-
- LICENSE
|
|
94
|
-
- README.rdoc
|
|
95
|
-
- CHANGELOG
|
|
96
96
|
- test/functional/controller_helpers_test.rb
|
|
97
97
|
- test/functional/registrations_controller_test.rb
|
|
98
98
|
- test/generators/views_generator_test.rb
|
|
@@ -117,6 +117,7 @@ files:
|
|
|
117
117
|
- test/rails_app/app/models/octopussy.rb
|
|
118
118
|
- test/rails_app/app/models/user.rb
|
|
119
119
|
- test/rails_app/app/views/admins/new.html.erb
|
|
120
|
+
- test/rails_app/app/views/devise/sessions/new.html.erb
|
|
120
121
|
- test/rails_app/app/views/free_invitations/new.html.erb
|
|
121
122
|
- test/rails_app/app/views/home/index.html.erb
|
|
122
123
|
- test/rails_app/app/views/layouts/application.html.erb
|
|
@@ -167,7 +168,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
167
168
|
version: 1.3.6
|
|
168
169
|
requirements: []
|
|
169
170
|
rubyforge_project:
|
|
170
|
-
rubygems_version: 2.
|
|
171
|
+
rubygems_version: 2.4.5
|
|
171
172
|
signing_key:
|
|
172
173
|
specification_version: 4
|
|
173
174
|
summary: An invitation strategy for Devise
|
|
@@ -196,6 +197,7 @@ test_files:
|
|
|
196
197
|
- test/rails_app/app/models/octopussy.rb
|
|
197
198
|
- test/rails_app/app/models/user.rb
|
|
198
199
|
- test/rails_app/app/views/admins/new.html.erb
|
|
200
|
+
- test/rails_app/app/views/devise/sessions/new.html.erb
|
|
199
201
|
- test/rails_app/app/views/free_invitations/new.html.erb
|
|
200
202
|
- test/rails_app/app/views/home/index.html.erb
|
|
201
203
|
- test/rails_app/app/views/layouts/application.html.erb
|