scrivito_sdk 0.70.2 → 0.71.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/scrivito/binary_redirect_controller.rb +12 -9
- data/app/controllers/scrivito/blobs_controller.rb +1 -2
- data/app/controllers/scrivito/objs_controller.rb +16 -0
- data/app/helpers/scrivito_helper.rb +6 -2
- data/app/views/cms/index.html.erb +1 -1
- data/app/views/scrivito/blobs/activate_upload.json.jbuilder +1 -0
- data/app/views/scrivito/objs/obj.json.jbuilder +1 -1
- data/app/views/scrivito/objs/transfer_modifications.json.jbuilder +6 -0
- data/config/ca-bundle.crt +128 -81
- data/config/precedence_routes.rb +1 -0
- data/lib/assets/images/scrivito/source_invalid.png +0 -0
- data/lib/assets/images/scrivito/source_too_large.png +0 -0
- data/lib/assets/images/scrivito/source_type_invalid.png +0 -0
- data/lib/assets/javascripts/scrivito_ui.js +1043 -645
- data/lib/assets/stylesheets/scrivito.css +1 -1
- data/lib/assets/stylesheets/scrivito_ui.css +1 -1
- data/lib/generators/scrivito/page/templates/thumbnail.html.erb +1 -1
- data/lib/scrivito/attribute_content.rb +54 -17
- data/lib/scrivito/attribute_deserializer.rb +1 -1
- data/lib/scrivito/attribute_serializer.rb +6 -4
- data/lib/scrivito/backend/obj_query.rb +11 -2
- data/lib/scrivito/base_widget_tag.rb +77 -0
- data/lib/scrivito/basic_obj.rb +67 -67
- data/lib/scrivito/basic_widget.rb +11 -6
- data/lib/scrivito/binary.rb +50 -5
- data/lib/scrivito/binary_param_verifier.rb +4 -5
- data/lib/scrivito/client_attribute_serializer.rb +3 -3
- data/lib/scrivito/cms_backend.rb +12 -0
- data/lib/scrivito/cms_field_tag.rb +8 -6
- data/lib/scrivito/cms_rest_api.rb +28 -8
- data/lib/scrivito/cms_rest_api/rate_limit.rb +1 -0
- data/lib/scrivito/cms_routing.rb +7 -1
- data/lib/scrivito/configuration.rb +1 -1
- data/lib/scrivito/controller_actions.rb +38 -35
- data/lib/scrivito/date_attribute.rb +3 -7
- data/lib/scrivito/editing_context.rb +3 -3
- data/lib/scrivito/editing_context_middleware.rb +3 -3
- data/lib/scrivito/errored_widget_tag.rb +34 -0
- data/lib/scrivito/errors.rb +6 -0
- data/lib/scrivito/future_binary.rb +23 -0
- data/lib/scrivito/link_parser.rb +12 -1
- data/lib/scrivito/membership_collection.rb +8 -8
- data/lib/scrivito/obj_collection.rb +2 -2
- data/lib/scrivito/obj_create_params_parser.rb +2 -2
- data/lib/scrivito/obj_params_parser.rb +5 -1
- data/lib/scrivito/obj_search_builder.rb +2 -12
- data/lib/scrivito/obj_search_enumerator.rb +48 -43
- data/lib/scrivito/page_config.rb +2 -1
- data/lib/scrivito/type_computer.rb +6 -6
- data/lib/scrivito/user.rb +9 -10
- data/lib/scrivito/user_definition.rb +2 -2
- data/lib/scrivito/warning.rb +17 -0
- data/lib/scrivito/widget_garbage_collection.rb +1 -1
- data/lib/scrivito/widget_tag.rb +28 -53
- data/lib/scrivito/workspace.rb +32 -23
- metadata +10 -6
- data/app/views/scrivito/objs/copy_widget.html.erb +0 -1
- data/app/views/scrivito/objs/create_widget.html.erb +0 -1
@@ -228,11 +228,14 @@ class BasicWidget
|
|
228
228
|
previous_obj_content =
|
229
229
|
CmsRestApi.get("revisions/#{workspace.base_revision_id}/objs/#{obj.id}")
|
230
230
|
previous_widget_content = previous_obj_content["_widget_pool"]["#{id}"]
|
231
|
+
previous_widget_content = self.class.reset_blank_attributes(previous_widget_content)
|
232
|
+
|
231
233
|
previous_widget_content.delete_if do |attribute_name, _|
|
232
234
|
type_of_attribute(attribute_name) == 'widgetlist'
|
233
235
|
end
|
234
236
|
CmsRestApi.put("workspaces/#{workspace.id}/objs/#{obj.id}",
|
235
237
|
{ obj: {_widget_pool: {id => previous_widget_content}} })
|
238
|
+
reload
|
236
239
|
else
|
237
240
|
raise ScrivitoError, "cannot revert changes, since widget is #{modification}."
|
238
241
|
end
|
@@ -243,17 +246,17 @@ class BasicWidget
|
|
243
246
|
obj_in_revision && obj_in_revision.widget_from_pool(id)
|
244
247
|
end
|
245
248
|
|
246
|
-
def new?(revision=
|
249
|
+
def new?(revision=workspace.base_revision)
|
247
250
|
return false unless revision
|
248
251
|
obj.new?(revision) || cms_data_for_revision(revision).nil?
|
249
252
|
end
|
250
253
|
|
251
|
-
def deleted?(revision=
|
254
|
+
def deleted?(revision=workspace.base_revision)
|
252
255
|
return false unless revision
|
253
256
|
obj.deleted?(revision)
|
254
257
|
end
|
255
258
|
|
256
|
-
def modification(revision=
|
259
|
+
def modification(revision=workspace.base_revision)
|
257
260
|
return Modification::UNMODIFIED unless revision
|
258
261
|
|
259
262
|
if deleted?(revision)
|
@@ -329,14 +332,16 @@ class BasicWidget
|
|
329
332
|
|
330
333
|
def forget_unsaved_attributes
|
331
334
|
@attributes_to_be_saved = nil
|
332
|
-
|
335
|
+
reload_data
|
333
336
|
end
|
334
337
|
|
335
338
|
def reload
|
336
339
|
obj.reload
|
340
|
+
reload_data
|
341
|
+
end
|
337
342
|
|
338
|
-
|
339
|
-
update_data(
|
343
|
+
def reload_data
|
344
|
+
update_data -> { obj.widget_data_from_pool(id) }
|
340
345
|
end
|
341
346
|
|
342
347
|
# @api public
|
data/lib/scrivito/binary.rb
CHANGED
@@ -6,13 +6,56 @@ module Scrivito
|
|
6
6
|
class Binary
|
7
7
|
attr_reader :id, :transformation_definition
|
8
8
|
|
9
|
-
def initialize(id, is_public, transformation_definition
|
9
|
+
def initialize(id, is_public, transformation_definition: nil, original: nil)
|
10
10
|
@id = id
|
11
11
|
@is_public = !!is_public
|
12
12
|
@transformation_definition = transformation_definition
|
13
13
|
@original = original
|
14
14
|
end
|
15
15
|
|
16
|
+
# @api public
|
17
|
+
# Uploads a local file to the CMS.
|
18
|
+
# @example Upload a single file
|
19
|
+
# @obj.update(blob: Scrivito::Binary.upload("/Desktop/kitten.jpg"))
|
20
|
+
#
|
21
|
+
# # equivalent to
|
22
|
+
# @obj.update(blob: File.new("/Desktop/kitten.jpg"))
|
23
|
+
# @example Upload a file with a different filename and content type
|
24
|
+
# @obj.update(blob: Scrivito::Binary.upload(
|
25
|
+
# "/Desktop/rick_astley", content_type: "Video/MP4", filename: "ufo_landing.m4v"))
|
26
|
+
# @param file_or_path [File, String] the file to be uploaded or
|
27
|
+
# the path of the file to be uploaded.
|
28
|
+
# @param filename [String, Nil] the desired filename. If +nil+,
|
29
|
+
# the name of the given file is used.
|
30
|
+
# @param content_type [String, Nil] the desired content type. If +nil+,
|
31
|
+
# the content type is calculated based on the extension of the filename.
|
32
|
+
# @return [Scrivito::FutureBinary] the returned object should be inserted into the binary field
|
33
|
+
# of an {Scrivito::BasicObj Obj} or {Scrivito::BasicWidget Widget}.
|
34
|
+
def self.upload(file_or_path, filename: nil, content_type: nil)
|
35
|
+
if file_or_path.is_a?(File)
|
36
|
+
file = file_or_path
|
37
|
+
else
|
38
|
+
file = File.new(file_or_path)
|
39
|
+
end
|
40
|
+
new_filename = filename || File.basename(file_or_path)
|
41
|
+
FutureBinary.new(content_type: content_type, filename: new_filename, file_to_be_uploaded: file)
|
42
|
+
end
|
43
|
+
|
44
|
+
# @api public
|
45
|
+
# Create a copy of this Binary with a different filename and/or content type.
|
46
|
+
# @example Change filename and content_type of an existing binary. Binary content remains the same.
|
47
|
+
# @obj.update(blob: @obj.blob.copy(filename: "cute_kitten.jpg", content_type: "video/mp4"))
|
48
|
+
# @param filename [String, Nil] the desired filename. If +nil+,
|
49
|
+
# the filename of the original binary is used.
|
50
|
+
# @param content_type [String, Nil] the desired content type. If +nil+,
|
51
|
+
# the content type is calculated based on the extension of the filename.
|
52
|
+
# @return [Scrivito::FutureBinary] the returned object should be inserted into the binary field
|
53
|
+
# of an Obj or Widget.
|
54
|
+
def copy(content_type: nil, filename: nil)
|
55
|
+
new_filename = filename || self.filename
|
56
|
+
FutureBinary.new(content_type: content_type, filename: new_filename, id_to_be_copied: id)
|
57
|
+
end
|
58
|
+
|
16
59
|
# @api public
|
17
60
|
# Some Scrivito data is considered private, i.e. it is not currently intended
|
18
61
|
# for the general public, for example content in a workspace that has not
|
@@ -131,8 +174,8 @@ class Binary
|
|
131
174
|
#
|
132
175
|
# If the given width and height are adjusted, the aspect ratio is preserved.
|
133
176
|
#
|
134
|
-
# @option definition [Symbol,String] :fit Controls how the
|
135
|
-
# given width and
|
177
|
+
# @option definition [Symbol,String] :fit Controls how the transformed image is fitted to the
|
178
|
+
# given width and height. Valid values are +:clip+ and +:crop+. The default value is +:clip+.
|
136
179
|
#
|
137
180
|
# If set to +:clip+, the image is resized so as to fit within the width and height boundaries
|
138
181
|
# without cropping or distorting the image. The resulting image is assured to match one of the
|
@@ -163,7 +206,9 @@ class Binary
|
|
163
206
|
# @see ScrivitoHelper#scrivito_image_tag
|
164
207
|
#
|
165
208
|
def transform(definition)
|
166
|
-
self.class.new(id, public?,
|
209
|
+
self.class.new(id, public?,
|
210
|
+
transformation_definition: (transformation_definition || {}).merge(definition),
|
211
|
+
original: original)
|
167
212
|
end
|
168
213
|
|
169
214
|
#
|
@@ -232,7 +277,7 @@ class Binary
|
|
232
277
|
deserialized_meta_data = {}
|
233
278
|
raw_meta_data.each_pair do |key, (type, value)|
|
234
279
|
deserialized_meta_data[key] = case type
|
235
|
-
when 'date' then DateAttribute.
|
280
|
+
when 'date' then DateAttribute.parse(value)
|
236
281
|
when 'number' then value.to_i
|
237
282
|
else value
|
238
283
|
end
|
@@ -6,18 +6,17 @@ module BinaryParamVerifier
|
|
6
6
|
class << self
|
7
7
|
def verify(params)
|
8
8
|
params = message_verifier.verify(params)
|
9
|
-
expires = DateAttribute.
|
9
|
+
expires = DateAttribute.parse(params['expires'])
|
10
10
|
raise InvalidSignature if expires && expires < Time.zone.now
|
11
|
-
Binary.new(params['binary_id'], expires.nil?,
|
11
|
+
Binary.new(params['binary_id'], expires.nil?,
|
12
|
+
transformation_definition: params['transformation_definition'])
|
12
13
|
rescue ActiveSupport::MessageVerifier::InvalidSignature
|
13
14
|
raise InvalidSignature
|
14
15
|
end
|
15
16
|
|
16
17
|
def generate(binary)
|
17
18
|
params = {binary_id: binary.id, transformation_definition: binary.transformation_definition}
|
18
|
-
if binary.private?
|
19
|
-
params[:expires] = DateAttribute.serialize_for_backend(Time.zone.now + 1.hour)
|
20
|
-
end
|
19
|
+
params[:expires] = DateAttribute.serialize(Time.zone.now + 1.hour) if binary.private?
|
21
20
|
message_verifier.generate(params)
|
22
21
|
end
|
23
22
|
|
@@ -19,7 +19,7 @@ module ClientAttributeSerializer
|
|
19
19
|
def self.serialize_system_attrs(obj)
|
20
20
|
{
|
21
21
|
'_id' => obj.id,
|
22
|
-
'_obj_class' => obj.
|
22
|
+
'_obj_class' => obj.obj_class,
|
23
23
|
'_path' => obj.path,
|
24
24
|
'_permalink' => obj.permalink,
|
25
25
|
}
|
@@ -71,7 +71,7 @@ module ClientAttributeSerializer
|
|
71
71
|
end
|
72
72
|
|
73
73
|
def self.serialize_date_value(value)
|
74
|
-
|
74
|
+
value.utc.iso8601
|
75
75
|
end
|
76
76
|
|
77
77
|
def self.serialize_enum_value(value)
|
@@ -126,7 +126,7 @@ module ClientAttributeSerializer
|
|
126
126
|
|
127
127
|
def self.serialize_widget(widget)
|
128
128
|
{
|
129
|
-
'_obj_class' => widget.
|
129
|
+
'_obj_class' => widget.obj_class
|
130
130
|
}.merge(serialize_custom_attrs(widget))
|
131
131
|
end
|
132
132
|
end
|
data/lib/scrivito/cms_backend.rb
CHANGED
@@ -250,8 +250,20 @@ module Scrivito
|
|
250
250
|
end
|
251
251
|
end
|
252
252
|
|
253
|
+
def create_obj(workspace_id, attributes)
|
254
|
+
write_obj(:post, "/workspaces/#{workspace_id}/objs", attributes)
|
255
|
+
end
|
256
|
+
|
257
|
+
def update_obj(workspace_id, obj_id, attributes)
|
258
|
+
write_obj(:put, "/workspaces/#{workspace_id}/objs/#{obj_id}", attributes)
|
259
|
+
end
|
260
|
+
|
253
261
|
private
|
254
262
|
|
263
|
+
def write_obj(verb, path, attributes)
|
264
|
+
Backend::ObjDataFromRest.new(CmsRestApi.task_unaware_request(verb, path, attributes))
|
265
|
+
end
|
266
|
+
|
255
267
|
def update_workspace_cache(id, cached_data_tag, changed_workspace, options)
|
256
268
|
if changed_workspace
|
257
269
|
workspace_data = changed_workspace
|
@@ -21,7 +21,8 @@ class CmsFieldTag < Struct.new(:view, :tag_name, :obj_or_widget, :editing_option
|
|
21
21
|
raise ArgumentError, 'No block allowed for widgetlist fields' if block_given?
|
22
22
|
modifications = modification_info || []
|
23
23
|
rendered_widgets = default_content.each_with_index.map do |widget, index|
|
24
|
-
WidgetTag.new(view, widget, modifications[index],
|
24
|
+
WidgetTag.new(view, widget, placement_modification: modifications[index],
|
25
|
+
render_context: widget_render_context, inner_tag: inner_tag).render
|
25
26
|
end
|
26
27
|
view.safe_join(rendered_widgets)
|
27
28
|
else
|
@@ -33,10 +34,11 @@ class CmsFieldTag < Struct.new(:view, :tag_name, :obj_or_widget, :editing_option
|
|
33
34
|
return {} unless authenticated_editor?
|
34
35
|
|
35
36
|
options = {
|
36
|
-
'private-field-workspace-id' => current_workspace_id,
|
37
37
|
'field-name' => field_name,
|
38
|
-
'field-obj-class' => obj_or_widget.
|
38
|
+
'field-obj-class' => obj_or_widget.obj_class,
|
39
39
|
'field-type' => field_type,
|
40
|
+
'private-editor' => editing_options[:editor],
|
41
|
+
'private-field-workspace-id' => current_workspace_id,
|
40
42
|
}
|
41
43
|
|
42
44
|
modification = comparison.modification_for_attribute(obj_or_widget, field_name)
|
@@ -61,7 +63,7 @@ class CmsFieldTag < Struct.new(:view, :tag_name, :obj_or_widget, :editing_option
|
|
61
63
|
if field_type == 'widgetlist'
|
62
64
|
options["private-field-widget-inner-tag"] = inner_tag
|
63
65
|
options['private-field-widget-allowed-classes'] = build_valid_widget_classes.to_json
|
64
|
-
options['private-field-widget-template'] =
|
66
|
+
options['private-field-widget-template'] = widget_render_context
|
65
67
|
end
|
66
68
|
|
67
69
|
options
|
@@ -84,8 +86,8 @@ class CmsFieldTag < Struct.new(:view, :tag_name, :obj_or_widget, :editing_option
|
|
84
86
|
end
|
85
87
|
end
|
86
88
|
|
87
|
-
def
|
88
|
-
editing_options[:
|
89
|
+
def widget_render_context
|
90
|
+
editing_options[:widget_render_context] || WidgetTag::DEFAULT_RENDER_CONTEXT
|
89
91
|
end
|
90
92
|
|
91
93
|
def field_name
|
@@ -53,7 +53,9 @@ module Scrivito
|
|
53
53
|
|
54
54
|
def self.task_unaware_request(method, resource_path, payload = nil)
|
55
55
|
raise "Unexpected method #{method}" unless [:delete, :get, :post, :put].include?(method)
|
56
|
-
|
56
|
+
log_api_request(method, resource_path, payload) do
|
57
|
+
response_for_request_cms_api(method, resource_path, payload, build_timer)
|
58
|
+
end
|
57
59
|
end
|
58
60
|
|
59
61
|
def self.count_requests(path)
|
@@ -64,10 +66,23 @@ module Scrivito
|
|
64
66
|
@number_of_requests
|
65
67
|
end
|
66
68
|
|
67
|
-
def self.
|
68
|
-
|
69
|
-
|
70
|
-
|
69
|
+
def self.upload_future_binary(binary, obj_id)
|
70
|
+
if binary.id_to_be_copied
|
71
|
+
{
|
72
|
+
content_type: binary.content_type,
|
73
|
+
filename: binary.filename,
|
74
|
+
id: binary.id_to_be_copied,
|
75
|
+
}
|
76
|
+
else
|
77
|
+
upload_permission = get('blobs/upload_permission')
|
78
|
+
upload = perform_file_upload(
|
79
|
+
file: binary.file_to_be_uploaded,
|
80
|
+
filename: binary.filename,
|
81
|
+
content_type: binary.content_type,
|
82
|
+
upload_permission: upload_permission,
|
83
|
+
)
|
84
|
+
activate_upload(upload: upload, obj_id: obj_id)
|
85
|
+
end
|
71
86
|
end
|
72
87
|
|
73
88
|
def self.activate_upload(params)
|
@@ -84,6 +99,8 @@ module Scrivito
|
|
84
99
|
private
|
85
100
|
|
86
101
|
def request_cms_api(action, resource_path, payload, options)
|
102
|
+
assert_valid_resource_path(resource_path)
|
103
|
+
|
87
104
|
log_api_request(action, resource_path, payload) do
|
88
105
|
@number_of_requests += 1 if resource_path == @count_requests
|
89
106
|
|
@@ -171,16 +188,15 @@ module Scrivito
|
|
171
188
|
end
|
172
189
|
end
|
173
190
|
|
174
|
-
def perform_file_upload(file
|
191
|
+
def perform_file_upload(file:, filename:, content_type:, upload_permission:)
|
175
192
|
uri = URI.parse(upload_permission['url'])
|
176
193
|
File.open(file) do |open_file|
|
177
|
-
content_type = MIME::Types.type_for(file.path).first.content_type
|
178
194
|
upload_io = UploadIO.new(open_file, content_type, File.basename(file))
|
179
195
|
params = upload_permission['fields'].merge('file' => upload_io)
|
180
196
|
request = Net::HTTP::Post::Multipart.new(uri.path, params)
|
181
197
|
response = ConnectionManager.request(uri, request)
|
182
198
|
if response.code.starts_with?('2')
|
183
|
-
upload_permission['blob'].merge('filename' =>
|
199
|
+
upload_permission['blob'].merge('filename' => filename, 'content_type' => content_type)
|
184
200
|
else
|
185
201
|
raise ScrivitoError, "File upload failed with code #{response.code}"
|
186
202
|
end
|
@@ -231,6 +247,10 @@ module Scrivito
|
|
231
247
|
def method_to_net_http_class(method)
|
232
248
|
METHOD_TO_NET_HTTP_CLASS.fetch(method)
|
233
249
|
end
|
250
|
+
|
251
|
+
def assert_valid_resource_path(resource_path)
|
252
|
+
URI(resource_path)
|
253
|
+
end
|
234
254
|
end
|
235
255
|
end
|
236
256
|
end
|
@@ -15,6 +15,7 @@ module RateLimit
|
|
15
15
|
time_to_sleep = calculate_time_to_sleep(response['Retry-After'].to_f, retry_count)
|
16
16
|
|
17
17
|
if request_timer.cover?(Time.now + time_to_sleep.seconds)
|
18
|
+
Warning.warn("Rate limit exceeded. Will retry after #{time_to_sleep} seconds.")
|
18
19
|
sleep time_to_sleep
|
19
20
|
internal_retry(request_proc, request_timer, retry_count + 1)
|
20
21
|
else
|
data/lib/scrivito/cms_routing.rb
CHANGED
@@ -98,7 +98,13 @@ class CmsRouting < Struct.new(:request, :main_app, :scrivito_engine, :image_opti
|
|
98
98
|
"cms_id_#{path_or_url}"
|
99
99
|
end
|
100
100
|
|
101
|
-
|
101
|
+
options[:id] = obj.id
|
102
|
+
|
103
|
+
# Options must have the key slug.
|
104
|
+
# Otherwise Rails will use the slug from current request params.
|
105
|
+
options[:slug] ||= nil
|
106
|
+
|
107
|
+
main_app.public_send(method_name, options)
|
102
108
|
end
|
103
109
|
|
104
110
|
def homepage?(obj)
|
@@ -273,7 +273,7 @@ module Scrivito
|
|
273
273
|
'_default' => proc do |obj, user|
|
274
274
|
{
|
275
275
|
id: obj.id,
|
276
|
-
|
276
|
+
obj_class: obj.obj_class,
|
277
277
|
description_for_editor: obj.description_for_editor,
|
278
278
|
modification: obj.modification,
|
279
279
|
has_conflict: obj.has_conflict?,
|
@@ -40,64 +40,67 @@ module ControllerActions
|
|
40
40
|
|
41
41
|
def show_widget
|
42
42
|
widget = load_widget
|
43
|
-
widget_tag = Scrivito::WidgetTag.new(view_context, widget,
|
44
|
-
|
43
|
+
widget_tag = Scrivito::WidgetTag.new(view_context, widget,
|
44
|
+
render_context: params[:template_name], inner_tag: params[:inner_tag])
|
45
45
|
render text: widget_tag.render, layout: false
|
46
46
|
end
|
47
47
|
|
48
48
|
def widget_details
|
49
49
|
assert_dialog_layout
|
50
50
|
widget = load_widget
|
51
|
-
|
52
|
-
|
53
|
-
render template_path, layout: 'scrivito_dialog', locals: {widget: widget}
|
51
|
+
@scrivito_widget_render_context = :details
|
52
|
+
render widget.details_view_path, layout: 'scrivito_dialog', locals: {widget: widget}
|
54
53
|
end
|
55
54
|
|
56
55
|
def page_details
|
57
56
|
assert_dialog_layout
|
58
|
-
@
|
57
|
+
@scrivito_widget_render_context = :details
|
59
58
|
render @obj.details_view_path, layout: 'scrivito_dialog'
|
60
59
|
rescue ActionView::MissingTemplate
|
61
60
|
render 'scrivito/page_details', layout: 'scrivito_dialog'
|
62
61
|
end
|
63
62
|
|
64
|
-
#
|
63
|
+
# @!method on_scrivito_widget_error(widget, error)
|
64
|
+
# How to handle widget errors in +production+.
|
65
65
|
#
|
66
|
-
#
|
67
|
-
#
|
68
|
-
#
|
69
|
-
#
|
66
|
+
# When an exception is raised from within a widget, the entire page is not available.
|
67
|
+
# Often, this is the case in production if the developer has forgotten to handle specific
|
68
|
+
# content scenarios such as empty date attributes, missing titles, unmanaged enum values, etc.
|
69
|
+
# but also for simple coding mistakes during development.
|
70
70
|
#
|
71
|
-
#
|
71
|
+
# By default, Scrivito handles exceptions raised while rendering a widget by adding a
|
72
|
+
# placeholder text to the HTML indicating that the widget couldn't be rendered. In addition,
|
73
|
+
# the exception is logged alongside with its backtrace.
|
72
74
|
#
|
73
|
-
#
|
74
|
-
# * catch a widget error and replace it with an HTML placeholder.
|
75
|
-
# * report a widget error to an external service like Honeybadger or Airbrake.
|
75
|
+
# Override this method to adapt this behaviour.
|
76
76
|
#
|
77
|
-
#
|
78
|
-
#
|
77
|
+
# The overridden method allows to:
|
78
|
+
# * catch a widget error and replace it with an HTML placeholder.
|
79
|
+
# * report a widget error to an external service like Honeybadger or Airbrake.
|
79
80
|
#
|
80
|
-
#
|
81
|
+
# This method is _not_ called if Rails is in the +development+ or +test+ environment.
|
82
|
+
# In those environments, all widget errors are just raised.
|
81
83
|
#
|
82
|
-
#
|
83
|
-
#
|
84
|
-
#
|
84
|
+
# @param widget [Scrivito::BasicWidget] the flawed widget
|
85
|
+
# @param error [StandardError] the error that occurred
|
86
|
+
# @raise [StandardError] if this method is not overridden, the +error+ passed to it
|
87
|
+
# is reraised.
|
85
88
|
#
|
86
|
-
#
|
87
|
-
#
|
88
|
-
#
|
89
|
-
#
|
90
|
-
#
|
89
|
+
# @example Notify external service about widget error and render an HTML placeholder:
|
90
|
+
# def on_scrivito_widget_error(widget, error)
|
91
|
+
# # Report error to external service (e.g. Honeybadger or Airbrake):
|
92
|
+
# Honeybadger.notify(error)
|
93
|
+
# notify_airbrake(error)
|
91
94
|
#
|
92
|
-
#
|
93
|
-
#
|
94
|
-
#
|
95
|
-
#
|
96
|
-
#
|
97
|
-
#
|
98
|
-
|
99
|
-
|
100
|
-
|
95
|
+
# # Replace corrupted widget output with a placeholder:
|
96
|
+
# message = "Rendering #{widget.description_for_editor} failed with #{error.message}"
|
97
|
+
# return "<!--#{message}-->".html_safe if Rails.env.production?
|
98
|
+
# "<p>#{message}</p>".html_safe
|
99
|
+
# end
|
100
|
+
#
|
101
|
+
# @api public
|
102
|
+
|
103
|
+
# This empty line is necessary for yard to function correctly
|
101
104
|
|
102
105
|
module ClassMethods
|
103
106
|
#
|