devise_invitable 1.0.2 → 1.0.3

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.

Potentially problematic release.


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

data/README.rdoc CHANGED
@@ -96,6 +96,8 @@ or directly as parameters to the <tt>devise</tt> method:
96
96
 
97
97
  * resend_invitation: resend invitation if user with invited status is invited again. Enabled by default.
98
98
 
99
+ * invited_by_class_name: The class name of the inviting model. If this is nil, polymorphic association is used.
100
+
99
101
  For more details, see <tt>config/initializers/devise.rb</tt> (after you invoked the "devise_invitable:install" generator described above).
100
102
 
101
103
  == Configuring views
@@ -153,6 +155,15 @@ You can add :skip_invitation to attributes hash if skip_invitation is added to a
153
155
  User.invite!(:email => "new_user@example.com", :name => "John Doe", :skip_invitation => true)
154
156
  # => the record will be created, but the invitation email will not be sent
155
157
 
158
+ You can send an invitation to an existing user if your workflow creates them separately:
159
+
160
+ user = User.find(42)
161
+ user.invite!(current_user) # current user is optional to set the invited_by attribute
162
+
163
+ You can also set <tt>invited_by</tt> when using the <tt>invite!</tt> class method:
164
+
165
+ User.invite!({:email => "new_user@example.com"}, current_user) # current_user will be set as invited_by
166
+
156
167
  === Accept an invitation
157
168
 
158
169
  To accept an invitation with a token use the <tt>accept_invitation!</tt> class method. <tt>:invitation_token</tt> must be present in the parameters hash. You can also include other attributes in the hash.
@@ -171,6 +182,13 @@ A callback event is fired before and after an invitation is accepted (User#accep
171
182
 
172
183
  The callbacks support all options and arguments available to the standard callbacks provided by AR.
173
184
 
185
+ === Scopes
186
+
187
+ A pair of scopes to find those users that have accepted, and those that have not accepted, invitations are defined:
188
+
189
+ User.invitation_accepted # => returns all Users for whom the invitation_accepted_at attribute is not nil
190
+ User.invitation_not_accepted # => returns all Users for whom the invitation_accepted_at attribute is nil
191
+
174
192
  == Integration in a Rails application
175
193
 
176
194
  Since the invitations controller take care of all the creation/acceptation of an invitation, in most cases you wouldn't call the <tt>invite!</tt> and <tt>accept_invitation!</tt> methods directly.
@@ -1,4 +1,4 @@
1
- <h2>Set your password</h2>
1
+ <h2><%= t 'devise.invitations.edit.header' %></h2>
2
2
 
3
3
  <%= form_for resource, :as => resource_name, :url => invitation_path(resource_name), :html => { :method => :put } do |f| %>
4
4
  <%= devise_error_messages! %>
@@ -10,5 +10,5 @@
10
10
  <p><%= f.label :password_confirmation %><br />
11
11
  <%= f.password_field :password_confirmation %></p>
12
12
 
13
- <p><%= f.submit "Set my password" %></p>
13
+ <p><%= f.submit t("devise.invitations.edit.submit_button") %></p>
14
14
  <% end %>
@@ -1,4 +1,4 @@
1
- <h2>Send invitation</h2>
1
+ <h2><%= t "devise.invitations.new.header" %></h2>
2
2
 
3
3
  <%= form_for resource, :as => resource_name, :url => invitation_path(resource_name), :html => {:method => :post} do |f| %>
4
4
  <%= devise_error_messages! %>
@@ -8,7 +8,5 @@
8
8
  <%= f.text_field field %></p>
9
9
  <% end -%>
10
10
 
11
- <p><%= f.submit "Send an invitation" %></p>
11
+ <p><%= f.submit t("devise.invitations.new.submit_button") %></p>
12
12
  <% end %>
13
-
14
- <%= link_to "Home", after_sign_in_path_for(resource_name) %><br />
@@ -5,6 +5,12 @@ en:
5
5
  invitation_token_invalid: 'The invitation token provided is not valid!'
6
6
  updated: 'Your password was set successfully. You are now signed in.'
7
7
  no_invitations_remaining: "No invitations remaining"
8
+ new:
9
+ header: "Send invitation"
10
+ submit_button: "Send an invitation"
11
+ edit:
12
+ header: "Set your password"
13
+ submit_button: "Set my password"
8
14
  mailer:
9
15
  invitation_instructions:
10
16
  subject: 'Invitation instructions'
@@ -54,6 +54,11 @@ module Devise
54
54
  # config.resend_invitation = false
55
55
  mattr_accessor :resend_invitation
56
56
  @@resend_invitation = true
57
+
58
+ # Public: The class name of the inviting model. If this is nil,
59
+ # the #invited_by association is declared to be polymorphic. (default: nil)
60
+ mattr_accessor :invited_by_class_name
61
+ @@invited_by_class_name = nil
57
62
  end
58
63
 
59
64
  Devise.add_module :invitable, :controller => :invitations, :model => 'devise_invitable/model', :route => :invitation
@@ -10,30 +10,29 @@ module DeviseInvitable::Controllers::Registrations
10
10
  if hash && hash[:email]
11
11
  resource = resource_class.where(:email => hash[:email], :encrypted_password => '').first
12
12
  if resource
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]
13
+ @invitation_info = Hash[resource.invitation_fields.map {|field|
14
+ [field, resource[field]]
15
+ }]
17
16
  resource.destroy
18
17
  end
19
18
  end
20
19
  end
21
-
20
+
22
21
  def keep_invitation_info
23
22
  resource_invitable = resource_class.devise_modules.include?(:invitable)
24
23
  destroy_if_previously_invited if resource_invitable
25
24
  yield
26
25
  reset_invitation_info if resource_invitable
27
26
  end
28
-
27
+
29
28
  def reset_invitation_info
30
29
  # Restore info about the last invitation (for later reference)
31
30
  # Reset the invitation_info only, if invited_by_id is still nil at this stage:
32
31
  resource = resource_class.where(:email => params[resource_name][:email], :invited_by_id => nil).first
33
32
  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
+ resource.invitation_fields.each do |field|
34
+ resource[field] = @invitation_info[field]
35
+ end
37
36
  resource.save!
38
37
  end
39
38
  end
@@ -1,6 +1,6 @@
1
1
  module DeviseInvitable
2
2
  module Mailer
3
-
3
+
4
4
  # Deliver an invitation email
5
5
  def invitation_instructions(record)
6
6
  devise_mail(record, :invitation_instructions)
@@ -27,55 +27,93 @@ module Devise
27
27
  attr_accessor :completing_invite
28
28
 
29
29
  included do
30
- include ::DeviseInvitable::Inviter
31
- belongs_to :invited_by, :polymorphic => true
32
-
30
+ include ::DeviseInvitable::Inviter
31
+ if Devise.invited_by_class_name
32
+ belongs_to :invited_by, :class_name => Devise.invited_by_class_name
33
+ else
34
+ belongs_to :invited_by, :polymorphic => true
35
+ end
36
+
33
37
  include ActiveSupport::Callbacks
34
38
  define_callbacks :invitation_accepted
35
-
39
+
36
40
  attr_writer :skip_password
41
+
42
+ scope :invitation_not_accepted, where(:invitation_accepted_at => nil)
43
+ if defined?(Mongoid) && self < Mongoid::Document
44
+ scope :invitation_accepted, where(:invitation_accepted_at.ne => nil)
45
+ else
46
+ scope :invitation_accepted, where(arel_table[:invitation_accepted_at].not_eq(nil))
47
+ end
48
+ end
49
+
50
+ def self.required_fields(klass)
51
+ fields = [:invitation_token, :invitation_sent_at, :invitation_accepted_at,
52
+ :invitation_limit, :invited_by_id, :invited_by_type]
53
+ if Devise.invited_by_class_name
54
+ fields -= :invited_by_type
55
+ end
56
+ fields
57
+ end
58
+
59
+ def invitation_fields
60
+ fields = [:invitation_sent_at, :invited_by_id, :invited_by_type]
61
+ if Devise.invited_by_class_name
62
+ fields -= :invited_by_type
63
+ end
64
+ fields
37
65
  end
38
66
 
39
67
  # Accept an invitation by clearing invitation token and and setting invitation_accepted_at
40
68
  # Confirms it if model is confirmable
41
69
  def accept_invitation!
42
- self.completing_invite = true
70
+ self.invitation_accepted_at = Time.now.utc
43
71
  if self.invited_to_sign_up? && self.valid?
44
72
  run_callbacks :invitation_accepted do
45
73
  self.invitation_token = nil
46
- self.invitation_accepted_at = Time.now.utc if respond_to? :"invitation_accepted_at="
47
- self.completing_invite = false
48
74
  self.save(:validate => false)
49
75
  end
50
76
  end
51
77
  end
52
78
 
53
- # Verifies whether a user has accepted an invite, was never invited, or is in the process of accepting an invitation, or not
79
+ # Verifies whether a user has accepted an invitation (or is accepting it), or was never invited
54
80
  def accepting_or_not_invited?
55
- !!completing_invite || !invited_to_sign_up?
81
+ ActiveSupport::Deprecation.warn "accepting_or_not_invited? is deprecated and will be removed from DeviseInvitable 1.1.0 (use accepted_or_not_invited? instead)"
82
+ accepted_or_not_invited?
56
83
  end
57
84
 
58
85
  # Verifies whether a user has been invited or not
59
86
  def invited_to_sign_up?
60
87
  persisted? && invitation_token.present?
61
88
  end
62
-
89
+
90
+ # Verifies whether a user accepted an invitation (or is accepting it)
91
+ def invitation_accepted?
92
+ invitation_accepted_at.present?
93
+ end
94
+
95
+ # Verifies whether a user has accepted an invitation (or is accepting it), or was never invited
96
+ def accepted_or_not_invited?
97
+ invitation_accepted? || !invited_to_sign_up?
98
+ end
99
+
63
100
  def invited?
101
+ ActiveSupport::Deprecation.warn "invited? is deprecated and will be removed from DeviseInvitable 1.1.0 (use invited_to_sign_up? instead)"
64
102
  invited_to_sign_up?
65
103
  end
66
- deprecate :invited?
67
104
 
68
105
  # Reset invitation token and send invitation again
69
- def invite!
106
+ def invite!(invited_by = nil)
70
107
  was_invited = invited_to_sign_up?
71
108
  self.skip_confirmation! if self.new_record? && self.respond_to?(:skip_confirmation!)
72
109
  generate_invitation_token if self.invitation_token.nil?
73
110
  self.invitation_sent_at = Time.now.utc
111
+ self.invited_by = invited_by if invited_by
74
112
 
75
113
  # Call these before_validate methods since we aren't validating on save
76
114
  self.downcase_keys if self.new_record? && self.respond_to?(:downcase_keys)
77
115
  self.strip_whitespace if self.new_record? && self.respond_to?(:strip_whitespace)
78
-
116
+
79
117
  if save(:validate => false)
80
118
  self.invited_by.decrement_invitation_limit! if !was_invited and self.invited_by.present?
81
119
  deliver_invitation unless @skip_invitation
@@ -93,12 +131,12 @@ module Devise
93
131
  def valid_password?(password)
94
132
  super unless invited_to_sign_up?
95
133
  end
96
-
134
+
97
135
  def reset_password!(new_password, new_password_confirmation)
98
136
  super
99
137
  accept_invitation!
100
138
  end
101
-
139
+
102
140
  def invite_key_valid?
103
141
  return true unless self.class.invite_key.is_a? Hash # FIXME: remove this line when deprecation is removed
104
142
  self.class.invite_key.all? do |key, regexp|
@@ -156,7 +194,7 @@ module Devise
156
194
  Array(invite_key)
157
195
  end
158
196
  end
159
-
197
+
160
198
  # Attempt to find a user by it's email. If a record is not found, create a new
161
199
  # user and send invitation to it. If user is found, returns the user with an
162
200
  # email already exists error.
@@ -190,7 +228,7 @@ module Devise
190
228
  end
191
229
  [invitable, mail]
192
230
  end
193
-
231
+
194
232
  # Override this method if the invitable is using Mass Assignment Security
195
233
  # and the inviter has a non-default role.
196
234
  def inviter_role(inviter)
@@ -226,16 +264,16 @@ module Devise
226
264
  def invitation_token
227
265
  generate_token(:invitation_token)
228
266
  end
229
-
267
+
230
268
  # Callback convenience methods
231
269
  def before_invitation_accepted(*args, &blk)
232
270
  set_callback(:invitation_accepted, :before, *args, &blk)
233
271
  end
234
-
272
+
235
273
  def after_invitation_accepted(*args, &blk)
236
274
  set_callback(:invitation_accepted, :after, *args, &blk)
237
275
  end
238
-
276
+
239
277
 
240
278
  Devise::Models.config(self, :invite_for)
241
279
  Devise::Models.config(self, :validate_on_invite)
@@ -1,6 +1,6 @@
1
1
  module ActionDispatch::Routing
2
2
  class Mapper
3
-
3
+
4
4
  protected
5
5
  def devise_invitation(mapping, controllers)
6
6
  resource :invitation, :only => [:new, :create, :update],
@@ -8,6 +8,6 @@ module ActionDispatch::Routing
8
8
  get :edit, :path => mapping.path_names[:accept], :as => :accept
9
9
  end
10
10
  end
11
-
11
+
12
12
  end
13
13
  end
@@ -1,3 +1,3 @@
1
1
  module DeviseInvitable
2
- VERSION = '1.0.2'
2
+ VERSION = '1.0.3'
3
3
  end
@@ -3,16 +3,16 @@ module DeviseInvitable
3
3
  class InstallGenerator < Rails::Generators::Base
4
4
  source_root File.expand_path("../../templates", __FILE__)
5
5
  desc "Add DeviseInvitable config variables to the Devise initializer and copy DeviseInvitable locale files to your application."
6
-
6
+
7
7
  # def devise_install
8
8
  # invoke "devise:install"
9
9
  # end
10
-
10
+
11
11
  def add_config_options_to_initializer
12
12
  devise_initializer_path = "config/initializers/devise.rb"
13
13
  if File.exist?(devise_initializer_path)
14
14
  old_content = File.read(devise_initializer_path)
15
-
15
+
16
16
  if old_content.match(Regexp.new(/^\s# ==> Configuration for :invitable\n/))
17
17
  false
18
18
  else
@@ -23,20 +23,20 @@ module DeviseInvitable
23
23
  # this period, the invited resource won't be able to accept the invitation.
24
24
  # When invite_for is 0 (the default), the invitation won't expire.
25
25
  # config.invite_for = 2.weeks
26
-
26
+
27
27
  # Number of invitations users can send.
28
28
  # If invitation_limit is nil, users can send unlimited invitations.
29
29
  # If invitation_limit is 0, users can't send invitations.
30
30
  # If invitation_limit n > 0, users can send n invitations.
31
31
  # Default: nil
32
32
  # config.invitation_limit = 5
33
-
33
+
34
34
  # The key to be used to check existing users when sending an invitation
35
35
  # and the regexp used to test it when validate_on_invite is not set.
36
36
  # config.invite_key = {:email => /\A[^@]+@[^@]+\z/}
37
37
  # config.invite_key = {:email => /\A[^@]+@[^@]+\z/, :username => nil}
38
-
39
- # Flag that force a record to be valid before being actually invited
38
+
39
+ # Flag that force a record to be valid before being actually invited
40
40
  # Default: false
41
41
  # config.validate_on_invite = true
42
42
 
@@ -45,11 +45,11 @@ CONTENT
45
45
  end
46
46
  end
47
47
  end
48
-
48
+
49
49
  def copy_locale
50
50
  copy_file "../../../config/locales/en.yml", "config/locales/devise_invitable.en.yml"
51
51
  end
52
-
52
+
53
53
  end
54
54
  end
55
55
  end
@@ -0,0 +1,11 @@
1
+ <h2><%= t 'devise.invitations.edit.header' %></h2>
2
+
3
+ <%= simple_form_for resource, :as => resource_name, :url => invitation_path(resource_name), :html => { :method => :put } do |f| %>
4
+ <%= devise_error_messages! %>
5
+ <%= f.hidden_field :invitation_token %>
6
+
7
+ <%= f.input :password %>
8
+ <%= f.input :password_confirmation %>
9
+
10
+ <%= f.submit t("devise.invitations.edit.submit_button") %>
11
+ <% end %>
@@ -0,0 +1,11 @@
1
+ <h2><%= t "devise.invitations.new.header" %></h2>
2
+
3
+ <%= simple_form_for resource, :as => resource_name, :url => invitation_path(resource_name), :html => {:method => :post} do |f| %>
4
+ <%= devise_error_messages! %>
5
+
6
+ <% resource.class.invite_key_fields.each do |field| -%>
7
+ <%= f.input field %>
8
+ <% end -%>
9
+
10
+ <%= f.submit t("devise.invitations.new.submit_button") %>
11
+ <% end %>
@@ -2,18 +2,40 @@ require 'generators/devise/views_generator'
2
2
 
3
3
  module DeviseInvitable
4
4
  module Generators
5
- class ViewsGenerator < Rails::Generators::Base
6
- desc 'Copies all DeviseInvitable views to your application.'
5
+ class InvitationViewsGenerator < Rails::Generators::Base
6
+ include ::Devise::Generators::ViewPathTemplates
7
+
8
+ def copy_views
9
+ view_directory :invitations
10
+ end
11
+ end
12
+
13
+ class SimpleFormForGenerator < InvitationViewsGenerator
14
+ source_root File.expand_path("../templates/simple_form_for", __FILE__)
15
+ end
7
16
 
8
- argument :scope, :required => false, :default => nil,
9
- :desc => "The scope to copy views to"
17
+ class FormForGenerator < InvitationViewsGenerator
18
+ source_root File.expand_path("../../../../app/views/devise", __FILE__)
19
+ end
10
20
 
21
+ class MailerViewsGenerator < Rails::Generators::Base
11
22
  include ::Devise::Generators::ViewPathTemplates
12
23
  source_root File.expand_path("../../../../app/views/devise", __FILE__)
24
+ desc "Copies Devise mail erb views to your application."
25
+ hide!
26
+
13
27
  def copy_views
14
- view_directory :invitations
15
28
  view_directory :mailer
16
29
  end
17
30
  end
31
+
32
+ class ViewsGenerator < Rails::Generators::Base
33
+ desc 'Copies all DeviseInvitable views to your application.'
34
+ argument :scope, :required => false, :default => nil, :desc => "The scope to copy views to"
35
+
36
+ invoke MailerViewsGenerator
37
+
38
+ hook_for :form_builder, :aliases => "-b", :desc => "Form builder to be used", :default => defined?(SimpleForm) ? "simple_form_for" : "form_for"
39
+ end
18
40
  end
19
41
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devise_invitable
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
4
+ hash: 17
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
8
  - 0
9
- - 2
10
- version: 1.0.2
9
+ - 3
10
+ version: 1.0.3
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-05-26 00:00:00 Z
18
+ date: 2012-07-20 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: bundler
@@ -23,7 +23,7 @@ dependencies:
23
23
  requirement: &id001 !ruby/object:Gem::Requirement
24
24
  none: false
25
25
  requirements:
26
- - - ~>
26
+ - - ">="
27
27
  - !ruby/object:Gem::Version
28
28
  hash: 19
29
29
  segments:
@@ -34,7 +34,7 @@ dependencies:
34
34
  type: :development
35
35
  version_requirements: *id001
36
36
  - !ruby/object:Gem::Dependency
37
- name: rails
37
+ name: railties
38
38
  prerelease: false
39
39
  requirement: &id002 !ruby/object:Gem::Requirement
40
40
  none: false
@@ -64,6 +64,21 @@ dependencies:
64
64
  version: 2.0.0
65
65
  type: :runtime
66
66
  version_requirements: *id003
67
+ - !ruby/object:Gem::Dependency
68
+ name: actionmailer
69
+ prerelease: false
70
+ requirement: &id004 !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ hash: 7
76
+ segments:
77
+ - 3
78
+ - 0
79
+ version: "3.0"
80
+ type: :runtime
81
+ version_requirements: *id004
67
82
  description: It adds support for send invitations by email (it requires to be authenticated) and accept the invitation by setting a password.
68
83
  email:
69
84
  - sergio@entrecables.com
@@ -97,6 +112,8 @@ files:
97
112
  - lib/generators/active_record/templates/migration.rb
98
113
  - lib/generators/active_record/devise_invitable_generator.rb
99
114
  - lib/generators/mongoid/devise_invitable_generator.rb
115
+ - lib/generators/devise_invitable/templates/simple_form_for/invitations/edit.html.erb
116
+ - lib/generators/devise_invitable/templates/simple_form_for/invitations/new.html.erb
100
117
  - lib/generators/devise_invitable/install_generator.rb
101
118
  - lib/generators/devise_invitable/views_generator.rb
102
119
  - lib/generators/devise_invitable/devise_invitable_generator.rb