devise_invitable 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of devise_invitable might be problematic. Click here for more details.

data/README.rdoc CHANGED
@@ -1,5 +1,5 @@
1
1
  = DeviseInvitable
2
- {<img src="https://secure.travis-ci.org/scambra/devise_invitable.png"/>}[http://travis-ci.org/scambra/devise_invitable]
2
+ {<img src="http://travis-ci.org/scambra/devise_invitable.png"/>}[http://travis-ci.org/scambra/devise_invitable]
3
3
 
4
4
  It adds support to devise[http://github.com/plataformatec/devise] for send invitations by email (it requires to be authenticated) and accept the invitation setting the password.
5
5
 
@@ -90,7 +90,7 @@ or directly as parameters to the <tt>devise</tt> method:
90
90
 
91
91
  * invitation_limit: The number of invitations users can send. The default value of nil means users can send as many invites as they want. A setting of 0 means they can't send invitations. A setting n > 0 means they can send n invitations.
92
92
 
93
- * invite_key: The key to be used to check existing users when sending an invitation. The key must be an unique field. The default value is looking for users by email.
93
+ * invite_key: The key to be used to check existing users when sending an invitation. You can use multiple keys. This value must be a hash with the invite key as hash keys, and regexp to validate format as values. If you don't to validate the key you can set nil as validation format. The default value is looking for users by email and validating with Devise.email_regexp {:email => Devise.email_regexp}.
94
94
 
95
95
  * validate_on_invite: force a record to be valid before being actually invited.
96
96
 
@@ -272,4 +272,4 @@ Special thanks to rymai[http://github.com/rymai] for the Rails 3 support, his fo
272
272
 
273
273
  == Copyright
274
274
 
275
- Copyright (c) 2009 Sergio Cambra. See LICENSE for details.
275
+ Copyright (c) 2012 Sergio Cambra. See LICENSE for details.
@@ -0,0 +1,48 @@
1
+ class Devise::InvitationsController < ApplicationController
2
+ include Devise::Controllers::InternalHelpers
3
+
4
+ before_filter :authenticate_inviter!, :only => [:new, :create]
5
+ before_filter :require_no_authentication, :only => [:edit, :update]
6
+ helper_method :after_sign_in_path_for
7
+
8
+ # GET /resource/invitation/new
9
+ def new
10
+ build_resource
11
+ render_with_scope :new
12
+ end
13
+
14
+ # POST /resource/invitation
15
+ def create
16
+ self.resource = resource_class.invite!(params[resource_name])
17
+
18
+ if resource.errors.empty?
19
+ puts params.inspect
20
+ set_flash_message :notice, :send_instructions, :email => self.resource.email
21
+ redirect_to after_sign_in_path_for(resource_name)
22
+ else
23
+ render_with_scope :new
24
+ end
25
+ end
26
+
27
+ # GET /resource/invitation/accept?invitation_token=abcdef
28
+ def edit
29
+ if params[:invitation_token] && self.resource = resource_class.first(:conditions => { :invitation_token => params[:invitation_token] })
30
+ render_with_scope :edit
31
+ else
32
+ set_flash_message(:alert, :invitation_token_invalid)
33
+ redirect_to after_sign_out_path_for(resource_name)
34
+ end
35
+ end
36
+
37
+ # PUT /resource/invitation
38
+ def update
39
+ self.resource = resource_class.accept_invitation!(params[resource_name])
40
+
41
+ if resource.errors.empty?
42
+ set_flash_message :notice, :updated
43
+ sign_in_and_redirect(resource_name, resource)
44
+ else
45
+ render_with_scope :edit
46
+ end
47
+ end
48
+ end
@@ -3,8 +3,10 @@
3
3
  <%= form_for resource, :as => resource_name, :url => invitation_path(resource_name), :html => {:method => :post} do |f| %>
4
4
  <%= devise_error_messages! %>
5
5
 
6
- <p><%= f.label :email %><br />
7
- <%= f.text_field :email %></p>
6
+ <% resource.class.invite_key_fields.each do |field| -%>
7
+ <p><%= f.label field %><br />
8
+ <%= f.text_field field %></p>
9
+ <% end -%>
8
10
 
9
11
  <p><%= f.submit "Send an invitation" %></p>
10
12
  <% end %>
@@ -0,0 +1,14 @@
1
+ <h2>Send invitation</h2>
2
+
3
+ <%= form_for resource, :as => resource_name, :url => invitation_path(resource_name), :html => {:method => :post} do |f| %>
4
+ <%= devise_error_messages! %>
5
+
6
+ <% Array(resource.class.invite_key).each do |field| -%>
7
+ <p><%= f.label field %><br />
8
+ <%= f.text_field field %></p>
9
+ <% end -%>
10
+
11
+ <p><%= f.submit "Send an invitation" %></p>
12
+ <% end %>
13
+
14
+ <%= link_to "Home", after_sign_in_path_for(resource_name) %><br />
@@ -37,13 +37,14 @@ module Devise
37
37
  mattr_accessor :invitation_limit
38
38
  @@invitation_limit = nil
39
39
 
40
- # Public: The key to be used to check existing users when sending an invitation
40
+ # Public: The key to be used to check existing users when sending an invitation,
41
+ # and the regexp used to test it when validate_on_invite is not set.
41
42
  #
42
43
  # Examples (in config/initializers/devise.rb)
43
44
  #
44
- # config.invite_key = :email
45
+ # config.invite_key = {:email => /\A[^@]+@[^@]+\z/}
45
46
  mattr_accessor :invite_key
46
- @@invite_key = :email
47
+ @@invite_key = {:email => Devise.email_regexp}
47
48
 
48
49
  # Public: Resend invitation if user with invited status is invited again
49
50
  # (default: true)
@@ -0,0 +1,68 @@
1
+ require 'devise'
2
+
3
+ module DeviseInvitable
4
+ autoload :Inviter, 'devise_invitable/inviter'
5
+ end
6
+
7
+ require 'devise_invitable/mailer'
8
+ require 'devise_invitable/routes'
9
+ require 'devise_invitable/controllers/url_helpers'
10
+ require 'devise_invitable/controllers/registrations'
11
+ require 'devise_invitable/controllers/helpers'
12
+ require 'devise_invitable/rails'
13
+
14
+ module Devise
15
+ # Public: Validity period of the invitation token (default: 0). If
16
+ # invite_for is 0 or nil, the invitation will never expire.
17
+ # Set invite_for in the Devise configuration file (in config/initializers/devise.rb).
18
+ #
19
+ # config.invite_for = 2.weeks # => The invitation token will be valid 2 weeks
20
+ mattr_accessor :invite_for
21
+ @@invite_for = 0
22
+
23
+ # Public: Flag that force a record to be valid before being actually invited
24
+ # (default: false).
25
+ #
26
+ # Examples (in config/initializers/devise.rb)
27
+ #
28
+ # config.validate_on_invite = true
29
+ mattr_accessor :validate_on_invite
30
+ @@validate_on_invite = false
31
+
32
+ # Public: number of invitations the user is allowed to send
33
+ #
34
+ # Examples (in config/initializers/devise.rb)
35
+ #
36
+ # config.invitation_limit = nil
37
+ mattr_accessor :invitation_limit
38
+ @@invitation_limit = nil
39
+
40
+ # Public: The key to be used to check existing users when sending an invitation.
41
+ # It can be an array.
42
+ #
43
+ # Examples (in config/initializers/devise.rb)
44
+ #
45
+ # config.invite_key = :email
46
+ mattr_accessor :invite_key
47
+ @@invite_key = :email
48
+
49
+ # Public: The regexp to be used to test invite key when sending an invitation.
50
+ # It must be a hash indexed by invite_key
51
+ #
52
+ # Examples (in config/initializers/devise.rb)
53
+ #
54
+ # config.invite_key = :email
55
+ mattr_accessor :invite_key_validation_regexp
56
+ @@invite_key_validation_regexp = {:email => Devise.email_regexp}
57
+
58
+ # Public: Resend invitation if user with invited status is invited again
59
+ # (default: true)
60
+ #
61
+ # Example (in config/initializers/devise.rb)
62
+ #
63
+ # config.resend_invitation = false
64
+ mattr_accessor :resend_invitation
65
+ @@resend_invitation = true
66
+ end
67
+
68
+ Devise.add_module :invitable, :controller => :invitations, :model => 'devise_invitable/model', :route => :invitation
@@ -1,36 +1,40 @@
1
1
  module DeviseInvitable::Controllers::Registrations
2
2
  def self.included(controller)
3
- controller.send :around_filter, :destroy_if_previously_invited, :only => :create
3
+ controller.send :around_filter, :keep_invitation_info, :only => :create
4
4
  end
5
5
 
6
6
  protected
7
7
 
8
8
  def destroy_if_previously_invited
9
- invitation_info = {}
10
-
11
9
  hash = params[resource_name]
12
10
  if hash && hash[:email]
13
- resource = resource_class.first(:conditions => { :email => hash[:email], :encrypted_password => '' })
11
+ resource = resource_class.where(:email => hash[:email], :encrypted_password => '').first
14
12
  if resource
15
- invitation_info[:invitation_sent_at] = resource[:invitation_sent_at]
16
- invitation_info[:invited_by_id] = resource[:invited_by_id]
17
- invitation_info[:invited_by_type] = resource[:invited_by_type]
13
+ @invitation_info = {}
14
+ @invitation_info[:invitation_sent_at] = resource[:invitation_sent_at]
15
+ @invitation_info[:invited_by_id] = resource[:invited_by_id]
16
+ @invitation_info[:invited_by_type] = resource[:invited_by_type]
18
17
  resource.destroy
19
18
  end
20
19
  end
21
-
22
- # execute the action (create)
20
+ end
21
+
22
+ def keep_invitation_info
23
+ resource_invitable = resource_class.devise_modules.include?(:invitable)
24
+ destroy_if_previously_invited if resource_invitable
23
25
  yield
24
- # Note that the after_filter is executed at THIS position !
25
-
26
+ reset_invitation_info if resource_invitable
27
+ end
28
+
29
+ def reset_invitation_info
26
30
  # Restore info about the last invitation (for later reference)
27
31
  # Reset the invitation_info only, if invited_by_id is still nil at this stage:
28
- resource = resource_class.first(:conditions => { :email => hash[:email], :invited_by_id => nil })
29
- if resource
30
- resource[:invitation_sent_at] = invitation_info[:invitation_sent_at]
31
- resource[:invited_by_id] = invitation_info[:invited_by_id]
32
- resource[:invited_by_type] = invitation_info[:invited_by_type]
32
+ resource = resource_class.where(:email => params[resource_name][:email], :invited_by_id => nil).first
33
+ if resource && @invitation_info
34
+ resource[:invitation_sent_at] = @invitation_info[:invitation_sent_at]
35
+ resource[:invited_by_id] = @invitation_info[:invited_by_id]
36
+ resource[:invited_by_type] = @invitation_info[:invited_by_type]
33
37
  resource.save!
34
38
  end
35
39
  end
36
- end
40
+ end
@@ -0,0 +1,43 @@
1
+ module DeviseInvitable::Controllers::Registrations
2
+ def self.included(controller)
3
+ controller.send :around_filter, :keep_invitation_info, :only => :create
4
+ end
5
+
6
+ protected
7
+
8
+ def destroy_if_previously_invited
9
+ @invitation_info = {}
10
+
11
+ hash = params[resource_name]
12
+ if hash && hash[:email]
13
+ resource = resource_class.where(:email => hash[:email], :encrypted_password => '').first
14
+ if resource
15
+ @invitation_info[:invitation_sent_at] = resource[:invitation_sent_at]
16
+ @invitation_info[:invited_by_id] = resource[:invited_by_id]
17
+ @invitation_info[:invited_by_type] = resource[:invited_by_type]
18
+ resource.destroy
19
+ end
20
+ end
21
+ end
22
+
23
+ def keep_invitation_info
24
+ resource_invitable = resource_class.devise_modules.include?(:invitable)
25
+ destroy_if_previously_invited if resource_invitable
26
+ yield
27
+ reset_invitation_info if resource_invitable
28
+ end
29
+
30
+ def reset_invitation_info
31
+ puts @invitation_info.inspect
32
+ # Restore info about the last invitation (for later reference)
33
+ # Reset the invitation_info only, if invited_by_id is still nil at this stage:
34
+ resource = resource_class.where(:email => params[resource_name][:email], :invited_by_id => nil).first
35
+ puts resource.inspect
36
+ if resource
37
+ resource[:invitation_sent_at] = @invitation_info[:invitation_sent_at]
38
+ resource[:invited_by_id] = @invitation_info[:invited_by_id]
39
+ resource[:invited_by_type] = @invitation_info[:invited_by_type]
40
+ resource.save!
41
+ end
42
+ end
43
+ end
@@ -1,3 +1,5 @@
1
+ require 'active_support/deprecation'
2
+
1
3
  module Devise
2
4
  module Models
3
5
  # Invitable is responsible for sending invitation emails.
@@ -13,7 +15,7 @@ module Devise
13
15
  #
14
16
  # Examples:
15
17
  #
16
- # User.find(1).invited? # => true/false
18
+ # User.find(1).invited_to_sign_up? # => true/false
17
19
  # User.invite!(:email => 'someone@example.com') # => send invitation
18
20
  # User.accept_invitation!(:invitation_token => '...') # => accept invitation with a token
19
21
  # User.find(1).accept_invitation! # => accept invitation
@@ -22,6 +24,7 @@ module Devise
22
24
  extend ActiveSupport::Concern
23
25
 
24
26
  attr_accessor :skip_invitation
27
+ attr_accessor :completing_invite
25
28
 
26
29
  included do
27
30
  include ::DeviseInvitable::Inviter
@@ -36,23 +39,35 @@ module Devise
36
39
  # Accept an invitation by clearing invitation token and and setting invitation_accepted_at
37
40
  # Confirms it if model is confirmable
38
41
  def accept_invitation!
39
- if self.invited? && self.valid?
42
+ self.completing_invite = true
43
+ if self.invited_to_sign_up? && self.valid?
40
44
  run_callbacks :invitation_accepted do
41
45
  self.invitation_token = nil
42
46
  self.invitation_accepted_at = Time.now.utc if respond_to? :"invitation_accepted_at="
47
+ self.completing_invite = false
43
48
  self.save(:validate => false)
44
49
  end
45
50
  end
46
51
  end
47
52
 
53
+ # Verifies whether a user has accepted an invite, was never invited, or is in the process of accepting an invitation, or not
54
+ def accepting_or_not_invited?
55
+ !!completing_invite || !invited_to_sign_up?
56
+ end
57
+
48
58
  # Verifies whether a user has been invited or not
49
- def invited?
59
+ def invited_to_sign_up?
50
60
  persisted? && invitation_token.present?
51
61
  end
62
+
63
+ def invited?
64
+ invited_to_sign_up?
65
+ end
66
+ deprecate :invited?
52
67
 
53
68
  # Reset invitation token and send invitation again
54
69
  def invite!
55
- was_invited = invited?
70
+ was_invited = invited_to_sign_up?
56
71
  self.skip_confirmation! if self.new_record? && self.respond_to?(:skip_confirmation!)
57
72
  generate_invitation_token if self.invitation_token.nil?
58
73
  self.invitation_sent_at = Time.now.utc
@@ -71,18 +86,25 @@ module Devise
71
86
  # invited, we need to calculate if the invitation time has not expired
72
87
  # for this user, in other words, if the invitation is still valid.
73
88
  def valid_invitation?
74
- invited? && invitation_period_valid?
89
+ invited_to_sign_up? && invitation_period_valid?
75
90
  end
76
91
 
77
92
  # Only verify password when is not invited
78
93
  def valid_password?(password)
79
- super unless invited?
94
+ super unless invited_to_sign_up?
80
95
  end
81
96
 
82
97
  def reset_password!(new_password, new_password_confirmation)
83
98
  super
84
99
  accept_invitation!
85
100
  end
101
+
102
+ def invite_key_valid?
103
+ return true unless self.class.invite_key.is_a? Hash # FIXME: remove this line when deprecation is removed
104
+ self.class.invite_key.all? do |key, regexp|
105
+ regexp.nil? || self.send(key).try(:match, regexp)
106
+ end
107
+ end
86
108
 
87
109
  protected
88
110
  # Overriding the method in Devise's :validatable module so password is not required on inviting
@@ -125,6 +147,16 @@ module Devise
125
147
  end
126
148
 
127
149
  module ClassMethods
150
+ # Return fields to invite
151
+ def invite_key_fields
152
+ if invite_key.is_a? Hash
153
+ invite_key.keys
154
+ else
155
+ ActiveSupport::Deprecation.warn("invite_key should be a hash like {#{invite_key.inspect} => /.../}")
156
+ Array(invite_key)
157
+ end
158
+ end
159
+
128
160
  # Attempt to find a user by it's email. If a record is not found, create a new
129
161
  # user and send invitation to it. If user is found, returns the user with an
130
162
  # email already exists error.
@@ -132,16 +164,24 @@ module Devise
132
164
  # resend_invitation is set to false
133
165
  # Attributes must contain the user email, other attributes will be set in the record
134
166
  def _invite(attributes={}, invited_by=nil, &block)
135
- invitable = find_or_initialize_with_error_by(invite_key, attributes.delete(invite_key))
167
+ invite_key_array = invite_key_fields
168
+ attributes_hash = {}
169
+ invite_key_array.each do |k,v|
170
+ attributes_hash[k] = attributes.delete(k)
171
+ end
172
+
173
+ invitable = find_or_initialize_with_errors(invite_key_array, attributes_hash)
136
174
  invitable.assign_attributes(attributes, :as => inviter_role(invited_by))
137
175
  invitable.invited_by = invited_by
138
176
 
139
177
  invitable.skip_password = true
140
178
  invitable.valid? if self.validate_on_invite
141
179
  if invitable.new_record?
142
- invitable.errors.clear if !self.validate_on_invite and invitable.email.try(:match, Devise.email_regexp)
143
- else
144
- invitable.errors.add(invite_key, :taken) unless invitable.invited? && self.resend_invitation
180
+ invitable.errors.clear if !self.validate_on_invite and invitable.invite_key_valid?
181
+ elsif !invitable.invited_to_sign_up? || !self.resend_invitation
182
+ invite_key_array.each do |key|
183
+ invitable.errors.add(key, :taken)
184
+ end
145
185
  end
146
186
 
147
187
  if invitable.errors.empty?
@@ -0,0 +1,249 @@
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
+ def invite_key_valid?
103
+ return true unless self.class.invite_key.is_a? Hash # FIXME: remove this line when deprecation is removed
104
+ self.class.invite_key.all? do |key, regexp|
105
+ regexp.nil? || self.send(key).try(:match, regexp)
106
+ end
107
+ end
108
+
109
+ protected
110
+ # Overriding the method in Devise's :validatable module so password is not required on inviting
111
+ def password_required?
112
+ !@skip_password && super
113
+ end
114
+
115
+ # Deliver the invitation email
116
+ def deliver_invitation
117
+ ::Devise.mailer.invitation_instructions(self).deliver
118
+ end
119
+
120
+ # Checks if the invitation for the user is within the limit time.
121
+ # We do this by calculating if the difference between today and the
122
+ # invitation sent date does not exceed the invite for time configured.
123
+ # Invite_for is a model configuration, must always be an integer value.
124
+ #
125
+ # Example:
126
+ #
127
+ # # invite_for = 1.day and invitation_sent_at = today
128
+ # invitation_period_valid? # returns true
129
+ #
130
+ # # invite_for = 5.days and invitation_sent_at = 4.days.ago
131
+ # invitation_period_valid? # returns true
132
+ #
133
+ # # invite_for = 5.days and invitation_sent_at = 5.days.ago
134
+ # invitation_period_valid? # returns false
135
+ #
136
+ # # invite_for = nil
137
+ # invitation_period_valid? # will always return true
138
+ #
139
+ def invitation_period_valid?
140
+ invitation_sent_at && (self.class.invite_for.to_i.zero? || invitation_sent_at.utc >= self.class.invite_for.ago)
141
+ end
142
+
143
+ # Generates a new random token for invitation, and stores the time
144
+ # this token is being generated
145
+ def generate_invitation_token
146
+ self.invitation_token = self.class.invitation_token
147
+ end
148
+
149
+ module ClassMethods
150
+ # Return fields to invite
151
+ def invite_key_fields
152
+ if invite_key.is_a? Hash
153
+ invite_key.keys
154
+ else
155
+ ActiveSupport::Deprecation.warn("invite_key should be a hash like {#{invite_key.inspect} => /.../}")
156
+ Array(invite_key)
157
+ end
158
+ end
159
+
160
+ # Attempt to find a user by it's email. If a record is not found, create a new
161
+ # user and send invitation to it. If user is found, returns the user with an
162
+ # email already exists error.
163
+ # If user is found and still have pending invitation, email is resend unless
164
+ # resend_invitation is set to false
165
+ # Attributes must contain the user email, other attributes will be set in the record
166
+ def _invite(attributes={}, invited_by=nil, &block)
167
+ invite_key_array = invite_key_fields
168
+ attributes_hash = {}
169
+ invite_key_array.each do |k,v|
170
+ attributes_hash[k] = attributes.delete(k)
171
+ end
172
+
173
+ invitable = find_or_initialize_with_errors(invite_key_array, attributes_hash)
174
+ invitable.assign_attributes(attributes, :as => inviter_role(invited_by))
175
+ invitable.invited_by = invited_by
176
+
177
+ invitable.skip_password = true
178
+ invitable.valid? if self.validate_on_invite
179
+ if invitable.new_record?
180
+ invitable.errors.clear if !self.validate_on_invite and invitable.invite_key_valid?
181
+ elsif !invitable.invited_to_sign_up? || !self.resend_invitation
182
+ invite_key_fields.each do |key|
183
+ invitable.errors.add(key, :taken)
184
+ end
185
+ end
186
+
187
+ if invitable.errors.empty?
188
+ yield invitable if block_given?
189
+ mail = invitable.invite!
190
+ end
191
+ [invitable, mail]
192
+ end
193
+
194
+ # Override this method if the invitable is using Mass Assignment Security
195
+ # and the inviter has a non-default role.
196
+ def inviter_role(inviter)
197
+ :default
198
+ end
199
+
200
+ def invite!(attributes={}, invited_by=nil, &block)
201
+ invitable, mail = _invite(attributes, invited_by, &block)
202
+ invitable
203
+ end
204
+
205
+ def invite_mail!(attributes={}, invited_by=nil, &block)
206
+ invitable, mail = _invite(attributes, invited_by, &block)
207
+ mail
208
+ end
209
+
210
+ # Attempt to find a user by it's invitation_token to set it's password.
211
+ # If a user is found, reset it's password and automatically try saving
212
+ # the record. If not user is found, returns a new user containing an
213
+ # error in invitation_token attribute.
214
+ # Attributes must contain invitation_token, password and confirmation
215
+ def accept_invitation!(attributes={})
216
+ invitable = find_or_initialize_with_error_by(:invitation_token, attributes.delete(:invitation_token))
217
+ invitable.errors.add(:invitation_token, :invalid) if invitable.invitation_token && invitable.persisted? && !invitable.valid_invitation?
218
+ if invitable.errors.empty?
219
+ invitable.attributes = attributes
220
+ invitable.accept_invitation!
221
+ end
222
+ invitable
223
+ end
224
+
225
+ # Generate a token checking if one does not already exist in the database.
226
+ def invitation_token
227
+ generate_token(:invitation_token)
228
+ end
229
+
230
+ # Callback convenience methods
231
+ def before_invitation_accepted(*args, &blk)
232
+ set_callback(:invitation_accepted, :before, *args, &blk)
233
+ end
234
+
235
+ def after_invitation_accepted(*args, &blk)
236
+ set_callback(:invitation_accepted, :after, *args, &blk)
237
+ end
238
+
239
+
240
+ Devise::Models.config(self, :invite_for)
241
+ Devise::Models.config(self, :validate_on_invite)
242
+ Devise::Models.config(self, :invitation_limit)
243
+ Devise::Models.config(self, :invite_key)
244
+ Devise::Models.config(self, :resend_invitation)
245
+ end
246
+ end
247
+ end
248
+ end
249
+
@@ -1,3 +1,3 @@
1
1
  module DeviseInvitable
2
- VERSION = '1.0.1'
2
+ VERSION = '1.0.2'
3
3
  end
@@ -32,7 +32,9 @@ module DeviseInvitable
32
32
  # config.invitation_limit = 5
33
33
 
34
34
  # The key to be used to check existing users when sending an invitation
35
- # config.invite_key = :email
35
+ # and the regexp used to test it when validate_on_invite is not set.
36
+ # config.invite_key = {:email => /\A[^@]+@[^@]+\z/}
37
+ # config.invite_key = {:email => /\A[^@]+@[^@]+\z/, :username => nil}
36
38
 
37
39
  # Flag that force a record to be valid before being actually invited
38
40
  # Default: false
@@ -0,0 +1,54 @@
1
+ module DeviseInvitable
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ source_root File.expand_path("../../templates", __FILE__)
5
+ desc "Add DeviseInvitable config variables to the Devise initializer and copy DeviseInvitable locale files to your application."
6
+
7
+ # def devise_install
8
+ # invoke "devise:install"
9
+ # end
10
+
11
+ def add_config_options_to_initializer
12
+ devise_initializer_path = "config/initializers/devise.rb"
13
+ if File.exist?(devise_initializer_path)
14
+ old_content = File.read(devise_initializer_path)
15
+
16
+ if old_content.match(Regexp.new(/^\s# ==> Configuration for :invitable\n/))
17
+ false
18
+ else
19
+ inject_into_file(devise_initializer_path, :before => " # ==> Configuration for :confirmable\n") do
20
+ <<-CONTENT
21
+ # ==> Configuration for :invitable
22
+ # The period the generated invitation token is valid, after
23
+ # this period, the invited resource won't be able to accept the invitation.
24
+ # When invite_for is 0 (the default), the invitation won't expire.
25
+ # config.invite_for = 2.weeks
26
+
27
+ # Number of invitations users can send.
28
+ # If invitation_limit is nil, users can send unlimited invitations.
29
+ # If invitation_limit is 0, users can't send invitations.
30
+ # If invitation_limit n > 0, users can send n invitations.
31
+ # Default: nil
32
+ # config.invitation_limit = 5
33
+
34
+ # The key to be used to check existing users when sending an invitation.
35
+ # config.invite_key = {:email => /\A[^@]+@[^@]+\z/}
36
+ # config.invite_key = {:email => /\A[^@]+@[^@]+\z/, :username => nil}
37
+
38
+ # Flag that force a record to be valid before being actually invited
39
+ # Default: false
40
+ # config.validate_on_invite = true
41
+
42
+ CONTENT
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ def copy_locale
49
+ copy_file "../../../config/locales/en.yml", "config/locales/devise_invitable.en.yml"
50
+ end
51
+
52
+ end
53
+ end
54
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devise_invitable
3
3
  version: !ruby/object:Gem::Version
4
- hash: 21
4
+ hash: 19
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
8
  - 0
9
- - 1
10
- version: 1.0.1
9
+ - 2
10
+ version: 1.0.2
11
11
  platform: ruby
12
12
  authors:
13
13
  - Sergio Cambra
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-04-03 00:00:00 Z
18
+ date: 2012-05-26 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: bundler
@@ -74,27 +74,33 @@ extensions: []
74
74
  extra_rdoc_files: []
75
75
 
76
76
  files:
77
- - app/controllers/devise/invitations_controller.rb
77
+ - app/views/devise/mailer/invitation_instructions.html.erb
78
+ - app/views/devise/invitations/new.html.erb~
78
79
  - app/views/devise/invitations/edit.html.erb
79
80
  - app/views/devise/invitations/new.html.erb
80
- - app/views/devise/mailer/invitation_instructions.html.erb
81
+ - app/controllers/devise/invitations_controller.rb
82
+ - app/controllers/devise/invitations_controller.rb~
81
83
  - config/locales/en.yml
82
- - lib/devise_invitable/controllers/helpers.rb
83
- - lib/devise_invitable/controllers/registrations.rb
84
- - lib/devise_invitable/controllers/url_helpers.rb
85
- - lib/devise_invitable/inviter.rb
86
- - lib/devise_invitable/mailer.rb
84
+ - lib/devise_invitable.rb
85
+ - lib/devise_invitable.rb~
86
+ - lib/devise_invitable/routes.rb
87
87
  - lib/devise_invitable/model.rb
88
88
  - lib/devise_invitable/rails.rb
89
- - lib/devise_invitable/routes.rb
89
+ - lib/devise_invitable/mailer.rb
90
90
  - lib/devise_invitable/version.rb
91
- - lib/devise_invitable.rb
92
- - lib/generators/active_record/devise_invitable_generator.rb
91
+ - lib/devise_invitable/model.rb~
92
+ - lib/devise_invitable/inviter.rb
93
+ - lib/devise_invitable/controllers/url_helpers.rb
94
+ - lib/devise_invitable/controllers/helpers.rb
95
+ - lib/devise_invitable/controllers/registrations.rb~
96
+ - lib/devise_invitable/controllers/registrations.rb
93
97
  - lib/generators/active_record/templates/migration.rb
94
- - lib/generators/devise_invitable/devise_invitable_generator.rb
98
+ - lib/generators/active_record/devise_invitable_generator.rb
99
+ - lib/generators/mongoid/devise_invitable_generator.rb
95
100
  - lib/generators/devise_invitable/install_generator.rb
96
101
  - lib/generators/devise_invitable/views_generator.rb
97
- - lib/generators/mongoid/devise_invitable_generator.rb
102
+ - lib/generators/devise_invitable/devise_invitable_generator.rb
103
+ - lib/generators/devise_invitable/install_generator.rb~
98
104
  - LICENSE
99
105
  - README.rdoc
100
106
  homepage: https://github.com/scambra/devise_invitable
@@ -132,7 +138,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
132
138
  requirements: []
133
139
 
134
140
  rubyforge_project:
135
- rubygems_version: 1.8.15
141
+ rubygems_version: 1.8.10
136
142
  signing_key:
137
143
  specification_version: 3
138
144
  summary: An invitation strategy for Devise