scrivito_sdk 0.50.1 → 0.60.0.rc1

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