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
         |