devise_invitable 0.4.rc → 0.4.rc2

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -61,7 +61,7 @@ or for a model that already exists, define a migration to add DeviseInvitable to
61
61
 
62
62
  == Model configuration
63
63
 
64
- DeviseInvitable adds a new configuration option:
64
+ DeviseInvitable adds two new configuration options:
65
65
 
66
66
  * invite_for: The period the generated invitation token is valid, after this period, the invited resource won't be able to accept the invitation. When invite_for is 0 (the default), the invitation won't expire.
67
67
 
@@ -77,6 +77,8 @@ or directly as parameters to the <tt>devise</tt> method:
77
77
 
78
78
  devise :database_authenticatable, :confirmable, :invitable, :invite_for => 2.weeks
79
79
 
80
+ * 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.
81
+
80
82
  For more details, see <tt>config/initializers/devise.rb</tt> (after you invoked the "devise_invitable:install" generator described above).
81
83
 
82
84
  == Configuring views
@@ -115,6 +117,8 @@ After an invitation is created and sent, the inviter will be redirected to after
115
117
  The invitation email includes a link to accept the invitation that looks like this: <tt>/users/invitation/accept?invitation_token=abcd123</tt>. When clicked, the invited must set a password in order to accept its invitation. Note that if the invitation_token is not present or not valid, the invited is redirected to after_sign_out_path_for(resource_name).
116
118
  You can also overwrite after_sign_in_path_for and after_sign_out_path_for to customize your redirect hooks. More on {Devise's README}[http://github.com/plataformatec/devise], "Controller filters and helpers" section.
117
119
 
120
+ The controller sets the invited_by_id attribute for the new user to the current user. This will let you easily keep track of who invited who.
121
+
118
122
  == Controller filter
119
123
 
120
124
  InvitationsController uses authenticate_inviter! filter to restrict who can send invitations. You can override this method in your ApplicationController.
@@ -2,6 +2,7 @@ class Devise::InvitationsController < ApplicationController
2
2
  include Devise::Controllers::InternalHelpers
3
3
 
4
4
  before_filter :authenticate_inviter!, :only => [:new, :create]
5
+ before_filter :has_invitations_left?, :only => [:create]
5
6
  before_filter :require_no_authentication, :only => [:edit, :update]
6
7
  helper_method :after_sign_in_path_for
7
8
 
@@ -13,9 +14,13 @@ class Devise::InvitationsController < ApplicationController
13
14
 
14
15
  # POST /resource/invitation
15
16
  def create
16
- self.resource = resource_class.invite!(params[resource_name])
17
+ self.resource = resource_class.invite!(params[resource_name], authenticate_inviter!)
17
18
 
18
19
  if resource.errors.empty?
20
+ if resource_class.invitation_limit.present?
21
+ current_user.invitation_limit ||= resource_class.invitation_limit
22
+ current_user.decrement!(:invitation_limit)
23
+ end
19
24
  set_flash_message :notice, :send_instructions, :email => self.resource.email
20
25
  redirect_to after_sign_in_path_for(resource_name)
21
26
  else
@@ -44,4 +49,14 @@ class Devise::InvitationsController < ApplicationController
44
49
  render_with_scope :edit
45
50
  end
46
51
  end
52
+
53
+ protected
54
+
55
+ def has_invitations_left?
56
+ if !current_user.has_invitations_left?
57
+ build_resource
58
+ set_flash_message :notice, :no_invitations_remaining
59
+ render_with_scope :new
60
+ end
61
+ end
47
62
  end
@@ -4,6 +4,7 @@ en:
4
4
  send_instructions: 'An invitation email has been sent to %{email}.'
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
- mailer:
8
- invitation_instructions:
9
- subject: 'Invitation instructions'
7
+ no_invitations_remaining: "No invitations remaining"
8
+ mailer:
9
+ invitation_instructions:
10
+ subject: 'Invitation instructions'
@@ -11,6 +11,14 @@ module Devise
11
11
  # The period the generated invitation token is valid.
12
12
  mattr_accessor :invite_for
13
13
  @@invite_for = 0
14
+
15
+ # Public: number of invitations the user is allowed to send
16
+ #
17
+ # Examples (in config/initializers/devise.rb)
18
+ #
19
+ # config.invitation_limit = nil
20
+ mattr_accessor :invitation_limit
21
+ @@invitation_limit = nil
14
22
  end
15
23
 
16
24
  Devise.add_module :invitable, :controller => :invitations, :model => 'devise_invitable/model', :route => :invitation
@@ -21,6 +21,10 @@ module Devise
21
21
  module Invitable
22
22
  extend ActiveSupport::Concern
23
23
 
24
+ included do
25
+ belongs_to :invited_by, :polymorphic => true
26
+ end
27
+
24
28
  # Accept an invitation by clearing invitation token and confirming it if model
25
29
  # is confirmable
26
30
  def accept_invitation!
@@ -35,6 +39,19 @@ module Devise
35
39
  persisted? && invitation_token.present?
36
40
  end
37
41
 
42
+ # Return true if this user has invitations left to send
43
+ def has_invitations_left?
44
+ if self.class.invitation_limit.present?
45
+ if invitation_limit
46
+ return invitation_limit > 0
47
+ else
48
+ return self.class.invitation_limit > 0
49
+ end
50
+ else
51
+ return true
52
+ end
53
+ end
54
+
38
55
  # Reset invitation token and send invitation again
39
56
  def invite!
40
57
  if new_record? || invited?
@@ -89,9 +106,10 @@ module Devise
89
106
  # user and send invitation to it. If user is found, returns the user with an
90
107
  # email already exists error.
91
108
  # Attributes must contain the user email, other attributes will be set in the record
92
- def invite!(attributes={})
109
+ def invite!(attributes={}, invited_by=nil)
93
110
  invitable = find_or_initialize_with_error_by(:email, attributes.delete(:email))
94
111
  invitable.attributes = attributes
112
+ invitable.invited_by = invited_by
95
113
 
96
114
  if invitable.new_record?
97
115
  invitable.errors.clear if invitable.email.try(:match, Devise.email_regexp)
@@ -124,6 +142,7 @@ module Devise
124
142
  end
125
143
 
126
144
  Devise::Models.config(self, :invite_for)
145
+ Devise::Models.config(self, :invitation_limit)
127
146
  end
128
147
  end
129
148
  end
@@ -26,6 +26,9 @@ module DeviseInvitable
26
26
  def invitable
27
27
  apply_devise_schema :invitation_token, String, :limit => 60
28
28
  apply_devise_schema :invitation_sent_at, DateTime
29
+ apply_devise_schema :invitation_limit, Integer
30
+ apply_devise_schema :invited_by_id, Integer
31
+ apply_devise_schema :invited_by_type, String
29
32
  end
30
33
  end
31
34
  end
@@ -1,3 +1,3 @@
1
1
  module DeviseInvitable
2
- VERSION = '0.4.rc'
2
+ VERSION = '0.4.rc2'
3
3
  end
@@ -3,7 +3,10 @@ class DeviseInvitableAddTo<%= table_name.camelize %> < ActiveRecord::Migration
3
3
  change_table :<%= table_name %> do |t|
4
4
  t.string :invitation_token, :limit => 60
5
5
  t.datetime :invitation_sent_at
6
+ t.integer :invitation_limit
7
+ t.integer :invited_by_id
6
8
  t.index :invitation_token # for invitable
9
+ t.index :invited_by_id
7
10
  end
8
11
 
9
12
  # And allow null encrypted_password and password_salt:
@@ -14,6 +17,8 @@ class DeviseInvitableAddTo<%= table_name.camelize %> < ActiveRecord::Migration
14
17
  end
15
18
 
16
19
  def self.down
20
+ remove_column :<%= table_name %>, :invited_by_id
21
+ remove_column :<%= table_name %>, :invitation_limit
17
22
  remove_column :<%= table_name %>, :invitation_sent_at
18
23
  remove_column :<%= table_name %>, :invitation_token
19
24
  end
@@ -0,0 +1,8 @@
1
+ require 'generators/devise/orm_helpers'
2
+
3
+ module Mongoid
4
+ module Generators
5
+ class DeviseInvitableGenerator < Rails::Generators::NamedBase
6
+ end
7
+ end
8
+ 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: 7712090
4
+ hash: 977940495
5
5
  prerelease: true
6
6
  segments:
7
7
  - 0
8
8
  - 4
9
- - rc
10
- version: 0.4.rc
9
+ - rc2
10
+ version: 0.4.rc2
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: 2011-01-06 00:00:00 +01:00
18
+ date: 2011-02-14 00:00:00 +01:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -26,36 +26,18 @@ dependencies:
26
26
  requirements:
27
27
  - - ~>
28
28
  - !ruby/object:Gem::Version
29
- hash: 11
29
+ hash: 27
30
30
  segments:
31
31
  - 2
32
- - 1
32
+ - 5
33
33
  - 0
34
- version: 2.1.0
34
+ version: 2.5.0
35
35
  type: :development
36
36
  version_requirements: *id001
37
- - !ruby/object:Gem::Dependency
38
- name: steak
39
- prerelease: false
40
- requirement: &id002 !ruby/object:Gem::Requirement
41
- none: false
42
- requirements:
43
- - - ~>
44
- - !ruby/object:Gem::Version
45
- hash: 15424051
46
- segments:
47
- - 1
48
- - 0
49
- - 0
50
- - rc
51
- - 3
52
- version: 1.0.0.rc.3
53
- type: :development
54
- version_requirements: *id002
55
37
  - !ruby/object:Gem::Dependency
56
38
  name: bundler
57
39
  prerelease: false
58
- requirement: &id003 !ruby/object:Gem::Requirement
40
+ requirement: &id002 !ruby/object:Gem::Requirement
59
41
  none: false
60
42
  requirements:
61
43
  - - ~>
@@ -67,11 +49,11 @@ dependencies:
67
49
  - 7
68
50
  version: 1.0.7
69
51
  type: :development
70
- version_requirements: *id003
52
+ version_requirements: *id002
71
53
  - !ruby/object:Gem::Dependency
72
54
  name: rails
73
55
  prerelease: false
74
- requirement: &id004 !ruby/object:Gem::Requirement
56
+ requirement: &id003 !ruby/object:Gem::Requirement
75
57
  none: false
76
58
  requirements:
77
59
  - - ~>
@@ -83,11 +65,11 @@ dependencies:
83
65
  - 0
84
66
  version: 3.0.0
85
67
  type: :runtime
86
- version_requirements: *id004
68
+ version_requirements: *id003
87
69
  - !ruby/object:Gem::Dependency
88
70
  name: devise
89
71
  prerelease: false
90
- requirement: &id005 !ruby/object:Gem::Requirement
72
+ requirement: &id004 !ruby/object:Gem::Requirement
91
73
  none: false
92
74
  requirements:
93
75
  - - ~>
@@ -99,7 +81,7 @@ dependencies:
99
81
  - rc
100
82
  version: 1.2.rc
101
83
  type: :runtime
102
- version_requirements: *id005
84
+ version_requirements: *id004
103
85
  description: It adds support for send invitations by email (it requires to be authenticated) and accept the invitation by setting a password.
104
86
  email:
105
87
  - sergio@entrecables.com
@@ -110,32 +92,31 @@ extensions: []
110
92
  extra_rdoc_files: []
111
93
 
112
94
  files:
95
+ - app/controllers/devise/invitations_controller.rb
113
96
  - app/views/devise/invitations/edit.html.erb
114
97
  - app/views/devise/invitations/new.html.erb
115
98
  - app/views/devise/mailer/invitation_instructions.html.erb
116
99
  - app/views/devise/mailer/invitation.html.erb
117
- - app/controllers/devise/invitations_controller.rb~
118
- - app/controllers/devise/invitations_controller.rb
119
100
  - config/locales/en.yml
120
- - lib/devise_invitable/rails.rb
121
- - lib/devise_invitable/schema.rb
101
+ - lib/devise_invitable.rb
122
102
  - lib/devise_invitable/mailer.rb
123
- - lib/devise_invitable/routes.rb
124
- - lib/devise_invitable/version.rb
125
103
  - lib/devise_invitable/model.rb
126
- - lib/devise_invitable/model.rb~
104
+ - lib/devise_invitable/rails.rb
105
+ - lib/devise_invitable/routes.rb
106
+ - lib/devise_invitable/schema.rb
127
107
  - lib/devise_invitable/controllers/helpers.rb
128
108
  - lib/devise_invitable/controllers/url_helpers.rb
129
- - lib/devise_invitable.rb
109
+ - lib/devise_invitable/version.rb
110
+ - lib/generators/active_record/devise_invitable_generator.rb
111
+ - lib/generators/active_record/templates/migration.rb
130
112
  - lib/generators/devise_invitable/views_generator.rb
131
113
  - lib/generators/devise_invitable/devise_invitable_generator.rb
132
114
  - lib/generators/devise_invitable/install_generator.rb
133
- - lib/generators/active_record/devise_invitable_generator.rb
134
- - lib/generators/active_record/templates/migration.rb
115
+ - lib/generators/mongoid/devise_invitable_generator.rb
135
116
  - LICENSE
136
117
  - README.rdoc
137
118
  has_rdoc: true
138
- homepage: http://github.com/rymai/devise_invitable
119
+ homepage: https://github.com/scambra/devise_invitable
139
120
  licenses: []
140
121
 
141
122
  post_install_message:
@@ -159,7 +140,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
159
140
  required_rubygems_version: !ruby/object:Gem::Requirement
160
141
  none: false
161
142
  requirements:
162
- - - ~>
143
+ - - ">="
163
144
  - !ruby/object:Gem::Version
164
145
  hash: 23
165
146
  segments:
@@ -1,48 +0,0 @@
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
@@ -1,129 +0,0 @@
1
- module Devise
2
- module Models
3
- # Invitable is responsible to send emails with invitations.
4
- # When an invitation is sent to an email, an account is created for it.
5
- # An invitation has a link to set the password, as reset password from recoverable.
6
- #
7
- # Configuration:
8
- #
9
- # invite_for: the time you want the user will have to confirm the account after
10
- # is invited. When invite_for is zero, the invitation won't expire.
11
- # By default invite_for is 0.
12
- #
13
- # Examples:
14
- #
15
- # User.find(1).invited? # true/false
16
- # User.invite!(:email => 'someone@example.com') # send invitation
17
- # User.accept_invitation!(:invitation_token => '...') # accept invitation with a token
18
- # User.find(1).accept_invitation! # accept invitation
19
- # User.find(1).invite! # reset invitation status and send invitation again
20
- module Invitable
21
- extend ActiveSupport::Concern
22
-
23
- # Accept an invitation by clearing invitation token and confirming it if model
24
- # is confirmable
25
- def accept_invitation!
26
- if self.invited?
27
- self.invitation_token = nil
28
- self.save
29
- end
30
- end
31
-
32
- # Verifies whether a user has been invited or not
33
- def invited?
34
- persisted? && invitation_token.present?
35
- end
36
-
37
- # Send invitation by email
38
- def send_invitation
39
- ::Devise.mailer.invitation(self).deliver
40
- end
41
-
42
- # Reset invitation token and send invitation again
43
- def invite!
44
- if new_record? || invited?
45
- self.skip_confirmation! if self.new_record? and self.respond_to? :skip_confirmation!
46
- generate_invitation_token
47
- save(:validate=>false)
48
- send_invitation
49
- end
50
- end
51
-
52
- # Verify whether a invitation is active or not. If the user has been
53
- # invited, we need to calculate if the invitation time has not expired
54
- # for this user, in other words, if the invitation is still valid.
55
- def valid_invitation?
56
- invited? && invitation_period_valid?
57
- end
58
-
59
- protected
60
-
61
- # Checks if the invitation for the user is within the limit time.
62
- # We do this by calculating if the difference between today and the
63
- # invitation sent date does not exceed the invite for time configured.
64
- # Invite_for is a model configuration, must always be an integer value.
65
- #
66
- # Example:
67
- #
68
- # # invite_for = 1.day and invitation_sent_at = today
69
- # invitation_period_valid? # returns true
70
- #
71
- # # invite_for = 5.days and invitation_sent_at = 4.days.ago
72
- # invitation_period_valid? # returns true
73
- #
74
- # # invite_for = 5.days and invitation_sent_at = 5.days.ago
75
- # invitation_period_valid? # returns false
76
- #
77
- # # invite_for = nil
78
- # invitation_period_valid? # will always return true
79
- #
80
- def invitation_period_valid?
81
- invitation_sent_at && (self.class.invite_for.to_i.zero? || invitation_sent_at.utc >= self.class.invite_for.ago)
82
- end
83
-
84
- # Generates a new random token for invitation, and stores the time
85
- # this token is being generated
86
- def generate_invitation_token
87
- self.invitation_token = Devise.friendly_token
88
- self.invitation_sent_at = Time.now.utc
89
- end
90
-
91
- module ClassMethods
92
- # Attempt to find a user by it's email. If a record is not found, create a new
93
- # user and send invitation to it. If user is found, returns the user with an
94
- # email already exists error.
95
- # Attributes must contain the user email, other attributes will be set in the record
96
- def invite!(attributes={})
97
- invitable = find_or_initialize_with_error_by(:email, attributes.delete(:email))
98
- invitable.attributes = attributes
99
-
100
- if invitable.new_record?
101
- invitable.errors.clear if invitable.email.try(:match, Devise.email_regexp)
102
- else
103
- invitable.errors.add(:email, :taken) unless invitable.invited?
104
- end
105
-
106
- invitable.invite! if invitable.errors.empty?
107
- invitable
108
- end
109
-
110
- # Attempt to find a user by it's invitation_token to set it's password.
111
- # If a user is found, reset it's password and automatically try saving
112
- # the record. If not user is found, returns a new user containing an
113
- # error in invitation_token attribute.
114
- # Attributes must contain invitation_token, password and confirmation
115
- def accept_invitation!(attributes={})
116
- invitable = find_or_initialize_with_error_by(:invitation_token, attributes.delete(:invitation_token))
117
- invitable.errors.add(:invitation_token, :invalid) if attributes[:invitation_token] && !invitable.new_record? && !invitable.valid_invitation?
118
- if invitable.errors.empty?
119
- invitable.attributes = attributes
120
- invitable.accept_invitation!
121
- end
122
- invitable
123
- end
124
-
125
- Devise::Models.config(self, :invite_for)
126
- end
127
- end
128
- end
129
- end