web47core 0.6.3 → 0.7.4
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/README.md +4 -0
- data/app/helpers/core_helper.rb +6 -5
- data/app/views/stack/delayed_jobs/index.html.haml +1 -0
- data/app/views/stack/delayed_jobs/show.html.haml +1 -0
- data/app/views/stack/system_configurations/edit.html.haml +1 -5
- data/app/views/stack/system_configurations/show.html.haml +1 -3
- data/lib/app/jobs/application_job.rb +112 -0
- data/lib/app/jobs/cron/switchboard_sync_configuration.rb +1 -2
- data/lib/app/jobs/cron/switchboard_sync_models.rb +1 -1
- data/lib/app/jobs/cron/trim_collection.rb +1 -2
- data/lib/app/jobs/cron/trim_notifications.rb +20 -0
- data/lib/app/models/audit_log.rb +1 -1
- data/lib/app/models/concerns/core_account.rb +14 -0
- data/lib/app/models/concerns/core_system_configuration.rb +18 -30
- data/lib/app/models/concerns/email_able.rb +1 -1
- data/lib/app/models/concerns/standard_model.rb +35 -57
- data/lib/app/models/concerns/switchboard_able.rb +2 -4
- data/lib/app/models/email_notification.rb +8 -14
- data/lib/app/models/email_template.rb +30 -0
- data/lib/app/models/notification.rb +10 -21
- data/lib/app/models/template.rb +27 -0
- data/lib/web47core/config.rb +13 -9
- data/lib/web47core/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8717398ffc274873a8057965f099744e56c95730a3b06dbf465b2ae56bb48a28
|
4
|
+
data.tar.gz: 3a519e7c3fd207057c8912c62ccb1e249fd268886bb82f6b431f4c7f88851109
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e9d493aeb49b89723db88f171fb8efb2d79ba8d91b3e77dbcc82cec171bca779accc0eb4a7dfb03aeef539436ec338b04f2ab79b5ca476cba2c7a2f043d8252f
|
7
|
+
data.tar.gz: 4e533fd3a6db8add02c7cfd40c3d5bce6512bec4656bd6f7a8eced741f8dae3a1ba030a6795feda080589540478b231e8670eff5d789ff0fc3c806262dacc743
|
data/README.md
CHANGED
data/app/helpers/core_helper.rb
CHANGED
@@ -60,15 +60,16 @@ module CoreHelper
|
|
60
60
|
# 3. Length 5-10 only show the first and last character
|
61
61
|
# 4. Otherwise show the first and last three characters
|
62
62
|
def mask_value(value, default: '************')
|
63
|
-
return default if value.blank?
|
63
|
+
return default if value.blank? || !value.respond_to?(:to_s)
|
64
64
|
|
65
|
-
|
65
|
+
string_value = value.to_s
|
66
|
+
case string_value.length
|
66
67
|
when 1..4
|
67
|
-
"#{
|
68
|
+
"#{string_value.first}***********"
|
68
69
|
when 5..10
|
69
|
-
"#{
|
70
|
+
"#{string_value.first}**********#{string_value.last}"
|
70
71
|
else
|
71
|
-
"#{
|
72
|
+
"#{string_value[0..2]}**********#{string_value[-3..-1]}"
|
72
73
|
end
|
73
74
|
end
|
74
75
|
|
@@ -0,0 +1 @@
|
|
1
|
+
=render '/delayed_jobs/index'
|
@@ -0,0 +1 @@
|
|
1
|
+
=render '/delayed_jobs/show'
|
@@ -1,5 +1 @@
|
|
1
|
-
|
2
|
-
.container
|
3
|
-
%form{action: stack_system_configuration_path, method: :post}
|
4
|
-
= render '/system_configurations/form', system_configuration: @system_configuration
|
5
|
-
= render '/common/form_actions', form_cancel_path: admin_system_configuration_path
|
1
|
+
= render '/system_configurations/edit'
|
@@ -4,7 +4,26 @@
|
|
4
4
|
# Base application job that all jobs extend from
|
5
5
|
#
|
6
6
|
class ApplicationJob < ActiveJob::Base
|
7
|
+
include ActionView::Helpers::NumberHelper
|
8
|
+
include ActionView::Helpers::TextHelper
|
7
9
|
include App47Logger
|
10
|
+
attr_accessor :payload, :started_at
|
11
|
+
|
12
|
+
#
|
13
|
+
# Standard approach to running jobs
|
14
|
+
#
|
15
|
+
def perform(payload = {})
|
16
|
+
Rails.cache.reconnect
|
17
|
+
@started_at = Time.now.utc
|
18
|
+
@payload = payload
|
19
|
+
parse_payload
|
20
|
+
execute
|
21
|
+
rescue StandardError => error
|
22
|
+
log_error "Failed to execute job: #{self.inspect}", error
|
23
|
+
raise error
|
24
|
+
ensure
|
25
|
+
GC.start
|
26
|
+
end
|
8
27
|
|
9
28
|
#
|
10
29
|
# If this job should run in this current environment, defaults to true
|
@@ -20,4 +39,97 @@ class ApplicationJob < ActiveJob::Base
|
|
20
39
|
my_environments = valid_environments
|
21
40
|
(my_environments.empty? || my_environments.include?(Rails.env))
|
22
41
|
end
|
42
|
+
|
43
|
+
#
|
44
|
+
# Internal: Parse the payload
|
45
|
+
#
|
46
|
+
# payload - The payload from the class attribute
|
47
|
+
#
|
48
|
+
# Examples
|
49
|
+
#
|
50
|
+
# parse_payload
|
51
|
+
#
|
52
|
+
# Assigns the class attributes from the payload
|
53
|
+
#
|
54
|
+
def parse_payload
|
55
|
+
attributes = payload.is_a?(Hash) ? payload : JSON.parse(payload)
|
56
|
+
# Set the values contained in the payload
|
57
|
+
attributes.each_pair do |key, value|
|
58
|
+
method = "#{key}=".to_sym
|
59
|
+
send(method, value) if respond_to?(method)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
#
|
64
|
+
# Public: Determine the duration of the process, only reporting on the values that are
|
65
|
+
# greater than zero.
|
66
|
+
#
|
67
|
+
# Examples
|
68
|
+
#
|
69
|
+
# duration
|
70
|
+
# # => '1 day 1 hour 32 minutes 10 seconds'
|
71
|
+
# # => '1 hour 32 minutes 10 seconds'
|
72
|
+
# # => '32 minutes 10 seconds'
|
73
|
+
# # => '10 seconds'
|
74
|
+
#
|
75
|
+
# Returns the duration up until this point in time.
|
76
|
+
#
|
77
|
+
def duration
|
78
|
+
start_time = started_at.is_a?(String) ? Time.parse(started_at) : started_at
|
79
|
+
minutes, seconds = split_duration(Time.zone.now - start_time)
|
80
|
+
hours, minutes = split_duration minutes
|
81
|
+
days, hours = split_duration hours, 24
|
82
|
+
report_duration(days, hours, minutes, seconds)
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
#
|
88
|
+
# Internal: Take the current value and split using the split value, return the dividend and modulus
|
89
|
+
#
|
90
|
+
# value - The value to split
|
91
|
+
# split - The split value to use
|
92
|
+
#
|
93
|
+
# Examples
|
94
|
+
#
|
95
|
+
# split_duration( 120 )
|
96
|
+
# # => [2, 0]
|
97
|
+
# split_duration( 125 )
|
98
|
+
# # => [2, 5]
|
99
|
+
# split_duration( 125 )
|
100
|
+
# # => [2, 5]
|
101
|
+
# split_duration( 48, 24 )
|
102
|
+
# # => [2, 0]
|
103
|
+
#
|
104
|
+
# Returns an arround with the first value as the dividend and the second as the modulus
|
105
|
+
#
|
106
|
+
def split_duration(value, split = 60)
|
107
|
+
[(value / split).to_i, (value % split).to_i]
|
108
|
+
end
|
109
|
+
|
110
|
+
#
|
111
|
+
# Internal: Put together the descriptive text of the duration.
|
112
|
+
#
|
113
|
+
# days - The number of days
|
114
|
+
# hours - The number of hours
|
115
|
+
# minutes - The number of minutes
|
116
|
+
# seconds - The number of seconds
|
117
|
+
#
|
118
|
+
# Examples
|
119
|
+
#
|
120
|
+
# report_duration(0, 0, 10, 1)
|
121
|
+
# # => "10 minutes 1 second"
|
122
|
+
# report_duration(1, 0, 10, 1)
|
123
|
+
# # => "1 day 0 hours 10 minutes 1 second"
|
124
|
+
#
|
125
|
+
# Returns the duplicated String.
|
126
|
+
#
|
127
|
+
def report_duration(days, hours, minutes, seconds)
|
128
|
+
dur = []
|
129
|
+
dur << pluralize(days, 'day') if days.positive?
|
130
|
+
dur << pluralize(hours, 'hour') if hours.positive? || days.positive?
|
131
|
+
dur << pluralize(minutes, 'minute') if minutes.positive? || hours.positive? || days.positive?
|
132
|
+
dur << pluralize(seconds, 'second')
|
133
|
+
dur.join(' ')
|
134
|
+
end
|
23
135
|
end
|
@@ -18,8 +18,7 @@ module Cron
|
|
18
18
|
#
|
19
19
|
# Cycle through all configuration keys
|
20
20
|
#
|
21
|
-
def
|
22
|
-
Rails.cache.reconnect
|
21
|
+
def execute
|
23
22
|
RestClient.get(switchboard_url,
|
24
23
|
ACCESS_TOKEN: SystemConfiguration.switchboard_stack_api_token,
|
25
24
|
content_type: 'application/json') do |response, _request, _result, &block|
|
@@ -20,5 +20,25 @@ module Cron
|
|
20
20
|
def collection
|
21
21
|
Notification.all
|
22
22
|
end
|
23
|
+
|
24
|
+
#
|
25
|
+
# Test if this should be archived
|
26
|
+
#
|
27
|
+
def archive?(item)
|
28
|
+
time = if item.is_a?(EmailNotification) && template?(item)
|
29
|
+
5.years.ago.utc
|
30
|
+
else
|
31
|
+
allowed_time
|
32
|
+
end
|
33
|
+
item.updated_at < time
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# Determine if the notification has a template associated with it
|
38
|
+
#
|
39
|
+
def template?(item)
|
40
|
+
item.notification_template.present? ||
|
41
|
+
item[:notification_template_id].present? && Template.where(_id: item[:notification_template_id]).present?
|
42
|
+
end
|
23
43
|
end
|
24
44
|
end
|
data/lib/app/models/audit_log.rb
CHANGED
@@ -39,4 +39,18 @@ module CoreAccount
|
|
39
39
|
def fetch_smtp_configuration
|
40
40
|
smtp_configuration || build_smtp_configuration
|
41
41
|
end
|
42
|
+
|
43
|
+
#
|
44
|
+
# return the email notifications templates, these are custom notifications sent by an admin.
|
45
|
+
#
|
46
|
+
def email_notification_templates
|
47
|
+
templates.where(_type: 'EmailNotificationTemplate')
|
48
|
+
end
|
49
|
+
|
50
|
+
#
|
51
|
+
# return the email templates, these are email templates for actions in the system
|
52
|
+
#
|
53
|
+
def email_templates
|
54
|
+
templates.where(_type: 'EmailTemplate')
|
55
|
+
end
|
42
56
|
end
|
@@ -16,6 +16,7 @@ module CoreSystemConfiguration
|
|
16
16
|
#
|
17
17
|
def self.included(base)
|
18
18
|
base.class_eval do
|
19
|
+
attr_accessor :configuration
|
19
20
|
#
|
20
21
|
# Fields
|
21
22
|
#
|
@@ -73,27 +74,12 @@ module CoreSystemConfiguration
|
|
73
74
|
end
|
74
75
|
|
75
76
|
module ClassMethods
|
76
|
-
#
|
77
|
-
# Fetch the system configuration based on environment name. First try the rails cache
|
78
|
-
# if not there, then go to the database.
|
79
|
-
#
|
80
|
-
# Also, if an argument error is thrown, then just fetch from the database.
|
81
|
-
#
|
82
77
|
def configuration
|
83
|
-
|
84
|
-
|
85
|
-
begin
|
86
|
-
config = Rails.cache.fetch(cache_key, expires_in: 90.seconds) do
|
87
|
-
SystemConfiguration.find_by(environment: Rails.env)
|
88
|
-
end
|
89
|
-
rescue StandardError
|
90
|
-
# This seems to happen in testing relative to Country, when it does, remove
|
91
|
-
# ourselves from the cache and return the DB entry.
|
92
|
-
Rails.cache.delete cache_key
|
93
|
-
config = SystemConfiguration.find_or_create_by!(environment: Rails.env)
|
94
|
-
end
|
78
|
+
@configuration ||= SystemConfiguration.find_or_create_by!(environment: Rails.env)
|
79
|
+
end
|
95
80
|
|
96
|
-
|
81
|
+
def reset
|
82
|
+
@configuration = nil
|
97
83
|
end
|
98
84
|
|
99
85
|
def smtp_configuration
|
@@ -110,19 +96,21 @@ module CoreSystemConfiguration
|
|
110
96
|
#
|
111
97
|
# NOTE: Currently ignored Codacy issue: When using 'method_missing', fall back on 'super'
|
112
98
|
#
|
113
|
-
# rubocop:disable Style/MethodMissingSuper
|
114
99
|
def method_missing(method, *args, &_block)
|
115
|
-
configuration.
|
100
|
+
if configuration.respond_to?(method)
|
101
|
+
configuration.send(method, *args)
|
102
|
+
else
|
103
|
+
super
|
104
|
+
end
|
116
105
|
end
|
117
106
|
|
118
|
-
def respond_to?(method_name,
|
119
|
-
|
107
|
+
def respond_to?(method_name, include_private = false)
|
108
|
+
configuration.respond_to?(method_name, include_private) || super
|
120
109
|
end
|
121
110
|
|
122
|
-
def respond_to_missing?(method_name,
|
123
|
-
|
111
|
+
def respond_to_missing?(method_name, include_private = false)
|
112
|
+
configuration.respond_to?(method_name, include_private) || super
|
124
113
|
end
|
125
|
-
# rubocop:enable Style/MethodMissingSuper
|
126
114
|
end
|
127
115
|
|
128
116
|
#
|
@@ -147,7 +135,7 @@ module CoreSystemConfiguration
|
|
147
135
|
#
|
148
136
|
# Determine if mailgun is configured
|
149
137
|
#
|
150
|
-
def
|
138
|
+
def mail_gun_configured?
|
151
139
|
smtp_configured? && mailgun_api_key.present?
|
152
140
|
end
|
153
141
|
|
@@ -203,7 +191,7 @@ module CoreSystemConfiguration
|
|
203
191
|
config = SystemConfiguration.configuration
|
204
192
|
path = if config.zendesk_configured? && user.present?
|
205
193
|
time_now = Time.now.to_i
|
206
|
-
jti = "#{time_now}/#{rand(36**64).to_s(36)}"
|
194
|
+
jti = "#{time_now}/#{rand(36 ** 64).to_s(36)}"
|
207
195
|
payload = { jwt: JWT.encode({ iat: time_now, # Seconds since epoch, determine when this token is stale
|
208
196
|
jti: jti, # Unique token identifier, helps prevent replay attacks
|
209
197
|
name: user.name,
|
@@ -262,12 +250,12 @@ module CoreSystemConfiguration
|
|
262
250
|
slack_api_url.present?
|
263
251
|
end
|
264
252
|
|
265
|
-
private
|
253
|
+
# private
|
266
254
|
|
267
255
|
#
|
268
256
|
# Clear the cache when the object is saved
|
269
257
|
#
|
270
258
|
def clear_cache
|
271
|
-
|
259
|
+
SystemConfiguration.reset
|
272
260
|
end
|
273
261
|
end
|
@@ -62,7 +62,7 @@ module EmailAble
|
|
62
62
|
# Reset the bounced email
|
63
63
|
#
|
64
64
|
def reset_bounce_status
|
65
|
-
return unless SystemConfiguration.
|
65
|
+
return unless SystemConfiguration.mail_gun_configured? && email_bounced?
|
66
66
|
|
67
67
|
reset_url = "https://api.mailgun.net/v3/#{SystemConfiguration.smtp_domain}/bounces/#{CGI.escape(email)}"
|
68
68
|
RestClient.delete(reset_url, user: 'api', password: SystemConfiguration.mailgun_api_key)
|
@@ -15,16 +15,16 @@ module StandardModel
|
|
15
15
|
#
|
16
16
|
# Fields
|
17
17
|
#
|
18
|
-
field :last_modified_by_email
|
19
|
-
field :last_modified_by_name
|
20
|
-
field :created_by_email
|
21
|
-
field :created_by_name
|
18
|
+
field :last_modified_by_email, type: String
|
19
|
+
field :last_modified_by_name, type: String
|
20
|
+
field :created_by_email, type: String
|
21
|
+
field :created_by_name, type: String
|
22
22
|
#
|
23
23
|
# Relationships
|
24
24
|
#
|
25
|
-
belongs_to :last_modified_by, class_name:
|
26
|
-
belongs_to :created_by, class_name:
|
27
|
-
has_many Web47core::Config.
|
25
|
+
belongs_to :last_modified_by, class_name: Web47core::Config.audit_model_class_name, optional: true
|
26
|
+
belongs_to :created_by, class_name: Web47core::Config.audit_model_class_name, optional: true
|
27
|
+
has_many Web47core::Config.audit_model_log_symbol, dependent: :nullify, inverse_of: :model
|
28
28
|
#
|
29
29
|
# Callbacks
|
30
30
|
#
|
@@ -42,9 +42,9 @@ module StandardModel
|
|
42
42
|
# Used by calling 'model.without_callback(*.args, &block) do'
|
43
43
|
def without_callback(*args, &_block)
|
44
44
|
skip_callback(*args)
|
45
|
-
|
45
|
+
yield
|
46
|
+
ensure
|
46
47
|
set_callback(*args)
|
47
|
-
result
|
48
48
|
end
|
49
49
|
|
50
50
|
#
|
@@ -60,10 +60,7 @@ module StandardModel
|
|
60
60
|
def allowed_param_names(filter_names = [])
|
61
61
|
# Always filter out the mongoid reserved items
|
62
62
|
filter_names += %w[created_at updated_at _type _id]
|
63
|
-
|
64
|
-
# filter out the relationship names so we don't have dups
|
65
|
-
associations.each { |association| filter_names << association.keys.first }
|
66
|
-
(field_names + associations).delete_if { |name| filter_names.include?(name) }
|
63
|
+
field_names(filter_names)
|
67
64
|
rescue StandardError
|
68
65
|
attribute_names.delete_if { |name| filter_names.include?(name) }
|
69
66
|
end
|
@@ -72,38 +69,19 @@ module StandardModel
|
|
72
69
|
# allow the model to filter out a name if they want to, meaning the model
|
73
70
|
# can return a subset of attribute names
|
74
71
|
#
|
75
|
-
def field_names
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
# Return a collection of many to many assocations. We basically
|
89
|
-
# need to turn the current value returned by attribute names
|
90
|
-
#
|
91
|
-
# relationship_ids
|
92
|
-
#
|
93
|
-
# to
|
94
|
-
#
|
95
|
-
# { relationship_ids => [] }
|
96
|
-
#
|
97
|
-
# Telling the permit command to accept the value as an array of items.
|
98
|
-
#
|
99
|
-
def many_to_many_associations
|
100
|
-
associations = []
|
101
|
-
reflect_on_all_associations.each do |association|
|
102
|
-
next unless association.macro == :has_and_belongs_to_many
|
103
|
-
|
104
|
-
associations << { association.key => [] }
|
105
|
-
end
|
106
|
-
associations
|
72
|
+
def field_names(filter_names)
|
73
|
+
fields.collect do |field|
|
74
|
+
next if filter_names.include?(field[0])
|
75
|
+
|
76
|
+
case field[1].options[:type].to_s
|
77
|
+
when 'Hash'
|
78
|
+
{ field[0] => {} }
|
79
|
+
when 'Array'
|
80
|
+
{ field[0] => [] }
|
81
|
+
else
|
82
|
+
field[0]
|
83
|
+
end
|
84
|
+
end.compact
|
107
85
|
end
|
108
86
|
|
109
87
|
#
|
@@ -142,10 +120,10 @@ module StandardModel
|
|
142
120
|
# Log the audit record
|
143
121
|
#
|
144
122
|
def log_change(user, model, changes)
|
145
|
-
Web47core::Config.
|
146
|
-
|
147
|
-
|
148
|
-
|
123
|
+
Web47core::Config.audit_model_log_class.create!(Web47core::Config.audit_model => user,
|
124
|
+
model: model,
|
125
|
+
action: model.audit_action,
|
126
|
+
changed_values: App47Logger.clean_params(changes).to_json)
|
149
127
|
end
|
150
128
|
end
|
151
129
|
|
@@ -196,10 +174,10 @@ module StandardModel
|
|
196
174
|
# record a change for the object instance
|
197
175
|
#
|
198
176
|
def log_change(user, changes, action)
|
199
|
-
Web47core::Config.
|
200
|
-
|
201
|
-
|
202
|
-
|
177
|
+
Web47core::Config.audit_model_log_class.create!(Web47core::Config.audit_model => user,
|
178
|
+
model: self,
|
179
|
+
action: action,
|
180
|
+
changed_values: App47Logger.clean_params(changes).to_json)
|
203
181
|
end
|
204
182
|
|
205
183
|
def audit_action
|
@@ -300,10 +278,10 @@ module StandardModel
|
|
300
278
|
# Log the deletion, capturing the current values of the record before it is removed from the system
|
301
279
|
#
|
302
280
|
def log_deletion(user, model)
|
303
|
-
Web47core::Config.
|
304
|
-
|
305
|
-
|
306
|
-
|
281
|
+
Web47core::Config.audit_model_log_class.create!(Web47core::Config.audit_model => user,
|
282
|
+
model: model,
|
283
|
+
action: AuditLog::DELETE_ACTION,
|
284
|
+
changed_values: App47Logger.clean_params(attributes).to_json)
|
307
285
|
end
|
308
286
|
|
309
287
|
def capture_user_info
|
@@ -9,8 +9,7 @@ module SwitchboardAble
|
|
9
9
|
def self.included(base)
|
10
10
|
base.class_eval do
|
11
11
|
field :switchboard_id, type: String
|
12
|
-
|
13
|
-
# after_update :switchboard_upsert
|
12
|
+
after_save :switchboard_upsert
|
14
13
|
before_destroy :switchboard_delete
|
15
14
|
end
|
16
15
|
end
|
@@ -55,13 +54,12 @@ module SwitchboardAble
|
|
55
54
|
def switchboard_delete
|
56
55
|
return unless SystemConfiguration.switchboard_configured? && switchboard_id.present?
|
57
56
|
|
58
|
-
RestClient.delete(switchboard_url, ACCESS_TOKEN: sw_api_token) do |response, _request, _result
|
57
|
+
RestClient.delete(switchboard_url, ACCESS_TOKEN: sw_api_token) do |response, _request, _result|
|
59
58
|
case response.code
|
60
59
|
when 200
|
61
60
|
App47Logger.log_debug "Switchboard deleted #{inspect}"
|
62
61
|
else
|
63
62
|
App47Logger.log_error "Unable to delete the switchboard object #{inspect}, #{response.inspect}"
|
64
|
-
response.return!(&block)
|
65
63
|
end
|
66
64
|
end
|
67
65
|
end
|
@@ -231,23 +231,17 @@ class EmailNotification < Notification
|
|
231
231
|
end
|
232
232
|
|
233
233
|
def subject_from_template(template_name, locals)
|
234
|
-
subject = account_subject_template(template_name) ||
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
234
|
+
subject = account_subject_template(template_name) || Template.from_file(template_name, prefix: 'subject')
|
235
|
+
if subject.present?
|
236
|
+
subject_from_liquid_text(subject, locals)
|
237
|
+
else
|
238
|
+
subject = Template.from_file(template_name, format: 'haml', prefix: 'subject')
|
239
|
+
subject.present? ? subject_from_haml_text(subject, locals) : nil
|
240
|
+
end
|
241
241
|
end
|
242
242
|
|
243
243
|
def account_subject_template(template_name)
|
244
|
-
account.
|
245
|
-
rescue StandardError
|
246
|
-
nil
|
247
|
-
end
|
248
|
-
|
249
|
-
def default_subject_template(template_name)
|
250
|
-
EmailTemplate.where(account: nil, name: template_name.to_s).template
|
244
|
+
account.email_templates.find_by(name: template_name.to_s).subject
|
251
245
|
rescue StandardError
|
252
246
|
nil
|
253
247
|
end
|
@@ -7,7 +7,37 @@ class EmailTemplate < Template
|
|
7
7
|
#
|
8
8
|
field :subject, type: String
|
9
9
|
#
|
10
|
+
# Callbacks
|
11
|
+
#
|
12
|
+
before_save :htmlize_template
|
13
|
+
#
|
10
14
|
# Validations
|
11
15
|
#
|
12
16
|
validates :subject, presence: true
|
17
|
+
|
18
|
+
#
|
19
|
+
# Make sure the template is wrapped in html
|
20
|
+
def htmlize_template
|
21
|
+
if template.present? && !template.strip.start_with?("<")
|
22
|
+
self.template = "<body><pre>#{template}</pre></body>"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def valid_liquid_template
|
27
|
+
super && Liquid::Template.parse(self.subject).nil?
|
28
|
+
rescue Exception => error
|
29
|
+
self.errors.add(:subject, "Invalid liquid text in subject: #{error.message}")
|
30
|
+
false
|
31
|
+
end
|
32
|
+
|
33
|
+
#
|
34
|
+
# Copy the default from disk
|
35
|
+
#
|
36
|
+
def self.copy_default(name)
|
37
|
+
template = EmailTemplate.new
|
38
|
+
template.name = name
|
39
|
+
template.template = from_file name
|
40
|
+
template.subject = from_file name, prefix: 'subject'
|
41
|
+
template
|
42
|
+
end
|
13
43
|
end
|
@@ -158,7 +158,7 @@ class Notification
|
|
158
158
|
# If the account does exists or the template in the account does exist, we catch the error and return nil
|
159
159
|
#
|
160
160
|
def account_message_template(template_name)
|
161
|
-
account.templates.find_by(name: template_name.to_s, _type: "
|
161
|
+
account.templates.find_by(name: template_name.to_s, _type: "#{delivery_channel.humanize}Template").template
|
162
162
|
rescue StandardError
|
163
163
|
nil
|
164
164
|
end
|
@@ -172,20 +172,6 @@ class Notification
|
|
172
172
|
nil
|
173
173
|
end
|
174
174
|
|
175
|
-
#
|
176
|
-
# Retrieve the template out of the project
|
177
|
-
#
|
178
|
-
def template_from_file(template_name, format: 'liquid', prefix: nil)
|
179
|
-
file_name = [template_name, prefix, format].compact.join('.')
|
180
|
-
if File.exist?(Rails.root.join('lib', 'templates', delivery_channel, file_name))
|
181
|
-
File.open(Rails.root.join('lib', 'templates', delivery_channel, file_name))
|
182
|
-
else
|
183
|
-
File.read(File.join(__dir__, '../../lib', 'templates', delivery_channel, file_name))
|
184
|
-
end.read
|
185
|
-
rescue StandardError
|
186
|
-
nil
|
187
|
-
end
|
188
|
-
|
189
175
|
#
|
190
176
|
# Default delivery channel is email, override for sms, SMTP or other channels
|
191
177
|
#
|
@@ -194,17 +180,20 @@ class Notification
|
|
194
180
|
end
|
195
181
|
|
196
182
|
def message_from_template(template_name, locals)
|
197
|
-
template = account_message_template(template_name) ||
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
183
|
+
template = account_message_template(template_name) ||
|
184
|
+
Template.from_file(template_name, delivery_channel: delivery_channel)
|
185
|
+
if template.present?
|
186
|
+
message_from_liquid_text(template, locals)
|
187
|
+
else
|
188
|
+
template = Template.from_file(template_name, format: 'haml', delivery_channel: delivery_channel)
|
189
|
+
template.present? ? message_from_haml_text(template, locals) : nil
|
190
|
+
end
|
202
191
|
end
|
203
192
|
|
204
193
|
def message_from_haml_text(haml_text, locals)
|
205
194
|
locals[:base_url] = SystemConfiguration.base_url
|
206
195
|
engine = Haml::Engine.new(haml_text)
|
207
|
-
self.message = engine.render(Object.new,
|
196
|
+
self.message = engine.render(Object.new, locals)
|
208
197
|
end
|
209
198
|
|
210
199
|
def message_from_haml_file(file_name, locals)
|
data/lib/app/models/template.rb
CHANGED
@@ -18,4 +18,31 @@ class Template
|
|
18
18
|
validates :name, uniqueness: { scope: :account_id }
|
19
19
|
validates :name, presence: true
|
20
20
|
validates :template, presence: true
|
21
|
+
validate :valid_liquid_template
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
#
|
26
|
+
# Ensure that the template is correct from a liquid statement
|
27
|
+
#
|
28
|
+
def valid_liquid_template
|
29
|
+
Liquid::Template.parse(self.template).nil?
|
30
|
+
rescue Exception => error
|
31
|
+
self.errors.add(:template, "Invalid liquid text in template: #{error.message}")
|
32
|
+
false
|
33
|
+
end
|
34
|
+
|
35
|
+
#
|
36
|
+
# Retrieve the template out of the project
|
37
|
+
#
|
38
|
+
def self.from_file(template_name, format: 'liquid', prefix: nil, delivery_channel: 'email')
|
39
|
+
file_name = [template_name, prefix, format].compact.join('.')
|
40
|
+
if File.exist?(Rails.root.join('lib/templates', delivery_channel, file_name))
|
41
|
+
File.open(Rails.root.join('lib/templates', delivery_channel, file_name))
|
42
|
+
else
|
43
|
+
File.read(File.join(__dir__, '../../lib/templates', delivery_channel, file_name))
|
44
|
+
end.read
|
45
|
+
rescue StandardError
|
46
|
+
nil
|
47
|
+
end
|
21
48
|
end
|
data/lib/web47core/config.rb
CHANGED
@@ -6,30 +6,34 @@ module Web47core
|
|
6
6
|
#
|
7
7
|
class Config
|
8
8
|
include Singleton
|
9
|
-
attr_accessor :email_able_models, :switchboard_able_models, :
|
9
|
+
attr_accessor :email_able_models, :switchboard_able_models, :audit_model
|
10
10
|
|
11
11
|
def initialize
|
12
12
|
@email_able_models = []
|
13
13
|
@switchboard_able_models = []
|
14
|
-
@
|
14
|
+
@audit_model = :user
|
15
15
|
end
|
16
16
|
|
17
17
|
def self.reset
|
18
18
|
instance.email_able_models = []
|
19
19
|
instance.switchboard_able_models = []
|
20
|
-
instance.
|
20
|
+
instance.audit_model = :user
|
21
21
|
end
|
22
22
|
|
23
|
-
def self.
|
24
|
-
|
23
|
+
def self.audit_model_log_class
|
24
|
+
audit_model_log_class_name.constantize
|
25
25
|
end
|
26
26
|
|
27
|
-
def self.
|
28
|
-
|
27
|
+
def self.audit_model_log_class_name
|
28
|
+
audit_model_log_symbol.camelize
|
29
29
|
end
|
30
30
|
|
31
|
-
def self.
|
32
|
-
"#{instance.
|
31
|
+
def self.audit_model_log_symbol
|
32
|
+
"#{instance.audit_model}_model_audit_log"
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.audit_model_class_name
|
36
|
+
instance.audit_model.to_s.camelize
|
33
37
|
end
|
34
38
|
|
35
39
|
#
|
data/lib/web47core/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: web47core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Schroeder
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-05-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -583,6 +583,8 @@ files:
|
|
583
583
|
- app/views/delayed_jobs/show.html.haml
|
584
584
|
- app/views/stack/cron/edit.html.haml
|
585
585
|
- app/views/stack/cron/index.html.haml
|
586
|
+
- app/views/stack/delayed_jobs/index.html.haml
|
587
|
+
- app/views/stack/delayed_jobs/show.html.haml
|
586
588
|
- app/views/stack/system_configurations/edit.html.haml
|
587
589
|
- app/views/stack/system_configurations/show.html.haml
|
588
590
|
- app/views/status/index.html.haml
|