web47core 0.1.10 → 0.1.11

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: 3b5aaf907f5f8e778ff9b3fac4d6d38db6f3c3314da02783a90d106ce06153e5
4
- data.tar.gz: 8dde405df315b3e1391c5e0f18fa8c495dd4f52ab0e7aaf36f4ebd538420125e
3
+ metadata.gz: eb0a71b40f9e5d0067b00d477ad3ea6b666c7972ea7d8894f0535e74e7ccd846
4
+ data.tar.gz: 7585bf07cbb2ae2f6ada91905afebb19a11ca81ac78de3e1d6046c411d4db892
5
5
  SHA512:
6
- metadata.gz: 881e3398e59b47a65f8fa840fe0e73fe98447cd67b58ff0e4a1cd38ac0205a19a3d676c1dc5c729d10f4c0e8c7406a7c743d98e0acc147119420c210ea271c25
7
- data.tar.gz: 75d4cb3c6120870f0eb97dd2599e4de9ae79e671557121570777a3ac263d17378de79f8fc7ed870e750b5d901288909134d5a282734afd33ccd15e9e314ec1f0
6
+ metadata.gz: 51df548129aab21318b9d01dc674cd8babdfdf990223821c35ba94c61ded799b27eb205cae2d634c9f2fba6c79f4cbc28f6c0ae81ab0b443a876f5ca2e4a89d9
7
+ data.tar.gz: 92f1f7602a1b12d2a6f204455c5e419a53d01016639052d5944010bdbbbd692eb7f127ebc7e53f008d8e63b6fac41ac2d789cf15aec6847606e0cf1121867526
Binary file
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Capture the exception and give a nice screen to the user instead of a stack track looking one.
5
+ #
6
+ class ExceptionsController < ActionController::Base
7
+ include App47Logger
8
+ layout 'application'
9
+ respond_to :html
10
+
11
+ #
12
+ # Show
13
+ #
14
+ def show
15
+ log_controller_error request.env['action_dispatch.exception']
16
+ @xhr = request.headers['HTTP_X_REQUESTED_WITH'].present?
17
+ end
18
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Record the user opening the email
5
+ #
6
+ class NotificationsController < ApplicationController
7
+ #
8
+ # Notification was viewed
9
+ #
10
+ def show
11
+ Notification.find(params[:id]).viewed
12
+ rescue StandardError => error
13
+ log_controller_error error
14
+ ensure
15
+ redirect_to ActionController::Base.helpers.asset_path('1x1.png')
16
+ end
17
+ end
@@ -1,4 +1,6 @@
1
1
  en:
2
+ update: Update
3
+ cancel: Cancel
2
4
  time:
3
5
  formats:
4
6
  long: '%Y-%m-%d %H:%M:%ss (%Z)'
@@ -8,4 +10,24 @@ en:
8
10
  formats:
9
11
  long: '%B %d, %Y'
10
12
  medium: '%b %d, %Y'
11
- short: '%m/%d/%Y'
13
+ short: '%m/%d/%Y'
14
+ ui_form:
15
+ system_configuration:
16
+ placeholders:
17
+ switchboard_base_url: https://switchboard.app47.com
18
+ switchboard_stack_id: ab1a34afa231
19
+ hints:
20
+ switchboard_base_url: The base URL for the switchboard application
21
+ switchboard_stack_id: Stack ID to pull configuraiton from
22
+ switchboard_stack_api_token: API token to use for the stack ID
23
+ labels:
24
+ switchboard_base_url: Base URL
25
+ switchboard_stack_id: Stack ID
26
+ switchboard_stack_api_token: API Token
27
+ system_configurations:
28
+ show:
29
+ title: "%{name} System Configuration"
30
+ field_header: Field
31
+ value_header: Value
32
+ edit:
33
+ title: Edit %{name} System Configuration
data/config/routes.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # web47core/config/routes.rb
2
2
  Rails.application.routes.draw do
3
3
  get 'status' => 'status#index'
4
+ get '/notifications/:id', to: 'notifications#show'
4
5
  end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Manage the system configuration page
5
+ #
6
+ module CoreSystemConfigurationsController
7
+ authorize_resource :system_configuration
8
+
9
+ #
10
+ # Edit the system configuration
11
+ #
12
+ def edit
13
+ load_configuration
14
+ end
15
+
16
+ #
17
+ # Update and log the system configuration changes
18
+ #
19
+ def update
20
+ # @system_configuration.update_attributes_and_log! @current_admin_user, system_configuration_params
21
+ SystemConfiguration.configuration.update! system_configuration_params
22
+ flash[:info] = 'System Configuration Updated, sync job running in the background'
23
+ Cron::SwitchboardSyncConfiguration.perform_later
24
+ redirect_to system_configuration_path
25
+ rescue StandardError => error
26
+ log_controller_error error
27
+ load_configuration
28
+ render :edit
29
+ end
30
+
31
+ private
32
+
33
+ #
34
+ # Get the system configuration properties from the request
35
+ #
36
+ def system_configuration_params
37
+ params.require(:system_configuration).permit(SystemConfiguration.allowed_param_names)
38
+ end
39
+
40
+ #
41
+ # Load the current system configuration
42
+ #
43
+ def load_configuration
44
+ @system_configuration = SystemConfiguration.configuration
45
+ end
46
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Reusable API methods
5
+ #
6
+ module RestfulController
7
+ #
8
+ # Render a JSON response with the results and status passed in. The payload will include
9
+ # an MD5 hash along with the response for validation.
10
+ #
11
+ def render_json_response(success = true, results = {}, status_code = :ok)
12
+ results[:success] = success
13
+ render status: status_code,
14
+ json: { results: results,
15
+ md5: hash(unescape_unicode(results.to_json)) }.to_json,
16
+ layout: false
17
+ end
18
+
19
+ #
20
+ # Convert UTF-8 unicode/escaped back to actual double byte character
21
+ #
22
+ def unescape_unicode(str)
23
+ str.gsub(/\\u([\da-fA-F]{4})/) do |_m|
24
+ [Regexp.last_match[1]].pack('H*').unpack('n*').pack('U*')
25
+ end
26
+ end
27
+
28
+ #
29
+ # Create a hash of the string contents
30
+ #
31
+ def hash(str)
32
+ Digest::MD5.hexdigest(str)
33
+ end
34
+ end
@@ -0,0 +1,266 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # helpers for creating forms
5
+ #
6
+ module CoreFormHelper
7
+ def materialize_form_for(options = {}, &block)
8
+ options[:form_authenticity_token] = form_authenticity_token
9
+ form = Materialize::Form.new(options)
10
+ form.render_form(&block)
11
+ end
12
+
13
+ #
14
+ # Text field
15
+ #
16
+ def form_text_field(model, field, options = {})
17
+ classes = options[:classes] || %w[s12 m6 l4 xl3]
18
+ value = model.send(field)
19
+ options[:type] = :text
20
+ options[:value] = value
21
+ tag_options = text_field_options(model, field, options)
22
+ content_tag(:div, class: (%w[input-field col] + classes).join(' ')) do
23
+ concat(tag(:input, tag_options))
24
+ concat(form_label_tag(model, field, value, options))
25
+ end
26
+ end
27
+
28
+ def form_date_field(model, field, options = {})
29
+ classes = options[:classes] || %w[s12 m6 l4 xl3]
30
+ value = model.send(field)
31
+ options[:value] = value.strftime('%d %B, %Y') if value.present?
32
+ options[:type] = :text
33
+ tag_options = text_field_options(model, field, options)
34
+ tag_options[:class] = options[:date_picker_type] || 'simple-date-picker'
35
+ content_tag(:div, class: (%w[input-field col] + classes).join(' ')) do
36
+ concat(tag(:input, tag_options))
37
+ concat(form_label_tag(model, field, value, options))
38
+ end
39
+ end
40
+
41
+ #
42
+ # Password field
43
+ #
44
+ def form_password(model, field, options = {})
45
+ classes = options[:classes] || %w[s12 m6 l4 xl3]
46
+ value = model.send(field)
47
+ options[:type] = :password
48
+ options[:place_holder] = mask_value(value)
49
+ tag_options = text_field_options(model, field, options)
50
+ content_tag(:div, class: (%w[input-field col] + classes).join(' ')) do
51
+ concat(tag(:input, tag_options))
52
+ concat(form_label_tag(model, field, value, options))
53
+ end
54
+ end
55
+
56
+ #
57
+ # Select field
58
+ #
59
+ def form_select(model, field, options = {})
60
+ value = model.send(field)
61
+ raise 'Prompt needed' if value.blank? && options[:prompt].blank? && !options[:no_label]
62
+
63
+ hint_key = "ui_form.#{model.class.to_s.underscore}.hints.#{field}"
64
+ if I18n.exists?(hint_key)
65
+ base_classes = %w[input-field col tooltipped]
66
+ data = { tooltip: I18n.t(hint_key), position: :top }
67
+ else
68
+ base_classes = %w[input-field col]
69
+ data = {}
70
+ end
71
+ classes = (base_classes + (options[:classes] || %w[s12 m6 l4])).join(' ')
72
+ content_tag(:div, class: classes, data: data) do
73
+ concat(form_select_tag(model, field, options))
74
+ concat(form_label_tag(model, field, value, options))
75
+ end
76
+ end
77
+
78
+ #
79
+ # Create the select tag
80
+ #
81
+ def form_select_tag(model, field, options = {})
82
+ form_name = form_field_name(model, field, options)
83
+ content_tag(:select, id: form_name, name: form_name, class: options[:input_classes]) do
84
+ form_select_option_key_values(model.send(field), options).each do |value|
85
+ concat(content_tag(:option, value: value[:key], selected: value[:selected]) do
86
+ concat(value[:value])
87
+ end)
88
+ end
89
+ end
90
+ end
91
+
92
+ #
93
+ # Return an array of hashes for the selection to work easily
94
+ def form_select_option_key_values(value, options)
95
+ select_options = []
96
+ select_options << { key: '', value: options[:prompt], selected: false } if options[:prompt].present?
97
+ options[:options].each do |option_value|
98
+ option_value[:display_name] = option_value.display_name if option_value.respond_to?(:display_name)
99
+ select_options << case option_value
100
+ when String, Integer
101
+ { key: option_value,
102
+ value: option_value,
103
+ selected: value.eql?(option_value) }
104
+ when Array
105
+ { key: option_value.first,
106
+ value: option_value.last,
107
+ selected: value.to_s.eql?(option_value.first.to_s) }
108
+ when Hash
109
+ { key: option_value[:key],
110
+ value: option_value[:value],
111
+ selected: value.eql?(option_value[:key]) }
112
+ else
113
+ { key: option_value[:_id],
114
+ value: option_value[:display_name] || option_value[:name],
115
+ selected: value.eql?(option_value) }
116
+ end
117
+ end
118
+ select_options
119
+ end
120
+
121
+ #
122
+ # Checkbox field
123
+ #
124
+ def form_checkbox(model, field, classes = %w[s12 m6 l4 xl3], options = {})
125
+ form_name = form_field_name model, field
126
+ value = model.send(field)
127
+ options[:disabled] ||= false
128
+ properties = {
129
+ class: 'validate',
130
+ id: form_name,
131
+ name: form_name,
132
+ type: :checkbox,
133
+ disabled: options[:disabled]
134
+ }
135
+ properties[:checked] = true if model.send(field)
136
+ checkbox_tag = tag(:input, properties)
137
+ content_tag(:div, class: (%w[input-field col] + classes).join(' ')) do
138
+ concat(content_tag(:label) do
139
+ concat(checkbox_tag)
140
+ concat(form_checkbox_label_tag(model, field, value, input_classes: classes))
141
+ end)
142
+ end
143
+ end
144
+
145
+ #
146
+ # get the label for checkbox
147
+ #
148
+ def form_checkbox_label_tag(model, field, value, options = {})
149
+ # dont do a label if we are in default browser mode
150
+ return if options[:input_classes].present? && options[:input_classes].include?('browser-default')
151
+
152
+ # or if we have a prompt with now value
153
+ place_holder = field_place_holder(model, field)
154
+ return if place_holder.blank? && value.blank? && options[:prompt].present?
155
+
156
+ error = model.errors[field]
157
+ key = "ui_form.#{model.class.to_s.underscore}.labels.#{field}"
158
+ classes = value.nil? && place_holder.blank? ? '' : 'active'
159
+ classes += error.present? ? ' invalid red-text' : ' valid'
160
+ options[:class] = classes
161
+ options['data-error'] = error.join(', ') if error.present?
162
+ content_tag(:span, options) do
163
+ concat(I18n.exists?(key) ? I18n.t(key) : field.to_s.humanize)
164
+ end
165
+ end
166
+
167
+ #
168
+ # get the label for field
169
+ #
170
+ def form_label_tag(model, field, value, options = {})
171
+ # dont do a label if we are in default browser mode
172
+ return if options[:no_label]
173
+ return if options[:input_classes].present? && options[:input_classes].include?('browser-default')
174
+
175
+ # or if we have a prompt with now value
176
+ place_holder = options[:place_holder] || field_place_holder(model, field)
177
+ return if place_holder.blank? && value.blank? && options[:prompt].present?
178
+
179
+ error = model.errors[field]
180
+ key = "ui_form.#{model.class.to_s.underscore}.labels.#{field}"
181
+ classes = value.nil? && place_holder.blank? ? '' : 'active'
182
+ classes += error.present? ? ' invalid red-text' : ' valid'
183
+ options[:class] = classes
184
+ options[:for] = form_field_name(model, field, options)
185
+ options['data-error'] = error.join(', ') if error.present?
186
+ content_tag(:label, options) do
187
+ concat(I18n.exists?(key) ? I18n.t(key) : field.to_s.humanize)
188
+ concat(' ' + error.join(', ')) if error.present?
189
+ end
190
+ end
191
+
192
+ #
193
+ # Get the hint for the field and add it
194
+ #
195
+ def form_hint_tag(model, field)
196
+ key = "ui_form.#{model.class.to_s.underscore}.hints.#{field}"
197
+ return nil unless I18n.exists?(key)
198
+
199
+ content_tag(:p, class: 'form-hint', for: form_field_name(model, field)) do
200
+ concat(I18n.t(key))
201
+ end
202
+ end
203
+
204
+ #
205
+ # Add the placeholder option if found in the translations
206
+ #
207
+ def text_field_options(model, field, options)
208
+ form_name = form_field_name model, field, options
209
+ hint_key = "ui_form.#{model.class.to_s.underscore}.hints.#{field}"
210
+ if I18n.exists?(hint_key)
211
+ classes = %w[validate tooltipped]
212
+ options[:data] = { tooltip: I18n.t(hint_key), position: :top }
213
+ else
214
+ classes = %w[validate]
215
+ end
216
+ classes += options[:input_classes] if options[:input_classes].present?
217
+ options[:name] = form_name
218
+ options[:id] = form_name
219
+ place_holder = options[:place_holder] || field_place_holder(model, field)
220
+ if place_holder.present?
221
+ classes << 'active'
222
+ options[:placeholder] = place_holder
223
+ end
224
+ classes << 'active' if options[:value].present?
225
+ options[:class] = classes.uniq
226
+ options
227
+ end
228
+
229
+ def field_place_holder(model, field)
230
+ place_holder_key = "ui_form.#{model.class.to_s.underscore}.placeholders.#{field}"
231
+ I18n.exists?(place_holder_key) ? I18n.t(place_holder_key) : nil
232
+ end
233
+
234
+ #
235
+ # Return a consistent form field name
236
+ #
237
+ def form_field_name(model, field, options = {})
238
+ return options[:form_name] if options[:form_name].present?
239
+
240
+ # TODO: Need to handle the other side of the 1:M use case where
241
+ # the field name needs to end in _ids, not _id.
242
+ field = "#{field}_id" if model.class.reflect_on_association(field).present?
243
+ if options[:index].present?
244
+ if options[:array_name].present?
245
+ if options[:base_name].present?
246
+ "#{options[:base_name]}[#{options[:array_name]}[#{options[:index]}][#{field}]]"
247
+ else
248
+ "#{options[:array_name]}[#{options[:index]}][#{field}]"
249
+ end
250
+ else
251
+ "#{model.class.to_s.underscore}[#{options[:index]}][#{field}]"
252
+ end
253
+ else
254
+ "#{model.class.to_s.underscore}[#{field}]"
255
+ end
256
+ end
257
+
258
+ def form_radio_button(model, field, options = {})
259
+ value = model.send(field)
260
+ classes = (%w[input-field col] + options[:classes] || []).join(' ')
261
+ content_tag(:div, class: classes) do
262
+ concat(form_select_tag(model, field, options))
263
+ concat(form_label_tag(model, field, value))
264
+ end
265
+ end
266
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # helpers for core views
5
+ #
6
+ module CoreHelper
7
+ #
8
+ # Set the title for the page
9
+ #
10
+ def title(page_title)
11
+ content_for(:title) { page_title }
12
+ end
13
+
14
+ def system_configuration_path
15
+ '/' + [Web47core.config.system_configuration_namespace, 'system_configuration'].join('/')
16
+ end
17
+
18
+ def edit_system_configuration_path
19
+ [system_configuration_path, 'edit'].join('/')
20
+ end
21
+
22
+ #
23
+ # Pull the value from system configuration and mask the ones that match the given strings
24
+ #
25
+ def mask_system_configuration(field)
26
+ should_mask = false
27
+ %w[password token secret access_key api_key].each do |mask|
28
+ should_mask |= field.include?(mask)
29
+ end
30
+ value = SystemConfiguration.send(field)
31
+ should_mask ? mask_value(value, default: 'No Set') : value
32
+ end
33
+
34
+ #
35
+ # Mask a value, i.e. password field
36
+ # 1. If blank or nil, return the default
37
+ # 2. Length of 1-4 only show the first character
38
+ # 3. Length 5-10 only show the first and last character
39
+ # 4. Otherwise show the first and last three characters
40
+ def mask_value(value, default: '************')
41
+ return default if value.blank?
42
+
43
+ case value.length
44
+ when 1..4
45
+ "#{value.first}***********"
46
+ when 5..10
47
+ "#{value.first}**********#{value.last}"
48
+ else
49
+ "#{value[0..2]}**********#{value[-3..-1]}"
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,273 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # common link generation helpers
5
+ #
6
+ module CoreLinkHelper
7
+ #
8
+ # Create a copy tag for data to be copied to the clipboard
9
+ #
10
+ def copy_tag(copy_text, options = {})
11
+ content_tag(:p, class: 'stack-tight') do
12
+ concat(copy_text)
13
+ concat(copy_text_tag(copy_text, options))
14
+ end
15
+ end
16
+
17
+ #
18
+ # Create a text element that is easy to copy
19
+ #
20
+ def copy_text_tag(copy_text, options = {})
21
+ options[:icon_name] ||= 'content_copy'
22
+ content_tag(:a, class: 'copy', href: '#', data: { 'clipboard-text' => copy_text }, onclick: 'return false;') do
23
+ concat(content_tag(:i, class: 'material-icons') do
24
+ options[:icon_name]
25
+ end)
26
+ end
27
+ end
28
+
29
+ #
30
+ # Add the table action button and setup the drop down
31
+ #
32
+ def table_action_button(action_id, icon_name = 'more_horiz')
33
+ datum = { activates: action_id,
34
+ constrainWidth: false,
35
+ gutter: 28,
36
+ alignment: 'right' }
37
+ content_tag(:a, class: 'dropdown-button', data: datum) do
38
+ concat(materialize_icon(icon_name))
39
+ end
40
+ end
41
+
42
+ #
43
+ # Add a material icon by name
44
+ #
45
+ def materialize_icon(name, options = {})
46
+ classes = %w[material-icons]
47
+ classes += options[:classes] if options[:classes].present?
48
+ content_tag(:i, class: classes.join(' '), title: options[:title]) { name }
49
+ end
50
+
51
+ #
52
+ # Generic link tag
53
+ #
54
+ def link_tag(title, path, options = {})
55
+ content_tag(:a, href: path) do
56
+ concat(content_tag(:i, class: 'material-icons left') { options[:icon_name] }) if options[:icon_name].present?
57
+ concat(title)
58
+ end
59
+ end
60
+
61
+ #
62
+ # Edit a thingy in a pull down list
63
+ #
64
+ def edit_list_tag(obj, path, options = {})
65
+ return unless can? :edit, obj
66
+
67
+ options[:label] ||= I18n.t('ui_form.actions.edit')
68
+ content_tag(:li) do
69
+ edit_link_tag(obj, path, options)
70
+ end
71
+ end
72
+
73
+ #
74
+ # Edit an thingy
75
+ #
76
+ def edit_link_tag(obj, path, options = {})
77
+ return unless can? :edit, obj
78
+
79
+ content_tag(:a, href: path) do
80
+ concat(content_tag(:i, class: 'material-icons') do
81
+ 'edit'
82
+ end)
83
+ concat(options[:label]) if options[:label].present?
84
+ end
85
+ end
86
+
87
+ def edit_modal_tag(obj, path, _options = {})
88
+ return unless can? :edit, obj
89
+
90
+ content_tag(:a, href: path, class: 'modal-action') do
91
+ concat(I18n.t('ui_form.actions.edit'))
92
+ end
93
+ end
94
+
95
+ #
96
+ # Restart a thingy
97
+ #
98
+ def replay_link_tag(obj, path, options = {})
99
+ return unless can? :edit, obj
100
+
101
+ icon_name = options[:icon_name] || 'replay'
102
+ content_tag(:a, href: path) do
103
+ concat(content_tag(:i, class: 'material-icons') do
104
+ icon_name
105
+ end)
106
+ concat(options[:label]) if options[:label].present?
107
+ end
108
+ end
109
+
110
+ #
111
+ # Restart a thingy
112
+ #
113
+ def replay_list_tag(obj, path, options = {})
114
+ return unless can? :edit, obj
115
+
116
+ options[:label] ||= I18n.t('ui_form.actions.replay')
117
+ content_tag(:li) do
118
+ replay_link_tag(obj, path, options)
119
+ end
120
+ end
121
+
122
+ #
123
+ # Details of a thingy in a pull down list
124
+ #
125
+ def info_list_tag(obj, path, options = {})
126
+ options[:label] ||= I18n.t('ui_form.actions.info')
127
+ content_tag(:li) do
128
+ info_link_tag(obj, path, options)
129
+ end
130
+ end
131
+
132
+ #
133
+ # Details of a thingy
134
+ #
135
+ def info_link_tag(obj, path, options = {})
136
+ return unless can? :read, obj
137
+
138
+ content_tag(:a, href: path) do
139
+ concat(content_tag(:i, class: 'material-icons') do
140
+ 'info'
141
+ end)
142
+ concat(options[:label]) if options[:label].present?
143
+ end
144
+ end
145
+
146
+ #
147
+ # Show a list of things in a pull down list if the condition is true
148
+ #
149
+ def action_list_tag(condition, path, options = {})
150
+ return unless condition
151
+ raise('Label Required') if options[:label].blank?
152
+
153
+ content_tag(:li) do
154
+ action_link_tag(condition, path, options)
155
+ end
156
+ end
157
+
158
+ #
159
+ # Show a list of things if the condition is true.
160
+ #
161
+ def action_link_tag(condition, path, options = {})
162
+ return unless condition
163
+
164
+ content_tag(:a, href: path) do
165
+ concat(content_tag(:i, class: 'material-icons') do
166
+ options[:icon_name] || 'info'
167
+ end)
168
+ concat(options[:label]) if options[:label].present?
169
+ end
170
+ end
171
+
172
+ #
173
+ # Delete a thingy in a pull down list
174
+ #
175
+ def delete_list_tag(obj, path, options = {})
176
+ return unless can? :delete, obj
177
+
178
+ options[:label] ||= I18n.t('ui_form.actions.delete')
179
+ content_tag(:li) do
180
+ delete_link_tag(obj, path, options)
181
+ end
182
+ end
183
+
184
+ #
185
+ # Delete an thingy
186
+ #
187
+ def delete_link_tag(obj, path, options = {})
188
+ return unless can? :delete, obj
189
+
190
+ confirmation = options[:confirmation] || t('links.deletion_confirmation', name: obj.class.to_s.underscore.humanize)
191
+ content_tag(:a, href: path, data: { method: :delete, confirm: confirmation }) do
192
+ concat(content_tag(:i, class: 'material-icons') do
193
+ options[:icon_name] || 'delete'
194
+ end)
195
+ concat(options[:label]) if options[:label].present?
196
+ end
197
+ end
198
+
199
+ #
200
+ # Send notification in a pull down list
201
+ #
202
+ def notification_list_tag(obj, path, options = {})
203
+ return unless can? :manage, obj
204
+
205
+ options[:label] ||= I18n.t('customer_account_users.index.reset_password')
206
+ content_tag(:li) do
207
+ notification_link_tag(obj, path, options)
208
+ end
209
+ end
210
+
211
+ #
212
+ # Send notification
213
+ #
214
+ def notification_link_tag(obj, path, options = {})
215
+ return unless can? :manage, obj
216
+
217
+ content_tag(:a, href: path) do
218
+ concat(content_tag(:i, class: 'material-icons') do
219
+ options[:icon_name] || 'email'
220
+ end)
221
+ concat(options[:label]) if options[:label].present?
222
+ end
223
+ end
224
+
225
+ #
226
+ # Edit a page
227
+ #
228
+ def edit_class_link_tag(klass, path)
229
+ return unless can?(:edit, klass)
230
+
231
+ content_tag(:div, class: 'fixed-action-btn horizontal') do
232
+ concat(content_tag(:a, href: path, class: 'btn-floating btn-large right', style: 'padding: 0;margin: 0px 15px') do
233
+ concat(content_tag(:i, class: 'material-icons') { 'edit' })
234
+ end)
235
+ end
236
+ end
237
+
238
+ #
239
+ # Add a thingy
240
+ #
241
+ def add_link_tag(klass, path)
242
+ return unless can?(:manage, klass)
243
+
244
+ content_tag(:div, class: 'fixed-action-btn horizontal') do
245
+ concat(content_tag(:a, href: path, class: 'btn-floating btn-large right', style: 'padding: 0;margin: 0px 15px') do
246
+ concat(content_tag(:i, class: 'material-icons') { 'add' })
247
+ end)
248
+ end
249
+ end
250
+
251
+ #
252
+ # Form cancel button
253
+ #
254
+ def form_cancel_button_tag(path)
255
+ options = { class: 'btn-large waves-effect waves-light secondary', href: path }
256
+ content_tag(:a, options) do
257
+ concat(t('ui_form.actions.cancel'))
258
+ end
259
+ end
260
+
261
+ #
262
+ # Form submit button
263
+ #
264
+ def form_submit_button_tag(obj)
265
+ return unless can? :update, obj
266
+
267
+ options = { class: 'btn-large waves-effect waves-light', type: :submit }
268
+ content_tag(:button, options) do
269
+ concat(t('ui_form.actions.save'))
270
+ concat(content_tag(:i, class: 'material-icons right') { 'save' })
271
+ end
272
+ end
273
+ end
@@ -0,0 +1,18 @@
1
+ - title t('.title', name: SystemConfiguration.environment.titleize)
2
+ .container
3
+ %form{action: system_configuration_path, method: :post}
4
+ %input{type: :hidden, value: form_authenticity_token, name: 'authenticity_token'}
5
+ %input{type: :hidden, name: '_method', value: 'put'}
6
+ .row
7
+ = form_text_field(@system_configuration, :switchboard_base_url, classes: %w[s12 m4])
8
+ = form_text_field(@system_configuration, :switchboard_stack_id, classes: %w[s12 m4])
9
+ = form_password(@system_configuration, :switchboard_stack_api_token, classes: %w[s12 m4])
10
+ .row
11
+ .col.s6.center-align
12
+ %a.btn-large.waves-effect.waves-light{href: system_configuration_path}= t(':cancel')
13
+ %i.material-icons.right
14
+ cancel
15
+ .col.s6.center-align
16
+ %button.btn-large.waves-effect.waves-light{type: :submit}= t(':update')
17
+ %i.material-icons.right
18
+ save
@@ -0,0 +1,15 @@
1
+ - title t('.title', name: SystemConfiguration.environment.titleize)
2
+ = edit_class_link_tag(SystemConfiguration, edit_system_configuration_path)
3
+ .container
4
+ .row
5
+ .s12
6
+ %table.highlight.striped
7
+ %thead
8
+ %tr
9
+ %th.center-align=t('.field_header')
10
+ %th.center-align=t('.value_header')
11
+ %tbody
12
+ - SystemConfiguration.allowed_param_names.sort.each do |field|
13
+ %tr
14
+ %td.right-align=field
15
+ %td.left-align=mask_system_configuration(field)
@@ -6,7 +6,9 @@
6
6
  module Web47core
7
7
  class Config
8
8
  include Singleton
9
- attr_accessor :email_able_models, :switchboard_able_models
9
+ attr_accessor :email_able_models,
10
+ :switchboard_able_models,
11
+ :system_configuration_namespace
10
12
 
11
13
  def initialize
12
14
  @email_able_models = []
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Web47core
4
- VERSION = '0.1.10'.freeze
4
+ VERSION = '0.1.11'
5
5
  end
data/lib/web47core.rb CHANGED
@@ -38,5 +38,7 @@ require 'app/jobs/cron/trim_failed_delayed_jobs'
38
38
  # Controllers
39
39
  #
40
40
  require 'web47core/engine'
41
+ require 'app/controllers/concerns/core_system_configuration_controller'
42
+ require 'app/controllers/concerns/restful_controller'
41
43
 
42
44
 
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.1.10
4
+ version: 0.1.11
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-03-28 00:00:00.000000000 Z
11
+ date: 2020-03-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -541,12 +541,20 @@ extra_rdoc_files: []
541
541
  files:
542
542
  - LICENSE
543
543
  - README.md
544
+ - app/assets/images/1x1.png
545
+ - app/controllers/exceptions_controller.rb
546
+ - app/controllers/notifications_controller.rb
544
547
  - app/controllers/status_controller.rb
545
548
  - app/views/status/index.html.haml
546
549
  - bin/cron_server
547
550
  - bin/delayed_job
548
551
  - config/locales/en.yml
549
552
  - config/routes.rb
553
+ - lib/app/controllers/concerns/core_system_configuration_controller.rb
554
+ - lib/app/controllers/concerns/restful_controller.rb
555
+ - lib/app/helpers/core_form_helper.rb
556
+ - lib/app/helpers/core_helper.rb
557
+ - lib/app/helpers/core_link_helper.rb
550
558
  - lib/app/jobs/application_job.rb
551
559
  - lib/app/jobs/cron/command.rb
552
560
  - lib/app/jobs/cron/job.rb
@@ -578,6 +586,8 @@ files:
578
586
  - lib/app/models/sms_notification.rb
579
587
  - lib/app/models/smtp_configuration.rb
580
588
  - lib/app/models/template.rb
589
+ - lib/app/views/system_configurations/edit.html.haml
590
+ - lib/app/views/system_configurations/show.html.haml
581
591
  - lib/templates/email/notification_failure.liquid
582
592
  - lib/templates/email/notification_failure.subject.liquid
583
593
  - lib/templates/slack/error_message.liquid