nuntius 1.0.27 → 1.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +77 -18
- data/Rakefile +16 -39
- data/app/controllers/nuntius/admin/campaigns_controller.rb +12 -10
- data/app/controllers/nuntius/admin/layouts/attachments_controller.rb +2 -1
- data/app/controllers/nuntius/admin/layouts_controller.rb +1 -1
- data/app/controllers/nuntius/admin/lists/subscribers_controller.rb +1 -1
- data/app/controllers/nuntius/admin/lists_controller.rb +4 -4
- data/app/controllers/nuntius/admin/locales_controller.rb +3 -2
- data/app/controllers/nuntius/admin/messages_controller.rb +2 -2
- data/app/controllers/nuntius/admin/templates_controller.rb +1 -1
- data/app/controllers/nuntius/api/events_controller.rb +2 -2
- data/app/controllers/nuntius/application_admin_controller.rb +1 -1
- data/app/controllers/nuntius/callbacks_controller.rb +4 -4
- data/app/controllers/nuntius/dashboard_controller.rb +1 -1
- data/app/controllers/nuntius/feedback_controller.rb +1 -2
- data/app/controllers/nuntius/inbound_messages/twilio_inbound_smses_controller.rb +7 -7
- data/app/controllers/nuntius/messages_controller.rb +1 -1
- data/app/controllers/nuntius/subscribers_controller.rb +33 -0
- data/app/drops/nuntius/list_drop.rb +7 -0
- data/app/drops/nuntius/subscriber_drop.rb +7 -0
- data/app/exceptions/nuntius/base_exception.rb +2 -0
- data/app/exceptions/nuntius/missing_messenger_exception.rb +2 -0
- data/app/helpers/nuntius/application_helper.rb +6 -11
- data/app/jobs/nuntius/deliver_inbound_message_job.rb +1 -1
- data/app/jobs/nuntius/purge_message_job.rb +2 -2
- data/app/jobs/nuntius/retrieve_mail_job.rb +2 -2
- data/app/jobs/nuntius/transport_delivery_job.rb +2 -2
- data/app/message_boxes/nuntius/base_message_box.rb +46 -23
- data/app/messengers/nuntius/base_messenger.rb +13 -12
- data/app/models/nuntius/application_record.rb +1 -1
- data/app/models/nuntius/attachment.rb +4 -2
- data/app/models/nuntius/campaign.rb +4 -51
- data/app/models/nuntius/concerns/yamlify.rb +2 -2
- data/app/models/nuntius/inbound_message.rb +2 -0
- data/app/models/nuntius/list.rb +11 -1
- data/app/models/nuntius/message.rb +34 -29
- data/app/models/nuntius/subscriber.rb +21 -1
- data/app/models/nuntius/template.rb +20 -20
- data/app/presenters/application_presenter.rb +1 -1
- data/app/presenters/template_presenter.rb +3 -3
- data/app/providers/nuntius/apnotic_push_provider.rb +17 -17
- data/app/providers/nuntius/base_provider.rb +6 -6
- data/app/providers/nuntius/firebase_push_provider.rb +8 -8
- data/app/providers/nuntius/houston_push_provider.rb +15 -15
- data/app/providers/nuntius/message_bird_sms_provider.rb +7 -7
- data/app/providers/nuntius/slack_slack_provider.rb +7 -7
- data/app/providers/nuntius/smtp_mail_provider.rb +36 -28
- data/app/providers/nuntius/teams_teams_provider.rb +24 -0
- data/app/providers/nuntius/twilio_sms_provider.rb +4 -4
- data/app/providers/nuntius/twilio_voice_provider.rb +13 -13
- data/app/services/nuntius/aws_sns_processor_service.rb +11 -11
- data/app/services/nuntius/deliver_campaign_service.rb +65 -0
- data/app/services/nuntius/deliver_inbound_message_service.rb +3 -2
- data/app/services/nuntius/retrieve_inbound_mail_service.rb +23 -16
- data/app/tables/nuntius_campaigns_table.rb +10 -1
- data/app/tables/nuntius_layouts_table.rb +4 -4
- data/app/tables/nuntius_lists_table.rb +1 -1
- data/app/tables/nuntius_locales_table.rb +1 -1
- data/app/tables/nuntius_messages_table.rb +5 -5
- data/app/tables/nuntius_subscribers_table.rb +3 -2
- data/app/tables/nuntius_templates_table.rb +12 -5
- data/app/transports/nuntius/base_transport.rb +1 -1
- data/app/transports/nuntius/mail_transport.rb +1 -1
- data/app/transports/nuntius/push_transport.rb +1 -1
- data/app/transports/nuntius/teams_transport.rb +6 -0
- data/app/views/nuntius/admin/campaigns/edit.html.slim +12 -16
- data/app/views/nuntius/admin/campaigns/index.html.slim +2 -2
- data/app/views/nuntius/admin/layouts/attachments/create.json.jbuilder +3 -3
- data/app/views/nuntius/admin/layouts/edit.html.slim +18 -19
- data/app/views/nuntius/admin/layouts/index.html.slim +2 -2
- data/app/views/nuntius/admin/lists/edit.html.slim +19 -9
- data/app/views/nuntius/admin/lists/index.html.slim +2 -2
- data/app/views/nuntius/admin/lists/subscribers/edit.html.slim +2 -2
- data/app/views/nuntius/admin/locales/edit.html.slim +3 -3
- data/app/views/nuntius/admin/locales/index.html.slim +2 -2
- data/app/views/nuntius/admin/messages/index.html.slim +2 -2
- data/app/views/nuntius/admin/messages/show.html.slim +12 -12
- data/app/views/nuntius/admin/templates/edit.html.slim +62 -64
- data/app/views/nuntius/admin/templates/index.html.slim +2 -2
- data/app/views/nuntius/dashboard/show.html.slim +2 -6
- data/app/views/nuntius/subscribers/show.html.slim +9 -0
- data/config/locales/en.yml +97 -1
- data/config/routes.rb +18 -8
- data/db/migrate/20190301201541_create_nuntius_templates.rb +5 -5
- data/db/migrate/20190301202436_create_nuntius_messages.rb +4 -4
- data/db/migrate/20190322114340_create_nuntius_campaigns.rb +2 -2
- data/db/migrate/20190322121338_create_nuntius_subscribers.rb +1 -1
- data/db/migrate/20190327124407_create_nuntius_layouts.rb +5 -5
- data/db/migrate/20190327143921_add_campaign_to_message.rb +1 -1
- data/db/migrate/20190417125153_change_message_status_default.rb +2 -2
- data/db/migrate/20190825080757_update_nuntius_message_sending_to_sent.rb +2 -2
- data/db/migrate/20200220154927_change_nuntius_templates_payload_type.rb +1 -1
- data/db/migrate/20200318095339_create_nuntius_attachments.rb +2 -2
- data/db/migrate/20201121185718_create_nuntius_inbound_messages.rb +1 -1
- data/db/migrate/20240205204719_add_description_to_list.rb +5 -0
- data/db/migrate/20240206203019_add_slug_to_nuntius_list.rb +9 -0
- data/lib/nuntius/active_record_helpers.rb +4 -4
- data/lib/nuntius/configuration.rb +64 -27
- data/lib/nuntius/deprecator.rb +2 -0
- data/lib/nuntius/devise.rb +1 -1
- data/lib/nuntius/engine.rb +13 -27
- data/lib/nuntius/i18n_store.rb +6 -5
- data/lib/nuntius/liquid/tags/attach_tag.rb +11 -11
- data/lib/nuntius/mail_allow_list.rb +2 -2
- data/lib/nuntius/nuntiable.rb +3 -1
- data/lib/nuntius/transactio.rb +1 -1
- data/lib/nuntius/version.rb +1 -1
- data/lib/nuntius.rb +13 -27
- data/lib/preamble.rb +18 -18
- data/lib/tasks/nuntius_tasks.rake +4 -4
- metadata +48 -33
- data/app/jobs/nuntius/process_inbound_message_job.rb +0 -10
- data/app/reportlets/nuntius/application_reportlet.rb +0 -35
- data/app/reportlets/nuntius/daily_messages_per_template_reportlet.rb +0 -50
- data/config/webpack/development.js +0 -5
- data/config/webpack/environment.js +0 -4
- data/config/webpack/production.js +0 -5
- data/config/webpack/test.js +0 -5
- data/config/webpacker.yml +0 -119
@@ -1,8 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# Base class for MessageBoxes
|
4
|
+
# These allow you to receive messages based on transport/provider and routes
|
3
5
|
module Nuntius
|
4
6
|
# Message boxes process inbound messages
|
5
7
|
class BaseMessageBox
|
8
|
+
attr_reader :message
|
9
|
+
|
10
|
+
def initialize(message)
|
11
|
+
@message = message
|
12
|
+
end
|
13
|
+
|
14
|
+
# In case of mail transport mail will return the mail itself, using https://github.com/mikel/mail
|
15
|
+
# For other transports it will return nil.
|
16
|
+
# @return [Mail::Message]
|
17
|
+
def mail
|
18
|
+
return nil unless self.class.transport.to_sym == :mail
|
19
|
+
return @mail if @mail
|
20
|
+
|
21
|
+
@mail = Mail.new(message.raw_message.download)
|
22
|
+
end
|
23
|
+
|
6
24
|
class << self
|
7
25
|
attr_reader :routes
|
8
26
|
|
@@ -16,23 +34,26 @@ module Nuntius
|
|
16
34
|
@transport
|
17
35
|
end
|
18
36
|
|
19
|
-
def route(
|
37
|
+
def route(attribute = :to, test = /.*/, to:)
|
20
38
|
@routes ||= {}
|
21
|
-
@routes =
|
39
|
+
@routes[attribute] = {test: test, to: to}
|
22
40
|
@routes
|
23
41
|
end
|
24
42
|
|
25
43
|
# Defines the settings
|
26
|
-
def settings(hash =
|
44
|
+
def settings(hash = nil)
|
27
45
|
@settings = hash if hash
|
28
46
|
@settings
|
29
47
|
end
|
30
48
|
|
31
49
|
def deliver(message)
|
32
50
|
klasses = message_box_for(transport: message.transport.to_sym, provider: message.provider.to_sym)
|
33
|
-
klass, method = message_box_for_route(klasses, message
|
51
|
+
klass, method = message_box_for_route(klasses, message)
|
34
52
|
|
35
|
-
|
53
|
+
if method
|
54
|
+
klass.new(message).send(method)
|
55
|
+
true
|
56
|
+
end
|
36
57
|
end
|
37
58
|
|
38
59
|
def message_box_for(transport: nil, provider: nil)
|
@@ -42,34 +63,36 @@ module Nuntius
|
|
42
63
|
result
|
43
64
|
end
|
44
65
|
|
45
|
-
def mail
|
46
|
-
return nil if transport != 'mail' && provider != 'imap'
|
47
|
-
return @mail if @mail
|
48
|
-
|
49
|
-
@mail = Mail.read(inbound_message.raw_message.download)
|
50
|
-
end
|
51
|
-
|
52
66
|
private
|
53
67
|
|
54
68
|
def descendants
|
55
69
|
ObjectSpace.each_object(Class).select { |k| k < self }
|
56
70
|
end
|
57
71
|
|
58
|
-
def message_box_for_route(message_boxes,
|
72
|
+
def message_box_for_route(message_boxes, message)
|
59
73
|
klass = message_boxes.find do |message_box|
|
60
|
-
routes =
|
61
|
-
routes.any?
|
74
|
+
routes = message_box.routes || {}
|
75
|
+
routes.any? do |attribute, hash|
|
76
|
+
value = message.send(attribute)
|
77
|
+
if value.is_a? Array
|
78
|
+
value.any? { |value_item| hash[:test].match(value_item) }
|
79
|
+
else
|
80
|
+
hash[:test].match(value)
|
81
|
+
end
|
82
|
+
end
|
62
83
|
end
|
63
|
-
method = klass.routes.find { |regexp, _method| [*recipients].any? { |recipient| regexp.match(recipient) } }&.last if klass
|
64
|
-
|
65
|
-
[klass, method] if method
|
66
|
-
end
|
67
|
-
end
|
68
84
|
|
69
|
-
|
85
|
+
route = klass&.routes&.find do |attribute, hash|
|
86
|
+
value = message.send(attribute)
|
87
|
+
if value.is_a? Array
|
88
|
+
value.any? { |value_item| hash[:test].match(value_item) }
|
89
|
+
else
|
90
|
+
hash[:test].match(value)
|
91
|
+
end
|
92
|
+
end
|
70
93
|
|
71
|
-
|
72
|
-
|
94
|
+
[klass, route.last[:to]] if route
|
95
|
+
end
|
73
96
|
end
|
74
97
|
end
|
75
98
|
end
|
@@ -61,20 +61,21 @@ module Nuntius
|
|
61
61
|
return obj.keys.first.to_s if obj.is_a?(Hash)
|
62
62
|
|
63
63
|
plural = obj.is_a?(Array) || obj.is_a?(ActiveRecord::Relation)
|
64
|
-
list
|
65
|
-
klass
|
66
|
-
klass
|
67
|
-
name
|
68
|
-
name
|
64
|
+
list = plural ? obj : [obj]
|
65
|
+
klass = list.first.class
|
66
|
+
klass = klass.base_class if klass.respond_to?(:base_class)
|
67
|
+
name = klass.name.demodulize
|
68
|
+
name = name.pluralize if plural
|
69
69
|
name.underscore
|
70
70
|
end
|
71
71
|
|
72
72
|
def class_name_for(obj)
|
73
|
-
|
73
|
+
case obj
|
74
|
+
when Array, ActiveRecord::Relation
|
74
75
|
obj.first.class.name.demodulize
|
75
|
-
|
76
|
-
|
77
|
-
|
76
|
+
when Hash
|
77
|
+
"Custom"
|
78
|
+
when Class
|
78
79
|
obj.name.demodulize
|
79
80
|
else
|
80
81
|
obj.class.name
|
@@ -143,7 +144,7 @@ module Nuntius
|
|
143
144
|
end
|
144
145
|
|
145
146
|
def timebased_scope(name, &scope_proc)
|
146
|
-
raise ArgumentError,
|
147
|
+
raise ArgumentError, "timebased_scope must start with before or after" unless %w[before after].detect { |prefix| name.to_s.start_with?(prefix) }
|
147
148
|
|
148
149
|
name = name.to_sym
|
149
150
|
timebased_scopes[name] = scope_proc if scope_proc.present?
|
@@ -183,10 +184,10 @@ module Nuntius
|
|
183
184
|
def liquid_context
|
184
185
|
assigns = @params || {}
|
185
186
|
instance_variables.reject { |i| %w[@params @object @locale @templates @template_scope].include? i.to_s }.each do |i|
|
186
|
-
assigns[i.to_s[1
|
187
|
+
assigns[i.to_s[1..]] = instance_variable_get(i)
|
187
188
|
end
|
188
189
|
|
189
|
-
context = {
|
190
|
+
context = {liquid_variable_name_for(@object) => (@object.is_a?(Hash) ? @object[@object.keys.first].deep_stringify_keys : @object)}
|
190
191
|
assigns.merge(context)
|
191
192
|
end
|
192
193
|
end
|
@@ -1,12 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Nuntius
|
2
4
|
class Attachment < ApplicationRecord
|
3
|
-
has_and_belongs_to_many :messages, class_name:
|
5
|
+
has_and_belongs_to_many :messages, class_name: "Message"
|
4
6
|
|
5
7
|
delegate :download, :content_type, :filename, :signed_id, to: :content
|
6
8
|
|
7
9
|
begin
|
8
10
|
has_one_attached :content, service: Nuntius.config.active_storage_service
|
9
|
-
rescue
|
11
|
+
rescue
|
10
12
|
nil
|
11
13
|
end
|
12
14
|
end
|
@@ -8,68 +8,21 @@ module Nuntius
|
|
8
8
|
accepts_nested_attributes_for :list, reject_if: :all_blank
|
9
9
|
|
10
10
|
belongs_to :layout, optional: true
|
11
|
-
has_many :messages, class_name:
|
11
|
+
has_many :messages, class_name: "Nuntius::Message"
|
12
12
|
validates :name, presence: true
|
13
13
|
|
14
14
|
state_machine initial: :draft do
|
15
|
-
after_transition any => any, do: :do_after_transition
|
16
|
-
|
17
15
|
event :publish do
|
18
16
|
transition draft: :sending
|
19
17
|
end
|
18
|
+
|
20
19
|
event :sent do
|
21
20
|
transition sending: :sent
|
22
21
|
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def deliver
|
26
|
-
t = BaseTransport.class_from_name(transport).new
|
27
|
-
list.subscribers.each do |subscriber|
|
28
|
-
t.deliver(new_message(subscriber))
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def new_message(subscriber, assigns = {})
|
33
|
-
if subscriber.nuntiable
|
34
|
-
name = Nuntius::BaseMessenger.liquid_variable_name_for(subscriber.nuntiable)
|
35
|
-
assigns[name] = subscriber.nuntiable
|
36
|
-
end
|
37
|
-
message = Nuntius::Message.new(transport: transport, campaign: self, nuntiable: subscriber.nuntiable, metadata: metadata)
|
38
|
-
|
39
|
-
locale_proc = Nuntius::BaseMessenger.messenger_for_obj(subscriber.nuntiable).locale
|
40
|
-
locale = instance_exec(object, &locale_proc) if locale_proc
|
41
22
|
|
42
|
-
|
43
|
-
|
44
|
-
%["#{subscriber.first_name} #{subscriber.last_name}" <#{subscriber.email}>]
|
45
|
-
elsif transport == 'sms'
|
46
|
-
subscriber.phone_number
|
47
|
-
elsif transport == 'voice'
|
48
|
-
subscriber.phone_number
|
49
|
-
end
|
50
|
-
|
51
|
-
message.subject = render(:subject, assigns, locale)
|
52
|
-
message.html = render(:html, assigns, locale, layout: layout&.data)
|
53
|
-
|
54
|
-
message
|
55
|
-
end
|
56
|
-
|
57
|
-
def translation_scope
|
58
|
-
scope = %w[nuntius]
|
59
|
-
scope << layout.name.underscore.tr(' ', '_') if layout
|
60
|
-
scope.join('.')
|
61
|
-
end
|
62
|
-
|
63
|
-
private
|
64
|
-
|
65
|
-
def render(attr, assigns, locale, options = {})
|
66
|
-
I18n.with_locale(locale) do
|
67
|
-
::Liquidum.render(send(attr), assigns: assigns.merge(options.merge('campaign' => self)), registers: { 'campaign' => self })
|
23
|
+
after_transition(on: :publish) do |campaign, transition|
|
24
|
+
DeliverCampaignService.perform(campaign: campaign)
|
68
25
|
end
|
69
26
|
end
|
70
|
-
|
71
|
-
def do_after_transition(transition)
|
72
|
-
deliver if transition.event == :publish
|
73
|
-
end
|
74
27
|
end
|
75
28
|
end
|
@@ -8,11 +8,11 @@ module Nuntius
|
|
8
8
|
class_methods do
|
9
9
|
def yamlify(attr)
|
10
10
|
define_method(:"#{attr}_yaml=") do |yaml|
|
11
|
-
write_attribute attr, YAML.safe_load(yaml.gsub("\t",
|
11
|
+
write_attribute attr, YAML.safe_load(yaml.gsub("\t", " "))
|
12
12
|
end
|
13
13
|
|
14
14
|
define_method(:"#{attr}_yaml") do
|
15
|
-
return
|
15
|
+
return "" if attributes[attr.to_s].blank?
|
16
16
|
|
17
17
|
YAML.dump(attributes[attr.to_s])
|
18
18
|
end
|
data/app/models/nuntius/list.rb
CHANGED
@@ -3,8 +3,18 @@
|
|
3
3
|
module Nuntius
|
4
4
|
class List < ApplicationRecord
|
5
5
|
include Nuntius::Concerns::MetadataScoped
|
6
|
+
include Nuntius::Concerns::Yamlify
|
7
|
+
|
8
|
+
has_many :subscribers, counter_cache: :subscribers_count, dependent: :delete_all
|
9
|
+
|
10
|
+
validates :name, presence: true
|
11
|
+
validates :slug, presence: true, uniqueness: true
|
12
|
+
|
13
|
+
yamlify :metadata
|
6
14
|
|
7
|
-
has_many :subscribers, counter_cache: :subscribers_count
|
8
15
|
accepts_nested_attributes_for :subscribers, reject_if: :all_blank
|
16
|
+
|
17
|
+
scope :subscribed_by, ->(nuntiable) { where(id: nuntiable.nuntius_subscriptions.select(:list_id)) }
|
18
|
+
scope :not_subscribed_by, ->(nuntiable) { where.not(id: nuntiable.nuntius_subscriptions.select(:list_id)) }
|
9
19
|
end
|
10
20
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "pry"
|
3
4
|
module Nuntius
|
4
5
|
# Stores individual messages to individual recipients
|
5
6
|
#
|
@@ -13,12 +14,12 @@ module Nuntius
|
|
13
14
|
class Message < ApplicationRecord
|
14
15
|
include Nuntius::Concerns::MetadataScoped
|
15
16
|
|
16
|
-
has_and_belongs_to_many :attachments, class_name:
|
17
|
+
has_and_belongs_to_many :attachments, class_name: "Attachment"
|
17
18
|
|
18
19
|
belongs_to :campaign, optional: true
|
19
20
|
belongs_to :template, optional: true
|
20
|
-
belongs_to :parent_message, class_name:
|
21
|
-
has_many :child_messages, class_name:
|
21
|
+
belongs_to :parent_message, class_name: "Message", optional: true
|
22
|
+
has_many :child_messages, class_name: "Message", foreign_key: "parent_message_id", dependent: :destroy
|
22
23
|
belongs_to :nuntiable, polymorphic: true, optional: true
|
23
24
|
|
24
25
|
validates :transport, presence: true
|
@@ -32,19 +33,19 @@ module Nuntius
|
|
32
33
|
# end
|
33
34
|
|
34
35
|
def pending?
|
35
|
-
status ==
|
36
|
+
status == "pending"
|
36
37
|
end
|
37
38
|
|
38
39
|
def sent?
|
39
|
-
status ==
|
40
|
+
status == "sent"
|
40
41
|
end
|
41
42
|
|
42
43
|
def blocked?
|
43
|
-
status ==
|
44
|
+
status == "blocked"
|
44
45
|
end
|
45
46
|
|
46
47
|
def delivered?
|
47
|
-
status ==
|
48
|
+
status == "delivered"
|
48
49
|
end
|
49
50
|
|
50
51
|
def delivered_or_blocked?
|
@@ -52,12 +53,12 @@ module Nuntius
|
|
52
53
|
end
|
53
54
|
|
54
55
|
def undelivered?
|
55
|
-
status ==
|
56
|
+
status == "undelivered"
|
56
57
|
end
|
57
58
|
|
58
59
|
# Removes only pending child messages
|
59
60
|
def cleanup!
|
60
|
-
Nuntius::Message.where(status:
|
61
|
+
Nuntius::Message.where(status: "pending").where(parent_message: self).destroy_all
|
61
62
|
end
|
62
63
|
|
63
64
|
def add_attachment(options)
|
@@ -65,35 +66,39 @@ module Nuntius
|
|
65
66
|
|
66
67
|
uri = options[:url] && URI.parse(options[:url])
|
67
68
|
|
68
|
-
if uri&.scheme ==
|
69
|
+
if uri&.scheme == "file"
|
70
|
+
# FIXME: This is a possible security problem
|
69
71
|
attachment[:io] = File.open(uri.path)
|
70
72
|
elsif uri
|
71
|
-
client =
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
73
|
+
client = Faraday.new(ssl: {verify: false}) do |builder|
|
74
|
+
builder.response :follow_redirects
|
75
|
+
builder.adapter Faraday.default_adapter
|
76
|
+
end
|
77
|
+
|
78
|
+
response = client.get(options[:url])
|
79
|
+
content_disposition = response.headers["Content-Disposition"] || ""
|
80
|
+
|
76
81
|
options[:filename] ||= content_disposition[/filename="([^"]+)"/, 1]
|
77
|
-
attachment[:content_type] = response.
|
82
|
+
attachment[:content_type] = response.headers["Content-Type"]
|
78
83
|
attachment[:io] = if response.body.is_a? String
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
+
StringIO.new(response.body)
|
85
|
+
else
|
86
|
+
# Assume IO object
|
87
|
+
response.body
|
88
|
+
end
|
84
89
|
elsif options[:content].respond_to?(:read)
|
85
90
|
attachment[:content_type] = options[:content_type]
|
86
91
|
attachment[:io] = options[:content]
|
87
92
|
else
|
88
|
-
raise
|
93
|
+
raise "Cannot add attachment without url or content"
|
89
94
|
end
|
90
95
|
|
91
96
|
# Set the filename
|
92
|
-
attachment[:filename] = options[:filename] || uri.path.split(
|
97
|
+
attachment[:filename] = options[:filename] || uri.path.split("/").last || "attachment"
|
93
98
|
|
94
99
|
# (Try to) add file extension if it is missing
|
95
|
-
file_extension = File.extname(attachment[:filename]).delete(
|
96
|
-
attachment[:filename] += ".#{Mime::Type.lookup(attachment[:content_type].split(
|
100
|
+
file_extension = File.extname(attachment[:filename]).delete(".")
|
101
|
+
attachment[:filename] += ".#{Mime::Type.lookup(attachment[:content_type].split(";").first).to_sym}" if file_extension.blank? && attachment[:content_type]
|
97
102
|
|
98
103
|
# Fix content type if file extension known but content type blank
|
99
104
|
attachment[:content_type] ||= Mime::Type.lookup_by_extension(file_extension)&.to_s if file_extension
|
@@ -103,17 +108,17 @@ module Nuntius
|
|
103
108
|
zio.put_next_entry attachment[:file_name]
|
104
109
|
zio.write attachment[:io].read
|
105
110
|
end
|
106
|
-
attachment[:content_type] =
|
111
|
+
attachment[:content_type] = "application/zip"
|
107
112
|
attachment[:io] = zip_stream
|
108
113
|
end
|
109
114
|
|
110
115
|
nuntius_attachment = Nuntius::Attachment.new
|
111
116
|
nuntius_attachment.content.attach(io: attachment[:io],
|
112
|
-
|
113
|
-
|
117
|
+
filename: attachment[:filename],
|
118
|
+
content_type: attachment[:content_type])
|
114
119
|
|
115
120
|
attachments.push(nuntius_attachment)
|
116
|
-
rescue
|
121
|
+
rescue => e
|
117
122
|
Nuntius.config.logger.error "Message: Could not attach #{attachment[:filename]} #{e.message}"
|
118
123
|
end
|
119
124
|
|
@@ -5,8 +5,28 @@ module Nuntius
|
|
5
5
|
belongs_to :list
|
6
6
|
belongs_to :nuntiable, polymorphic: true, optional: true
|
7
7
|
|
8
|
+
scope :subscribed, -> { where(unsubscribed_at: nil) }
|
9
|
+
|
8
10
|
def name
|
9
|
-
[first_name, last_name].join(
|
11
|
+
[first_name, last_name].compact.join(" ").presence || email
|
12
|
+
end
|
13
|
+
|
14
|
+
def first_name
|
15
|
+
return nuntiable.first_name if nuntiable.respond_to?(:first_name)
|
16
|
+
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
def last_name
|
21
|
+
return nuntiable.last_name if nuntiable.respond_to?(:last_name)
|
22
|
+
|
23
|
+
super
|
24
|
+
end
|
25
|
+
|
26
|
+
def email
|
27
|
+
return nuntiable.email if nuntiable.respond_to?(:email)
|
28
|
+
|
29
|
+
super
|
10
30
|
end
|
11
31
|
end
|
12
32
|
end
|
@@ -6,9 +6,9 @@ module Nuntius
|
|
6
6
|
include Nuntius::Concerns::Yamlify
|
7
7
|
|
8
8
|
belongs_to :layout, optional: true
|
9
|
-
has_many :messages, class_name:
|
9
|
+
has_many :messages, class_name: "Nuntius::Message", dependent: :nullify
|
10
10
|
|
11
|
-
LIQUID_TAGS = /{%(?:(?!%}).)*%}|{{(?:(?!}}).)*}}
|
11
|
+
LIQUID_TAGS = /{%(?:(?!%}).)*%}|{{(?:(?!}}).)*}}/
|
12
12
|
|
13
13
|
validates :description, presence: true
|
14
14
|
validates :from, liquid: true
|
@@ -18,8 +18,8 @@ module Nuntius
|
|
18
18
|
validates :text, liquid: true
|
19
19
|
|
20
20
|
validates :event, presence: true
|
21
|
-
validates :event, format: {
|
22
|
-
validates :interval, format: {
|
21
|
+
validates :event, format: {with: /.+#.+/}, if: ->(t) { t.klass == "Custom" }
|
22
|
+
validates :interval, format: {allow_blank: true, with: /\A[1-9][0-9]*\s(month|week|day|hour|minute)s?\z/}
|
23
23
|
|
24
24
|
yamlify :metadata
|
25
25
|
|
@@ -31,10 +31,10 @@ module Nuntius
|
|
31
31
|
locale = instance_exec(object, &locale_proc) if locale_proc
|
32
32
|
locale = params[:locale].to_sym if params[:locale]
|
33
33
|
|
34
|
-
options = {
|
34
|
+
options = {registers: {"template" => self, "message" => message}}
|
35
35
|
|
36
|
-
message.to = render(:to, assigns, locale, options).split(
|
37
|
-
message.from = render(:from, assigns, locale, options).split(
|
36
|
+
message.to = render(:to, assigns, locale, options).split(",").reject(&:empty?).join(",")
|
37
|
+
message.from = render(:from, assigns, locale, options).split(",").reject(&:empty?).join(",")
|
38
38
|
message.subject = render(:subject, assigns, locale, options)
|
39
39
|
message.html = render(:html, assigns, locale, options.merge(layout: layout&.data))
|
40
40
|
message.text = render(:text, assigns, locale, options)
|
@@ -45,12 +45,12 @@ module Nuntius
|
|
45
45
|
|
46
46
|
def translation_scope
|
47
47
|
scope = %w[]
|
48
|
-
scope << layout.name.underscore.tr(
|
49
|
-
scope << klass.underscore.tr(
|
48
|
+
scope << layout.name.underscore.tr(" ", "_") if layout
|
49
|
+
scope << klass.underscore.tr("/", "_")
|
50
50
|
scope << event
|
51
51
|
scope << transport
|
52
|
-
scope << description.underscore.gsub(/[^a-z]+/,
|
53
|
-
scope.join(
|
52
|
+
scope << description.underscore.gsub(/[^a-z]+/, "_") if description
|
53
|
+
scope.join(".")
|
54
54
|
end
|
55
55
|
|
56
56
|
# Trix correctly escapes the HTML, but for liquid this is not what we need.
|
@@ -64,7 +64,7 @@ module Nuntius
|
|
64
64
|
|
65
65
|
def interval_duration
|
66
66
|
unless interval.blank?
|
67
|
-
number, type = interval.split(
|
67
|
+
number, type = interval.split(" ")
|
68
68
|
number = number.to_i
|
69
69
|
|
70
70
|
return number.public_send(type) if number.respond_to?(type)
|
@@ -76,11 +76,11 @@ module Nuntius
|
|
76
76
|
def interval_time_range
|
77
77
|
return 0.seconds..0.seconds if interval.blank?
|
78
78
|
|
79
|
-
start = if event.start_with?(
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
79
|
+
start = if event.start_with?("before")
|
80
|
+
interval_duration.after
|
81
|
+
else
|
82
|
+
interval_duration.ago
|
83
|
+
end
|
84
84
|
|
85
85
|
(start - 1.hour)..start
|
86
86
|
end
|
@@ -90,11 +90,11 @@ module Nuntius
|
|
90
90
|
def render(attr, assigns, locale, options = {})
|
91
91
|
I18n.with_locale(locale) do
|
92
92
|
if attr == :payload
|
93
|
-
YAML.safe_load(::Liquidum.render(send(attr), {
|
93
|
+
YAML.safe_load(::Liquidum.render(send(attr), {assigns: assigns.merge("template" => self), registers: {"template" => self}}.merge(options)))
|
94
94
|
elsif attr == :html
|
95
|
-
::Liquidum.render(send(attr), {
|
95
|
+
::Liquidum.render(send(attr), {filter: "markdown", assigns: assigns.merge("template" => self), registers: {"template" => self}}.merge(options))
|
96
96
|
else
|
97
|
-
::Liquidum.render(send(attr), {
|
97
|
+
::Liquidum.render(send(attr), {assigns: assigns.merge("template" => self), registers: {"template" => self}}.merge(options))
|
98
98
|
end
|
99
99
|
end
|
100
100
|
end
|
@@ -4,12 +4,12 @@ class TemplatePresenter < ApplicationPresenter
|
|
4
4
|
def all_events
|
5
5
|
events = []
|
6
6
|
Nuntius.config.nuntiable_class_names.each do |class_name|
|
7
|
-
next if class_name ==
|
7
|
+
next if class_name == "Custom"
|
8
8
|
|
9
9
|
messenger = Nuntius::BaseMessenger.messenger_for_class(class_name)
|
10
10
|
messenger.instance_methods(false).each do |m|
|
11
|
-
events << [m, m, {
|
12
|
-
|
11
|
+
events << [m, m, {"data-chain": class_name,
|
12
|
+
"data-timebased": messenger.timebased_scopes.include?(m)}]
|
13
13
|
end
|
14
14
|
end
|
15
15
|
events.sort_by(&:first)
|
@@ -1,31 +1,31 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "apnotic"
|
4
4
|
|
5
5
|
module Nuntius
|
6
6
|
class ApnoticPushProvider < BaseProvider
|
7
7
|
transport :push
|
8
8
|
|
9
9
|
setting_reader :certificate,
|
10
|
-
|
11
|
-
|
10
|
+
required: true,
|
11
|
+
description: "The contents of a valid APNS push certificate in .pem format"
|
12
12
|
setting_reader :passphrase,
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
required: false,
|
14
|
+
description: "If the APNS certificate is protected by a passphrase, " \
|
15
|
+
"provide this variable to use when decrypting it."
|
16
16
|
setting_reader :environment,
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
required: false,
|
18
|
+
default: :production,
|
19
|
+
description: "Development or production, defaults to production"
|
20
20
|
|
21
21
|
def deliver
|
22
22
|
return message if message.to.size != 64
|
23
23
|
|
24
24
|
connection = if environment.to_sym == :development
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
25
|
+
Apnotic::Connection.development(cert_path: StringIO.new(certificate), cert_pass: passphrase)
|
26
|
+
else
|
27
|
+
Apnotic::Connection.new(cert_path: StringIO.new(certificate), cert_pass: passphrase)
|
28
|
+
end
|
29
29
|
|
30
30
|
notification = Apnotic::Notification.new(message.to)
|
31
31
|
notification.alert = message.text
|
@@ -34,10 +34,10 @@ module Nuntius
|
|
34
34
|
response = connection.push(notification)
|
35
35
|
|
36
36
|
message.status = if response.ok?
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
37
|
+
"sent"
|
38
|
+
else
|
39
|
+
"undelivered"
|
40
|
+
end
|
41
41
|
message
|
42
42
|
end
|
43
43
|
end
|