active_scaffold 4.4.5 → 4.4.6
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/CHANGELOG.rdoc +25 -0
- data/app/assets/javascripts/active_scaffold/callback_registry.js +67 -0
- data/app/assets/javascripts/active_scaffold/date_picker_bridge.js +2 -6
- data/app/assets/javascripts/active_scaffold/load.js +2 -0
- data/app/assets/javascripts/active_scaffold/record_select_bridge.js +9 -0
- data/app/assets/javascripts/active_scaffold/tiny_mce_bridge.js +1 -1
- data/lib/active_scaffold/assets.rb +5 -4
- data/lib/active_scaffold/bridges/record_select.rb +7 -1
- data/lib/active_scaffold/extensions/localize.rb +18 -4
- data/lib/active_scaffold/helpers/association_helpers.rb +16 -1
- data/lib/active_scaffold/responds_to_parent.rb +23 -10
- data/lib/active_scaffold/translation_html_safety.rb +25 -0
- data/lib/active_scaffold/version.rb +1 -1
- data/vendor/assets/javascripts/jquery-ui-timepicker-addon.js +1 -5
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2475af8fa6929537031a900309db3ff1c3b91bcb9e911ccd61676aa06bf3009f
|
|
4
|
+
data.tar.gz: 0df3c5893800ffc86159e1a9c73e0b022f5b70a64afcc39e6da59266540f37ba
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e37f5f28f8bade8de9c86d69e68166a0ed7d12acde4c0e908c6c92626505b24511578cfb8394d238393659162ef7cc1dcb3916e693630a9192d03ea5acd8fa3e
|
|
7
|
+
data.tar.gz: 2aaae36010f59a87cf009a2ddca97c1f45f620d0493d100ff997f185d732592514acce727b1a929c2411028e6964e20de9007673f874deec8f2eb191a97cb1ae
|
data/CHANGELOG.rdoc
CHANGED
|
@@ -1,3 +1,28 @@
|
|
|
1
|
+
= 4.4.6
|
|
2
|
+
- Execute iframe responses by injecting a nonce-aware script into the parent document, avoiding +eval()+ under a strict Content Security Policy
|
|
3
|
+
- Use RecordSelect's named callback registry for the add-existing subform link when available, avoiding +eval()+ under a strict Content Security Policy while retaining compatibility with older RecordSelect releases.
|
|
4
|
+
- Add named callback registries for datepicker and TinyMCE configuration. Inline JavaScript remains supported in 4.4, but successful evaluation emits a browser deprecation warning because support will be removed in ActiveScaffold 4.5.
|
|
5
|
+
|
|
6
|
+
Define callbacks in an application JavaScript file:
|
|
7
|
+
|
|
8
|
+
ActiveScaffold.datepickerCallbacks.availableDate = function(date) {
|
|
9
|
+
return [date.getDay() !== 0, ''];
|
|
10
|
+
};
|
|
11
|
+
ActiveScaffold.tinymceCallbacks.configureEditor = function(editor) {
|
|
12
|
+
editor.on('init', function() { /* ... */ });
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
Reference their names from configuration instead of embedding function source:
|
|
16
|
+
|
|
17
|
+
config.columns[:starts_on].options[:data] = {beforeshowday: 'availableDate'}
|
|
18
|
+
|
|
19
|
+
# config/tinymce.yml
|
|
20
|
+
default:
|
|
21
|
+
setup: configureEditor
|
|
22
|
+
|
|
23
|
+
Migrate warnings before upgrading to 4.5. Non-callback datepicker settings should use JSON-compatible values.
|
|
24
|
+
- Add explicit HTML translations to +as_+. Move translations containing markup from +key+ to +key.html+. Interpolation follows Rails' HTML translation behavior, escaping unsafe values and preserving already HTML-safe values. Legacy HTML translations remain HTML-safe in 4.4 but emit one deprecation warning per key and scope; ordinary translations will no longer be marked HTML-safe in ActiveScaffold 4.5
|
|
25
|
+
|
|
1
26
|
= 4.4.5
|
|
2
27
|
- Authorize the PaperTrail deleted-records action and allow overriding its version query with +deleted_records_query+
|
|
3
28
|
- Deprecate passing optional boolean arguments positionally; keyword arguments will be required in 4.5.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
(function(global) {
|
|
2
|
+
var ActiveScaffold = global.ActiveScaffold = global.ActiveScaffold || {};
|
|
3
|
+
var warned = {};
|
|
4
|
+
|
|
5
|
+
ActiveScaffold.datepickerCallbacks = ActiveScaffold.datepickerCallbacks || {};
|
|
6
|
+
ActiveScaffold.tinymceCallbacks = ActiveScaffold.tinymceCallbacks || {};
|
|
7
|
+
|
|
8
|
+
function warnOnce(key, message) {
|
|
9
|
+
if (warned[key]) return;
|
|
10
|
+
|
|
11
|
+
warned[key] = true;
|
|
12
|
+
if (global.console && typeof global.console.warn === 'function') {
|
|
13
|
+
global.console.warn(message);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function evaluateLegacy(value, description, wrap) {
|
|
18
|
+
try {
|
|
19
|
+
var evaluated = global.eval(wrap ? '(' + value + ')' : value);
|
|
20
|
+
warnOnce(description + ':' + value,
|
|
21
|
+
'DEPRECATION WARNING: JavaScript in ' + description + ' is deprecated and will be removed in ActiveScaffold 4.5. ' +
|
|
22
|
+
'Register callbacks by name and use JSON-compatible values instead.');
|
|
23
|
+
return {success: true, value: evaluated};
|
|
24
|
+
} catch (error) {
|
|
25
|
+
return {success: false};
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
ActiveScaffold.resolveCallback = function(registryName, value, description) {
|
|
30
|
+
if (typeof value === 'function' || typeof value !== 'string') return value;
|
|
31
|
+
|
|
32
|
+
var registry = ActiveScaffold[registryName] || {};
|
|
33
|
+
if (Object.prototype.hasOwnProperty.call(registry, value)) {
|
|
34
|
+
if (typeof registry[value] === 'function') return registry[value];
|
|
35
|
+
|
|
36
|
+
throw new TypeError('ActiveScaffold.' + registryName + '.' + value + ' must be a function');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
var legacy = evaluateLegacy(value, description, true);
|
|
40
|
+
if (legacy.success) return legacy.value;
|
|
41
|
+
|
|
42
|
+
throw new Error(description + ' references unknown callback ' + value +
|
|
43
|
+
'. Register it in ActiveScaffold.' + registryName + '.');
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
var datepickerCallbackOptions = {
|
|
47
|
+
afterInject: true,
|
|
48
|
+
beforeShow: true,
|
|
49
|
+
beforeShowDay: true,
|
|
50
|
+
calculateWeek: true,
|
|
51
|
+
onChangeMonthYear: true,
|
|
52
|
+
onClose: true,
|
|
53
|
+
onSelect: true,
|
|
54
|
+
onUpdateDatepicker: true
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
ActiveScaffold.resolveDatepickerSetting = function(option, value) {
|
|
58
|
+
if (typeof value !== 'string') return value;
|
|
59
|
+
|
|
60
|
+
if (datepickerCallbackOptions[option]) {
|
|
61
|
+
return ActiveScaffold.resolveCallback('datepickerCallbacks', value, 'datepicker option ' + option);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
var legacy = evaluateLegacy(value, 'datepicker option ' + option, false);
|
|
65
|
+
return legacy.success ? legacy.value : value;
|
|
66
|
+
};
|
|
67
|
+
})(window);
|
|
@@ -6,11 +6,7 @@ jQuery.extend(Object.getPrototypeOf(jQuery.datepicker), {
|
|
|
6
6
|
if(this._defaults.hasOwnProperty(attrName)){
|
|
7
7
|
var attrValue = $target.data(attrName.toLowerCase());
|
|
8
8
|
if (attrValue !== undefined) {
|
|
9
|
-
|
|
10
|
-
inlineSettings[attrName] = eval(attrValue);
|
|
11
|
-
} catch (err) {
|
|
12
|
-
inlineSettings[attrName] = attrValue;
|
|
13
|
-
}
|
|
9
|
+
inlineSettings[attrName] = ActiveScaffold.resolveDatepickerSetting(attrName, attrValue);
|
|
14
10
|
}
|
|
15
11
|
}
|
|
16
12
|
}
|
|
@@ -46,4 +42,4 @@ jQuery(document).on('change', 'input.datetime_picker', function(e) {
|
|
|
46
42
|
$this.data('first-event', true);
|
|
47
43
|
e.stopImmediatePropagation();
|
|
48
44
|
}
|
|
49
|
-
});
|
|
45
|
+
});
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
// Load dependencies based on configuration
|
|
10
10
|
const loadDependencies = async () => {
|
|
11
|
+
await loadScript(RAILS_ASSET_URL('/active_scaffold/callback_registry.js'));
|
|
12
|
+
|
|
11
13
|
// Always load these
|
|
12
14
|
await loadScript(RAILS_ASSET_URL('/jquery.ba-throttle-debounce.js'));
|
|
13
15
|
await loadScript(RAILS_ASSET_URL('/jquery.visible.min.js'));
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
(function(global) {
|
|
2
|
+
global.RecordSelect.registerCallback('active_scaffold.add_existing', function(id) {
|
|
3
|
+
global.ActiveScaffold.record_select_onselect(
|
|
4
|
+
this.obj.attr('data-edit-associated-url'),
|
|
5
|
+
this.obj.attr('data-active-scaffold-id'),
|
|
6
|
+
id
|
|
7
|
+
);
|
|
8
|
+
});
|
|
9
|
+
})(window);
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
var id = jQuery(this).attr('id');
|
|
41
41
|
if (tinymce && tinymce.majorVersion >= 6) {
|
|
42
42
|
settings.selector = '#' + id;
|
|
43
|
-
var userSetup = settings.setup ?
|
|
43
|
+
var userSetup = settings.setup ? ActiveScaffold.resolveCallback('tinymceCallbacks', settings.setup, 'TinyMCE setup') : function () {};
|
|
44
44
|
settings.setup = function(editor) {
|
|
45
45
|
userSetup(editor); // run what the user already had
|
|
46
46
|
|
|
@@ -7,6 +7,7 @@ module ActiveScaffold
|
|
|
7
7
|
autoload :JqueryUiThemeGenerator, 'active_scaffold/assets/jquery_ui_theme_generator'
|
|
8
8
|
|
|
9
9
|
CORE_STYLESHEETS = ['active_scaffold/core'].freeze
|
|
10
|
+
CALLBACK_REGISTRY_JAVASCRIPT = 'active_scaffold/callback_registry'
|
|
10
11
|
CORE_JAVASCRIPTS = ['jquery.ba-throttle-debounce', 'jquery.visible.min', 'active_scaffold/jquery.editinplace', 'active_scaffold/active_scaffold'].freeze
|
|
11
12
|
|
|
12
13
|
class << self
|
|
@@ -34,17 +35,17 @@ module ActiveScaffold
|
|
|
34
35
|
def active_scaffold_javascripts(load = :all)
|
|
35
36
|
case load
|
|
36
37
|
when :all
|
|
37
|
-
jquery_ui_javascripts + CORE_JAVASCRIPTS + ActiveScaffold.javascripts + bridge_javascripts
|
|
38
|
+
[CALLBACK_REGISTRY_JAVASCRIPT] + jquery_ui_javascripts + CORE_JAVASCRIPTS + ActiveScaffold.javascripts + bridge_javascripts
|
|
38
39
|
when :deps
|
|
39
|
-
jquery_ui_javascripts + ActiveScaffold.javascripts + bridge_javascripts
|
|
40
|
+
[CALLBACK_REGISTRY_JAVASCRIPT] + jquery_ui_javascripts + ActiveScaffold.javascripts + bridge_javascripts
|
|
40
41
|
when :core
|
|
41
|
-
CORE_JAVASCRIPTS
|
|
42
|
+
[CALLBACK_REGISTRY_JAVASCRIPT] + CORE_JAVASCRIPTS
|
|
42
43
|
when :plugins
|
|
43
44
|
ActiveScaffold.javascripts
|
|
44
45
|
when :bridges
|
|
45
46
|
bridge_javascripts
|
|
46
47
|
when :jquery_ui
|
|
47
|
-
jquery_ui_javascripts
|
|
48
|
+
[CALLBACK_REGISTRY_JAVASCRIPT] + jquery_ui_javascripts
|
|
48
49
|
else
|
|
49
50
|
raise ArgumentError, "Unknown load mode: #{load.inspect}"
|
|
50
51
|
end
|
|
@@ -10,6 +10,12 @@ class ActiveScaffold::Bridges::RecordSelect < ActiveScaffold::DataStructures::Br
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def self.javascripts
|
|
13
|
-
'record_select'
|
|
13
|
+
assets = ['record_select']
|
|
14
|
+
assets << 'active_scaffold/record_select_bridge' if callback_registry?
|
|
15
|
+
assets
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.callback_registry?
|
|
19
|
+
defined?(::RecordSelect) && ::RecordSelect.respond_to?(:callback_registry?) && ::RecordSelect.callback_registry?
|
|
14
20
|
end
|
|
15
21
|
end
|
|
@@ -1,12 +1,26 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'active_support/html_safe_translation'
|
|
4
|
+
require 'active_scaffold/translation_html_safety'
|
|
5
|
+
|
|
3
6
|
class Object
|
|
4
7
|
def as_(key, options = {})
|
|
5
8
|
if key.present?
|
|
6
|
-
|
|
7
|
-
options =
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
options = options.merge(scope: [:active_scaffold, *options[:scope]])
|
|
10
|
+
options[:default] = key.is_a?(String) ? key : key.to_s.titleize unless options.key?(:default)
|
|
11
|
+
|
|
12
|
+
missing_html_translation = Object.new
|
|
13
|
+
html = ActiveSupport::HtmlSafeTranslation.translate(
|
|
14
|
+
"#{key}.html", **options, default: missing_html_translation
|
|
15
|
+
)
|
|
16
|
+
text =
|
|
17
|
+
if html.equal?(missing_html_translation)
|
|
18
|
+
translation = I18n.t(key.to_s, **options)
|
|
19
|
+
ActiveScaffold::TranslationHtmlSafety.check_once(key, options[:scope], translation) if translation.is_a?(String)
|
|
20
|
+
translation.html_safe # rubocop:disable Rails/OutputSafety
|
|
21
|
+
else
|
|
22
|
+
html
|
|
23
|
+
end
|
|
10
24
|
end
|
|
11
25
|
text || key
|
|
12
26
|
end
|
|
@@ -128,7 +128,22 @@ module ActiveScaffold
|
|
|
128
128
|
end
|
|
129
129
|
|
|
130
130
|
def record_select_params_for_add_existing(association, edit_associated_url_options, record)
|
|
131
|
-
|
|
131
|
+
if ActiveScaffold::Bridges::RecordSelect.callback_registry?
|
|
132
|
+
{
|
|
133
|
+
onselect: 'active_scaffold.add_existing',
|
|
134
|
+
html: {
|
|
135
|
+
data: {
|
|
136
|
+
edit_associated_url: url_for(edit_associated_url_options),
|
|
137
|
+
active_scaffold_id: active_scaffold_id
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
else
|
|
142
|
+
{
|
|
143
|
+
onselect: "ActiveScaffold.record_select_onselect(#{url_for(edit_associated_url_options).to_json}, " \
|
|
144
|
+
"#{active_scaffold_id.to_json}, id);"
|
|
145
|
+
}
|
|
146
|
+
end
|
|
132
147
|
end
|
|
133
148
|
end
|
|
134
149
|
end
|
|
@@ -19,16 +19,10 @@ module ActiveScaffold
|
|
|
19
19
|
end
|
|
20
20
|
response.status = 200 if (300...400).cover? response.status
|
|
21
21
|
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
|
|
26
|
-
# setTimeout - scope the execution in the windows parent for safari
|
|
27
|
-
# window.eval - legal eval for Opera
|
|
28
|
-
script = "<html><body><script type='text/javascript' charset='utf-8'>
|
|
29
|
-
var loc = document.location;
|
|
30
|
-
with(window.parent) { setTimeout(function() { window.eval('#{self.class.helpers.escape_javascript script}'); window.loc && loc.replace('about:blank'); }, 1) }
|
|
31
|
-
</script></body></html>"
|
|
22
|
+
# Inject the response into the parent document so it executes in the parent scope.
|
|
23
|
+
# Reuse its CSP nonce when one is available, then replace the frame location so the
|
|
24
|
+
# back button doesn't replay the action on targeted forms.
|
|
25
|
+
script = responds_to_parent_html(script)
|
|
32
26
|
|
|
33
27
|
# Clear out the previous render to prevent double render and then render
|
|
34
28
|
if respond_to?(:erase_results, true)
|
|
@@ -41,5 +35,24 @@ module ActiveScaffold
|
|
|
41
35
|
render html: script.html_safe, content_type: 'text/html' # rubocop:disable Rails/OutputSafety
|
|
42
36
|
end
|
|
43
37
|
alias respond_to_parent responds_to_parent
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def responds_to_parent_html(response_javascript)
|
|
42
|
+
escaped_response = self.class.helpers.escape_javascript(response_javascript)
|
|
43
|
+
"<html><body><script type='text/javascript' charset='utf-8'>
|
|
44
|
+
var loc = document.location;
|
|
45
|
+
var responseJavaScript = '#{escaped_response}';
|
|
46
|
+
var parentDocument = window.parent.document;
|
|
47
|
+
var script = parentDocument.createElement('script');
|
|
48
|
+
var nonceSource = parentDocument.querySelector('script[nonce]');
|
|
49
|
+
|
|
50
|
+
if (nonceSource) script.nonce = nonceSource.nonce;
|
|
51
|
+
script.text = responseJavaScript;
|
|
52
|
+
parentDocument.head.appendChild(script);
|
|
53
|
+
script.remove();
|
|
54
|
+
loc.replace('about:blank');
|
|
55
|
+
</script></body></html>"
|
|
56
|
+
end
|
|
44
57
|
end
|
|
45
58
|
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'concurrent/map'
|
|
4
|
+
|
|
5
|
+
module ActiveScaffold
|
|
6
|
+
module TranslationHtmlSafety
|
|
7
|
+
HTML_PATTERN = /<[^>]+>|&(?:[a-z][a-z0-9]+|#\d+|#x[0-9a-f]+);/i
|
|
8
|
+
|
|
9
|
+
@checked_translations = Concurrent::Map.new
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
def check_once(key, scope, translation)
|
|
13
|
+
translation_key_parts = [*scope, key].freeze
|
|
14
|
+
return unless @checked_translations.put_if_absent(translation_key_parts, true).nil?
|
|
15
|
+
return unless translation.match?(HTML_PATTERN)
|
|
16
|
+
|
|
17
|
+
translation_key = translation_key_parts.join('.')
|
|
18
|
+
ActiveScaffold.deprecator.warn(
|
|
19
|
+
"HTML in translation #{translation_key} is deprecated. Move it to #{translation_key}.html. " \
|
|
20
|
+
'Ordinary translations will no longer be marked HTML-safe in ActiveScaffold 4.5.'
|
|
21
|
+
)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -176,11 +176,7 @@
|
|
|
176
176
|
if (this._defaults.hasOwnProperty(attrName)) {
|
|
177
177
|
var attrValue = $input.data(attrName.toLowerCase());
|
|
178
178
|
if (attrValue !== undefined) {
|
|
179
|
-
|
|
180
|
-
inlineSettings[attrName] = eval(attrValue);
|
|
181
|
-
} catch (err) {
|
|
182
|
-
inlineSettings[attrName] = attrValue;
|
|
183
|
-
}
|
|
179
|
+
inlineSettings[attrName] = ActiveScaffold.resolveDatepickerSetting(attrName, attrValue);
|
|
184
180
|
}
|
|
185
181
|
}
|
|
186
182
|
}
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: active_scaffold
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 4.4.
|
|
4
|
+
version: 4.4.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Many, see README
|
|
@@ -110,10 +110,12 @@ files:
|
|
|
110
110
|
- app/assets/javascripts/active_scaffold.js.erb
|
|
111
111
|
- app/assets/javascripts/active_scaffold/active_scaffold.js
|
|
112
112
|
- app/assets/javascripts/active_scaffold/active_scaffold_chosen.js
|
|
113
|
+
- app/assets/javascripts/active_scaffold/callback_registry.js
|
|
113
114
|
- app/assets/javascripts/active_scaffold/date_picker_bridge.js
|
|
114
115
|
- app/assets/javascripts/active_scaffold/draggable_lists.js
|
|
115
116
|
- app/assets/javascripts/active_scaffold/jquery.editinplace.js
|
|
116
117
|
- app/assets/javascripts/active_scaffold/load.js
|
|
118
|
+
- app/assets/javascripts/active_scaffold/record_select_bridge.js
|
|
117
119
|
- app/assets/javascripts/active_scaffold/tiny_mce_bridge.js
|
|
118
120
|
- app/assets/stylesheets/active_scaffold-ie.scss
|
|
119
121
|
- app/assets/stylesheets/active_scaffold.scss.erb
|
|
@@ -360,6 +362,7 @@ files:
|
|
|
360
362
|
- lib/active_scaffold/responds_to_parent.rb
|
|
361
363
|
- lib/active_scaffold/tableless.rb
|
|
362
364
|
- lib/active_scaffold/testing/assert_embedded_load.rb
|
|
365
|
+
- lib/active_scaffold/translation_html_safety.rb
|
|
363
366
|
- lib/active_scaffold/version.rb
|
|
364
367
|
- lib/generators/active_scaffold/controller/USAGE
|
|
365
368
|
- lib/generators/active_scaffold/controller/controller_generator.rb
|