scrivito_sdk 0.50.1 → 0.60.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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/scrivito/objs_controller.rb +25 -21
  3. data/app/controllers/scrivito/ui_controller.rb +1 -1
  4. data/app/controllers/scrivito/webservice_controller.rb +1 -1
  5. data/app/models/scrivito/content_widget.rb +1 -3
  6. data/app/views/cms/index.html.erb +3 -0
  7. data/app/views/scrivito/objs/destroy.json.jbuilder +1 -0
  8. data/app/views/scrivito/objs/show.json.jbuilder +2 -0
  9. data/app/views/scrivito/objs/update.json.jbuilder +1 -1
  10. data/app/views/scrivito/objs/{widget_modification.json.jbuilder → widget.json.jbuilder} +0 -0
  11. data/config/ca-bundle.crt +24 -219
  12. data/config/precedence_routes.rb +60 -0
  13. data/config/routes.rb +13 -46
  14. data/lib/assets/javascripts/scrivito_ui.js +360 -202
  15. data/lib/assets/stylesheets/scrivito_sdk.css +1 -1
  16. data/lib/assets/stylesheets/scrivito_ui.css +1 -1
  17. data/lib/generators/scrivito/install/templates/app/models/download.rb +1 -3
  18. data/lib/generators/scrivito/install/templates/app/models/headline_widget.rb +1 -5
  19. data/lib/generators/scrivito/install/templates/app/models/image.rb +1 -3
  20. data/lib/generators/scrivito/install/templates/app/models/image_widget.rb +1 -5
  21. data/lib/generators/scrivito/install/templates/app/models/page.rb +3 -6
  22. data/lib/generators/scrivito/install/templates/app/models/text_widget.rb +1 -5
  23. data/lib/generators/scrivito/install/templates/scrivito/migrate/install_scrivito_migration.rb +1 -25
  24. data/lib/generators/scrivito/migration/templates/migration.erb +2 -26
  25. data/lib/generators/scrivito/page/page_generator.rb +0 -11
  26. data/lib/generators/scrivito/page/templates/model.erb +3 -7
  27. data/lib/generators/scrivito/widget/widget_generator.rb +0 -11
  28. data/lib/scrivito/attribute.rb +10 -16
  29. data/lib/scrivito/attribute_collection.rb +4 -0
  30. data/lib/scrivito/attribute_content.rb +239 -161
  31. data/lib/scrivito/attribute_definition.rb +16 -10
  32. data/lib/scrivito/attribute_definition_collection.rb +7 -4
  33. data/lib/scrivito/attribute_definition_migrator.rb +1 -3
  34. data/lib/scrivito/attribute_deserializer.rb +111 -0
  35. data/lib/scrivito/attribute_serializer.rb +189 -0
  36. data/lib/scrivito/basic_obj.rb +113 -78
  37. data/lib/scrivito/basic_widget.rb +103 -37
  38. data/lib/scrivito/binary.rb +3 -3
  39. data/lib/scrivito/cache/chainable.rb +10 -4
  40. data/lib/scrivito/cache/file_store.rb +7 -4
  41. data/lib/scrivito/cache/ram_store.rb +1 -1
  42. data/lib/scrivito/class_collection.rb +2 -2
  43. data/lib/scrivito/client_config.rb +7 -6
  44. data/lib/scrivito/cms_backend.rb +2 -2
  45. data/lib/scrivito/cms_field_tag.rb +33 -25
  46. data/lib/scrivito/cms_rest_api/{attribute_serializer.rb → legacy_attribute_serializer.rb} +1 -1
  47. data/lib/scrivito/cms_routing.rb +20 -14
  48. data/lib/scrivito/configuration.rb +1 -1
  49. data/lib/scrivito/controller_actions.rb +12 -4
  50. data/lib/scrivito/date_attribute.rb +8 -0
  51. data/lib/scrivito/diff.rb +1 -1
  52. data/lib/scrivito/editing_context.rb +1 -1
  53. data/lib/scrivito/editing_context_middleware.rb +1 -1
  54. data/lib/scrivito/errors.rb +4 -0
  55. data/lib/scrivito/link_parser.rb +6 -1
  56. data/lib/scrivito/migrations/cms_backend.rb +17 -60
  57. data/lib/scrivito/migrations/migration_store.rb +0 -1
  58. data/lib/scrivito/model_library.rb +6 -6
  59. data/lib/scrivito/obj_class.rb +34 -48
  60. data/lib/scrivito/obj_class_collection.rb +21 -3
  61. data/lib/scrivito/obj_create_params_parser.rb +2 -1
  62. data/lib/scrivito/obj_data.rb +9 -103
  63. data/lib/scrivito/obj_data_from_hash.rb +19 -21
  64. data/lib/scrivito/obj_data_from_service.rb +28 -48
  65. data/lib/scrivito/obj_params_parser.rb +14 -14
  66. data/lib/scrivito/obj_search_enumerator.rb +1 -1
  67. data/lib/scrivito/obj_update_params_parser.rb +2 -2
  68. data/lib/scrivito/restriction_set.rb +1 -1
  69. data/lib/scrivito/sdk_engine.rb +2 -2
  70. data/lib/scrivito/test_request.rb +10 -0
  71. data/lib/scrivito/type_computer.rb +5 -1
  72. data/lib/scrivito/widget_collection.rb +15 -0
  73. data/lib/scrivito/widget_garbage_collection.rb +1 -1
  74. data/lib/scrivito/widget_tag.rb +2 -0
  75. data/lib/scrivito/workspace.rb +2 -1
  76. data/lib/scrivito/workspace_data_from_service.rb +1 -0
  77. data/lib/scrivito_sdk.rb +17 -1
  78. metadata +36 -34
  79. data/app/views/scrivito/objs/modification.json.jbuilder +0 -1
  80. data/config/cms_routes.rb +0 -22
  81. data/lib/generators/scrivito/page/templates/migration.erb +0 -9
  82. data/lib/generators/scrivito/widget/templates/migration.erb +0 -7
@@ -3,6 +3,8 @@ module Scrivito
3
3
  # The CMS widget class
4
4
  # @api public
5
5
  class BasicWidget
6
+ # @!parse extend Scrivito::AttributeContent::ClassMethods
7
+
6
8
  include AttributeContent
7
9
 
8
10
  def self.type_computer
@@ -13,11 +15,43 @@ class BasicWidget
13
15
  @_type_computer = nil
14
16
  end
15
17
 
16
- def self.hide_from_widget_class_selection?
17
- false
18
+ #
19
+ # @api public
20
+ # This method can be overridden in subclasses to control to which pages or
21
+ # widgets the given widget class can be added. This method can be used
22
+ # to ensure that only a special type of widget can be added to a specific
23
+ # container. An example for this is a +TabGroupWidget+ that should
24
+ # contain widgets of the +TabWidget+ type only, and, vice versa, a +TabWidget+
25
+ # should only be contained in a +TabGroupWidget+. A value of +nil+ means that
26
+ # no additional restrictions are applied.
27
+ #
28
+ # This method only further restricts the list of valid classes defined
29
+ # by means of {AttributeContent#valid_widget_classes_for}.
30
+ #
31
+ # @return [Array<String, Symbol, Class>, nil]
32
+ #
33
+ # @example
34
+ # class TabGroupWidget < Widget
35
+ # def valid_widget_classes_for(field_name)
36
+ # [TabWidget]
37
+ # end
38
+ # end
39
+ #
40
+ # class TabWidget < Widget
41
+ # def self.valid_container_classes
42
+ # [TabGroupWidget]
43
+ # end
44
+ # end
45
+ #
46
+ def self.valid_container_classes
18
47
  end
19
48
 
20
- attr_accessor :container, :container_field_name
49
+ def self.valid_inside_container?(container_class)
50
+ valid_container_classes.nil? ||
51
+ valid_container_classes.map(&:to_s).include?(container_class.name)
52
+ end
53
+
54
+ attr_accessor :container, :container_attribute_name
21
55
 
22
56
  attr_writer :obj, :id
23
57
 
@@ -46,7 +80,16 @@ class BasicWidget
46
80
  # my_obj(my_widget_field: [new_widget])
47
81
  #
48
82
  def initialize(attributes = {})
49
- @attributes_to_be_saved = self.class.with_default_obj_class(attributes)
83
+ @attributes_to_be_saved = self.class.prepare_attributes_for_instantiation(attributes)
84
+ @attribute_cache = {}
85
+ end
86
+
87
+ def self.new(attributes = {})
88
+ if obj_class = extract_obj_class_from_attributes(attributes)
89
+ obj_class.new(attributes)
90
+ else
91
+ super
92
+ end
50
93
  end
51
94
 
52
95
  def revision
@@ -66,8 +109,8 @@ class BasicWidget
66
109
  # Destroys the {Scrivito::BasicWidget Widget} in the current {Workspace}
67
110
  # @api public
68
111
  def destroy
69
- new_widget_list = container[container_field_name] - [self]
70
- container.update(container_field_name => new_widget_list)
112
+ new_widget_list = container[container_attribute_name] - [self]
113
+ container.update(container_attribute_name => new_widget_list)
71
114
  end
72
115
 
73
116
  #
@@ -83,25 +126,30 @@ class BasicWidget
83
126
  # obj.update(my_widgets: obj.my_widgets.push(obj.my_widgets.first.copy))
84
127
  #
85
128
  def copy
86
- attrs = {}
87
- each_custom_attribute do |attr_name, attr_value, attr_type|
88
- attrs[attr_name] = attr_type == 'widget' ? attr_value.map(&:copy) : attr_value
129
+ attributes = {}
130
+ attribute_definitions.each do |attribute_definition|
131
+ attribute_name = attribute_definition.name
132
+ attribute_value = read_attribute(attribute_name)
133
+ attribute_value = attribute_value.map(&:copy) if attribute_definition.widgetlist?
134
+ attributes[attribute_name] = attribute_value
89
135
  end
90
- self.class.new(attrs)
136
+ self.class.new(attributes)
91
137
  end
92
138
 
93
139
  def copy_for_restore(referenced_ids)
94
- attrs = {}
95
- each_custom_attribute do |attr_name, attr_value, attr_type|
96
- attrs[attr_name] = if attr_type == 'widget'
97
- attr_value.reject { |widget| referenced_ids.include?(widget.id) }
140
+ attributes = {}
141
+ attribute_definitions.each do |attribute_definition|
142
+ attribute_name = attribute_definition.name
143
+ attribute_value = read_attribute(attribute_name)
144
+ if attribute_definition.widgetlist?
145
+ attribute_value = attribute_value
146
+ .reject { |widget| referenced_ids.include?(widget.id) }
98
147
  .map { |widget| widget.copy_for_restore(referenced_ids) }
99
- else
100
- attr_value
101
148
  end
149
+ attributes[attribute_name] = attribute_value
102
150
  end
103
- attrs['_id'] = id
104
- self.class.new(attrs)
151
+ attributes['_id'] = id
152
+ self.class.new(attributes)
105
153
  end
106
154
 
107
155
  def clone
@@ -154,7 +202,7 @@ class BasicWidget
154
202
  CmsRestApi.get("revisions/#{workspace.base_revision_id}/objs/#{obj.id}")
155
203
  previous_widget_content = previous_obj_content["_widget_pool"]["#{id}"]
156
204
  previous_widget_content.delete_if do |attribute_name, _|
157
- type_of_attribute(attribute_name) == "widget"
205
+ type_of_attribute(attribute_name) == 'widgetlist'
158
206
  end
159
207
  CmsRestApi.put("workspaces/#{workspace.id}/objs/#{obj.id}",
160
208
  { obj: {_widget_pool: {id => previous_widget_content}} })
@@ -205,17 +253,27 @@ class BasicWidget
205
253
  # returns the entity ({Scrivito::BasicObj} or {Scrivito::BasicWidget}) that references this widget
206
254
  # @api public
207
255
  def container
208
- @container || cache_container_and_field_name_for_widget.first
256
+ @container || container_and_attribute_name.first
257
+ end
258
+
259
+ # returns the name of the widget attribute that references this widget
260
+ # @api public
261
+ def container_attribute_name
262
+ @container_attribute_name || container_and_attribute_name.second
209
263
  end
210
264
 
211
265
  # returns the name of the widget field that references this widget
212
266
  # @api public
267
+ # @deprecated
213
268
  def container_field_name
214
- @container_field_name || cache_container_and_field_name_for_widget.second
269
+ Scrivito::Deprecation.warn('Scrivito::BasicWidget#container_field_name is deprecated'\
270
+ ' and will be removed in a future version.'\
271
+ ' Please use Scrivito::BasicWidget#container_attribute_name instead.')
272
+ container_attribute_name
215
273
  end
216
274
 
217
- def container_field_index
218
- container[container_field_name].index(self)
275
+ def container_attribute_index
276
+ container[container_attribute_name].index(self)
219
277
  end
220
278
 
221
279
  def has_attribute?(key)
@@ -261,6 +319,14 @@ class BasicWidget
261
319
  obj_class_name
262
320
  end
263
321
 
322
+ def data_from_cms
323
+ if persisted?
324
+ super
325
+ else
326
+ raise_not_persisted_error
327
+ end
328
+ end
329
+
264
330
  private
265
331
 
266
332
  def workspace
@@ -271,14 +337,6 @@ class BasicWidget
271
337
  end
272
338
  end
273
339
 
274
- def data_from_cms
275
- if persisted?
276
- super
277
- else
278
- raise_not_persisted_error
279
- end
280
- end
281
-
282
340
  def raise_not_persisted_error
283
341
  raise ScrivitoError.new('Can not access a new widget before it has been saved')
284
342
  end
@@ -287,14 +345,22 @@ class BasicWidget
287
345
  obj.widget_data_for_revision(id, revision)
288
346
  end
289
347
 
290
- def cache_container_and_field_name_for_widget
291
- @cache_container_and_field_name_for_widget ||= obj.container_and_field_name_for_widget(id)
348
+ def container_and_attribute_name
349
+ @container_and_attribute_name ||= obj.find_container_and_attribute_name_for_widget(id)
292
350
  end
293
351
 
294
- def each_custom_attribute
295
- data_from_cms.all_custom_attributes.each do |attr_name|
296
- yield attr_name, read_attribute(attr_name), type_of_attribute(attr_name)
297
- end
352
+ def has_system_attribute?(attribute_name)
353
+ attribute_name == '_obj_class'
354
+ end
355
+
356
+ alias_method :has_public_system_attribute?, :has_system_attribute?
357
+
358
+ def type_of_system_attribute(attribute_name)
359
+ 'string' if attribute_name == 'obj_class'
360
+ end
361
+
362
+ def value_of_system_attribute(attribute_name)
363
+ data_from_cms.value_of(attribute_name)
298
364
  end
299
365
  end
300
366
 
@@ -4,6 +4,8 @@ module Scrivito
4
4
  # The Binary class represents the data stored in a binary attribute of an Obj
5
5
  # or Widget
6
6
  class Binary
7
+ attr_reader :id
8
+
7
9
  def initialize(id, public_content)
8
10
  @id = id
9
11
  @public_content = public_content
@@ -11,7 +13,7 @@ class Binary
11
13
 
12
14
  # @api public
13
15
  # Some Scrivito data is considered private, i.e. it is not currently intended
14
- # for the general public, for example content in a workspace that has not
16
+ # for the general public, for example content in a workspace that has not
15
17
  # been published yet.
16
18
  # @return [Boolean]
17
19
  def private?
@@ -53,8 +55,6 @@ class Binary
53
55
 
54
56
  private
55
57
 
56
- attr_reader :id
57
-
58
58
  def public_content?
59
59
  !!@public_content
60
60
  end
@@ -11,10 +11,12 @@ module Scrivito
11
11
  value
12
12
  end
13
13
 
14
- def write(key, value, options = {})
14
+ def write(key, value, expires_in = nil)
15
15
  transformed_key = transform_key(key)
16
- next_store.write(transformed_key, value, options) if next_store
17
- internal_write(transformed_key, value, options)
16
+ if forward_write?(key, value, expires_in)
17
+ next_store.write(transformed_key, value, expires_in)
18
+ end
19
+ internal_write(transformed_key, value, expires_in)
18
20
  end
19
21
 
20
22
  def fetch(key, &block)
@@ -40,13 +42,17 @@ module Scrivito
40
42
  raise NotImplementedError
41
43
  end
42
44
 
43
- def internal_write(key, value, options = {})
45
+ def internal_write(key, value, expires_in = nil)
44
46
  raise NotImplementedError
45
47
  end
46
48
 
47
49
  def transform_key(key)
48
50
  key
49
51
  end
52
+
53
+ def forward_write?(key, value, expires_in)
54
+ next_store.present?
55
+ end
50
56
  end
51
57
  end
52
58
  end
@@ -15,11 +15,14 @@ module Cache
15
15
 
16
16
  private
17
17
 
18
- delegate :read, :write, to: :internal_store, prefix: :internal
18
+ delegate :read, to: :internal_store, prefix: :internal
19
19
 
20
- def transform_key(key)
21
- # Workaround for rails/rails#15616. Will be fixed in Rails 4.2.
22
- Digest::SHA1.hexdigest(key)
20
+ def internal_write(key, value, expires_in = nil)
21
+ internal_store.write(key, value, expires_in: expires_in)
22
+ end
23
+
24
+ def forward_write?(key, value, expires_in)
25
+ super && !expires_in
23
26
  end
24
27
  end
25
28
  end
@@ -19,7 +19,7 @@ module Cache
19
19
  internal_store[key]
20
20
  end
21
21
 
22
- def internal_write(key, value, options = {})
22
+ def internal_write(key, value, expires_in = nil)
23
23
  internal_store[key] = value
24
24
  end
25
25
 
@@ -1,7 +1,7 @@
1
1
  module Scrivito
2
2
 
3
3
  #
4
- # @api private
4
+ # @api public
5
5
  #
6
6
  class ClassCollection
7
7
  include Enumerable
@@ -13,7 +13,7 @@ class ClassCollection
13
13
  #
14
14
  # @!method each
15
15
  # Iterates over the classes.
16
- # @api private
16
+ # @api public
17
17
  # @yieldparam [Class] class if a block is given.
18
18
  # @return [Enumerator] enumerator if no block is given.
19
19
  #
@@ -7,12 +7,13 @@ class ClientConfig < Struct.new(:obj, :editing_context, :lookup_context, :resour
7
7
 
8
8
  def to_json
9
9
  config = {}
10
- config[:current_page] = current_page_config
11
- config[:editing_context] = editing_context_config
12
- config[:i18n] = i18n_config
13
- config[:resource_dialog] = resource_dialog_config
14
- config[:user] = user_config
15
- config[:user_permissions] = user_permissions_config
10
+ config[:current_page] = current_page_config
11
+ config[:editing_context] = editing_context_config
12
+ config[:i18n] = i18n_config
13
+ config[:resource_dialog] = resource_dialog_config
14
+ config[:user] = user_config
15
+ config[:user_permissions] = user_permissions_config
16
+ config[:is_development_mode] = Rails.env.development?
16
17
  config.to_json
17
18
  end
18
19
 
@@ -205,7 +205,7 @@ module Scrivito
205
205
  %w[get head].each do |verb|
206
206
  blob_data = blob_datas[access][verb]
207
207
  CmsDataCache.cache.write(blob_data_cache_key(id, access, verb),
208
- blob_data, expires_in: blob_data['maxage'])
208
+ blob_data, blob_data['maxage'])
209
209
  end
210
210
  end
211
211
  end
@@ -242,7 +242,7 @@ module Scrivito
242
242
  def store_blob_metadata_in_cache(id, blob_metadata)
243
243
  max_age = blob_metadata.delete(:cache_control) =~ /max-age=(.*),/ && $1
244
244
  max_age = max_age.to_i if max_age
245
- CmsDataCache.cache.write(blob_metadata_cache_key(id), blob_metadata, expires_in: max_age)
245
+ CmsDataCache.cache.write(blob_metadata_cache_key(id), blob_metadata, max_age)
246
246
  end
247
247
 
248
248
  def blob_metadata_cache_key(id)
@@ -1,14 +1,24 @@
1
1
  module Scrivito
2
2
 
3
3
  # this class is the server-side equivalent of the JavaScript class `cms_field_element`
4
- class CmsFieldTag < Struct.new(
5
- :view, :tag_name, :obj_or_widget, :field_name)
4
+ class CmsFieldTag < Struct.new(:view, :tag_name, :obj_or_widget, :field_name)
5
+ FIELD_TYPES_WITH_ORIGINAL_CONTENT = %w[
6
+ binary
7
+ date
8
+ enum
9
+ html
10
+ multienum
11
+ reference
12
+ referencelist
13
+ string
14
+ text
15
+ ]
6
16
 
7
17
  include TagRenderer
8
18
 
9
19
  def content(&block)
10
- if field_type == 'widget'
11
- raise ArgumentError, 'No block allowed for widget fields' if block_given?
20
+ if field_type == 'widgetlist'
21
+ raise ArgumentError, 'No block allowed for widgetlist fields' if block_given?
12
22
  modifications = modification_info || []
13
23
  rendered_widgets = default_content.each_with_index.map do |widget, index|
14
24
  WidgetTag.new(view, widget, modifications[index]).render
@@ -30,7 +40,7 @@ class CmsFieldTag < Struct.new(
30
40
  }
31
41
 
32
42
  modification = comparison.modification_for_attribute(obj_or_widget, field_name)
33
- if modification == Modification::EDITED && field_type != 'widget'
43
+ if modification == Modification::EDITED && field_type != 'widgetlist'
34
44
  options['field-modification'] = modification
35
45
  end
36
46
 
@@ -48,11 +58,8 @@ class CmsFieldTag < Struct.new(
48
58
  options['private-field-original-content'] = encoded_content
49
59
  end
50
60
 
51
- if field_type == 'widget'
52
- valid_widget_classes = obj_or_widget.valid_widget_classes_for(field_name.to_s)
53
- if valid_widget_classes
54
- options['private-field-widget-allowed-classes'] = valid_widget_classes.to_json
55
- end
61
+ if field_type == 'widgetlist'
62
+ options['private-field-widget-allowed-classes'] = build_valid_widget_classes.to_json
56
63
  end
57
64
 
58
65
  options
@@ -65,10 +72,23 @@ class CmsFieldTag < Struct.new(
65
72
 
66
73
  private
67
74
 
75
+ def build_valid_widget_classes
76
+ obj_or_widget.valid_widget_class_names_for(field_name).map do |widget_class|
77
+ {
78
+ name: widget_class,
79
+ description: description_for_widget_class(widget_class),
80
+ }
81
+ end
82
+ end
83
+
68
84
  def field_type
69
85
  @field_type ||= obj_or_widget.type_of_attribute(field_name)
70
86
  end
71
87
 
88
+ def description_for_widget_class(class_name)
89
+ class_name.constantize.description_for_editor
90
+ end
91
+
72
92
  def default_content
73
93
  calculate_content_and_modification
74
94
  @content_and_modification.first
@@ -91,18 +111,6 @@ class CmsFieldTag < Struct.new(
91
111
  Workspace.current.id
92
112
  end
93
113
 
94
- FIELD_TYPES_WITH_ORIGINAL_CONTENT = %w[
95
- enum
96
- html
97
- multienum
98
- reference
99
- referencelist
100
- string
101
- text
102
- binary
103
- date
104
- ]
105
-
106
114
  def authenticated_editor?
107
115
  editing_context.authenticated_editor?
108
116
  end
@@ -113,10 +121,10 @@ class CmsFieldTag < Struct.new(
113
121
 
114
122
  def original_content(field_type, field_value, raw_value)
115
123
  case field_type
116
- when 'reference' then field_value.try(:id)
124
+ when 'binary' then field_value && {}
125
+ when 'date' then raw_value.try(:utc).try(:iso8601)
126
+ when 'reference' then field_value.try(:id)
117
127
  when 'referencelist' then field_value.map(&:id)
118
- when 'binary' then field_value && {}
119
- when 'date' then raw_value.try(:utc).try(:iso8601)
120
128
  else field_value
121
129
  end
122
130
  end