devise_invitable 1.1.5 → 1.1.6

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 CHANGED
@@ -14,7 +14,7 @@ Install DeviseInvitable gem, it will also install dependencies (such as devise a
14
14
  Add DeviseInvitable to your Gemfile (and Devise if you weren't using them):
15
15
 
16
16
  gem 'devise', '>= 2.0.0'
17
- gem 'devise_invitable', '~> 1.0.0'
17
+ gem 'devise_invitable', '~> 1.1.0'
18
18
 
19
19
  === Automatic installation
20
20
 
@@ -67,8 +67,6 @@ or for a model that already exists, define a migration to add DeviseInvitable to
67
67
 
68
68
  # Allow null encrypted_password
69
69
  change_column :users, :encrypted_password, :string, :null => true
70
- # Allow null password_salt (add it if you are using Devise's encryptable module)
71
- change_column :users, :password_salt, :string, :null => true
72
70
 
73
71
  == Model configuration
74
72
 
@@ -230,7 +228,7 @@ If you want to get all records invited by a resource, you should define has_many
230
228
 
231
229
  For the default behavior, define it like this:
232
230
 
233
- has_many :invitations, :class_name => self.class.to_s, :as => :invited_by
231
+ has_many :invitations, :class_name => self.to_s, :as => :invited_by
234
232
 
235
233
  For the previous example, where admins send invitations to users, define it like this:
236
234
 
@@ -38,7 +38,8 @@ class Devise::InvitationsController < DeviseController
38
38
  self.resource = resource_class.accept_invitation!(resource_params)
39
39
 
40
40
  if resource.errors.empty?
41
- set_flash_message :notice, :updated
41
+ flash_message = resource.active_for_authentication? ? :updated : :updated_not_active
42
+ set_flash_message :notice, flash_message
42
43
  sign_in(resource_name, resource)
43
44
  respond_with resource, :location => after_accept_path_for(resource)
44
45
  else
@@ -0,0 +1,15 @@
1
+ class DeviseInvitable::RegistrationsController < Devise::RegistrationsController
2
+ protected
3
+
4
+ def build_resource(*args)
5
+ hash = args.pop || resource_params || {}
6
+ if hash[:email]
7
+ self.resource = resource_class.where(:email => hash[:email], :encrypted_password => '').first
8
+ if self.resource
9
+ self.resource.attributes = hash
10
+ self.resource.accept_invitation!
11
+ end
12
+ end
13
+ self.resource ||= super(hash, *args)
14
+ end
15
+ end
@@ -0,0 +1,65 @@
1
+ module DeviseInvitable
2
+ autoload :Inviter, 'devise_invitable/inviter'
3
+ autoload :Mailer, 'devise_invitable/mailer'
4
+ module Controllers
5
+ autoload :UrlHelpers, 'devise_invitable/controllers/url_helpers'
6
+ autoload :Registrations, 'devise_invitable/controllers/registrations'
7
+ autoload :Helpers, 'devise_invitable/controllers/helpers'
8
+ end
9
+ end
10
+
11
+ require 'devise'
12
+ require 'devise_invitable/routes'
13
+ require 'devise_invitable/rails'
14
+
15
+ module Devise
16
+ # Public: Validity period of the invitation token (default: 0). If
17
+ # invite_for is 0 or nil, the invitation will never expire.
18
+ # Set invite_for in the Devise configuration file (in config/initializers/devise.rb).
19
+ #
20
+ # config.invite_for = 2.weeks # => The invitation token will be valid 2 weeks
21
+ mattr_accessor :invite_for
22
+ @@invite_for = 0
23
+
24
+ # Public: Flag that force a record to be valid before being actually invited
25
+ # (default: false).
26
+ #
27
+ # Examples (in config/initializers/devise.rb)
28
+ #
29
+ # config.validate_on_invite = true
30
+ mattr_accessor :validate_on_invite
31
+ @@validate_on_invite = false
32
+
33
+ # Public: number of invitations the user is allowed to send
34
+ #
35
+ # Examples (in config/initializers/devise.rb)
36
+ #
37
+ # config.invitation_limit = nil
38
+ mattr_accessor :invitation_limit
39
+ @@invitation_limit = nil
40
+
41
+ # Public: The key to be used to check existing users when sending an invitation,
42
+ # and the regexp used to test it when validate_on_invite is not set.
43
+ #
44
+ # Examples (in config/initializers/devise.rb)
45
+ #
46
+ # config.invite_key = {:email => /\A[^@]+@[^@]+\z/}
47
+ mattr_accessor :invite_key
48
+ @@invite_key = {:email => Devise.email_regexp}
49
+
50
+ # Public: Resend invitation if user with invited status is invited again
51
+ # (default: true)
52
+ #
53
+ # Example (in config/initializers/devise.rb)
54
+ #
55
+ # config.resend_invitation = false
56
+ mattr_accessor :resend_invitation
57
+ @@resend_invitation = true
58
+
59
+ # Public: The class name of the inviting model. If this is nil,
60
+ # the #invited_by association is declared to be polymorphic. (default: nil)
61
+ mattr_accessor :invited_by_class_name
62
+ @@invited_by_class_name = nil
63
+ end
64
+
65
+ Devise.add_module :invitable, :controller => :invitations, :model => 'devise_invitable/model', :route => :invitation
@@ -0,0 +1,21 @@
1
+ module DeviseInvitable::Controllers::Helpers
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ hide_action :after_invite_path_for, :after_accept_path_for
6
+ end
7
+
8
+ def after_invite_path_for(resource)
9
+ after_sign_in_path_for(resource)
10
+ end
11
+
12
+ def after_accept_path_for(resource)
13
+ after_sign_in_path_for(resource)
14
+ end
15
+
16
+ protected
17
+ def authenticate_inviter!
18
+ send(:"authenticate_#{resource_name}!", :force => true)
19
+ end
20
+
21
+ end
@@ -0,0 +1,21 @@
1
+ module DeviseInvitable::Controllers::Registrations
2
+ def self.included(controller)
3
+ controller.alias_method_chain :build_resource, :invitation
4
+ end
5
+
6
+ protected
7
+
8
+ def build_resource_with_invitation(*args)
9
+ hash = args.pop || resource_params || {}
10
+ if hash[:email]
11
+ self.resource = resource_class.where(:email => hash[:email], :encrypted_password => '').first
12
+ if self.resource
13
+ puts self.resource.inspect
14
+ self.resource.attributes = hash
15
+ self.resource.accept_invitation!
16
+ puts self.resource.inspect
17
+ end
18
+ end
19
+ self.resource ||= build_resource_without_invitation(hash, *args)
20
+ end
21
+ end
@@ -218,7 +218,7 @@ module Devise
218
218
  invite_key_array = invite_key_fields
219
219
  attributes_hash = {}
220
220
  invite_key_array.each do |k,v|
221
- attributes_hash[k] = attributes.delete(k).try(:strip)
221
+ attributes_hash[k] = attributes.delete(k).to_s.strip
222
222
  end
223
223
 
224
224
  invitable = find_or_initialize_with_errors(invite_key_array, attributes_hash)
@@ -267,7 +267,7 @@ module Devise
267
267
  invitable = find_or_initialize_with_error_by(:invitation_token, attributes.delete(:invitation_token))
268
268
  invitable.errors.add(:invitation_token, :invalid) if invitable.invitation_token && invitable.persisted? && !invitable.valid_invitation?
269
269
  if invitable.errors.empty?
270
- invitable.attributes = attributes
270
+ invitable.assign_attributes(attributes, :as => inviter_role(self))
271
271
  invitable.accept_invitation!
272
272
  end
273
273
  invitable
@@ -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
+
@@ -0,0 +1,21 @@
1
+ module DeviseInvitable
2
+ class Engine < ::Rails::Engine
3
+
4
+ ActiveSupport.on_load(:action_controller) do
5
+ include DeviseInvitable::Controllers::UrlHelpers
6
+ include DeviseInvitable::Controllers::Helpers
7
+ end
8
+ ActiveSupport.on_load(:action_view) { include DeviseInvitable::Controllers::UrlHelpers }
9
+
10
+ # We use to_prepare instead of after_initialize here because Devise is a Rails engine; its
11
+ # mailer is reloaded like the rest of the user's app. Got to make sure that our mailer methods
12
+ # are included each time Devise::Mailer is (re)loaded.
13
+ config.to_prepare do
14
+ require 'devise/mailer'
15
+ Devise::Mailer.send :include, DeviseInvitable::Mailer
16
+ Devise::Mapping.send :include, DeviseInvitable::Mapping
17
+ end
18
+ config.after_initialize do
19
+ end
20
+ end
21
+ end
@@ -1,3 +1,3 @@
1
1
  module DeviseInvitable
2
- VERSION = '1.1.5'
2
+ VERSION = '1.1.6'
3
3
  end
@@ -36,8 +36,8 @@ module DeviseInvitable
36
36
 
37
37
  # The key to be used to check existing users when sending an invitation
38
38
  # and the regexp used to test it when validate_on_invite is not set.
39
- # config.invite_key = {:email => /\A[^@]+@[^@]+\z/}
40
- # config.invite_key = {:email => /\A[^@]+@[^@]+\z/, :username => nil}
39
+ # config.invite_key = {:email => /\\A[^@]+@[^@]+\\z/}
40
+ # config.invite_key = {:email => /\\A[^@]+@[^@]+\\z/, :username => nil}
41
41
 
42
42
  # Flag that force a record to be valid before being actually invited
43
43
  # Default: false
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: 25
4
+ hash: 31
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
8
  - 1
9
- - 5
10
- version: 1.1.5
9
+ - 6
10
+ version: 1.1.6
11
11
  platform: ruby
12
12
  authors:
13
13
  - Sergio Cambra
@@ -15,10 +15,11 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2013-01-24 00:00:00 Z
18
+ date: 2013-02-21 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
- type: :development
21
+ name: bundler
22
+ prerelease: false
22
23
  requirement: &id001 !ruby/object:Gem::Requirement
23
24
  none: false
24
25
  requirements:
@@ -30,27 +31,27 @@ dependencies:
30
31
  - 1
31
32
  - 0
32
33
  version: 1.1.0
34
+ type: :development
33
35
  version_requirements: *id001
34
- prerelease: false
35
- name: bundler
36
36
  - !ruby/object:Gem::Dependency
37
- type: :runtime
37
+ name: devise
38
+ prerelease: false
38
39
  requirement: &id002 !ruby/object:Gem::Requirement
39
40
  none: false
40
41
  requirements:
41
42
  - - ">="
42
43
  - !ruby/object:Gem::Version
43
- hash: 9
44
+ hash: 15
44
45
  segments:
45
46
  - 2
46
47
  - 1
47
- - 1
48
- version: 2.1.1
48
+ - 2
49
+ version: 2.1.2
50
+ type: :runtime
49
51
  version_requirements: *id002
50
- prerelease: false
51
- name: devise
52
52
  - !ruby/object:Gem::Dependency
53
- type: :runtime
53
+ name: railties
54
+ prerelease: false
54
55
  requirement: &id003 !ruby/object:Gem::Requirement
55
56
  none: false
56
57
  requirements:
@@ -61,11 +62,11 @@ dependencies:
61
62
  - 3
62
63
  - 0
63
64
  version: "3.0"
65
+ type: :runtime
64
66
  version_requirements: *id003
65
- prerelease: false
66
- name: actionmailer
67
67
  - !ruby/object:Gem::Dependency
68
- type: :runtime
68
+ name: actionmailer
69
+ prerelease: false
69
70
  requirement: &id004 !ruby/object:Gem::Requirement
70
71
  none: false
71
72
  requirements:
@@ -76,9 +77,8 @@ dependencies:
76
77
  - 3
77
78
  - 0
78
79
  version: "3.0"
80
+ type: :runtime
79
81
  version_requirements: *id004
80
- prerelease: false
81
- name: railties
82
82
  description: It adds support for send invitations by email (it requires to be authenticated) and accept the invitation by setting a password.
83
83
  email:
84
84
  - sergio@entrecables.com
@@ -91,28 +91,34 @@ extra_rdoc_files: []
91
91
  files:
92
92
  - app/controllers/devise/invitations_controller.rb
93
93
  - app/controllers/devise_invitable/registrations_controller.rb
94
- - app/views/devise/invitations/new.html.erb
94
+ - app/controllers/devise_invitable/registrations_controller.rb~
95
95
  - app/views/devise/invitations/edit.html.erb
96
+ - app/views/devise/invitations/new.html.erb
96
97
  - app/views/devise/mailer/invitation_instructions.html.erb
97
98
  - config/locales/en.yml
98
- - lib/generators/active_record/devise_invitable_generator.rb
99
- - lib/generators/active_record/templates/migration.rb
100
- - lib/generators/devise_invitable/install_generator.rb
101
- - lib/generators/devise_invitable/devise_invitable_generator.rb
102
- - lib/generators/devise_invitable/views_generator.rb
103
- - lib/generators/devise_invitable/templates/simple_form_for/invitations/new.html.erb
104
- - lib/generators/devise_invitable/templates/simple_form_for/invitations/edit.html.erb
105
- - lib/generators/mongoid/devise_invitable_generator.rb
106
99
  - lib/devise_invitable.rb
100
+ - lib/devise_invitable/mailer.rb
101
+ - lib/devise_invitable/model.rb
107
102
  - lib/devise_invitable/rails.rb
108
- - lib/devise_invitable/inviter.rb
109
- - lib/devise_invitable/mapping.rb
110
103
  - lib/devise_invitable/routes.rb
111
104
  - lib/devise_invitable/version.rb
112
- - lib/devise_invitable/mailer.rb
113
- - lib/devise_invitable/model.rb
114
105
  - lib/devise_invitable/controllers/helpers.rb
115
106
  - lib/devise_invitable/controllers/url_helpers.rb
107
+ - lib/devise_invitable/controllers/registrations.rb~
108
+ - lib/devise_invitable/controllers/helpers.rb~
109
+ - lib/devise_invitable/rails.rb~
110
+ - lib/devise_invitable/inviter.rb
111
+ - lib/devise_invitable/mapping.rb
112
+ - lib/devise_invitable/model.rb~
113
+ - lib/generators/active_record/devise_invitable_generator.rb
114
+ - lib/generators/active_record/templates/migration.rb
115
+ - lib/generators/devise_invitable/views_generator.rb
116
+ - lib/generators/devise_invitable/devise_invitable_generator.rb
117
+ - lib/generators/devise_invitable/install_generator.rb
118
+ - lib/generators/devise_invitable/templates/simple_form_for/invitations/edit.html.erb
119
+ - lib/generators/devise_invitable/templates/simple_form_for/invitations/new.html.erb
120
+ - lib/generators/mongoid/devise_invitable_generator.rb
121
+ - lib/devise_invitable.rb~
116
122
  - LICENSE
117
123
  - README.rdoc
118
124
  homepage: https://github.com/scambra/devise_invitable
@@ -150,7 +156,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
150
156
  requirements: []
151
157
 
152
158
  rubyforge_project:
153
- rubygems_version: 1.8.24
159
+ rubygems_version: 1.8.23
154
160
  signing_key:
155
161
  specification_version: 3
156
162
  summary: An invitation strategy for Devise