simple_teams 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +28 -0
  4. data/Rakefile +8 -0
  5. data/app/assets/config/simple_teams_manifest.js +1 -0
  6. data/app/assets/stylesheets/simple_teams/application.css +15 -0
  7. data/app/controllers/simple_teams/accept_invitations_controller.rb +57 -0
  8. data/app/controllers/simple_teams/admin/invitations_controller.rb +60 -0
  9. data/app/controllers/simple_teams/admin/memberships_controller.rb +60 -0
  10. data/app/controllers/simple_teams/admin/teams_controller.rb +60 -0
  11. data/app/controllers/simple_teams/application_controller.rb +13 -0
  12. data/app/controllers/simple_teams/invitations_controller.rb +96 -0
  13. data/app/controllers/simple_teams/leave_teams_controller.rb +25 -0
  14. data/app/controllers/simple_teams/memberships_controller.rb +61 -0
  15. data/app/controllers/simple_teams/related_members_controller.rb +33 -0
  16. data/app/controllers/simple_teams/teams_controller.rb +19 -0
  17. data/app/forms/simple_teams/invitation_form.rb +77 -0
  18. data/app/forms/simple_teams/invitation_forms/create.rb +24 -0
  19. data/app/forms/simple_teams/invitation_forms/create_bulk.rb +125 -0
  20. data/app/forms/simple_teams/invitation_forms/create_combo.rb +75 -0
  21. data/app/forms/simple_teams/invitation_forms/update.rb +29 -0
  22. data/app/forms/simple_teams/membership_form.rb +91 -0
  23. data/app/helpers/simple_teams/application_helper.rb +4 -0
  24. data/app/helpers/simple_teams/invitations_helper.rb +4 -0
  25. data/app/helpers/simple_teams/memberships_helper.rb +4 -0
  26. data/app/helpers/simple_teams/teams_helper.rb +4 -0
  27. data/app/jobs/simple_teams/application_job.rb +4 -0
  28. data/app/mailers/simple_teams/application_mailer.rb +6 -0
  29. data/app/mailers/simple_teams/mailer.rb +17 -0
  30. data/app/models/concerns/simple_teams/member_object.rb +22 -0
  31. data/app/models/concerns/simple_teams/team_object.rb +23 -0
  32. data/app/models/simple_teams/ability.rb +34 -0
  33. data/app/models/simple_teams/application_record.rb +5 -0
  34. data/app/models/simple_teams/invitation.rb +85 -0
  35. data/app/models/simple_teams/membership.rb +16 -0
  36. data/app/models/simple_teams/team.rb +23 -0
  37. data/app/notifications/application_notification.rb +4 -0
  38. data/app/notifications/simple_teams/bulk_invitations_notification.rb +51 -0
  39. data/app/notifications/simple_teams/invitation_notification.rb +54 -0
  40. data/app/notifications/simple_teams/invitations/accepted_notification.rb +13 -0
  41. data/app/notifications/simple_teams/invitations/created_notification.rb +13 -0
  42. data/app/notifications/simple_teams/invitations/destroyed_notification.rb +13 -0
  43. data/app/notifications/simple_teams/invitations/updated_notification.rb +13 -0
  44. data/app/notifications/simple_teams/membership_notification.rb +58 -0
  45. data/app/notifications/simple_teams/memberships/destroyed_notification.rb +13 -0
  46. data/app/notifications/simple_teams/memberships/left_notification.rb +17 -0
  47. data/app/notifications/simple_teams/memberships/updated_notification.rb +17 -0
  48. data/app/service_objects/simple_teams/accept_invitation_service.rb +61 -0
  49. data/app/service_objects/simple_teams/initialize_team_service.rb +25 -0
  50. data/app/service_objects/simple_teams/leave_team_service.rb +56 -0
  51. data/app/views/layouts/simple_teams/application.html.erb +20 -0
  52. data/app/views/simple_teams/_roles_field.html.erb +22 -0
  53. data/app/views/simple_teams/accept_invitations/invalid_token.html.erb +7 -0
  54. data/app/views/simple_teams/accept_invitations/new.html.erb +18 -0
  55. data/app/views/simple_teams/admin/invitations/_form.html.erb +62 -0
  56. data/app/views/simple_teams/admin/invitations/_invitation.html.erb +47 -0
  57. data/app/views/simple_teams/admin/invitations/edit.html.erb +10 -0
  58. data/app/views/simple_teams/admin/invitations/index.html.erb +14 -0
  59. data/app/views/simple_teams/admin/invitations/new.html.erb +9 -0
  60. data/app/views/simple_teams/admin/invitations/show.html.erb +10 -0
  61. data/app/views/simple_teams/admin/memberships/_form.html.erb +32 -0
  62. data/app/views/simple_teams/admin/memberships/_membership.html.erb +17 -0
  63. data/app/views/simple_teams/admin/memberships/edit.html.erb +10 -0
  64. data/app/views/simple_teams/admin/memberships/index.html.erb +14 -0
  65. data/app/views/simple_teams/admin/memberships/new.html.erb +9 -0
  66. data/app/views/simple_teams/admin/memberships/show.html.erb +10 -0
  67. data/app/views/simple_teams/admin/teams/_form.html.erb +22 -0
  68. data/app/views/simple_teams/admin/teams/_team.html.erb +7 -0
  69. data/app/views/simple_teams/admin/teams/edit.html.erb +10 -0
  70. data/app/views/simple_teams/admin/teams/index.html.erb +14 -0
  71. data/app/views/simple_teams/admin/teams/new.html.erb +9 -0
  72. data/app/views/simple_teams/admin/teams/show.html.erb +10 -0
  73. data/app/views/simple_teams/invitations/_bulk_form.html.erb +63 -0
  74. data/app/views/simple_teams/invitations/_form.html.erb +15 -0
  75. data/app/views/simple_teams/invitations/_invitation.html.erb +19 -0
  76. data/app/views/simple_teams/invitations/_invitation.json.jbuilder +2 -0
  77. data/app/views/simple_teams/invitations/edit.html.erb +12 -0
  78. data/app/views/simple_teams/invitations/new.html.erb +12 -0
  79. data/app/views/simple_teams/leave_teams/edit.html.erb +2 -0
  80. data/app/views/simple_teams/memberships/_form.html.erb +12 -0
  81. data/app/views/simple_teams/memberships/_membership.html.erb +23 -0
  82. data/app/views/simple_teams/memberships/edit.html.erb +12 -0
  83. data/app/views/simple_teams/related_members/index.json.jbuilder +5 -0
  84. data/app/views/simple_teams/related_members/select2.json.jbuilder +6 -0
  85. data/app/views/simple_teams/teams/_add_member_link.html.erb +3 -0
  86. data/app/views/simple_teams/teams/_members_table.html.erb +20 -0
  87. data/app/views/simple_teams/teams/show.html.erb +38 -0
  88. data/app/views/simple_teams/teams_mailer/invitation_notification.html.erb +9 -0
  89. data/config/locales/simple_teams.en.yml +18 -0
  90. data/config/routes.rb +21 -0
  91. data/db/migrate/20240311052758_create_simple_teams_teams.rb +9 -0
  92. data/db/migrate/20240311053006_create_simple_teams_memberships.rb +11 -0
  93. data/db/migrate/20240311053607_create_simple_teams_invitations.rb +17 -0
  94. data/lib/simple_teams/engine.rb +22 -0
  95. data/lib/simple_teams/version.rb +3 -0
  96. data/lib/simple_teams.rb +6 -0
  97. data/lib/tasks/simple_teams_tasks.rake +4 -0
  98. 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,4 @@
1
+ module SimpleTeams
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module SimpleTeams
2
+ module InvitationsHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module SimpleTeams
2
+ module MembershipsHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module SimpleTeams
2
+ module TeamsHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module SimpleTeams
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module SimpleTeams
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: "from@example.com"
4
+ layout "mailer"
5
+ end
6
+ 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,5 @@
1
+ module SimpleTeams
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ 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,4 @@
1
+ class ApplicationNotification < Noticed::Base
2
+ deliver_by :database
3
+
4
+ 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