scrivito_sdk 1.0.0 → 1.1.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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -0
  3. data/README.md +1 -1
  4. data/app/controllers/scrivito/legacy_redirect_controller.rb +11 -0
  5. data/app/controllers/scrivito/objs_controller.rb +15 -5
  6. data/app/controllers/scrivito/webservice_controller.rb +4 -3
  7. data/app/controllers/scrivito/workspaces_controller.rb +24 -29
  8. data/app/helpers/scrivito_helper.rb +82 -37
  9. data/app/views/scrivito/objs/binary_no_cache.json.jbuilder +1 -0
  10. data/app/views/scrivito/objs/search.json.jbuilder +5 -5
  11. data/app/views/scrivito/webservice/error.json.jbuilder +2 -2
  12. data/config/ca-bundle.crt +1 -1
  13. data/config/precedence_routes.rb +51 -48
  14. data/config/routes.rb +1 -14
  15. data/lib/assets/javascripts/scrivito_ui.js +2447 -266
  16. data/lib/assets/stylesheets/scrivito.css +1 -1
  17. data/lib/assets/stylesheets/scrivito_ui.css +1 -1
  18. data/lib/generators/scrivito/install/install_generator.rb +12 -0
  19. data/lib/generators/scrivito/install/templates/config/initializers/scrivito.rb +3 -0
  20. data/lib/scrivito/attribute_definition.rb +13 -1
  21. data/lib/scrivito/attribute_deserializer.rb +4 -4
  22. data/lib/scrivito/attribute_value_renderer.rb +79 -0
  23. data/lib/scrivito/backend/obj_data_cache.rb +15 -15
  24. data/lib/scrivito/backend/obj_data_from_rest.rb +1 -21
  25. data/lib/scrivito/backend/obj_query.rb +2 -2
  26. data/lib/scrivito/basic_obj.rb +17 -6
  27. data/lib/scrivito/binary.rb +13 -5
  28. data/lib/scrivito/cache/chainable.rb +10 -5
  29. data/lib/scrivito/cache/file_store.rb +4 -0
  30. data/lib/scrivito/cache/ram_store.rb +4 -0
  31. data/lib/scrivito/child_list_tag.rb +16 -14
  32. data/lib/scrivito/client_error.rb +10 -0
  33. data/lib/scrivito/cms_backend.rb +66 -291
  34. data/lib/scrivito/cms_data_cache.rb +7 -9
  35. data/lib/scrivito/cms_dispatch_controller.rb +1 -10
  36. data/lib/scrivito/cms_field_tag.rb +2 -1
  37. data/lib/scrivito/cms_rest_api.rb +2 -0
  38. data/lib/scrivito/cms_routing.rb +26 -22
  39. data/lib/scrivito/configuration.rb +38 -0
  40. data/lib/scrivito/connection_manager.rb +69 -0
  41. data/lib/scrivito/controller_actions.rb +2 -2
  42. data/lib/scrivito/controller_helper.rb +2 -2
  43. data/lib/scrivito/date_attribute.rb +4 -1
  44. data/lib/scrivito/deprecation.rb +5 -4
  45. data/lib/scrivito/editing_context.rb +2 -2
  46. data/lib/scrivito/errors.rb +17 -0
  47. data/lib/scrivito/image_tag.rb +2 -2
  48. data/lib/scrivito/link_parser.rb +10 -5
  49. data/lib/scrivito/meta_data_collection.rb +11 -0
  50. data/lib/scrivito/obj_collection.rb +1 -1
  51. data/lib/scrivito/obj_facet_value.rb +19 -7
  52. data/lib/scrivito/obj_params_parser.rb +43 -14
  53. data/lib/scrivito/obj_search_builder.rb +1 -2
  54. data/lib/scrivito/obj_search_enumerator/batch.rb +22 -0
  55. data/lib/scrivito/obj_search_enumerator/batch_iterator.rb +36 -0
  56. data/lib/scrivito/obj_search_enumerator/query_executor.rb +35 -0
  57. data/lib/scrivito/obj_search_enumerator.rb +218 -93
  58. data/lib/scrivito/obj_update_params_parser.rb +1 -0
  59. data/lib/scrivito/preset_routes.rb +25 -0
  60. data/lib/scrivito/revision.rb +13 -11
  61. data/lib/scrivito/route.rb +62 -0
  62. data/lib/scrivito/routing_extensions.rb +92 -0
  63. data/lib/scrivito/sdk_engine.rb +4 -0
  64. data/lib/scrivito/task.rb +77 -0
  65. data/lib/scrivito/type_computer.rb +3 -3
  66. data/lib/scrivito/ui_config.rb +4 -0
  67. data/lib/scrivito/user.rb +24 -18
  68. data/lib/scrivito/workspace/publish_checker.rb +2 -3
  69. data/lib/scrivito/workspace.rb +38 -6
  70. data/lib/scrivito/workspace_data.rb +0 -14
  71. metadata +14 -10
  72. data/lib/scrivito/content_service.rb +0 -121
  73. data/lib/scrivito/content_state.rb +0 -109
  74. data/lib/scrivito/content_state_caching.rb +0 -47
  75. data/lib/scrivito/content_state_visitor.rb +0 -19
  76. data/lib/scrivito/obj_data_from_service.rb +0 -63
  77. data/lib/scrivito/workspace_data_from_service.rb +0 -43
@@ -72,6 +72,18 @@ module Scrivito
72
72
  copy_file 'app/views/image_widget/show.html.erb'
73
73
  copy_file 'app/views/image_widget/thumbnail.html.erb'
74
74
  end
75
+
76
+ def append_scrivito_routes
77
+ log :route, "Appending Scrivito routes"
78
+ definition = "\n"\
79
+ " # Default Scrivito routes. Adapt them to change the routing of CMS objects.\n"\
80
+ " # See the documentation of 'scrivito_route' for a detailed description.\n"\
81
+ " scrivito_route '/', using: 'homepage'\n"\
82
+ " scrivito_route '(/)(*slug-):id', using: 'slug_id'\n"\
83
+ " scrivito_route '/*permalink', using: 'permalink', format: false\n"\
84
+
85
+ inject_into_file 'config/routes.rb', definition, before: /^end\s*\z/, verbose: false
86
+ end
75
87
  end
76
88
  end
77
89
  end
@@ -7,4 +7,7 @@ Scrivito.configure do |config|
7
7
  # config.tenant = 'my-tenant-123'
8
8
  # config.api_key = 'secret'
9
9
  #
10
+
11
+ # Disable the default routes to allow route configuration
12
+ config.inject_preset_routes = false
10
13
  end
@@ -44,7 +44,19 @@ class AttributeDefinition
44
44
  private
45
45
 
46
46
  def assert_valid_options
47
- if (values = @options[:values]) && values.include?('')
47
+ @options.assert_valid_keys(:values, 'values')
48
+ if values = @options[:values]
49
+ values.each(&method(:assert_valid_value))
50
+ end
51
+ end
52
+
53
+ def assert_valid_value(value)
54
+ unless value.is_a?(String)
55
+ raise ScrivitoError,
56
+ %{#{value.class} is not allowed as value for #{type} attribute "#{name}".}
57
+ end
58
+
59
+ if value == ''
48
60
  raise ScrivitoError, %{Empty string is not allowed as value for #{type} attribute "#{name}".}
49
61
  end
50
62
  end
@@ -41,7 +41,7 @@ class AttributeDeserializer < Struct.new(:model, :workspace)
41
41
 
42
42
  def deserialize_link_value(attribute_value)
43
43
  return unless attribute_value
44
- if attribute_value['destination']
44
+ if attribute_value['obj_id']
45
45
  deserialize_internal_link(attribute_value)
46
46
  else
47
47
  deserialize_external_link(attribute_value)
@@ -52,10 +52,10 @@ class AttributeDeserializer < Struct.new(:model, :workspace)
52
52
  if link_definitions.present?
53
53
  link_definitions = link_definitions.map(&:with_indifferent_access)
54
54
 
55
- object_ids = link_definitions.map { |link_data| link_data[:destination] }.compact.uniq
55
+ object_ids = link_definitions.map { |link_data| link_data[:obj_id] }.compact.uniq
56
56
  objects = object_ids.empty? ? [] : workspace.objs.find(object_ids)
57
57
  link_definitions.each_with_object([]) do |link_data, links|
58
- obj = objects.detect { |o| o && o.id == link_data[:destination] }
58
+ obj = objects.detect { |o| o && o.id == link_data[:obj_id] }
59
59
  link = Link.new(link_data.merge(obj: obj))
60
60
  links << link if link.resolved?
61
61
  end
@@ -82,7 +82,7 @@ class AttributeDeserializer < Struct.new(:model, :workspace)
82
82
 
83
83
  def deserialize_internal_link(attribute_value)
84
84
  Link.new(attribute_value.slice('title', 'query', 'fragment', 'target').symbolize_keys
85
- .merge(obj: workspace.objs.find(attribute_value['destination'])))
85
+ .merge(obj: workspace.objs.find(attribute_value['obj_id'])))
86
86
  rescue ResourceNotFound
87
87
  end
88
88
 
@@ -0,0 +1,79 @@
1
+ module Scrivito
2
+ class AttributeValueRenderer < Struct.new(:view)
3
+ def render(value, type)
4
+ method_name = "render_as_#{type}"
5
+ respond_to?(method_name) ? public_send(method_name, value) : value
6
+ end
7
+
8
+ def render_as_date(date)
9
+ if date
10
+ html_escape(localize(date))
11
+ end
12
+ end
13
+
14
+ def render_as_enum(enum)
15
+ enum.to_s
16
+ end
17
+
18
+ def render_as_html(html)
19
+ if html
20
+ cms_routing = CmsRouting.new(view.request, view, view.scrivito_engine)
21
+ cms_routing.convert_links(html).html_safe
22
+ end
23
+ end
24
+
25
+ def render_as_link(link)
26
+ if link
27
+ link_to(link.display_title, scrivito_path(link))
28
+ end
29
+ end
30
+
31
+ def render_as_linklist(linklist)
32
+ if linklist.any?
33
+ content_tag(:ul) do
34
+ capture do
35
+ linklist.each do |link|
36
+ concat content_tag(:li) { render_as_link(link) }
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ def render_as_multienum(multienum)
44
+ render_as_stringlist(multienum)
45
+ end
46
+
47
+ def render_as_reference(reference)
48
+ reference.try(:description_for_editor)
49
+ end
50
+
51
+ def render_as_referencelist(referencelist)
52
+ if referencelist.any?
53
+ content_tag(:ul) do
54
+ capture do
55
+ referencelist.each do |reference|
56
+ concat content_tag(:li) { render_as_reference(reference) }
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ def render_as_string(string)
64
+ string.is_a?(HtmlString) ? render_as_html(string) : html_escape(string)
65
+ end
66
+
67
+ def render_as_stringlist(stringlist)
68
+ stringlist.join(', ')
69
+ end
70
+
71
+ private
72
+
73
+ delegate :capture, :concat, :content_tag, :localize, :link_to, :scrivito_path, to: :view
74
+
75
+ def html_escape(string)
76
+ ERB::Util.html_escape(string)
77
+ end
78
+ end
79
+ end
@@ -45,7 +45,7 @@ module ObjDataCache
45
45
  class SimpleCacheView < Struct.new(:cache_id)
46
46
 
47
47
  def read_obj(id)
48
- if tag = CmsDataCache.read_obj_data_rest(cache_id, "id", id)
48
+ if tag = CmsDataCache.read_obj_data(cache_id, "id", id)
49
49
  CmsDataCache.read_data_from_tag(tag)
50
50
  end
51
51
  end
@@ -58,11 +58,11 @@ module ObjDataCache
58
58
  index_access
59
59
  end
60
60
 
61
- def write_index(index, key, data)
61
+ def write_index(index, key, data, **options)
62
62
  index_access
63
63
  end
64
64
 
65
- def write_index_not_updatable(index, key, data)
65
+ def write_index_not_updatable(index, key, data, **options)
66
66
  index_access
67
67
  end
68
68
 
@@ -77,7 +77,7 @@ module ObjDataCache
77
77
  def write_cache(index, key, data)
78
78
  raise InternalError unless data
79
79
 
80
- CmsDataCache.write_obj_data_rest(cache_id, index, key, data)
80
+ CmsDataCache.write_obj_data(cache_id, index, key, data)
81
81
  end
82
82
 
83
83
  end
@@ -85,7 +85,7 @@ module ObjDataCache
85
85
  class IncrementalCacheView < Struct.new(:cache_id, :viewed_state)
86
86
 
87
87
  def read_index(index, key, &update_function)
88
- if hit = CmsDataCache.read_obj_data_rest(cache_id, index, key)
88
+ if hit = CmsDataCache.read_obj_data(cache_id, index, key)
89
89
 
90
90
  cached_at = hit.first
91
91
  cached_result = hit.second
@@ -106,17 +106,17 @@ module ObjDataCache
106
106
  end
107
107
  end
108
108
 
109
- def write_index(index, key, data)
110
- write_cache_updatable(index, key, data)
109
+ def write_index(index, key, data, **options)
110
+ write_cache_updatable(index, key, data, **options)
111
111
  end
112
112
 
113
113
  # use this to cache data that cannot be updated
114
- def write_index_not_updatable(index, key, data)
115
- write_cache(index, key, viewed_state.content_state_id, data)
114
+ def write_index_not_updatable(index, key, data, **options)
115
+ write_cache(index, key, viewed_state.content_state_id, data, **options)
116
116
  end
117
117
 
118
118
  def read_obj(id)
119
- if hit = CmsDataCache.read_obj_data_rest(cache_id, "id", id)
119
+ if hit = CmsDataCache.read_obj_data(cache_id, "id", id)
120
120
  cached_at, cached_tag = hit
121
121
 
122
122
  tag_in_changes = viewed_state.obj_from_changes(id, cached_at)
@@ -155,15 +155,15 @@ module ObjDataCache
155
155
 
156
156
  private
157
157
 
158
- def write_cache_updatable(index, key, data)
159
- write_cache(index, key, stable_csid, data)
158
+ def write_cache_updatable(index, key, data, **options)
159
+ write_cache(index, key, stable_csid, data, **options)
160
160
  end
161
161
 
162
- def write_cache(index, key, csid, data)
162
+ def write_cache(index, key, csid, data, **options)
163
163
  raise InternalError unless data
164
164
 
165
- CmsDataCache.write_obj_data_rest(
166
- cache_id, index, key, [csid, data])
165
+ CmsDataCache.write_obj_data(
166
+ cache_id, index, key, [csid, data], **options)
167
167
  end
168
168
 
169
169
  # tries to update an outdated cache result using the changes recorded in the
@@ -14,8 +14,7 @@ class ObjDataFromRest < ObjData
14
14
  if attribute_name.starts_with?('_')
15
15
  [attribute_data, nil]
16
16
  else
17
- type = attribute_data[0]
18
- [legacy_compatible(attribute_data[1], type), type]
17
+ [attribute_data[1], attribute_data[0]]
19
18
  end
20
19
  else
21
20
  [nil, nil]
@@ -32,25 +31,6 @@ class ObjDataFromRest < ObjData
32
31
 
33
32
  private
34
33
 
35
- def legacy_compatible(value, type)
36
- if type == "linklist"
37
- value.map(&method(:legacy_link))
38
- elsif type == "link"
39
- legacy_link(value)
40
- else
41
- value
42
- end
43
- end
44
-
45
- def legacy_link(link)
46
- return link unless link["obj_id"]
47
-
48
- transformed = link.dup
49
- transformed["destination"] = transformed.delete("obj_id")
50
-
51
- transformed
52
- end
53
-
54
34
  def value_of_widget_pool
55
35
  if widget_pool = @data['_widget_pool']
56
36
  # using Hash#merge to "map" to a new Hash
@@ -28,7 +28,7 @@ class << self
28
28
  return load_nested_ids(revision, ids_from_cache)
29
29
  end
30
30
 
31
- backend_ids = revision.obj_search_request(index.query(missing_keys))
31
+ backend_ids, tentative = revision.obj_search_request(index.query(missing_keys))
32
32
 
33
33
  all_obj_datas = load_nested_ids(revision, ids_from_cache + [backend_ids])
34
34
 
@@ -39,7 +39,7 @@ class << self
39
39
 
40
40
  grouped_backend_results.each_with_index do |result, i|
41
41
  ids = result.map { |obj_data| obj_data.value_of("_id") }
42
- cache.write_index(index.id, missing_keys[i], ids)
42
+ cache.write_index(index.id, missing_keys[i], ids, persistent: !tentative)
43
43
  end
44
44
 
45
45
  cache_obj_datas.map do |obj_datas|
@@ -600,12 +600,23 @@ module Scrivito
600
600
  toclist
601
601
  end
602
602
 
603
- # @param objs_to_be_sorted [Array<Scrivito::BasicObj>] unsorted list of CMS objects
604
- # @param list [Array<Scrivito::BasicObj>] list of objects that defines the order
605
- # @return [Array<Scrivito::BasicObj>] a sorted list of objects. Objects present in
606
- # +objs_to_be_sorted+ but not in +list+ are appended to the end, sorted by +Obj#id+
607
- def self.sort_by_list(objs_to_be_sorted, list)
608
- (list & objs_to_be_sorted) + (objs_to_be_sorted - list).sort_by(&:id)
603
+ # @api public
604
+ #
605
+ # Returns a list of children that are sorted according to the order specifed in the
606
+ # +child_order+ attribute. The same sort order is used when rendering navigations using
607
+ # the {ScrivitoHelper#scrivito_tag_list scrivito_tag_list} helper.
608
+ #
609
+ # @return [Array<Obj>]
610
+ def sorted_toclist
611
+ if sortable_toclist?
612
+ (child_order & toclist) + (toclist - child_order).sort_by(&:id)
613
+ else
614
+ toclist
615
+ end
616
+ end
617
+
618
+ def sortable_toclist?
619
+ has_attribute?(:child_order)
609
620
  end
610
621
 
611
622
  # This should be a Set, because it's faster in this particular case.
@@ -89,9 +89,15 @@ class Binary
89
89
  find_url('get')
90
90
  end
91
91
 
92
+ def no_cache_url
93
+ if private?
94
+ CmsBackend.instance.find_blob_data(id, 'private_access', 'get', no_cache: true)['url']
95
+ end
96
+ end
97
+
92
98
  def url_from_cache
93
- blob_data = CmsBackend.instance
94
- .find_blob_data_from_cache(id, access_type, 'get', transformation_definition)
99
+ blob_data = CmsBackend.instance.find_blob_data_from_cache(id, access_type, 'get',
100
+ transformation_definition: transformation_definition)
95
101
  if blob_data
96
102
  blob_data['url']
97
103
  end
@@ -111,7 +117,7 @@ class Binary
111
117
  # @raise [Scrivito::ScrivitoError] If the binary is the result of a transformation.
112
118
  def content_type
113
119
  raise_field_not_available('Content type') if transformed?
114
- headers[:content_type]
120
+ meta_data[:content_type]
115
121
  end
116
122
 
117
123
  # @api public
@@ -121,7 +127,7 @@ class Binary
121
127
  # @raise [Scrivito::ScrivitoError] If the binary is the result of a transformation.
122
128
  def content_length
123
129
  raise_field_not_available('Content length') if transformed?
124
- headers[:content_length].to_i
130
+ meta_data[:content_length]
125
131
  end
126
132
 
127
133
  #
@@ -283,7 +289,9 @@ class Binary
283
289
  end
284
290
 
285
291
  def find_url(verb)
286
- CmsBackend.instance.find_blob_data(id, access_type, verb, transformation_definition)['url']
292
+ blob_data = CmsBackend.instance.find_blob_data(id, access_type, verb,
293
+ transformation_definition: transformation_definition)
294
+ blob_data['url']
287
295
  rescue ClientError => e
288
296
  case e.backend_code
289
297
  when /\Abinary\.unprocessable\.image\.transform\.source\./
@@ -11,12 +11,13 @@ module Scrivito
11
11
  value
12
12
  end
13
13
 
14
- def write(key, value, expires_in = nil)
14
+ def write(key, value, expires_in: nil, persistent: true)
15
15
  transformed_key = transform_key(key)
16
- if forward_write?(key, value, expires_in)
17
- next_store.write(transformed_key, value, expires_in)
16
+ if persistent && forward_write?(key, value, expires_in)
17
+ next_store.write(transformed_key, value, expires_in: expires_in)
18
18
  end
19
- internal_write(transformed_key, value, expires_in)
19
+
20
+ internal_write(transformed_key, value, expires_in) if persistent || tentative?
20
21
  end
21
22
 
22
23
  def fetch(key, &block)
@@ -34,6 +35,10 @@ module Scrivito
34
35
 
35
36
  private
36
37
 
38
+ def tentative?
39
+ raise NotImplementedError
40
+ end
41
+
37
42
  def next_store
38
43
  raise NotImplementedError
39
44
  end
@@ -42,7 +47,7 @@ module Scrivito
42
47
  raise NotImplementedError
43
48
  end
44
49
 
45
- def internal_write(key, value, expires_in = nil)
50
+ def internal_write(key, value, expires_in)
46
51
  raise NotImplementedError
47
52
  end
48
53
 
@@ -17,6 +17,10 @@ module Cache
17
17
 
18
18
  delegate :read, to: :internal_store, prefix: :internal
19
19
 
20
+ def tentative?
21
+ false
22
+ end
23
+
20
24
  def internal_write(key, value, expires_in = nil)
21
25
  internal_store.write(key, value, expires_in: expires_in)
22
26
  end
@@ -15,6 +15,10 @@ module Cache
15
15
 
16
16
  private
17
17
 
18
+ def tentative?
19
+ true
20
+ end
21
+
18
22
  def internal_read(key)
19
23
  internal_store[key]
20
24
  end
@@ -23,7 +23,7 @@ class ChildListTag < Struct.new(:tag_name, :obj, :field_name, :view)
23
23
  'private-obj-description-for-editor' => obj.description_for_editor,
24
24
  }
25
25
 
26
- if obj.has_attribute?(:child_order)
26
+ if obj.sortable_toclist?
27
27
  options['private-child-list-order-obj-id'] = obj.id
28
28
  modification = comparison.modification_for_attribute(obj, :child_order)
29
29
  if modification == Modification::EDITED
@@ -37,8 +37,8 @@ class ChildListTag < Struct.new(:tag_name, :obj, :field_name, :view)
37
37
  def content(&block)
38
38
  return unless block_given?
39
39
 
40
- is_sortable = obj.has_attribute?(:child_order) && user_present?
41
- rendered_children = sorted_children.map do |child|
40
+ is_sortable = obj.sortable_toclist? && user_present?
41
+ rendered_children = obj.sorted_toclist.map do |child|
42
42
  obj_tag = ObjTag.new(child, is_sortable, comparison.modification(child), view)
43
43
  yield obj_tag, child
44
44
  obj_tag.rendered
@@ -48,6 +48,7 @@ class ChildListTag < Struct.new(:tag_name, :obj, :field_name, :view)
48
48
 
49
49
  #
50
50
  # This is a helper class for {ScrivitoHelper#scrivito_tag_list}.
51
+ #
51
52
  # @api public
52
53
  #
53
54
  class ObjTag < Struct.new(:obj, :is_sortable, :modification, :view)
@@ -55,17 +56,23 @@ class ChildListTag < Struct.new(:tag_name, :obj, :field_name, :view)
55
56
 
56
57
  attr_reader :tag_name, :rendered
57
58
 
59
+ #
60
+ # Renders HTML for the corresponding child with in-place editing enabled.
58
61
  #
59
62
  # @api public
60
63
  #
61
- # @param tag_name [String, Symbol] Name of the html tag (e.g. +:div+ or +:span+).
62
- # @param html_options [Hash] Additional options, which are passed to +content_tag+.
63
- # Use them to add HTML attributes to the tag.
64
+ # @note This method can be called only _once_ per child.
65
+ #
66
+ # @param tag_name [String, Symbol] Name of the HTML tag (e.g. +:li+ or +:div+).
67
+ # @param html_options [Hash] Options to be passed to +content_tag+. Use them to add HTML
68
+ # attributes to the tag.
64
69
  #
65
- # @return [String] The rendered html tag
70
+ # @return [String] The rendered HTML tag.
66
71
  #
67
- # @example Render a <div> tag containing the text "random content" and assigns the tag a css class called +very_important+.
68
- # list.tag(:div, class: "very_important") do
72
+ # @see ScrivitoHelper#scrivito_tag_list
73
+ #
74
+ # @example Render a +div+ element containing text, and assign to it the +my_list_element+ CSS class.
75
+ # list.tag(:div, class: "my_list_element") do
69
76
  # "random content"
70
77
  # end
71
78
  #
@@ -102,11 +109,6 @@ class ChildListTag < Struct.new(:tag_name, :obj, :field_name, :view)
102
109
  def comparison
103
110
  EditingContextMiddleware.from_request(view.request).comparison
104
111
  end
105
-
106
- def sorted_children
107
- children = obj.toclist
108
- obj.has_attribute?(:child_order) ? Obj.sort_by_list(children, obj.child_order) : children
109
- end
110
112
  end
111
113
 
112
114
  end
@@ -8,6 +8,16 @@ module Scrivito
8
8
  @backend_code = backend_code
9
9
  super(message)
10
10
  end
11
+
12
+ def ==(client_error)
13
+ message == client_error.message &&
14
+ http_code == client_error.http_code &&
15
+ backend_code == client_error.backend_code
16
+ end
17
+
18
+ def as_json
19
+ {message: message, backend_code: backend_code}
20
+ end
11
21
  end
12
22
 
13
23
  end