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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 030670e1f995d60e8bf25b1fb67c9bd91a628e859225114fcfe6203f3c77e818
4
- data.tar.gz: 7db5d7cbaa718e3a296b209bf4c5e3b943441958c0019601ec031972803f6a09
3
+ metadata.gz: 8717398ffc274873a8057965f099744e56c95730a3b06dbf465b2ae56bb48a28
4
+ data.tar.gz: 3a519e7c3fd207057c8912c62ccb1e249fd268886bb82f6b431f4c7f88851109
5
5
  SHA512:
6
- metadata.gz: d9dedd01621102e871fd5f6f19b99f46ff331788d1feba24095daf10f023c058ed61a1b7cd654e8d965c478954e41e3e53ee2c39e33efc3f46f3f4d36233e038
7
- data.tar.gz: 1e77420c64920f5ffde02efa94def8a5d57d3832ebbff25177b80d9253949fbd26049b6e95fc952e7b5da415d9ab33d835e8e29f8032492dc888696134c806fc
6
+ metadata.gz: e9d493aeb49b89723db88f171fb8efb2d79ba8d91b3e77dbcc82cec171bca779accc0eb4a7dfb03aeef539436ec338b04f2ab79b5ca476cba2c7a2f043d8252f
7
+ data.tar.gz: 4e533fd3a6db8add02c7cfd40c3d5bce6512bec4656bd6f7a8eced741f8dae3a1ba030a6795feda080589540478b231e8670eff5d789ff0fc3c806262dacc743
data/README.md CHANGED
@@ -232,3 +232,7 @@ end
232
232
  #### ~/bin Directory
233
233
 
234
234
  Remove the delayed_job, delayed_job.sh and cron_server.sh files
235
+
236
+ #### Notification Templates
237
+
238
+ Move any notification templates from `~/app/assets/templates` to `~/lib/templates`
@@ -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
- case value.length
65
+ string_value = value.to_s
66
+ case string_value.length
66
67
  when 1..4
67
- "#{value.first}***********"
68
+ "#{string_value.first}***********"
68
69
  when 5..10
69
- "#{value.first}**********#{value.last}"
70
+ "#{string_value.first}**********#{string_value.last}"
70
71
  else
71
- "#{value[0..2]}**********#{value[-3..-1]}"
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
- - title t('.title', name: SystemConfiguration.environment.titleize)
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'
@@ -1,3 +1 @@
1
- - title t('.title', name: SystemConfiguration.environment.titleize)
2
- = edit_class_link_tag(SystemConfiguration, edit_stack_system_configuration_path)
3
- = render '/system_configurations/table'
1
+ = render '/system_configurations/show'
@@ -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 perform
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|
@@ -18,7 +18,7 @@ module Cron
18
18
  #
19
19
  # Cycle through the collection and perform an upsert on it
20
20
  #
21
- def perform
21
+ def execute
22
22
  Web47core::Config.switchboard_able_models.each { |model| model.each(&:switchboard_upsert) }
23
23
  end
24
24
  end
@@ -9,8 +9,7 @@ module Cron
9
9
  #
10
10
  # Fetch each item and delete it if hasn't been updated in 30 days
11
11
  #
12
- def perform
13
- # Rails.cache.reconnect
12
+ def execute
14
13
  count = 0
15
14
  total = collection.count
16
15
  while count <= total
@@ -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
@@ -20,7 +20,7 @@ class AuditLog
20
20
  #
21
21
  # Validations
22
22
  #
23
- validates :action, inclusion: { in: ALL_ACTIONS }
23
+ # validates :action, inclusion: { in: ALL_ACTIONS }
24
24
 
25
25
  def self.sort_order
26
26
  [:created_at, -1]
@@ -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
- cache_key = "SystemConfiguration::#{Rails.env}"
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
- config.nil? ? SystemConfiguration.create(environment: Rails.env) : config
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.send method, *args
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, _include_private = false)
119
- SystemConfiguration.fields.include?(method_name)
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, _include_private = false)
123
- SystemConfiguration.fields.include?(method_name)
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 mailgun_configured?
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
- Rails.cache.delete "SystemConfiguration::#{Rails.env}"
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.mailgun_configured? && email_bounced?
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: 'User', optional: true
26
- belongs_to :created_by, class_name: 'User', optional: true
27
- has_many Web47core::Config.user_audit_model_log_symbol, dependent: :nullify
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
- result = yield
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
- associations = all_associations
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
- attribute_names
77
- end
78
-
79
- #
80
- # gather up the collections we care about and return them. For now, the
81
- # many to many associations are the ones that need some extra help.
82
- #
83
- def all_associations
84
- many_to_many_associations
85
- end
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.user_audit_model_log_class.create!(Web47core::Config.user_audit_model => user,
146
- model: model,
147
- action: model.audit_action,
148
- changed_values: App47Logger.clean_params(changes).to_json)
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.user_audit_model_log_class.create!(Web47core::Config.user_audit_model => user,
200
- model: self,
201
- action: action,
202
- changed_values: App47Logger.clean_params(changes).to_json)
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.user_audit_model_log_class.create!(Web47core::Config.user_audit_model => user,
304
- model: model,
305
- action: AuditLog::DELETE_ACTION,
306
- changed_values: App47Logger.clean_params(attributes).to_json)
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
- after_create :switchboard_upsert
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, &block|
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
- default_subject_template(template_name) ||
236
- template_from_file(template_name, prefix: 'subject')
237
- return subject_from_liquid_text(subject, locals) if subject.present?
238
-
239
- subject = template_from_file(template_name, format: 'haml', prefix: 'subject')
240
- subject_from_haml_text(subject, locals) if subject.present?
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.templates.emails.find_by(name: template_name.to_s).subject
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: "Account#{delivery_channel.humanize}Template").template
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) || template_from_file(template_name)
198
- return message_from_liquid_text(template, locals) if template.present?
199
-
200
- template = template_from_file(template_name, format: 'haml')
201
- message_from_haml_text(template, locals) if template.present?
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, stringify_all(locals))
196
+ self.message = engine.render(Object.new, locals)
208
197
  end
209
198
 
210
199
  def message_from_haml_file(file_name, locals)
@@ -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
@@ -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, :user_audit_model
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
- @user_audit_model = :user
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.user_audit_model = :user
20
+ instance.audit_model = :user
21
21
  end
22
22
 
23
- def self.user_audit_model_log_class
24
- user_audit_model_log_class_name.constantize
23
+ def self.audit_model_log_class
24
+ audit_model_log_class_name.constantize
25
25
  end
26
26
 
27
- def self.user_audit_model_log_class_name
28
- user_audit_model_log_symbol.camelize
27
+ def self.audit_model_log_class_name
28
+ audit_model_log_symbol.camelize
29
29
  end
30
30
 
31
- def self.user_audit_model_log_symbol
32
- "#{instance.user_audit_model}_model_audit_log"
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
  #
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Web47core
4
- VERSION = '0.6.3'
4
+ VERSION = '0.7.4'
5
5
  end
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.6.3
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-04-17 00:00:00.000000000 Z
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