simple_teams 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +28 -0
- data/Rakefile +8 -0
- data/app/assets/config/simple_teams_manifest.js +1 -0
- data/app/assets/stylesheets/simple_teams/application.css +15 -0
- data/app/controllers/simple_teams/accept_invitations_controller.rb +57 -0
- data/app/controllers/simple_teams/admin/invitations_controller.rb +60 -0
- data/app/controllers/simple_teams/admin/memberships_controller.rb +60 -0
- data/app/controllers/simple_teams/admin/teams_controller.rb +60 -0
- data/app/controllers/simple_teams/application_controller.rb +13 -0
- data/app/controllers/simple_teams/invitations_controller.rb +96 -0
- data/app/controllers/simple_teams/leave_teams_controller.rb +25 -0
- data/app/controllers/simple_teams/memberships_controller.rb +61 -0
- data/app/controllers/simple_teams/related_members_controller.rb +33 -0
- data/app/controllers/simple_teams/teams_controller.rb +19 -0
- data/app/forms/simple_teams/invitation_form.rb +77 -0
- data/app/forms/simple_teams/invitation_forms/create.rb +24 -0
- data/app/forms/simple_teams/invitation_forms/create_bulk.rb +125 -0
- data/app/forms/simple_teams/invitation_forms/create_combo.rb +75 -0
- data/app/forms/simple_teams/invitation_forms/update.rb +29 -0
- data/app/forms/simple_teams/membership_form.rb +91 -0
- data/app/helpers/simple_teams/application_helper.rb +4 -0
- data/app/helpers/simple_teams/invitations_helper.rb +4 -0
- data/app/helpers/simple_teams/memberships_helper.rb +4 -0
- data/app/helpers/simple_teams/teams_helper.rb +4 -0
- data/app/jobs/simple_teams/application_job.rb +4 -0
- data/app/mailers/simple_teams/application_mailer.rb +6 -0
- data/app/mailers/simple_teams/mailer.rb +17 -0
- data/app/models/concerns/simple_teams/member_object.rb +22 -0
- data/app/models/concerns/simple_teams/team_object.rb +23 -0
- data/app/models/simple_teams/ability.rb +34 -0
- data/app/models/simple_teams/application_record.rb +5 -0
- data/app/models/simple_teams/invitation.rb +85 -0
- data/app/models/simple_teams/membership.rb +16 -0
- data/app/models/simple_teams/team.rb +23 -0
- data/app/notifications/application_notification.rb +4 -0
- data/app/notifications/simple_teams/bulk_invitations_notification.rb +51 -0
- data/app/notifications/simple_teams/invitation_notification.rb +54 -0
- data/app/notifications/simple_teams/invitations/accepted_notification.rb +13 -0
- data/app/notifications/simple_teams/invitations/created_notification.rb +13 -0
- data/app/notifications/simple_teams/invitations/destroyed_notification.rb +13 -0
- data/app/notifications/simple_teams/invitations/updated_notification.rb +13 -0
- data/app/notifications/simple_teams/membership_notification.rb +58 -0
- data/app/notifications/simple_teams/memberships/destroyed_notification.rb +13 -0
- data/app/notifications/simple_teams/memberships/left_notification.rb +17 -0
- data/app/notifications/simple_teams/memberships/updated_notification.rb +17 -0
- data/app/service_objects/simple_teams/accept_invitation_service.rb +61 -0
- data/app/service_objects/simple_teams/initialize_team_service.rb +25 -0
- data/app/service_objects/simple_teams/leave_team_service.rb +56 -0
- data/app/views/layouts/simple_teams/application.html.erb +20 -0
- data/app/views/simple_teams/_roles_field.html.erb +22 -0
- data/app/views/simple_teams/accept_invitations/invalid_token.html.erb +7 -0
- data/app/views/simple_teams/accept_invitations/new.html.erb +18 -0
- data/app/views/simple_teams/admin/invitations/_form.html.erb +62 -0
- data/app/views/simple_teams/admin/invitations/_invitation.html.erb +47 -0
- data/app/views/simple_teams/admin/invitations/edit.html.erb +10 -0
- data/app/views/simple_teams/admin/invitations/index.html.erb +14 -0
- data/app/views/simple_teams/admin/invitations/new.html.erb +9 -0
- data/app/views/simple_teams/admin/invitations/show.html.erb +10 -0
- data/app/views/simple_teams/admin/memberships/_form.html.erb +32 -0
- data/app/views/simple_teams/admin/memberships/_membership.html.erb +17 -0
- data/app/views/simple_teams/admin/memberships/edit.html.erb +10 -0
- data/app/views/simple_teams/admin/memberships/index.html.erb +14 -0
- data/app/views/simple_teams/admin/memberships/new.html.erb +9 -0
- data/app/views/simple_teams/admin/memberships/show.html.erb +10 -0
- data/app/views/simple_teams/admin/teams/_form.html.erb +22 -0
- data/app/views/simple_teams/admin/teams/_team.html.erb +7 -0
- data/app/views/simple_teams/admin/teams/edit.html.erb +10 -0
- data/app/views/simple_teams/admin/teams/index.html.erb +14 -0
- data/app/views/simple_teams/admin/teams/new.html.erb +9 -0
- data/app/views/simple_teams/admin/teams/show.html.erb +10 -0
- data/app/views/simple_teams/invitations/_bulk_form.html.erb +63 -0
- data/app/views/simple_teams/invitations/_form.html.erb +15 -0
- data/app/views/simple_teams/invitations/_invitation.html.erb +19 -0
- data/app/views/simple_teams/invitations/_invitation.json.jbuilder +2 -0
- data/app/views/simple_teams/invitations/edit.html.erb +12 -0
- data/app/views/simple_teams/invitations/new.html.erb +12 -0
- data/app/views/simple_teams/leave_teams/edit.html.erb +2 -0
- data/app/views/simple_teams/memberships/_form.html.erb +12 -0
- data/app/views/simple_teams/memberships/_membership.html.erb +23 -0
- data/app/views/simple_teams/memberships/edit.html.erb +12 -0
- data/app/views/simple_teams/related_members/index.json.jbuilder +5 -0
- data/app/views/simple_teams/related_members/select2.json.jbuilder +6 -0
- data/app/views/simple_teams/teams/_add_member_link.html.erb +3 -0
- data/app/views/simple_teams/teams/_members_table.html.erb +20 -0
- data/app/views/simple_teams/teams/show.html.erb +38 -0
- data/app/views/simple_teams/teams_mailer/invitation_notification.html.erb +9 -0
- data/config/locales/simple_teams.en.yml +18 -0
- data/config/routes.rb +21 -0
- data/db/migrate/20240311052758_create_simple_teams_teams.rb +9 -0
- data/db/migrate/20240311053006_create_simple_teams_memberships.rb +11 -0
- data/db/migrate/20240311053607_create_simple_teams_invitations.rb +17 -0
- data/lib/simple_teams/engine.rb +22 -0
- data/lib/simple_teams/version.rb +3 -0
- data/lib/simple_teams.rb +6 -0
- data/lib/tasks/simple_teams_tasks.rake +4 -0
- metadata +160 -0
@@ -0,0 +1,125 @@
|
|
1
|
+
module SimpleTeams
|
2
|
+
class InvitationForms::CreateBulk < ApplicationForm
|
3
|
+
|
4
|
+
attr_accessor :team, :current_user, :single_vs_multiple, :select2_emails, :accessible_emails, :emails, :role
|
5
|
+
|
6
|
+
def initialize(team, current_user)
|
7
|
+
@team = team
|
8
|
+
@current_user = current_user
|
9
|
+
@emails = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def perform(params)
|
13
|
+
self.assign_attributes(params)
|
14
|
+
if accessible?
|
15
|
+
self.emails = accessible_emails.split(",").map(&:strip).uniq.reject(&:empty?)
|
16
|
+
else
|
17
|
+
self.emails = select2_emails.map(&:strip).uniq.reject(&:empty?)
|
18
|
+
end
|
19
|
+
|
20
|
+
if valid?
|
21
|
+
create_invitations
|
22
|
+
generate_notification
|
23
|
+
else
|
24
|
+
false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Attributes
|
29
|
+
def available_roles
|
30
|
+
SimpleTeams::Membership.roles
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def accessible?
|
36
|
+
single_vs_multiple == "accessible"
|
37
|
+
end
|
38
|
+
|
39
|
+
def address_attribute
|
40
|
+
accessible? ? :accessible_emails : :select2_emails
|
41
|
+
end
|
42
|
+
|
43
|
+
def valid?
|
44
|
+
self.errors.clear
|
45
|
+
validate_attributes_present
|
46
|
+
validate_invitations
|
47
|
+
validate_role_is_available
|
48
|
+
validate_current_user_has_rights_to_invite_an_owner
|
49
|
+
!self.errors.present?
|
50
|
+
end
|
51
|
+
|
52
|
+
def validate_attributes_present
|
53
|
+
[:team, :current_user, :role].each do |a|
|
54
|
+
unless self.send(a).present?
|
55
|
+
self.errors.add(a, "can't be blank")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
unless self.emails.present?
|
59
|
+
self.errors.add(address_attribute, "can't be blank")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def validate_invitations
|
64
|
+
emails.each do |email|
|
65
|
+
invitation = SimpleTeams::Invitation.new(
|
66
|
+
:team => team,
|
67
|
+
:inviter => current_user,
|
68
|
+
:role => role,
|
69
|
+
:email => email
|
70
|
+
)
|
71
|
+
unless invitation.valid?
|
72
|
+
invitation.errors[:email].each do |error|
|
73
|
+
if error == "is invalid"
|
74
|
+
error_message = "#{email} is not a valid email address"
|
75
|
+
else
|
76
|
+
error_message = error.gsub("this email address", email)
|
77
|
+
end
|
78
|
+
self.errors.add(address_attribute, error_message)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def validate_role_is_available
|
85
|
+
# Note: Users will never see this error message unless they hack the form
|
86
|
+
if role.present?
|
87
|
+
unless available_roles.keys.include? role
|
88
|
+
self.errors.add(:role, "that role is not available for this resource")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def validate_current_user_has_rights_to_invite_an_owner
|
94
|
+
if role == "owner"
|
95
|
+
unless current_user.membership_for_team(team).owner?
|
96
|
+
self.errors.add(:role, "you must be an owner yourself to add add another owner")
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def create_invitations
|
102
|
+
ActiveRecord::Base.transaction do
|
103
|
+
emails.each do |email|
|
104
|
+
SimpleTeams::Invitation.create!(
|
105
|
+
:team => team,
|
106
|
+
:inviter => current_user,
|
107
|
+
:role => role,
|
108
|
+
:email => email
|
109
|
+
)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def generate_notification
|
115
|
+
SimpleTeams::BulkInvitationsNotification.with(
|
116
|
+
:team_id => team.id,
|
117
|
+
:user_id => current_user.id,
|
118
|
+
:team_name => team.name,
|
119
|
+
:user_name => current_user.full_name,
|
120
|
+
:invitation_names => self.emails,
|
121
|
+
).deliver_later(team.members)
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module SimpleTeams
|
2
|
+
class InvitationForms::CreateCombo < ApplicationForm
|
3
|
+
|
4
|
+
attr_accessor :team, :current_user, :single_vs_multiple, :email, :emails, :select2_emails, :accessible_emails, :role, :form_instance
|
5
|
+
|
6
|
+
def initialize(team, current_user)
|
7
|
+
@team = team
|
8
|
+
@current_user = current_user
|
9
|
+
end
|
10
|
+
|
11
|
+
def perform(params)
|
12
|
+
# Store input
|
13
|
+
self.assign_attributes(params)
|
14
|
+
|
15
|
+
# Set form instance from input
|
16
|
+
@form_instance = selected_form_klass.new(team, current_user)
|
17
|
+
|
18
|
+
# Perform
|
19
|
+
if form_instance.perform(filter_params(params))
|
20
|
+
return true
|
21
|
+
else
|
22
|
+
reassign_errors
|
23
|
+
unless single?
|
24
|
+
self.emails = form_instance.emails
|
25
|
+
end
|
26
|
+
false
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def available_roles
|
31
|
+
SimpleTeams::Membership.roles
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def single?
|
37
|
+
!multiple? and !accessible?
|
38
|
+
end
|
39
|
+
|
40
|
+
def multiple?
|
41
|
+
single_vs_multiple == "multiple"
|
42
|
+
end
|
43
|
+
|
44
|
+
def accessible?
|
45
|
+
single_vs_multiple == "accessible"
|
46
|
+
end
|
47
|
+
|
48
|
+
def selected_form_klass
|
49
|
+
if single?
|
50
|
+
SimpleTeams::InvitationForms::Create
|
51
|
+
else
|
52
|
+
SimpleTeams::InvitationForms::CreateBulk
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def filter_params(params)
|
57
|
+
if single?
|
58
|
+
params.except(:single_vs_multiple, :select2_emails, :accessible_emails)
|
59
|
+
else
|
60
|
+
params.except(:email)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def reassign_errors
|
65
|
+
[:team, :current_user, :email, :select2_emails, :accessible_emails, :role].each do |a|
|
66
|
+
if form_instance.errors[a].present?
|
67
|
+
form_instance.errors[a].each do |error|
|
68
|
+
self.errors.add(a, error)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module SimpleTeams
|
2
|
+
class InvitationForms::Update < SimpleTeams::InvitationForm
|
3
|
+
|
4
|
+
def initialize(invitation, current_user)
|
5
|
+
@invitation = invitation
|
6
|
+
@team = @invitation.team
|
7
|
+
@current_user = current_user
|
8
|
+
|
9
|
+
# Get existing attributes
|
10
|
+
self.class.invitation_attributes.each do |a|
|
11
|
+
self.assign_attributes(a => @invitation.send(a))
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def generate_notification
|
18
|
+
SimpleTeams::Invitations::UpdatedNotification.with(
|
19
|
+
:team_id => team.id,
|
20
|
+
:invitation_id => invitation.id,
|
21
|
+
:user_id => current_user.id,
|
22
|
+
:team_name => team.name,
|
23
|
+
:invitation_name => invitation.email,
|
24
|
+
:user_name => current_user.full_name
|
25
|
+
).deliver_later(team.members)
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module SimpleTeams
|
2
|
+
class MembershipForm < ApplicationForm
|
3
|
+
|
4
|
+
attr_accessor :membership, :current_user, :role
|
5
|
+
|
6
|
+
def initialize(membership, current_user)
|
7
|
+
@membership = membership
|
8
|
+
@current_user = current_user
|
9
|
+
|
10
|
+
# Get existing attributes
|
11
|
+
@role = @membership.role
|
12
|
+
end
|
13
|
+
|
14
|
+
def valid?
|
15
|
+
self.errors.clear
|
16
|
+
validate_role_is_available
|
17
|
+
validate_current_user_has_rights_to_modify_an_existing_owner
|
18
|
+
validate_current_user_has_rights_to_add_an_owner
|
19
|
+
validate_not_last_owner
|
20
|
+
!self.errors.present?
|
21
|
+
end
|
22
|
+
|
23
|
+
def perform(params)
|
24
|
+
self.assign_attributes(params)
|
25
|
+
|
26
|
+
if valid?
|
27
|
+
membership.update(params)
|
28
|
+
generate_notification
|
29
|
+
else
|
30
|
+
false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def available_roles
|
35
|
+
SimpleTeams::Membership.roles
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def validate_role_is_available
|
41
|
+
# Note: Users will never see this error message unless they hack the form
|
42
|
+
if role.present?
|
43
|
+
unless available_roles.keys.include? role
|
44
|
+
self.errors.add(:role, "that role is not available for this resource")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def validate_not_last_owner
|
50
|
+
if membership.owner? and only_one_owner? and role != "owner"
|
51
|
+
self.errors.add(:role, "you can't remove the last owner of a team")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def validate_current_user_has_rights_to_modify_an_existing_owner
|
56
|
+
if membership.owner?
|
57
|
+
unless current_user.membership_for_team(team).owner?
|
58
|
+
self.errors.add(:role, "you must be an owner yourself to modify another owner")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def validate_current_user_has_rights_to_add_an_owner
|
64
|
+
if role == "owner" and !membership.owner?
|
65
|
+
unless current_user.membership_for_team(team).owner?
|
66
|
+
self.errors.add(:role, "you must be an owner yourself to add add another owner")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def only_one_owner?
|
72
|
+
team.memberships.where(:role => :owner).count == 1
|
73
|
+
end
|
74
|
+
|
75
|
+
def team
|
76
|
+
membership.team
|
77
|
+
end
|
78
|
+
|
79
|
+
def generate_notification
|
80
|
+
SimpleTeams::Memberships::UpdatedNotification.with(
|
81
|
+
:team_id => team.id,
|
82
|
+
:member_id => membership.member.id,
|
83
|
+
:user_id => current_user.id,
|
84
|
+
:team_name => team.name,
|
85
|
+
:member_name => membership.member.full_name,
|
86
|
+
:user_name => current_user.full_name
|
87
|
+
).deliver_later(team.members)
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module SimpleTeams
|
2
|
+
class Mailer < ApplicationMailer
|
3
|
+
|
4
|
+
def invitation_notification(invitation)
|
5
|
+
@invitation = invitation
|
6
|
+
@name = invitation.team.name
|
7
|
+
@resource_name = invitation.team.teamable.class.model_name.human
|
8
|
+
@invitation_url = accept_team_invitation_url(@invitation.token)
|
9
|
+
|
10
|
+
mail(
|
11
|
+
to: (@invitation.email),
|
12
|
+
subject: "Invitation to #{@name} #{@resource_name}"
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module SimpleTeams::MemberObject
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
has_many :team_memberships, :class_name => "SimpleTeams::Membership", :foreign_key => :member_id, :dependent => :destroy
|
6
|
+
has_many :teams, :through => :team_memberships, :class_name => "SimpleTeams::Team"
|
7
|
+
has_many :team_members, :through => :teams, :source => :members, :class_name => SimpleTeams.member_class.to_s
|
8
|
+
end
|
9
|
+
|
10
|
+
def member_of_team?(team)
|
11
|
+
team_memberships.where(team: team).present?
|
12
|
+
end
|
13
|
+
|
14
|
+
def membership_for_team(team)
|
15
|
+
team_memberships.find_by(:team_id => team.id)
|
16
|
+
end
|
17
|
+
|
18
|
+
def role_for_team(team)
|
19
|
+
membership_for_team(team).role if membership_for_team(team).present?
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module SimpleTeams::TeamObject
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
attr_accessor :team_owner
|
6
|
+
|
7
|
+
has_one :team, :as => :teamable, :class_name => "SimpleTeams::Team", :dependent => :destroy
|
8
|
+
|
9
|
+
after_create :generate_team
|
10
|
+
|
11
|
+
def self.team_object?
|
12
|
+
true
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def generate_team
|
20
|
+
self.create_team!
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module SimpleTeams
|
2
|
+
class Ability
|
3
|
+
include ::CanCan::Ability
|
4
|
+
|
5
|
+
def initialize(user)
|
6
|
+
|
7
|
+
return unless user.present?
|
8
|
+
|
9
|
+
can :create, Team
|
10
|
+
|
11
|
+
# Team
|
12
|
+
can :read, Team do |team|
|
13
|
+
user.member_of_team?(team)
|
14
|
+
end
|
15
|
+
|
16
|
+
can [:update], Team do |team|
|
17
|
+
%w"owner administrator".include? user.role_for_team(team)
|
18
|
+
end
|
19
|
+
|
20
|
+
can [:destroy], Team do |team|
|
21
|
+
%w"owner".include? user.role_for_team(team)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Team Memberships/Invitations
|
25
|
+
can :read, [Membership, Invitation] do |object|
|
26
|
+
user.member_of_team?(object.team)
|
27
|
+
end
|
28
|
+
|
29
|
+
can :manage, [Membership, Invitation] do |object|
|
30
|
+
%w"owner administrator".include? user.role_for_team(object.team)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module SimpleTeams
|
2
|
+
class Invitation < ApplicationRecord
|
3
|
+
belongs_to :team
|
4
|
+
belongs_to :inviter, :class_name => SimpleTeams.member_class.to_s
|
5
|
+
belongs_to :membership, :optional => true
|
6
|
+
|
7
|
+
# validations
|
8
|
+
validates_presence_of :email, :role
|
9
|
+
validates_format_of :email, :with => URI::MailTo::EMAIL_REGEXP
|
10
|
+
validate :email_uninvited, :on => :create
|
11
|
+
|
12
|
+
# callbacks
|
13
|
+
before_validation :format_email
|
14
|
+
before_create :generate_token
|
15
|
+
before_create :set_expires_at
|
16
|
+
after_create :send_invitation_notification
|
17
|
+
after_update :send_invitation_notification, :if => :saved_change_to_email?
|
18
|
+
|
19
|
+
# scopes
|
20
|
+
scope :expired, -> { where("? > expires_at", Time.now) }
|
21
|
+
scope :unexpired, -> { where("? <= expires_at", Time.now) }
|
22
|
+
|
23
|
+
# enums
|
24
|
+
enum :role, [:member, :administrator, :owner]
|
25
|
+
enum :status, [:initial, :accepted, :declined]
|
26
|
+
|
27
|
+
def expired?
|
28
|
+
Time.now > expires_at
|
29
|
+
end
|
30
|
+
|
31
|
+
def unexpired?
|
32
|
+
Time.now <= expires_at
|
33
|
+
end
|
34
|
+
|
35
|
+
def reset_invitation_token
|
36
|
+
generate_token
|
37
|
+
set_expires_at
|
38
|
+
self.save
|
39
|
+
end
|
40
|
+
|
41
|
+
def resend_invitation_notification(options = {})
|
42
|
+
if Time.now > expires_at - self.class.expiration_window + 1.day
|
43
|
+
reset_invitation_token
|
44
|
+
end
|
45
|
+
send_invitation_notification(options)
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.expiration_window
|
49
|
+
2.weeks
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def format_email
|
55
|
+
self.email = self.email.downcase.strip if email.present?
|
56
|
+
end
|
57
|
+
|
58
|
+
def generate_token
|
59
|
+
self.token = SecureRandom.hex(32)
|
60
|
+
end
|
61
|
+
|
62
|
+
def set_expires_at
|
63
|
+
self.expires_at = Time.now + self.class.expiration_window
|
64
|
+
end
|
65
|
+
|
66
|
+
def send_invitation_notification(options = {})
|
67
|
+
if options[:now] == true
|
68
|
+
SimpleTeams::Mailer.invitation_notification(self).deliver_now
|
69
|
+
else
|
70
|
+
SimpleTeams::Mailer.invitation_notification(self).deliver_later
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def email_uninvited
|
75
|
+
if team.members.map { |m| m.email }.include?(email)
|
76
|
+
self.errors.add :email, "A person with this email address is already a member of this team."
|
77
|
+
return false
|
78
|
+
elsif team.invitations.initial.where(:email => email).present?
|
79
|
+
self.errors.add :email, "A person with this email address has already been invited to this team."
|
80
|
+
return false
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module SimpleTeams
|
2
|
+
class Membership < ApplicationRecord
|
3
|
+
belongs_to :team
|
4
|
+
belongs_to :member, :class_name => SimpleTeams.member_class.to_s
|
5
|
+
has_one :invitation, :dependent => :nullify
|
6
|
+
|
7
|
+
enum :role, [:member, :administrator, :owner]
|
8
|
+
|
9
|
+
validates_presence_of :role
|
10
|
+
|
11
|
+
def role_to_s
|
12
|
+
role.humanize.capitalize
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module SimpleTeams
|
2
|
+
class Team < ApplicationRecord
|
3
|
+
|
4
|
+
# Polymorphic relationship
|
5
|
+
belongs_to :teamable, polymorphic: true
|
6
|
+
|
7
|
+
# Team Relationships
|
8
|
+
has_many :invitations, :dependent => :destroy
|
9
|
+
has_many :memberships, :dependent => :destroy
|
10
|
+
has_many :members, :through => :memberships
|
11
|
+
|
12
|
+
delegate :name, :to => :teamable
|
13
|
+
|
14
|
+
def membership_for(member)
|
15
|
+
memberships.find_by(:member_id => member.id)
|
16
|
+
end
|
17
|
+
|
18
|
+
def remove_member(member)
|
19
|
+
memberships.find_by(:member_id => member.id).destroy
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module SimpleTeams
|
2
|
+
class BulkInvitationsNotification < ApplicationNotification
|
3
|
+
|
4
|
+
param :team_id, :user_id, :team_name, :user_name, :invitation_names
|
5
|
+
|
6
|
+
def message
|
7
|
+
"#{user_name} invited #{invitation_names.to_sentence} to #{team_name}."
|
8
|
+
end
|
9
|
+
|
10
|
+
def subject
|
11
|
+
"Users Invited"
|
12
|
+
end
|
13
|
+
|
14
|
+
def url
|
15
|
+
if team.present? and team.members.include? recipient
|
16
|
+
"/teams/#{team.id}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def link_text
|
21
|
+
"View Invitations"
|
22
|
+
end
|
23
|
+
|
24
|
+
# Objects
|
25
|
+
def team
|
26
|
+
Team.find_by(id: params[:team_id])
|
27
|
+
end
|
28
|
+
|
29
|
+
def user
|
30
|
+
SimpleTeams.member_class.find_by(id: params[:user_id])
|
31
|
+
end
|
32
|
+
|
33
|
+
# Names (fallback)
|
34
|
+
def team_name
|
35
|
+
team.present? ? team.name : params[:team_name]
|
36
|
+
end
|
37
|
+
|
38
|
+
def invitation_names
|
39
|
+
params[:invitation_names]
|
40
|
+
end
|
41
|
+
|
42
|
+
def user_name
|
43
|
+
if recipient.id == params[:user_id]
|
44
|
+
"You"
|
45
|
+
else
|
46
|
+
user.present? ? user.full_name : params[:user_name]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|