scrivito_sdk 1.16.0.rc1 → 1.17.0.rc3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 76243f40035c3d7c864fe322c0f35f55d4ed1c1c38a158b492b1a5403af08352
4
- data.tar.gz: 27765ae148888b8d98a439c036cfdff78b942014d9f1977c5a0cee45f78fe84f
3
+ metadata.gz: 24bd5086592408534913facabf378aca4cee04e9d749b33bc9b22e2fd253849f
4
+ data.tar.gz: c840807dab6c5ec6c0f9f4cfa97b900a27ef739f415c06a18fb91c70609f2833
5
5
  SHA512:
6
- metadata.gz: 4b6b7548be93614eeebdc6573ca2a89b3f5a86a68f1fc7e6a058e0b7dcc4e47456ff48d2f9fb84e59ec24dca71c2dd98bb248b9dfb884690fe09fc6f571ae538
7
- data.tar.gz: 487530d43384f921eb2b75cec781ad69961e15d970354a3a25883bcd591ffb2f6bbc69eb0f63b61612a4fc6c2f64df84b03c756f625b16fa9bbbe364a7656d10
6
+ metadata.gz: ae653310b1e55e892d45e37c1a1748a01231b5d34b8e1180cae0000ef6e2dddde3694415cdc609e065cf530f929587c5cb1e1d431d20f296a201b8a6f4286da9
7
+ data.tar.gz: 7ad67012b3de012bada117385977b1438d6791bb2a5750da7e27dbadff818af5d0e84da79921929277b6e5f228f57b50f4c499af86a3a4ed40616252c6363533
data/README.md CHANGED
@@ -12,7 +12,7 @@ of concepts and APIs, tutorials, and release notes.
12
12
 
13
13
  ## License
14
14
 
15
- Copyright (c) 2009 - 2019 Infopark AG (http://www.infopark.com)
15
+ Copyright (c) 2009 - 2020 Infopark Group GmbH (http://www.infopark.com)
16
16
 
17
17
  This software can be used and modified under the LGPL-3.0. Please refer to
18
18
  http://www.gnu.org/licenses/lgpl-3.0.html for the license text.
@@ -29,6 +29,8 @@ class AttributeSerializer < Struct.new(:obj_class, :obj_id)
29
29
  _permalink
30
30
  ].include?(attribute_name)
31
31
  [attribute_name, attribute_value.to_s]
32
+ elsif attribute_name == '_restriction'
33
+ [attribute_name, attribute_value]
32
34
  else
33
35
  raise_unknown_attribute(attribute_name)
34
36
  end
@@ -1,4 +1,3 @@
1
- require 'json'
2
1
  require 'ostruct'
3
2
  require 'active_model/naming'
4
3
 
@@ -25,15 +24,18 @@ module Scrivito
25
24
  PublicSystemAttributeDefinition = Class.new(AttributeDefinition)
26
25
 
27
26
  SYSTEM_ATTRIBUTES = AttributeDefinitionCollection.new(
28
- '_id' => PublicSystemAttributeDefinition.new(:_id, :string),
29
- '_last_changed' => PublicSystemAttributeDefinition.new(:_last_changed, :date),
30
- '_obj_class' => PublicSystemAttributeDefinition.new(:_obj_class, :string),
31
- '_path' => PublicSystemAttributeDefinition.new(:_path, :string),
32
- '_permalink' => PublicSystemAttributeDefinition.new(:_permalink, :string),
33
-
34
- '_conflicts' => AttributeDefinition.new(:_conflicts, nil),
35
- '_modification' => AttributeDefinition.new(:_modification, nil),
36
- '_widget_pool' => AttributeDefinition.new(:_widget_pool, nil),
27
+ '_id' => PublicSystemAttributeDefinition.new(:_id, :string),
28
+ '_created_at' => PublicSystemAttributeDefinition.new(:_created_at, :date),
29
+ '_last_changed' => PublicSystemAttributeDefinition.new(:_last_changed, :date),
30
+ '_first_published_at' => PublicSystemAttributeDefinition.new(:_first_published_at, :date),
31
+ '_published_at' => PublicSystemAttributeDefinition.new(:_published_at, :date),
32
+ '_obj_class' => PublicSystemAttributeDefinition.new(:_obj_class, :string),
33
+ '_path' => PublicSystemAttributeDefinition.new(:_path, :string),
34
+ '_permalink' => PublicSystemAttributeDefinition.new(:_permalink, :string),
35
+
36
+ '_conflicts' => AttributeDefinition.new(:_conflicts, nil),
37
+ '_modification' => AttributeDefinition.new(:_modification, nil),
38
+ '_widget_pool' => AttributeDefinition.new(:_widget_pool, nil),
37
39
  )
38
40
 
39
41
  UNIQ_ATTRIBUTES = %w[
@@ -44,7 +46,10 @@ module Scrivito
44
46
 
45
47
  GENERATED_ATTRIBUTES = %w[
46
48
  _conflicts
49
+ _created_at
47
50
  _last_changed
51
+ _first_published_at
52
+ _published_at
48
53
  _modification
49
54
  _obj_type
50
55
  _text_links
@@ -138,14 +143,14 @@ module Scrivito
138
143
  else
139
144
  attributes = build_attributes_with_defaults(attributes, context)
140
145
  attributes = prepare_attributes_for_instantiation(attributes)
141
- api_attributes, widget_properties = prepare_attributes_for_rest_api(attributes)
146
+ extraction, api_attributes = prepare_attributes_for_rest_api(attributes)
142
147
 
143
148
  workspace = Workspace.current
144
149
  obj_data = workspace.create_obj(obj: api_attributes)
145
150
  obj = BasicObj.instantiate(obj_data)
146
151
  obj.revision = workspace.revision
147
152
 
148
- CmsRestApi::WidgetExtractor.notify_persisted_widgets(obj, widget_properties)
153
+ extraction.notify_persisted(obj)
149
154
  obj
150
155
  end
151
156
  end
@@ -301,7 +306,7 @@ module Scrivito
301
306
  # @api public
302
307
  def self.find_by_path(path)
303
308
  Workspace.current.objs.find_one_by(:path, path,
304
- obj_class_name: type_computer.obj_class_name_for_type(self))
309
+ type_computer.obj_class_name_for_type(self))
305
310
  end
306
311
 
307
312
  # Find an {Scrivito::BasicObj Obj} with the given name.
@@ -329,7 +334,7 @@ module Scrivito
329
334
  # @api public
330
335
  def self.find_by_permalink(permalink)
331
336
  Workspace.current.objs.find_one_by(:permalink, permalink,
332
- obj_class_name: type_computer.obj_class_name_for_type(self))
337
+ type_computer.obj_class_name_for_type(self))
333
338
  end
334
339
 
335
340
  # Returns the {Scrivito::BasicObj Obj} with the given permalink, or raises
@@ -414,9 +419,9 @@ module Scrivito
414
419
  # }
415
420
  # )
416
421
  def update(attributes)
417
- api_attributes, widget_properties = prepare_attributes_for_rest_api(attributes)
422
+ extraction, api_attributes = prepare_attributes_for_rest_api(attributes)
418
423
  update_data(workspace.update_obj(id, obj: api_attributes))
419
- CmsRestApi::WidgetExtractor.notify_persisted_widgets(self, widget_properties)
424
+ extraction.notify_persisted(self)
420
425
  self
421
426
  end
422
427
 
@@ -633,7 +638,7 @@ module Scrivito
633
638
  return [] if binary?
634
639
  toclist = children
635
640
  toclist = toclist.reject { |toc| toc.binary? } unless args.include?(:all)
636
- toclist
641
+ toclist.sort_by(&:id)
637
642
  end
638
643
 
639
644
  # @api public
@@ -659,13 +664,23 @@ module Scrivito
659
664
  SYSTEM_KEYS = Set.new(%w[
660
665
  body
661
666
  _id
667
+ _created_at
662
668
  _last_changed
669
+ _first_published_at
670
+ _published_at
663
671
  _path
664
672
  _permalink
665
673
  _obj_class
666
674
  title
667
675
  ])
668
676
 
677
+ DATE_KEYS = Set.new(%w[
678
+ _created_at
679
+ _last_changed
680
+ _first_published_at
681
+ _published_at
682
+ ])
683
+
669
684
  # Returns the value of a system or custom attribute specified by its name.
670
685
  # Passing an invalid key will not raise an error but return +nil+.
671
686
  # @api public
@@ -704,11 +719,39 @@ module Scrivito
704
719
  }
705
720
  end
706
721
 
722
+ # @note If the custom attribute +created_at+ is defined, this value will be returned
723
+ # instead of the system attribute +_created_at+. To access the system attribute
724
+ # regardless of a custom attribute, call {#[] <code>obj[:_created_at]</code>}.
725
+ # @return [nil, ActiveSupport::TimeWithZone]
726
+ # @api public
727
+ def created_at
728
+ read_custom_or_system_attribute('created_at')
729
+ end
730
+
731
+ # @return [ActiveSupport::TimeWithZone]
707
732
  # @api public
708
733
  def last_changed
709
734
  read_attribute('_last_changed')
710
735
  end
711
736
 
737
+ # @note If the custom attribute +first_published_at+ is defined, this value will be returned
738
+ # instead of the system attribute +_first_published_at+. To access the system attribute
739
+ # regardless of a custom attribute, call {#[] <code>obj[:_first_published_at]</code>}.
740
+ # @return [nil, ActiveSupport::TimeWithZone]
741
+ # @api public
742
+ def first_published_at
743
+ read_custom_or_system_attribute('first_published_at')
744
+ end
745
+
746
+ # @note If the custom attribute +published_at+ is defined, this value will be returned
747
+ # instead of the system attribute +_published_at+. To access the system attribute
748
+ # regardless of a custom attribute, call {#[] <code>obj[:_published_at]</code>}.
749
+ # @return [nil, ActiveSupport::TimeWithZone]
750
+ # @api public
751
+ def published_at
752
+ read_custom_or_system_attribute('published_at')
753
+ end
754
+
712
755
  def new?(revision=workspace.base_revision)
713
756
  quick_modification(revision) == "new"
714
757
  end
@@ -991,15 +1034,16 @@ module Scrivito
991
1034
  end
992
1035
 
993
1036
  def self.prepare_attributes_for_rest_api(attributes, obj = nil)
994
- widget_pool_attributes, obj_attributes = CmsRestApi::WidgetExtractor.call(attributes, obj)
1037
+ extraction = CmsRestApi::WidgetExtractor.call(attributes, obj)
1038
+ obj_attributes = extraction.attributes
995
1039
  obj_id = obj ? obj.id : obj_attributes.fetch(:_id)
996
1040
  workspace = obj ? obj.revision.workspace : Workspace.current
997
1041
 
998
1042
  api_attributes = serialize_attributes(
999
- obj_attributes, widget_pool_attributes, workspace, obj_id
1043
+ obj_attributes, extraction.widget_pool_attributes, workspace, obj_id
1000
1044
  )
1001
1045
 
1002
- [api_attributes, widget_pool_attributes]
1046
+ [extraction, api_attributes]
1003
1047
  end
1004
1048
 
1005
1049
  def self.find_attribute_definitions(obj_class, basic_class = self)
@@ -1025,7 +1069,7 @@ module Scrivito
1025
1069
 
1026
1070
  private_class_method def self.serialize_widget_pool_attributes(serializer, widget_pool_attributes)
1027
1071
  {}.tap do |serialized_attributes|
1028
- widget_pool_attributes.each_pair do |widget, widget_attributes|
1072
+ widget_pool_attributes.each_value do |(widget, widget_attributes)|
1029
1073
  obj_class = widget_attributes['_obj_class']
1030
1074
  widget_serializer = AttributeSerializer.new(obj_class, serializer.obj_id)
1031
1075
 
@@ -1037,6 +1081,12 @@ module Scrivito
1037
1081
 
1038
1082
  private
1039
1083
 
1084
+ def read_custom_or_system_attribute(attribute_name)
1085
+ return read_attribute(attribute_name) if has_custom_attribute?(attribute_name)
1086
+
1087
+ read_attribute("_#{attribute_name}")
1088
+ end
1089
+
1040
1090
  def children_including_deleted
1041
1091
  workspace.objs.find_by_parent_path_including_deleted(path)
1042
1092
  end
@@ -1066,8 +1116,7 @@ module Scrivito
1066
1116
 
1067
1117
  def instantiate_widget(widget_id, widget_data)
1068
1118
  BasicWidget.instantiate(widget_data).tap do |widget|
1069
- widget.id = widget_id
1070
- widget.obj = self
1119
+ widget.attach_to_obj(self, widget_id)
1071
1120
  end
1072
1121
  end
1073
1122
 
@@ -1151,7 +1200,7 @@ module Scrivito
1151
1200
 
1152
1201
  def value_of_system_attribute(attribute_name)
1153
1202
  attribute_value = data_from_cms.value_of(attribute_name)
1154
- if attribute_name == '_last_changed'
1203
+ if DATE_KEYS.include?(attribute_name)
1155
1204
  DateConversion.deserialize_from_backend(attribute_value)
1156
1205
  else
1157
1206
  attribute_value
@@ -95,8 +95,6 @@ class BasicWidget
95
95
 
96
96
  attr_accessor :container, :container_attribute_name
97
97
 
98
- attr_writer :obj, :id
99
-
100
98
  attr_reader :attributes_to_be_saved
101
99
 
102
100
  delegate :widget_from_pool, to: :obj
@@ -221,12 +219,29 @@ class BasicWidget
221
219
  @attributes_to_be_saved.nil?
222
220
  end
223
221
 
222
+ def attach_to_obj(an_obj, widget_id = compute_widget_id(an_obj))
223
+ @obj = an_obj
224
+ @id = widget_id
225
+
226
+ attributes_to_be_saved
227
+ end
228
+
224
229
  def ==(other)
225
- other.respond_to?(:obj) && obj == other.obj && other.respond_to?(:id) && id == other.id
230
+ eql?(other)
226
231
  end
227
232
 
228
233
  def eql?(other)
229
- self == other
234
+ other.respond_to?(:equality_identifier, true) &&
235
+ equality_identifier.eql?(other.equality_identifier)
236
+ end
237
+
238
+ def hash
239
+ unless persisted?
240
+ # An unbound widget will likely be attached soon, which mutates its #hash value. Evil.
241
+ raise ScrivitoError, 'a new widget is not allowed to be used as a hash key'
242
+ end
243
+
244
+ equality_identifier.hash
230
245
  end
231
246
 
232
247
  #
@@ -262,7 +277,7 @@ class BasicWidget
262
277
  { obj: {_widget_pool: {id => previous_widget_content}} })
263
278
  reload
264
279
  else
265
- raise ScrivitoError, "cannot revert changes, since widget is #{modification}."
280
+ raise ScrivitoError, "cannot revert changes, since widget is #{modification}"
266
281
  end
267
282
  end
268
283
 
@@ -297,14 +312,6 @@ class BasicWidget
297
312
  end
298
313
  end
299
314
 
300
- def hash
301
- if @obj && @id
302
- (id + obj.id).hash
303
- else
304
- super
305
- end
306
- end
307
-
308
315
  # Returns the entity ({Scrivito::BasicObj} or {Scrivito::BasicWidget})
309
316
  # that references this widget.
310
317
  # @api public
@@ -360,7 +367,8 @@ class BasicWidget
360
367
  end
361
368
  end
362
369
 
363
- def forget_unsaved_attributes
370
+ def notify_persisted(obj)
371
+ @obj = obj
364
372
  @attributes_to_be_saved = nil
365
373
  reload_data
366
374
  end
@@ -398,6 +406,12 @@ class BasicWidget
398
406
  referenced + referenced.map { |widget| widget.contained_widgets }.flatten
399
407
  end
400
408
 
409
+ protected
410
+
411
+ def equality_identifier
412
+ [id, obj.id]
413
+ end
414
+
401
415
  private
402
416
 
403
417
  def workspace
@@ -433,6 +447,14 @@ class BasicWidget
433
447
  def value_of_system_attribute(attribute_name)
434
448
  data_from_cms.value_of(attribute_name)
435
449
  end
450
+
451
+ def compute_widget_id(obj)
452
+ @attributes_to_be_saved.delete('_id') || generate_widget_id(obj)
453
+ end
454
+
455
+ def generate_widget_id(obj)
456
+ obj.present? ? obj.generate_widget_pool_id : BasicObj.generate_widget_pool_id
457
+ end
436
458
  end
437
459
 
438
460
  end
@@ -206,7 +206,7 @@ module Scrivito
206
206
  private_class_method :blob_data_cache_key
207
207
 
208
208
  def self.search_params_cache_key(params)
209
- MultiJson.encode(normalize_search_param(params))
209
+ JSON.generate(normalize_search_param(params))
210
210
  end
211
211
  private_class_method :search_params_cache_key
212
212
 
@@ -67,7 +67,7 @@ class CmsFieldTag < Struct.new(:view, :tag_name, :obj_or_widget, :editing_option
67
67
  if FIELD_TYPES_WITH_ORIGINAL_CONTENT.include?(field_type)
68
68
  original_value = view.scrivito_value(current_value)
69
69
  original_content = original_content(field_type, original_value, current_value)
70
- encoded_content = Base64.strict_encode64(MultiJson.encode(original_content))
70
+ encoded_content = Base64.strict_encode64(JSON.generate(original_content))
71
71
  options['private-field-original-content'] = encoded_content
72
72
  end
73
73
 
@@ -161,7 +161,7 @@ class CmsFieldTag < Struct.new(:view, :tag_name, :obj_or_widget, :editing_option
161
161
  end
162
162
 
163
163
  def valid_values
164
- MultiJson.encode(obj_or_widget.attribute_definitions[field_name].try(:values) || [])
164
+ JSON.generate(obj_or_widget.attribute_definitions[field_name].try(:values) || [])
165
165
  end
166
166
 
167
167
  def assert_valid_attribute
@@ -123,7 +123,7 @@ module Scrivito
123
123
  timer)
124
124
  request = method_to_net_http_class(method).new(path(resource_path))
125
125
  set_headers(request)
126
- request.body = MultiJson.encode(payload) if payload.present?
126
+ request.body = JSON.generate(payload) if payload.present?
127
127
 
128
128
  response = retry_once_on_network_error(method, timer) do
129
129
  CmsRestApi::RateLimit.retry_on_rate_limit(timer) do
@@ -138,12 +138,12 @@ module Scrivito
138
138
  private_class_method def self.handle_response(resource_path, response)
139
139
  http_code = response.code.to_i
140
140
  if response.code.start_with?('2')
141
- MultiJson.load(response.body)
141
+ JSON.parse(response.body)
142
142
  elsif response.code == '403'
143
143
  raise AccessDenied.new(response.body)
144
144
  else
145
145
  begin
146
- error_body = MultiJson.decode(response.body)
146
+ error_body = JSON.parse(response.body)
147
147
  specific_output = error_body['error']
148
148
 
149
149
  if response.code.start_with?('4')
@@ -155,7 +155,7 @@ module Scrivito
155
155
  else # 3xx and >500 are treated as NetworkErrors
156
156
  raise NetworkError.new(response.body, http_code)
157
157
  end
158
- rescue MultiJson::DecodeError
158
+ rescue JSON::ParserError, TypeError
159
159
  raise NetworkError.new(response.body, http_code)
160
160
  end
161
161
  end
@@ -170,6 +170,9 @@ module Scrivito
170
170
  message = task_data["message"] ||
171
171
  "Missing error message in task response #{task_data}"
172
172
 
173
+ if task_data['status'] == 'exception'
174
+ raise BackendError.new(message, 500)
175
+ end
173
176
  backend_code = task_data['code']
174
177
  raise ClientError.new(message, backend_code: backend_code)
175
178
  end
@@ -1,53 +1,68 @@
1
1
  module Scrivito
2
2
  class CmsRestApi
3
3
  class WidgetExtractor
4
-
5
4
  # Extract widgets from a given attribute tree
6
5
  # first return param: a flat hash of widget_like => updated widget attributes
7
6
  # second return param: the updated attributes
8
7
  def self.call(attributes, obj=nil)
8
+ new(obj).extract_widgets(attributes)
9
+ end
10
+
11
+ def initialize(obj)
12
+ @obj = obj
13
+ end
14
+
15
+ def extract_widgets(attributes)
16
+ @extracted_widgets = {}
9
17
  updated_attributes = attributes.dup
10
- extracted_widgets = {}
18
+ widget_pool = updated_attributes.delete(:_widget_pool)
19
+ extract_widgets_from_pool(widget_pool) if widget_pool
20
+ extract_widgets_from_attributes(updated_attributes)
11
21
 
12
- (updated_attributes.delete(:_widget_pool) || {}).each do |widget, widget_properties|
13
- unless widget.persisted?
14
- raise ScrivitoError, 'new top-level widgets are not allowed in the _widget_pool'
15
- end
16
- extracted_widgets[widget] = widget_properties
17
- extracted_widgets.merge!(call(widget_properties, obj).first)
22
+ Extraction.new(extracted_widgets, updated_attributes)
23
+ end
24
+
25
+ private
26
+
27
+ attr_reader :obj
28
+ attr_reader :extracted_widgets
29
+
30
+ def extract_widgets_from_pool(widget_pool)
31
+ widget_pool.each do |widget, widget_properties|
32
+ extracted_widgets[widget.id] = [widget, widget_properties]
33
+ extract_widgets_from_attributes(widget_properties)
18
34
  end
35
+ end
19
36
 
20
- updated_attributes.each do |attribute_name, value|
37
+ def extract_widgets_from_attributes(attributes)
38
+ attributes.each do |attribute_name, value|
21
39
  if value.is_a?(Array) && value.all? { |w| w.is_a?(BasicWidget) }
22
40
  value.each do |widget|
23
41
  unless widget.persisted?
24
- attach_widget_to_obj(widget, obj)
25
- extracted_widgets[widget] = widget.attributes_to_be_saved
26
- extracted_widgets.merge!(call(widget.attributes_to_be_saved, obj).first)
42
+ widget_attributes = widget.attach_to_obj(obj)
43
+ extracted_widgets[widget.id] = [widget, widget_attributes]
44
+ extract_widgets_from_attributes(widget_attributes)
27
45
  end
28
46
  end
29
47
  end
30
48
  end
31
-
32
- return extracted_widgets, updated_attributes
33
49
  end
34
50
 
35
- def self.attach_widget_to_obj(widget, obj)
36
- widget.id = widget.attributes_to_be_saved.delete('_id') || generate_widget_pool_id(obj)
37
- widget.obj = obj
38
- end
51
+ class Extraction
52
+ attr_reader :widget_pool_attributes, :attributes
39
53
 
40
- def self.notify_persisted_widgets(obj, widget_hash)
41
- widget_hash.each do |widget, attributes|
42
- if attributes.present?
43
- widget.obj = obj
44
- widget.forget_unsaved_attributes
45
- end
54
+ def initialize(widget_pool_attributes, attributes)
55
+ @widget_pool_attributes = widget_pool_attributes
56
+ @attributes = attributes
46
57
  end
47
- end
48
58
 
49
- def self.generate_widget_pool_id(obj)
50
- obj.presence ? obj.generate_widget_pool_id : BasicObj.generate_widget_pool_id
59
+ def notify_persisted(obj)
60
+ widget_pool_attributes.each_value do |(widget, attributes)|
61
+ if attributes.present?
62
+ widget.notify_persisted(obj)
63
+ end
64
+ end
65
+ end
51
66
  end
52
67
  end
53
68
  end