effective_messaging 0.8.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/helpers/effective_messaging_helper.rb +0 -1
- data/app/mailers/effective/notifications_mailer.rb +23 -47
- data/app/models/effective/notification.rb +32 -59
- data/app/views/admin/notifications/_form_notification.html.haml +20 -15
- data/app/views/admin/notifications/_summary.html.haml +22 -10
- data/app/views/effective/notifications_mailer/notification.liquid +10 -0
- data/db/migrate/101_create_effective_messaging.rb +1 -0
- data/lib/effective_messaging/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 473c0318cc028c7e4eced72d90d9f257af48060615ab9368e267aefeb8912923
|
4
|
+
data.tar.gz: b113796bf1d6eb615d732817bfd1b773a7341255774b90fc4bc1a48d4074a3a1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 130940194d693e5dab80793b47a54ae2614f6e8df8ba80d986e20ac35fdd0071a017365807a076ad5ebf2f2e176582b6aa4eb5981fb1a8b0a491497bd062dc1f
|
7
|
+
data.tar.gz: '0356873babc77f9124b8164d179fe7852d19a70eb8e4a8fbf5e9966f017c71b69bd9588b61adcdc1b2784f38df6590f202636af690ccf17b71436e6f889118dc'
|
@@ -1,63 +1,36 @@
|
|
1
1
|
module Effective
|
2
2
|
class NotificationsMailer < EffectiveMessaging.parent_mailer_class
|
3
3
|
include EffectiveMailer
|
4
|
+
include EffectiveEmailTemplatesMailer
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
def notify(notification, opts = {})
|
6
|
+
def notification(notification, resource = nil, opts = {})
|
8
7
|
raise('expected an Effective::Notification') unless notification.kind_of?(Effective::Notification)
|
9
8
|
|
10
|
-
|
11
|
-
|
12
|
-
|
9
|
+
@assigns = assigns_for(notification, resource)
|
10
|
+
|
11
|
+
# Find the TO email address for this resource
|
12
|
+
to = notification.to_email(resource)
|
13
|
+
raise('expected a to email address') unless to.present?
|
13
14
|
|
14
15
|
# Attach report
|
15
16
|
attach_report!(notification)
|
16
|
-
|
17
|
-
|
18
|
-
# Works with effective_logging to associate this email with the notification
|
19
|
-
headers = headers_for(notification, opts)
|
17
|
+
opts.delete(:content_type) if notification.attach_report?
|
20
18
|
|
21
19
|
# Use postmark broadcast-stream
|
22
20
|
if defined?(Postmark)
|
23
|
-
|
24
|
-
|
21
|
+
opts.merge!(message_stream: 'broadcast-stream')
|
22
|
+
append_unsubscribe_link!(notification, opts)
|
25
23
|
end
|
26
24
|
|
27
|
-
|
28
|
-
subject = subject_for(__method__, rendered.fetch(:subject), notification, opts)
|
29
|
-
|
30
|
-
# Pass everything to mail
|
31
|
-
mail(rendered.merge(headers).merge(subject: subject))
|
25
|
+
mail(to: to, **headers_for(resource, opts))
|
32
26
|
end
|
33
27
|
|
34
|
-
|
35
|
-
def notify_resource(notification, resource, opts = {})
|
36
|
-
raise('expected an Effective::Notification') unless notification.kind_of?(Effective::Notification)
|
37
|
-
raise('expected an acts_as_reportable resource') unless resource.class.try(:acts_as_reportable?)
|
38
|
-
|
39
|
-
# Returns a Hash of params to pass to mail()
|
40
|
-
# Includes a :to, :from, :subject and :body
|
41
|
-
rendered = notification.assign_renderer(view_context).render_email(resource)
|
42
|
-
|
43
|
-
# Works with effective_logging to associate this email with the notification
|
44
|
-
headers = headers_for(notification, opts)
|
45
|
-
|
46
|
-
# Use postmark broadcast-stream
|
47
|
-
if defined?(Postmark)
|
48
|
-
headers.merge!(message_stream: 'broadcast-stream')
|
49
|
-
attach_unsubscribe_link!(rendered)
|
50
|
-
end
|
51
|
-
|
52
|
-
# Calls effective_resources subject proc, so we can prepend [LETTERS]
|
53
|
-
subject = subject_for(__method__, rendered.fetch(:subject), resource, opts)
|
28
|
+
private
|
54
29
|
|
55
|
-
|
56
|
-
|
30
|
+
def assigns_for(notification, resource)
|
31
|
+
notification.assign_renderer(view_context).assigns_for(resource)
|
57
32
|
end
|
58
33
|
|
59
|
-
private
|
60
|
-
|
61
34
|
def attach_report!(notification)
|
62
35
|
return unless notification.attach_report?
|
63
36
|
raise("expected a scheduled email notification") unless notification.scheduled_email?
|
@@ -74,9 +47,9 @@ module Effective
|
|
74
47
|
}
|
75
48
|
end
|
76
49
|
|
77
|
-
def
|
78
|
-
raise('expected a Hash') unless
|
79
|
-
raise('expected a Hash with a :body') unless
|
50
|
+
def append_unsubscribe_link!(notification, opts)
|
51
|
+
raise('expected a Hash') unless opts.kind_of?(Hash)
|
52
|
+
raise('expected a Hash with a :body') unless opts.key?(:body)
|
80
53
|
|
81
54
|
name = EffectiveResources.et('acronym')
|
82
55
|
url = view_context.root_url
|
@@ -87,10 +60,13 @@ module Effective
|
|
87
60
|
"Please understand that unsubscribing means you will no longer receive mandatory messages and announcements."
|
88
61
|
].join(" ")
|
89
62
|
|
90
|
-
|
91
|
-
|
63
|
+
if notification.email_notification_html?
|
64
|
+
opts.merge!(body: "#{opts[:body]}\r\n<br/><p>#{unsubscribe}</p>")
|
65
|
+
else
|
66
|
+
opts.merge!(body: "#{opts[:body]}\r\n\r\n#{unsubscribe}")
|
67
|
+
end
|
92
68
|
|
93
|
-
|
69
|
+
true
|
94
70
|
end
|
95
71
|
|
96
72
|
def mailer_settings
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# This could be called ReportNotification. It only sends notifications for effective_reports right now.
|
4
|
+
|
3
5
|
module Effective
|
4
6
|
class Notification < ActiveRecord::Base
|
5
7
|
self.table_name = (EffectiveMessaging.notifications_table_name || :notifications).to_s
|
@@ -8,6 +10,7 @@ module Effective
|
|
8
10
|
attr_accessor :current_resource
|
9
11
|
attr_accessor :view_context
|
10
12
|
|
13
|
+
acts_as_email_notification # effective_resources
|
11
14
|
log_changes if respond_to?(:log_changes)
|
12
15
|
|
13
16
|
# Unused. If we want to use notifications in a has_many way
|
@@ -17,7 +20,7 @@ module Effective
|
|
17
20
|
belongs_to :user, polymorphic: true, optional: true
|
18
21
|
|
19
22
|
# Effective namespace
|
20
|
-
belongs_to :report, class_name: 'Effective::Report'
|
23
|
+
belongs_to :report, class_name: 'Effective::Report'
|
21
24
|
|
22
25
|
# Tracks the send outs
|
23
26
|
has_many :notification_logs, dependent: :delete_all
|
@@ -65,6 +68,7 @@ module Effective
|
|
65
68
|
from :string
|
66
69
|
cc :string
|
67
70
|
bcc :string
|
71
|
+
content_type :string
|
68
72
|
|
69
73
|
# Background tracking
|
70
74
|
last_notified_at :datetime
|
@@ -108,7 +112,7 @@ module Effective
|
|
108
112
|
end
|
109
113
|
|
110
114
|
validate(if: -> { immediate? && immediate_days.present? && immediate_times.present? }) do
|
111
|
-
|
115
|
+
errors.add(:immediate_times, "must be 1 when when using every 0 days") if immediate_days == 0 && immediate_times != 1
|
112
116
|
end
|
113
117
|
|
114
118
|
# Scheduled
|
@@ -125,30 +129,10 @@ module Effective
|
|
125
129
|
end
|
126
130
|
end
|
127
131
|
|
128
|
-
# Email
|
129
|
-
validates :from, presence: true, email: true
|
130
|
-
validates :subject, presence: true, liquid: true
|
131
|
-
validates :body, presence: true, liquid: true
|
132
|
-
|
133
|
-
# Report
|
134
|
-
validates :report, presence: true
|
135
|
-
|
136
132
|
validate(if: -> { report.present? }) do
|
137
133
|
errors.add(:report, 'must include an email, user, organization or owner column') unless report.email_report_column || report.emailable_report_column
|
138
134
|
end
|
139
135
|
|
140
|
-
validate(if: -> { report.present? && subject.present? }) do
|
141
|
-
if(invalid = template_variables(body: false) - report_variables).present?
|
142
|
-
errors.add(:subject, "Invalid variable: #{invalid.to_sentence}")
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
validate(if: -> { report.present? && body.present? }) do
|
147
|
-
if(invalid = template_variables(subject: false) - report_variables).present?
|
148
|
-
errors.add(:body, "Invalid variable: #{invalid.to_sentence}")
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
136
|
def to_s
|
153
137
|
subject.presence || model_name.human
|
154
138
|
end
|
@@ -195,15 +179,11 @@ module Effective
|
|
195
179
|
Array(self[:scheduled_dates]) - [nil, '']
|
196
180
|
end
|
197
181
|
|
198
|
-
def
|
199
|
-
|
200
|
-
end
|
201
|
-
|
202
|
-
def template_body
|
203
|
-
Liquid::Template.parse(body)
|
182
|
+
def email_template
|
183
|
+
:notification # We always use this email template
|
204
184
|
end
|
205
185
|
|
206
|
-
def
|
186
|
+
def email_template_variables
|
207
187
|
assigns_for().keys
|
208
188
|
end
|
209
189
|
|
@@ -272,6 +252,20 @@ module Effective
|
|
272
252
|
scheduled_email? ? notify_by_schedule!(force: force) : notify_by_resources!(force: force)
|
273
253
|
end
|
274
254
|
|
255
|
+
# Returns a message. Do not call deliver.
|
256
|
+
def preview
|
257
|
+
return unless report.present?
|
258
|
+
|
259
|
+
if audience_emails?
|
260
|
+
# notify_by_schedule
|
261
|
+
Effective::NotificationsMailer.notification(self, nil, email_notification_params)
|
262
|
+
else
|
263
|
+
# notify_by_resources
|
264
|
+
resource = report.collection.order('RANDOM()').first
|
265
|
+
Effective::NotificationsMailer.notification(self, resource, email_notification_params) if resource
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
275
269
|
# Operates on every resource in the data source. Sends one email for each row
|
276
270
|
def notify_by_resources!(force: false)
|
277
271
|
notified = 0
|
@@ -280,7 +274,7 @@ module Effective
|
|
280
274
|
next unless notifiable?(resource) || force
|
281
275
|
|
282
276
|
# Send Now functionality. Don't duplicate if it's same day.
|
283
|
-
next if
|
277
|
+
next if already_notified_today?(resource) && !force
|
284
278
|
|
285
279
|
print('.')
|
286
280
|
|
@@ -289,7 +283,7 @@ module Effective
|
|
289
283
|
assign_attributes(current_resource: resource)
|
290
284
|
|
291
285
|
# Send the resource email
|
292
|
-
Effective::NotificationsMailer.
|
286
|
+
Effective::NotificationsMailer.notification(self, resource, email_notification_params).deliver_now
|
293
287
|
|
294
288
|
# Log that it was sent
|
295
289
|
build_notification_log(resource: resource).save!
|
@@ -299,6 +293,7 @@ module Effective
|
|
299
293
|
rescue => e
|
300
294
|
EffectiveLogger.error(e.message, associated: self) if defined?(EffectiveLogger)
|
301
295
|
ExceptionNotifier.notify_exception(e, data: { notification_id: id, resource_id: resource.id, resource_type: resource.class.name }) if defined?(ExceptionNotifier)
|
296
|
+
raise(e) if Rails.env.test? || Rails.env.development?
|
302
297
|
end
|
303
298
|
|
304
299
|
GC.start if (notified % 250) == 0
|
@@ -312,7 +307,7 @@ module Effective
|
|
312
307
|
|
313
308
|
if notifiable_scheduled? || force
|
314
309
|
begin
|
315
|
-
Effective::NotificationsMailer.
|
310
|
+
Effective::NotificationsMailer.notification(self, nil, email_notification_params).deliver_now
|
316
311
|
|
317
312
|
# Log that it was sent
|
318
313
|
build_notification_log(resource: nil).save!
|
@@ -322,6 +317,7 @@ module Effective
|
|
322
317
|
rescue => e
|
323
318
|
EffectiveLogger.error(e.message, associated: self) if defined?(EffectiveLogger)
|
324
319
|
ExceptionNotifier.notify_exception(e, data: { notification_id: id }) if defined?(ExceptionNotifier)
|
320
|
+
raise(e) if Rails.env.test? || Rails.env.development?
|
325
321
|
end
|
326
322
|
|
327
323
|
end
|
@@ -391,26 +387,11 @@ module Effective
|
|
391
387
|
end
|
392
388
|
end
|
393
389
|
|
394
|
-
def
|
395
|
-
|
396
|
-
|
397
|
-
to = (audience == 'emails' ? audience_emails.presence : resource_emails_to_s(resource))
|
398
|
-
raise('expected a to email address') unless to.present?
|
399
|
-
|
400
|
-
assigns = assigns_for(resource)
|
401
|
-
|
402
|
-
{
|
403
|
-
to: to,
|
404
|
-
from: from,
|
405
|
-
cc: cc.presence,
|
406
|
-
bcc: bcc.presence,
|
407
|
-
content_type: CONTENT_TYPES.first,
|
408
|
-
subject: template_subject.render(assigns),
|
409
|
-
body: template_body.render(assigns)
|
410
|
-
}.compact
|
390
|
+
def to_email(resource)
|
391
|
+
audience == 'emails' ? audience_emails.presence : resource_emails_to_s(resource)
|
411
392
|
end
|
412
393
|
|
413
|
-
# We pull the Assigns from
|
394
|
+
# We pull the Assigns from 2 places:
|
414
395
|
# 1. The report.report_columns
|
415
396
|
# 2. The class's def reportable_view_assigns(view) method
|
416
397
|
def assigns_for(resource = nil)
|
@@ -427,7 +408,7 @@ module Effective
|
|
427
408
|
reportable_view_assigns = resource.reportable_view_assigns(renderer).deep_stringify_keys
|
428
409
|
raise('expected notification assigns to return a Hash') unless reportable_view_assigns.kind_of?(Hash)
|
429
410
|
|
430
|
-
# Merge all
|
411
|
+
# Merge all assigns
|
431
412
|
report_assigns.merge(reportable_view_assigns)
|
432
413
|
end
|
433
414
|
|
@@ -442,14 +423,6 @@ module Effective
|
|
442
423
|
|
443
424
|
private
|
444
425
|
|
445
|
-
def template_variables(body: true, subject: true)
|
446
|
-
[(template_body.presence if body), (template_subject.presence if subject)].compact.map do |template|
|
447
|
-
Liquid::ParseTreeVisitor.for(template.root).add_callback_for(Liquid::VariableLookup) do |node|
|
448
|
-
[node.name, *node.lookups].join('.')
|
449
|
-
end.visit
|
450
|
-
end.flatten.uniq.compact
|
451
|
-
end
|
452
|
-
|
453
426
|
def audience_emails_to_s
|
454
427
|
audience_emails.presence&.join(', ')
|
455
428
|
end
|
@@ -1,7 +1,11 @@
|
|
1
1
|
= effective_form_with(model: [:admin, notification], engine: true) do |f|
|
2
2
|
%h2 Audience
|
3
3
|
%p Please select who the notifications should be sent to
|
4
|
-
|
4
|
+
|
5
|
+
= f.radios :audience, Effective::Notification::AUDIENCES, label: false, buttons: true,
|
6
|
+
'data-load-ajax-url': effective_messaging.new_admin_notification_path,
|
7
|
+
'data-load-ajax-div': '#effective-messaging-ajax',
|
8
|
+
'data-load-ajax-all': true
|
5
9
|
|
6
10
|
= f.show_if(:audience, 'report') do
|
7
11
|
%p The notification will be sent to the user or email from the report.
|
@@ -46,29 +50,30 @@
|
|
46
50
|
= f.select :report_id, Effective::Report.sorted.notifiable.all,
|
47
51
|
hint: "Please select a #{link_to 'report', effective_reports.admin_reports_path, target: '_blank'} with a user or email column to use as the data source",
|
48
52
|
'data-load-ajax-url': effective_messaging.new_admin_notification_path,
|
49
|
-
'data-load-ajax-div': '#effective-messaging-ajax'
|
53
|
+
'data-load-ajax-div': '#effective-messaging-ajax',
|
54
|
+
'data-load-ajax-all': true
|
50
55
|
|
51
56
|
= f.show_if(:audience, 'emails') do
|
52
57
|
= f.show_if(:schedule_type, 'scheduled') do
|
53
58
|
= f.check_box :attach_report, label: 'Yes, attach a .csv file with the report data', hint: 'only available to scheduled emails sent to specific addresses'
|
54
59
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
- f.object.from ||= EffectiveMessaging.mailer_froms.first
|
59
|
-
= f.select :from, mailer_froms_collection()
|
60
|
+
= f.show_if(:audience, 'report') do
|
61
|
+
= f.hidden_field :attach_report, value: false
|
60
62
|
|
61
|
-
|
62
|
-
= f
|
63
|
-
= f.text_area :body, rows: 20
|
63
|
+
%h2 Notification
|
64
|
+
= email_notification_fields(f, :notification)
|
64
65
|
|
65
66
|
#effective-messaging-ajax
|
66
67
|
- if f.object.report.present?
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
%
|
68
|
+
= card do
|
69
|
+
- if f.object.audience == 'report'
|
70
|
+
%p You can use the following variables in the subject and body:
|
71
|
+
|
72
|
+
%ul
|
73
|
+
- f.object.email_template_variables.each do |name|
|
74
|
+
%li {{ #{name} }}
|
75
|
+
- elsif f.object.audience == 'emails'
|
76
|
+
%p No variables available for Send to specific address audience.
|
72
77
|
|
73
78
|
= f.submit do
|
74
79
|
= f.save 'Save'
|
@@ -14,36 +14,48 @@
|
|
14
14
|
%h4= notification.report.to_s
|
15
15
|
= render 'admin/reports/report', report: notification.report
|
16
16
|
|
17
|
-
-
|
18
|
-
|
17
|
+
- message = notification.preview()
|
18
|
+
|
19
|
+
- if message.present?
|
20
|
+
- if notification.audience_emails?
|
21
|
+
%p A preview of the "Send to specific addresses" notification follows:
|
22
|
+
- else
|
23
|
+
%p Using a random row from the data source, a preview of the "Send to user or email from the report" notification follows:
|
19
24
|
|
20
25
|
= card('Preview') do
|
21
|
-
-
|
22
|
-
- rendered = notification.assign_renderer(self).render_email(resource)
|
26
|
+
- message = notification.preview()
|
23
27
|
|
24
28
|
%table.table
|
25
29
|
%tbody
|
26
30
|
%tr
|
27
31
|
%th To
|
28
|
-
%td=
|
32
|
+
%td= Array(message.to).join(', ')
|
29
33
|
%tr
|
30
34
|
%th From
|
31
|
-
%td=
|
35
|
+
%td= Array(message.from).join(', ')
|
36
|
+
|
37
|
+
- if (content_type = message.content_type).present?
|
38
|
+
%tr
|
39
|
+
%th Content-Type
|
40
|
+
%td= content_type
|
32
41
|
|
33
|
-
- if (cc =
|
42
|
+
- if (cc = message.cc).present?
|
34
43
|
%tr
|
35
44
|
%th CC
|
36
45
|
%td= cc
|
37
46
|
|
38
|
-
- if (bcc =
|
47
|
+
- if (bcc = message.bcc).present?
|
39
48
|
%tr
|
40
49
|
%th BCC
|
41
50
|
%td= bcc
|
42
51
|
|
43
52
|
%tr
|
44
53
|
%th Subject
|
45
|
-
%td=
|
54
|
+
%td= message.subject
|
46
55
|
|
47
56
|
%tr
|
48
57
|
%td{colspan: 2}
|
49
|
-
|
58
|
+
- if email_message_html?(message)
|
59
|
+
= iframe_srcdoc_tag(email_message_body(message))
|
60
|
+
- else
|
61
|
+
= simple_format(email_message_body(message))
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: effective_messaging
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Code and Effect
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-06-
|
11
|
+
date: 2024-06-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -246,6 +246,7 @@ files:
|
|
246
246
|
- app/views/effective/chats/_summary.html.haml
|
247
247
|
- app/views/effective/messaging/_dashboard.html.haml
|
248
248
|
- app/views/effective/messaging_mailer/chat_new_message.liquid
|
249
|
+
- app/views/effective/notifications_mailer/notification.liquid
|
249
250
|
- config/effective_messaging.rb
|
250
251
|
- config/locales/effective_messaging.yml
|
251
252
|
- config/routes.rb
|