scrivito_sdk 1.0.0 → 1.1.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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