Skip to content

Commit 1aff1ca

Browse files
committed
Notify authors of new authorship assignments
1 parent 9f434ee commit 1aff1ca

File tree

9 files changed

+413
-52
lines changed

9 files changed

+413
-52
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# frozen_string_literal: true
2+
3+
module BetterTogether
4+
# Sends email notifications for page authorship changes
5+
class AuthorshipMailer < ApplicationMailer
6+
# rubocop:todo Metrics/PerceivedComplexity
7+
# rubocop:todo Metrics/MethodLength
8+
def authorship_changed_notification # rubocop:todo Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
9+
@platform = BetterTogether::Platform.find_by(host: true)
10+
@page = params[:page]
11+
@recipient = params[:recipient]
12+
@action = params[:action]
13+
@actor_name = params[:actor_name]
14+
@actor_name ||= BetterTogether::Person.find_by(id: params[:actor_id])&.name if params[:actor_id]
15+
16+
subject = if @action == 'removed'
17+
if @actor_name.present?
18+
t('better_together.authorship_mailer.authorship_changed_notification.subject_removed_by',
19+
page: @page.title, actor_name: @actor_name)
20+
else
21+
t('better_together.authorship_mailer.authorship_changed_notification.subject_removed',
22+
page: @page.title)
23+
end
24+
elsif @actor_name.present?
25+
t('better_together.authorship_mailer.authorship_changed_notification.subject_added_by',
26+
page: @page.title, actor_name: @actor_name)
27+
else
28+
t('better_together.authorship_mailer.authorship_changed_notification.subject_added',
29+
page: @page.title)
30+
end
31+
32+
# Respect locale and time zone preferences
33+
self.locale = @recipient.locale
34+
self.time_zone = @recipient.time_zone
35+
36+
mail(to: @recipient.email, subject:)
37+
end
38+
# rubocop:enable Metrics/MethodLength
39+
# rubocop:enable Metrics/PerceivedComplexity
40+
end
41+
end

app/models/better_together/authorship.rb

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,37 @@ class Authorship < ApplicationRecord
99
class_name: 'BetterTogether::Person'
1010
belongs_to :authorable,
1111
polymorphic: true
12+
13+
# Notify authors when they are added to or removed from a Page
14+
after_commit :notify_added_to_page, on: :create
15+
after_commit :notify_removed_from_page, on: :destroy
16+
17+
private
18+
19+
def notify_added_to_page
20+
return unless authorable.is_a?(BetterTogether::Page)
21+
22+
actor = defined?(::Current) && ::Current.respond_to?(:person) ? ::Current.person : nil
23+
BetterTogether::PageAuthorshipNotifier
24+
.with(record: authorable,
25+
page_id: authorable.id,
26+
action: 'added',
27+
actor_id: actor&.id,
28+
actor_name: actor&.name)
29+
.deliver_later(author)
30+
end
31+
32+
def notify_removed_from_page
33+
return unless authorable.is_a?(BetterTogether::Page)
34+
35+
actor = defined?(::Current) && ::Current.respond_to?(:person) ? ::Current.person : nil
36+
BetterTogether::PageAuthorshipNotifier
37+
.with(record: authorable,
38+
page_id: authorable.id,
39+
action: 'removed',
40+
actor_id: actor&.id,
41+
actor_name: actor&.name)
42+
.deliver_later(author)
43+
end
1244
end
1345
end
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# frozen_string_literal: true
2+
3+
module BetterTogether
4+
# Notifies a person when added to or removed from a Page as an author
5+
class PageAuthorshipNotifier < ApplicationNotifier
6+
deliver_by :action_cable, channel: 'BetterTogether::NotificationsChannel', message: :build_message
7+
8+
deliver_by :email,
9+
mailer: 'BetterTogether::AuthorshipMailer',
10+
method: :authorship_changed_notification,
11+
params: :email_params do |config|
12+
config.wait = 15.minutes
13+
config.if = -> { send_email_notification? }
14+
end
15+
16+
validates :record, presence: true
17+
18+
# record is the Page; keep naming helpers similar to NewMessageNotifier
19+
def page
20+
record
21+
end
22+
23+
def action
24+
params[:action]
25+
end
26+
27+
def actor_id
28+
params[:actor_id]
29+
end
30+
31+
def actor_name
32+
params[:actor_name]
33+
end
34+
35+
def actor
36+
@actor ||= BetterTogether::Person.find_by(id: actor_id)
37+
end
38+
39+
notification_methods do
40+
delegate :page, to: :event
41+
delegate :url, to: :event
42+
delegate :identifier, to: :event
43+
delegate :action, to: :event
44+
delegate :actor, to: :event
45+
delegate :actor_name, to: :event
46+
47+
def send_email_notification?
48+
recipient.email.present? && recipient.notify_by_email && should_send_email?
49+
end
50+
51+
def should_send_email?
52+
# Mirror conversation notifier grouping by related record (page)
53+
unread_notifications = recipient.notifications.where(
54+
event_id: BetterTogether::PageAuthorshipNotifier.where(params: { page_id: page.id }).select(:id),
55+
read_at: nil
56+
).order(created_at: :desc)
57+
58+
if unread_notifications.none?
59+
false
60+
else
61+
# Only send one email per unread notifications per page
62+
page.id == unread_notifications.last.event.record_id
63+
end
64+
end
65+
end
66+
67+
def identifier
68+
page.id
69+
end
70+
71+
def url
72+
page.url
73+
end
74+
75+
# rubocop:todo Metrics/PerceivedComplexity
76+
# rubocop:todo Metrics/MethodLength
77+
def title # rubocop:todo Metrics/AbcSize, Metrics/MethodLength
78+
name = actor_name || actor&.name
79+
if action == 'removed'
80+
if name.present?
81+
I18n.t('better_together.page_authorship_notifier.removed_by', page_title: page.title, actor_name: name)
82+
else
83+
I18n.t('better_together.page_authorship_notifier.removed', page_title: page.title)
84+
end
85+
elsif name.present?
86+
I18n.t('better_together.page_authorship_notifier.added_by', page_title: page.title, actor_name: name)
87+
else
88+
I18n.t('better_together.page_authorship_notifier.added', page_title: page.title)
89+
end
90+
end
91+
# rubocop:enable Metrics/MethodLength
92+
# rubocop:enable Metrics/PerceivedComplexity
93+
94+
def body
95+
# Keep body concise; UI partial will display details
96+
title
97+
end
98+
99+
def build_message(notification)
100+
{
101+
title:,
102+
body:,
103+
identifier:,
104+
url:,
105+
unread_count: notification.recipient.notifications.unread.count
106+
}
107+
end
108+
109+
def email_params(notification)
110+
{ page: notification.record, action: action, actor_id:, actor_name: }
111+
end
112+
end
113+
end
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<!-- app/views/better_together/authorship_mailer/authorship_changed_notification.html.erb -->
2+
3+
<p><%= t('.greeting', recipient_name: @recipient.name) %></p>
4+
5+
<% if @action == 'removed' %>
6+
<% if @actor_name.present? %>
7+
<p><%= t('.intro_removed_by', page: @page.title, actor_name: @actor_name) %></p>
8+
<% else %>
9+
<p><%= t('.intro_removed', page: @page.title) %></p>
10+
<% end %>
11+
<% else %>
12+
<% if @actor_name.present? %>
13+
<p><%= t('.intro_added_by', page: @page.title, actor_name: @actor_name) %></p>
14+
<% else %>
15+
<p><%= t('.intro_added', page: @page.title) %></p>
16+
<% end %>
17+
<% end %>
18+
19+
<p><%= t('.view_page') %></p>
20+
21+
<p><%= link_to t('.view_page_link'), @page.url %></p>
22+
23+
<p><%= t('.signature_html', platform: @platform.name) %></p>
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<!-- app/views/better_together/page_authorship_notifier/notifications/_notification.html.erb -->
2+
<%= render layout: 'better_together/notifications/notification',
3+
locals: { notification: notification,
4+
notification_title: (
5+
if notification.action == 'removed'
6+
notification.actor.present? ?
7+
t('better_together.page_authorship_notifier.removed_by', page_title: notification.page.title, actor_name: notification.actor.name) :
8+
t('better_together.page_authorship_notifier.removed', page_title: notification.page.title)
9+
else
10+
notification.actor.present? ?
11+
t('better_together.page_authorship_notifier.added_by', page_title: notification.page.title, actor_name: notification.actor.name) :
12+
t('better_together.page_authorship_notifier.added', page_title: notification.page.title)
13+
end
14+
),
15+
notification_url: notification.url } do %>
16+
<p class="mb-1">
17+
<%= notification.page.title %>
18+
</p>
19+
<% if notification.actor.present? || notification.actor_name.present? %>
20+
<p class="mb-0 text-muted small">
21+
<%= t('shared.by') rescue '' %>
22+
<% if notification.actor.present? && defined?(policy) && policy(notification.actor).respond_to?(:show?) && policy(notification.actor).show? %>
23+
<%= link_to(notification.actor, notification.actor, class: 'text-decoration-none') %>
24+
<% else %>
25+
<%= notification.actor_name || notification.actor&.name %>
26+
<% end %>
27+
</p>
28+
<% end %>
29+
<% end %>

config/locales/en.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,17 @@ en:
257257
recipient: :activerecord.models.recipient
258258
recipient_type: Recipient type
259259
seen_at: Seen at
260+
better_together/page_authorship_notifier:
261+
notifications: Notifications
262+
params: Params
263+
record: :activerecord.models.record
264+
record_type: Record type
265+
better_together/page_authorship_notifier/notification:
266+
event: :activerecord.models.event
267+
read_at: Read at
268+
recipient: :activerecord.models.recipient
269+
recipient_type: Recipient type
270+
seen_at: Seen at
260271
better_together/page:
261272
blocks: Blocks
262273
identifier: Identifier
@@ -818,6 +829,25 @@ en:
818829
content: "%{content}"
819830
title: "%{sender}: %{conversation}"
820831
time_ago: "%{time} ago"
832+
page_authorship_notifier:
833+
added: "You were added as an author on %{page_title}"
834+
added_by: "%{actor_name} added you as an author on %{page_title}"
835+
removed: "You were removed as an author on %{page_title}"
836+
removed_by: "%{actor_name} removed you as an author on %{page_title}"
837+
authorship_mailer:
838+
authorship_changed_notification:
839+
subject_added: "You've been added as an author to %{page}"
840+
subject_added_by: "%{actor_name} added you as an author to %{page}"
841+
subject_removed: "Your authorship was removed from %{page}"
842+
subject_removed_by: "%{actor_name} removed your authorship from %{page}"
843+
greeting: "Hi %{recipient_name},"
844+
intro_added: "You've been added as an author to the page %{page}."
845+
intro_added_by: "%{actor_name} added you as an author to the page %{page}."
846+
intro_removed: "You've been removed as an author from the page %{page}."
847+
intro_removed_by: "%{actor_name} removed you as an author from the page %{page}."
848+
view_page: "View the page"
849+
view_page_link: "Click here to view the page"
850+
signature_html: "— The %{platform} team"
821851
pages:
822852
form:
823853
create_page_before_adding_content: Create page before adding content
@@ -1488,6 +1518,8 @@ en:
14881518
previous: Previous
14891519
today: Today
14901520
week: Week
1521+
shared:
1522+
by: "by"
14911523
support:
14921524
array:
14931525
last_word_connector: ", and "

0 commit comments

Comments
 (0)