devise_invitable 1.0.2 → 1.0.3

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
@@ -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