scrivito_sdk 0.70.2 → 0.71.0.rc1
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/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
|
#
|