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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/scrivito/binary_redirect_controller.rb +12 -9
  3. data/app/controllers/scrivito/blobs_controller.rb +1 -2
  4. data/app/controllers/scrivito/objs_controller.rb +16 -0
  5. data/app/helpers/scrivito_helper.rb +6 -2
  6. data/app/views/cms/index.html.erb +1 -1
  7. data/app/views/scrivito/blobs/activate_upload.json.jbuilder +1 -0
  8. data/app/views/scrivito/objs/obj.json.jbuilder +1 -1
  9. data/app/views/scrivito/objs/transfer_modifications.json.jbuilder +6 -0
  10. data/config/ca-bundle.crt +128 -81
  11. data/config/precedence_routes.rb +1 -0
  12. data/lib/assets/images/scrivito/source_invalid.png +0 -0
  13. data/lib/assets/images/scrivito/source_too_large.png +0 -0
  14. data/lib/assets/images/scrivito/source_type_invalid.png +0 -0
  15. data/lib/assets/javascripts/scrivito_ui.js +1043 -645
  16. data/lib/assets/stylesheets/scrivito.css +1 -1
  17. data/lib/assets/stylesheets/scrivito_ui.css +1 -1
  18. data/lib/generators/scrivito/page/templates/thumbnail.html.erb +1 -1
  19. data/lib/scrivito/attribute_content.rb +54 -17
  20. data/lib/scrivito/attribute_deserializer.rb +1 -1
  21. data/lib/scrivito/attribute_serializer.rb +6 -4
  22. data/lib/scrivito/backend/obj_query.rb +11 -2
  23. data/lib/scrivito/base_widget_tag.rb +77 -0
  24. data/lib/scrivito/basic_obj.rb +67 -67
  25. data/lib/scrivito/basic_widget.rb +11 -6
  26. data/lib/scrivito/binary.rb +50 -5
  27. data/lib/scrivito/binary_param_verifier.rb +4 -5
  28. data/lib/scrivito/client_attribute_serializer.rb +3 -3
  29. data/lib/scrivito/cms_backend.rb +12 -0
  30. data/lib/scrivito/cms_field_tag.rb +8 -6
  31. data/lib/scrivito/cms_rest_api.rb +28 -8
  32. data/lib/scrivito/cms_rest_api/rate_limit.rb +1 -0
  33. data/lib/scrivito/cms_routing.rb +7 -1
  34. data/lib/scrivito/configuration.rb +1 -1
  35. data/lib/scrivito/controller_actions.rb +38 -35
  36. data/lib/scrivito/date_attribute.rb +3 -7
  37. data/lib/scrivito/editing_context.rb +3 -3
  38. data/lib/scrivito/editing_context_middleware.rb +3 -3
  39. data/lib/scrivito/errored_widget_tag.rb +34 -0
  40. data/lib/scrivito/errors.rb +6 -0
  41. data/lib/scrivito/future_binary.rb +23 -0
  42. data/lib/scrivito/link_parser.rb +12 -1
  43. data/lib/scrivito/membership_collection.rb +8 -8
  44. data/lib/scrivito/obj_collection.rb +2 -2
  45. data/lib/scrivito/obj_create_params_parser.rb +2 -2
  46. data/lib/scrivito/obj_params_parser.rb +5 -1
  47. data/lib/scrivito/obj_search_builder.rb +2 -12
  48. data/lib/scrivito/obj_search_enumerator.rb +48 -43
  49. data/lib/scrivito/page_config.rb +2 -1
  50. data/lib/scrivito/type_computer.rb +6 -6
  51. data/lib/scrivito/user.rb +9 -10
  52. data/lib/scrivito/user_definition.rb +2 -2
  53. data/lib/scrivito/warning.rb +17 -0
  54. data/lib/scrivito/widget_garbage_collection.rb +1 -1
  55. data/lib/scrivito/widget_tag.rb +28 -53
  56. data/lib/scrivito/workspace.rb +32 -23
  57. metadata +10 -6
  58. data/app/views/scrivito/objs/copy_widget.html.erb +0 -1
  59. 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=Workspace.current.base_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=Workspace.current.base_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=Workspace.current.base_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
- reload
335
+ reload_data
333
336
  end
334
337
 
335
338
  def reload
336
339
  obj.reload
340
+ reload_data
341
+ end
337
342
 
338
- update_proc = -> { obj.widget_data_from_pool(id) }
339
- update_data(update_proc)
343
+ def reload_data
344
+ update_data -> { obj.widget_data_from_pool(id) }
340
345
  end
341
346
 
342
347
  # @api public
@@ -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 = nil, original = nil)
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 tranformed image is fitted to the
135
- # given width and heigth. Valid values are +:clip+ and +:crop+. The default value is +:clip+.
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?, (transformation_definition || {}).merge(definition), original)
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.deserialize_from_backend(value)
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.deserialize_from_backend(params['expires'])
9
+ expires = DateAttribute.parse(params['expires'])
10
10
  raise InvalidSignature if expires && expires < Time.zone.now
11
- Binary.new(params['binary_id'], expires.nil?, params['transformation_definition'])
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.obj_class_name,
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
- DateAttribute.serialize_for_client(value)
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.obj_class_name
129
+ '_obj_class' => widget.obj_class
130
130
  }.merge(serialize_custom_attrs(widget))
131
131
  end
132
132
  end
@@ -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], widget_template_name, inner_tag).render
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.obj_class_name,
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'] = widget_template_name
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 widget_template_name
88
- editing_options[:widget_template_name]
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
- response_for_request_cms_api(method, resource_path, payload, build_timer)
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.upload_file(file, obj_id)
68
- upload_permission = get('blobs/upload_permission')
69
- upload = perform_file_upload(file, upload_permission)
70
- activate_upload(upload: upload, obj_id: obj_id)
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, upload_permission)
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' => File.basename(file.path))
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
@@ -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
- main_app.public_send(method_name, options.merge(id: obj.id))
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
- obj_class_name: obj.obj_class_name,
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, nil,
44
- params[:template_name], params[:inner_tag])
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
- template_path = "#{widget.obj_class_name.underscore}/details"
52
- @scrivito_default_widget_template = :details
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
- @scrivito_default_widget_template = :details
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
- # How to handle widget errors in +production+.
63
+ # @!method on_scrivito_widget_error(widget, error)
64
+ # How to handle widget errors in +production+.
65
65
  #
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.
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
- # Override this method to prevent the entire page from being unavailable due to widget errors.
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
- # The overridden method allows to:
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
- # This method is _not_ called if Rails is in the +development+ or +test+ environment.
78
- # In those environments, all widget errors are just raised.
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
- # By default, this method just reraises the given error.
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
- # @param widget [Scrivito::BasicWidget] the flawed widget
83
- # @param error [StandardError] the error that occurred
84
- # @raise [StandardError] if this method is not overridden, the +error+ passed to it is reraised.
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
- # @example Notify external service about widget error and render an HTML placeholder:
87
- # def on_scrivito_widget_error(widget, error)
88
- # # Report error to external service (e.g. Honeybadger or Airbrake):
89
- # Honeybadger.notify(error)
90
- # notify_airbrake(error)
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
- # # Replace corrupted widget output with a placeholder:
93
- # message = "Rendering #{widget.description_for_editor} failed with #{error.message}"
94
- # return "<!--#{message}-->".html_safe if Rails.env.production?
95
- # "<p>#{message}</p>".html_safe
96
- # end
97
- # @api public
98
- def on_scrivito_widget_error(widget, error)
99
- raise error
100
- end
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
  #