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.
- checksums.yaml +4 -4
- data/app/controllers/scrivito/objs_controller.rb +25 -21
- data/app/controllers/scrivito/ui_controller.rb +1 -1
- data/app/controllers/scrivito/webservice_controller.rb +1 -1
- data/app/models/scrivito/content_widget.rb +1 -3
- data/app/views/cms/index.html.erb +3 -0
- data/app/views/scrivito/objs/destroy.json.jbuilder +1 -0
- data/app/views/scrivito/objs/show.json.jbuilder +2 -0
- data/app/views/scrivito/objs/update.json.jbuilder +1 -1
- data/app/views/scrivito/objs/{widget_modification.json.jbuilder → widget.json.jbuilder} +0 -0
- data/config/ca-bundle.crt +24 -219
- data/config/precedence_routes.rb +60 -0
- data/config/routes.rb +13 -46
- data/lib/assets/javascripts/scrivito_ui.js +360 -202
- data/lib/assets/stylesheets/scrivito_sdk.css +1 -1
- data/lib/assets/stylesheets/scrivito_ui.css +1 -1
- data/lib/generators/scrivito/install/templates/app/models/download.rb +1 -3
- data/lib/generators/scrivito/install/templates/app/models/headline_widget.rb +1 -5
- data/lib/generators/scrivito/install/templates/app/models/image.rb +1 -3
- data/lib/generators/scrivito/install/templates/app/models/image_widget.rb +1 -5
- data/lib/generators/scrivito/install/templates/app/models/page.rb +3 -6
- data/lib/generators/scrivito/install/templates/app/models/text_widget.rb +1 -5
- data/lib/generators/scrivito/install/templates/scrivito/migrate/install_scrivito_migration.rb +1 -25
- data/lib/generators/scrivito/migration/templates/migration.erb +2 -26
- data/lib/generators/scrivito/page/page_generator.rb +0 -11
- data/lib/generators/scrivito/page/templates/model.erb +3 -7
- data/lib/generators/scrivito/widget/widget_generator.rb +0 -11
- data/lib/scrivito/attribute.rb +10 -16
- data/lib/scrivito/attribute_collection.rb +4 -0
- data/lib/scrivito/attribute_content.rb +239 -161
- data/lib/scrivito/attribute_definition.rb +16 -10
- data/lib/scrivito/attribute_definition_collection.rb +7 -4
- data/lib/scrivito/attribute_definition_migrator.rb +1 -3
- data/lib/scrivito/attribute_deserializer.rb +111 -0
- data/lib/scrivito/attribute_serializer.rb +189 -0
- data/lib/scrivito/basic_obj.rb +113 -78
- data/lib/scrivito/basic_widget.rb +103 -37
- data/lib/scrivito/binary.rb +3 -3
- data/lib/scrivito/cache/chainable.rb +10 -4
- data/lib/scrivito/cache/file_store.rb +7 -4
- data/lib/scrivito/cache/ram_store.rb +1 -1
- data/lib/scrivito/class_collection.rb +2 -2
- data/lib/scrivito/client_config.rb +7 -6
- data/lib/scrivito/cms_backend.rb +2 -2
- data/lib/scrivito/cms_field_tag.rb +33 -25
- data/lib/scrivito/cms_rest_api/{attribute_serializer.rb → legacy_attribute_serializer.rb} +1 -1
- data/lib/scrivito/cms_routing.rb +20 -14
- data/lib/scrivito/configuration.rb +1 -1
- data/lib/scrivito/controller_actions.rb +12 -4
- data/lib/scrivito/date_attribute.rb +8 -0
- data/lib/scrivito/diff.rb +1 -1
- data/lib/scrivito/editing_context.rb +1 -1
- data/lib/scrivito/editing_context_middleware.rb +1 -1
- data/lib/scrivito/errors.rb +4 -0
- data/lib/scrivito/link_parser.rb +6 -1
- data/lib/scrivito/migrations/cms_backend.rb +17 -60
- data/lib/scrivito/migrations/migration_store.rb +0 -1
- data/lib/scrivito/model_library.rb +6 -6
- data/lib/scrivito/obj_class.rb +34 -48
- data/lib/scrivito/obj_class_collection.rb +21 -3
- data/lib/scrivito/obj_create_params_parser.rb +2 -1
- data/lib/scrivito/obj_data.rb +9 -103
- data/lib/scrivito/obj_data_from_hash.rb +19 -21
- data/lib/scrivito/obj_data_from_service.rb +28 -48
- data/lib/scrivito/obj_params_parser.rb +14 -14
- data/lib/scrivito/obj_search_enumerator.rb +1 -1
- data/lib/scrivito/obj_update_params_parser.rb +2 -2
- data/lib/scrivito/restriction_set.rb +1 -1
- data/lib/scrivito/sdk_engine.rb +2 -2
- data/lib/scrivito/test_request.rb +10 -0
- data/lib/scrivito/type_computer.rb +5 -1
- data/lib/scrivito/widget_collection.rb +15 -0
- data/lib/scrivito/widget_garbage_collection.rb +1 -1
- data/lib/scrivito/widget_tag.rb +2 -0
- data/lib/scrivito/workspace.rb +2 -1
- data/lib/scrivito/workspace_data_from_service.rb +1 -0
- data/lib/scrivito_sdk.rb +17 -1
- metadata +36 -34
- data/app/views/scrivito/objs/modification.json.jbuilder +0 -1
- data/config/cms_routes.rb +0 -22
- data/lib/generators/scrivito/page/templates/migration.erb +0 -9
- data/lib/generators/scrivito/widget/templates/migration.erb +0 -7
@@ -3,36 +3,42 @@ module Scrivito
|
|
3
3
|
#
|
4
4
|
# This class represents a definition of an attribute.
|
5
5
|
#
|
6
|
-
# @api
|
6
|
+
# @api public
|
7
7
|
#
|
8
8
|
class AttributeDefinition
|
9
9
|
#
|
10
10
|
# @!attribute [r] name
|
11
|
-
# @api
|
12
|
-
# @return [
|
11
|
+
# @api public
|
12
|
+
# @return [String] the name of the attribute.
|
13
13
|
#
|
14
14
|
# @!attribute [r] type
|
15
|
-
# @api
|
16
|
-
# @return [
|
15
|
+
# @api public
|
16
|
+
# @return [String] the type of the attribute.
|
17
17
|
#
|
18
18
|
attr_reader :name, :type
|
19
19
|
|
20
20
|
def initialize(name, type, options = {})
|
21
|
-
@name, @type, @options = name, type, options
|
21
|
+
@name, @type, @options = name.to_s, type.to_s, options.with_indifferent_access
|
22
22
|
end
|
23
23
|
|
24
24
|
#
|
25
25
|
# Allowed values for an attribute.
|
26
26
|
#
|
27
|
-
# @api
|
28
|
-
# @return [
|
29
|
-
# If no values have been specified, then an empty array will be returned.
|
27
|
+
# @api public
|
28
|
+
# @return [Array<String>] allowed values if type is +enum+ or +multienum+ or an empty array
|
29
|
+
# otherwise. If no values have been specified, then an empty array will be returned.
|
30
30
|
#
|
31
31
|
def values
|
32
|
-
if type ==
|
32
|
+
if type == 'enum' || type == 'multienum'
|
33
33
|
@options[:values] || []
|
34
|
+
else
|
35
|
+
[]
|
34
36
|
end
|
35
37
|
end
|
38
|
+
|
39
|
+
def widgetlist?
|
40
|
+
type == 'widgetlist'
|
41
|
+
end
|
36
42
|
end
|
37
43
|
|
38
44
|
end
|
@@ -3,11 +3,14 @@ module Scrivito
|
|
3
3
|
#
|
4
4
|
# This class represents a collection of attribute definitions.
|
5
5
|
#
|
6
|
-
# @api
|
6
|
+
# @api public
|
7
7
|
#
|
8
8
|
class AttributeDefinitionCollection
|
9
9
|
include Enumerable
|
10
10
|
|
11
|
+
# Compatibility for Object#blank?
|
12
|
+
delegate :empty?, to: :attribute_definitions
|
13
|
+
|
11
14
|
def initialize(attribute_definitions)
|
12
15
|
@attribute_definitions = attribute_definitions
|
13
16
|
end
|
@@ -15,7 +18,7 @@ class AttributeDefinitionCollection
|
|
15
18
|
#
|
16
19
|
# Iterates over the attribute definitions.
|
17
20
|
#
|
18
|
-
# @api
|
21
|
+
# @api public
|
19
22
|
# @yieldparam [Scrivito:AttributeDefinition] attribute_definition
|
20
23
|
#
|
21
24
|
def each(&block)
|
@@ -25,12 +28,12 @@ class AttributeDefinitionCollection
|
|
25
28
|
#
|
26
29
|
# Find definition of an attribute.
|
27
30
|
#
|
28
|
-
# @api
|
31
|
+
# @api public
|
29
32
|
# @param name [Symbol, String] the name of the attribute.
|
30
33
|
# @return [Scrivito::AttributeDefinition, nil] attribute definition if found or +nil+ otherwise.
|
31
34
|
#
|
32
35
|
def [](name)
|
33
|
-
attribute_definitions[name.
|
36
|
+
attribute_definitions[name.to_s]
|
34
37
|
end
|
35
38
|
|
36
39
|
private
|
@@ -30,9 +30,7 @@ class AttributeDefinitionMigrator
|
|
30
30
|
with_padding do
|
31
31
|
say %{[warning] Model "#{model_class}" has no corresponding ObjClass in published workspace.
|
32
32
|
Ignore this message if it is an abstract class and should not have an ObjClass by design.
|
33
|
-
|
34
|
-
- Is there a pending migration which will create an ObjClass for "#{model_class}"? Please run and publish this migration.
|
35
|
-
- Or maybe "#{model_class}" is not used anymore and file #{relative_path} can be removed?
|
33
|
+
Or maybe "#{model_class}" is not used anymore and file #{relative_path} can be removed?
|
36
34
|
}, :yellow
|
37
35
|
end
|
38
36
|
say_status :skipping, relative_path
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module Scrivito
|
2
|
+
|
3
|
+
class AttributeDeserializer < Struct.new(:model, :workspace)
|
4
|
+
def deserialize(attribute_value, attribute_definition)
|
5
|
+
case attribute_definition.type
|
6
|
+
when 'binary' then deserialize_binary_value(attribute_value)
|
7
|
+
when 'date' then deserialize_date_value(attribute_value)
|
8
|
+
when 'enum' then deserialize_enum_value(attribute_value, attribute_definition)
|
9
|
+
when 'html' then deserialize_html_value(attribute_value)
|
10
|
+
when 'link' then deserialize_link_value(attribute_value)
|
11
|
+
when 'linklist' then deserialize_linklist_value(attribute_value)
|
12
|
+
when 'multienum' then deserialize_multienum_value(attribute_value, attribute_definition)
|
13
|
+
when 'reference' then deserialize_reference_value(attribute_value)
|
14
|
+
when 'referencelist' then deserialize_referencelist_value(attribute_value)
|
15
|
+
when 'widget' then deserialize_legacy_widget_value(attribute_value, attribute_definition)
|
16
|
+
when 'widgetlist' then deserialize_widgetlist_value(attribute_value, attribute_definition)
|
17
|
+
else attribute_value
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def deserialize_binary_value(attribute_value)
|
24
|
+
if attribute_value && (id = attribute_value['id'])
|
25
|
+
Binary.new(id, workspace.published?)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def deserialize_date_value(attribute_value)
|
30
|
+
DateAttribute.parse(attribute_value) if attribute_value
|
31
|
+
end
|
32
|
+
|
33
|
+
def deserialize_enum_value(attribute_value, attribute_definition)
|
34
|
+
attribute_value if attribute_definition.values.include?(attribute_value)
|
35
|
+
end
|
36
|
+
|
37
|
+
def deserialize_html_value(attribute_value)
|
38
|
+
StringTagging.tag_as_html(attribute_value)
|
39
|
+
end
|
40
|
+
|
41
|
+
def deserialize_link_value(attribute_value)
|
42
|
+
return unless attribute_value
|
43
|
+
if attribute_value['destination']
|
44
|
+
deserialize_internal_link(attribute_value)
|
45
|
+
else
|
46
|
+
deserialize_external_link(attribute_value)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def deserialize_linklist_value(link_definitions)
|
51
|
+
if link_definitions.present?
|
52
|
+
link_definitions = link_definitions.map(&:with_indifferent_access)
|
53
|
+
|
54
|
+
object_ids = link_definitions.map { |link_data| link_data[:destination] }.compact.uniq
|
55
|
+
objects = object_ids.empty? ? [] : workspace.objs.find(object_ids)
|
56
|
+
link_definitions.each_with_object([]) do |link_data, links|
|
57
|
+
obj = objects.detect { |o| o && o.id == link_data[:destination] }
|
58
|
+
link = Link.new(link_data.merge(obj: obj))
|
59
|
+
links << link if link.resolved?
|
60
|
+
end
|
61
|
+
else
|
62
|
+
[]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def deserialize_multienum_value(attribute_value, attribute_definition)
|
67
|
+
attribute_value & attribute_definition.values
|
68
|
+
end
|
69
|
+
|
70
|
+
def deserialize_reference_value(attribute_value)
|
71
|
+
workspace.objs.find([attribute_value]).first
|
72
|
+
end
|
73
|
+
|
74
|
+
def deserialize_referencelist_value(attribute_value)
|
75
|
+
workspace.objs.find(attribute_value).compact
|
76
|
+
end
|
77
|
+
|
78
|
+
def deserialize_internal_link(attribute_value)
|
79
|
+
Link.new(attribute_value.slice('title', 'query', 'fragment', 'target').symbolize_keys
|
80
|
+
.merge(obj: workspace.objs.find(attribute_value['destination'])))
|
81
|
+
rescue ResourceNotFound
|
82
|
+
end
|
83
|
+
|
84
|
+
def deserialize_external_link(attribute_value)
|
85
|
+
Link.new(attribute_value.slice('url', 'title', 'target').symbolize_keys)
|
86
|
+
end
|
87
|
+
|
88
|
+
def deserialize_legacy_widget_value(widget_ids, attribute_definition)
|
89
|
+
if workspace.uses_obj_classes
|
90
|
+
deserialize_widgetlist_value(widget_ids, attribute_definition)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def deserialize_widgetlist_value(widget_ids, attribute_definition)
|
95
|
+
if widget_ids
|
96
|
+
widget_ids.map { |widget_id| deserialize_widget_value(widget_id, attribute_definition) }
|
97
|
+
else
|
98
|
+
[]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def deserialize_widget_value(widget_id, attribute_definition)
|
103
|
+
model.widget_from_pool(widget_id).tap do |widget|
|
104
|
+
raise ScrivitoError, "Widget with ID #{widget_id} not found!" unless widget
|
105
|
+
widget.container = model
|
106
|
+
widget.container_attribute_name = attribute_definition.name
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
@@ -0,0 +1,189 @@
|
|
1
|
+
module Scrivito
|
2
|
+
|
3
|
+
class AttributeSerializer
|
4
|
+
def serialize(attributes, attribute_definitions)
|
5
|
+
return attributes if attributes.blank?
|
6
|
+
serialized_attributes = attributes.map do |attribute_name, attribute_value|
|
7
|
+
serialize_attribute(attribute_name, attribute_value, attribute_definitions)
|
8
|
+
end
|
9
|
+
Hash[serialized_attributes]
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def serialize_attribute(attribute_name, attribute_value, attribute_definitions)
|
15
|
+
attribute_name = attribute_name.to_s
|
16
|
+
if attribute_name.starts_with?('_')
|
17
|
+
serialize_system_attribute(attribute_name, attribute_value)
|
18
|
+
else
|
19
|
+
attribute_definition = attribute_definitions[attribute_name]
|
20
|
+
serialize_custom_attribute(attribute_name, attribute_value, attribute_definition)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def serialize_system_attribute(attribute_name, attribute_value)
|
25
|
+
if %w[
|
26
|
+
_id
|
27
|
+
_obj_class
|
28
|
+
_path
|
29
|
+
_permalink
|
30
|
+
].include?(attribute_name)
|
31
|
+
[attribute_name, attribute_value.to_s]
|
32
|
+
else
|
33
|
+
raise ScrivitoError, "Unknown attribute '#{attribute_name}'"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def serialize_custom_attribute(attribute_name, attribute_value, attribute_definition)
|
38
|
+
raise ScrivitoError, "Unknown attribute '#{attribute_name}'" unless attribute_definition
|
39
|
+
attribute_value = case attribute_type = attribute_definition.type
|
40
|
+
when 'binary' then serialize_binary_value(attribute_value, attribute_definition)
|
41
|
+
when 'date' then serialize_date_value(attribute_value, attribute_definition)
|
42
|
+
when 'enum' then serialize_enum_value(attribute_value, attribute_definition)
|
43
|
+
when 'html' then serialize_html_value(attribute_value)
|
44
|
+
when 'link' then serialize_link_value(attribute_value, attribute_definition)
|
45
|
+
when 'linklist' then serialize_linklist_value(attribute_value, attribute_definition)
|
46
|
+
when 'multienum' then serialize_multienum_value(attribute_value, attribute_definition)
|
47
|
+
when 'reference' then serialize_reference_value(attribute_value)
|
48
|
+
when 'referencelist' then serialize_referencelist_value(attribute_value, attribute_definition)
|
49
|
+
when 'string' then serialize_string_value(attribute_value)
|
50
|
+
when 'stringlist' then serialize_stringlist_value(attribute_value, attribute_definition)
|
51
|
+
when 'widget' then serialize_widget_value(attribute_value, attribute_definition)
|
52
|
+
when 'widgetlist' then serialize_widgetlist_value(attribute_value, attribute_definition)
|
53
|
+
end
|
54
|
+
[attribute_name, [serialize_attribute_type(attribute_type), attribute_value]]
|
55
|
+
end
|
56
|
+
|
57
|
+
def serialize_attribute_type(attribute_type)
|
58
|
+
case attribute_type
|
59
|
+
when 'enum' then 'string'
|
60
|
+
when 'multienum' then 'stringlist'
|
61
|
+
else attribute_type
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def serialize_binary_value(attribute_value, attribute_definition)
|
66
|
+
return unless attribute_value
|
67
|
+
case attribute_value
|
68
|
+
when File then CmsRestApi.upload_file(attribute_value)
|
69
|
+
when UploadedBinary then attribute_value.params
|
70
|
+
else
|
71
|
+
raise_validation_error(attribute_definition.name,
|
72
|
+
'an instance of File, Scrivito::UploadedBinary or nil', attribute_value)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def serialize_date_value(attribute_value, attribute_definition)
|
77
|
+
return unless attribute_value
|
78
|
+
attribute_value = DateAttribute.serialize(attribute_value)
|
79
|
+
unless attribute_value
|
80
|
+
raise_validation_error(attribute_definition.name, 'an instance of Date or Time',
|
81
|
+
attribute_value)
|
82
|
+
end
|
83
|
+
attribute_value
|
84
|
+
end
|
85
|
+
|
86
|
+
def serialize_enum_value(attribute_value, attribute_definition)
|
87
|
+
return unless attribute_value
|
88
|
+
attribute_value = attribute_value.to_s
|
89
|
+
attribute_values = attribute_definition.values
|
90
|
+
if attribute_values.include?(attribute_value)
|
91
|
+
attribute_value
|
92
|
+
else
|
93
|
+
raise_validation_error(attribute_definition.name, format_array(attribute_values),
|
94
|
+
attribute_value)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def serialize_html_value(attribute_value)
|
99
|
+
attribute_value.to_s
|
100
|
+
end
|
101
|
+
|
102
|
+
def serialize_link_value(attribute_value, attribute_definition)
|
103
|
+
return unless attribute_value
|
104
|
+
if attribute_value.is_a?(Link)
|
105
|
+
attribute_value.to_cms_api_linklist_params
|
106
|
+
else
|
107
|
+
raise_validation_error(attribute_definition.name, 'an instance of Scrivito::Link',
|
108
|
+
attribute_value)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def serialize_linklist_value(attribute_value, attribute_definition)
|
113
|
+
if attribute_value.is_a?(Enumerable) && attribute_value.all? { |item| item.is_a?(Link) }
|
114
|
+
attribute_value.map(&:to_cms_api_linklist_params)
|
115
|
+
else
|
116
|
+
raise_validation_error(attribute_definition.name,
|
117
|
+
'Enumerable containing instances of Scrivito::Link', attribute_value)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def serialize_multienum_value(attribute_value, attribute_definition)
|
122
|
+
attribute_value = attribute_value.map(&:to_s)
|
123
|
+
attribute_values = attribute_definition.values
|
124
|
+
forbidden_values = attribute_value - attribute_values
|
125
|
+
if forbidden_values.any?
|
126
|
+
raise_validation_error(attribute_definition.name, format_array(attribute_values),
|
127
|
+
attribute_value)
|
128
|
+
end
|
129
|
+
attribute_value
|
130
|
+
end
|
131
|
+
|
132
|
+
def serialize_reference_value(attribute_value)
|
133
|
+
return unless attribute_value
|
134
|
+
attribute_value.is_a?(BasicObj) ? attribute_value.id : attribute_value.to_s
|
135
|
+
end
|
136
|
+
|
137
|
+
def serialize_referencelist_value(attribute_value, attribute_definition)
|
138
|
+
if attribute_value.is_a?(Enumerable)
|
139
|
+
attribute_value.map(&method(:serialize_reference_value))
|
140
|
+
else
|
141
|
+
raise_validation_error(attribute_definition.name,
|
142
|
+
'Enumerable containing instances of String or Scrivito::BasicWidget', attribute_value)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def serialize_string_value(attribute_value)
|
147
|
+
attribute_value.to_s
|
148
|
+
end
|
149
|
+
|
150
|
+
def serialize_stringlist_value(attribute_value, attribute_definition)
|
151
|
+
if attribute_value.is_a?(Enumerable)
|
152
|
+
attrubute_value.map(&:to_s)
|
153
|
+
else
|
154
|
+
raise_validation_error(attribute_definition.name, 'Enumerable containing instances of String',
|
155
|
+
attribute_value)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def serialize_widget_value(attribute_value, attribute_definition)
|
160
|
+
return unless attribute_value
|
161
|
+
if attribute_value.is_a?(BasicWidget)
|
162
|
+
attribute_value.id
|
163
|
+
else
|
164
|
+
raise_validation_error(attribute_definition.name, 'an instance of Scrivito::BasicWidget',
|
165
|
+
attribute_value)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def serialize_widgetlist_value(attribute_value, attribute_definition)
|
170
|
+
if attribute_value.is_a?(Enumerable) && attribute_value.all? { |item| item.is_a?(BasicWidget) }
|
171
|
+
attribute_value.map(&:id)
|
172
|
+
else
|
173
|
+
raise_validation_error(attribute_definition.name,
|
174
|
+
'Enumerable containing instances of Scrivito::BasicWidget', attribute_value)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def raise_validation_error(attribute_name, expected_value, actual_value)
|
179
|
+
error_message = "Unexpected value #{actual_value.inspect} for attribute '#{attribute_name}'."\
|
180
|
+
" Expected: #{expected_value}."
|
181
|
+
raise ClientError.new(error_message, 412)
|
182
|
+
end
|
183
|
+
|
184
|
+
def format_array(values)
|
185
|
+
values.map(&:inspect).to_sentence(two_words_connector: ' or ', last_word_connector: ' or ')
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
data/lib/scrivito/basic_obj.rb
CHANGED
@@ -3,13 +3,31 @@ require 'ostruct'
|
|
3
3
|
require 'active_model/naming'
|
4
4
|
|
5
5
|
module Scrivito
|
6
|
+
#
|
6
7
|
# The abstract base class for cms objects.
|
7
8
|
#
|
8
9
|
# @note Please do not use {Scrivito::BasicObj} directly,
|
9
10
|
# as it is intended as an abstract class.
|
10
11
|
# Always use {Obj} or a subclass of {Obj}.
|
11
12
|
# @api public
|
13
|
+
#
|
12
14
|
class BasicObj
|
15
|
+
# @!parse extend Scrivito::AttributeContent::ClassMethods
|
16
|
+
|
17
|
+
PublicSystemAttributeDefinition = Class.new(AttributeDefinition)
|
18
|
+
|
19
|
+
SYSTEM_ATTRIBUTES = AttributeDefinitionCollection.new(
|
20
|
+
'_id' => PublicSystemAttributeDefinition.new(:_id, :string),
|
21
|
+
'_last_changed' => PublicSystemAttributeDefinition.new(:_last_changed, :date),
|
22
|
+
'_obj_class' => PublicSystemAttributeDefinition.new(:_obj_class, :string),
|
23
|
+
'_path' => PublicSystemAttributeDefinition.new(:_path, :string),
|
24
|
+
'_permalink' => PublicSystemAttributeDefinition.new(:_permalink, :string),
|
25
|
+
|
26
|
+
'_conflicts' => AttributeDefinition.new(:_conflicts, nil),
|
27
|
+
'_modification' => AttributeDefinition.new(:_modification, nil),
|
28
|
+
'_widget_pool' => AttributeDefinition.new(:_widget_pool, nil),
|
29
|
+
)
|
30
|
+
|
13
31
|
UNIQ_ATTRIBUTES = %w[
|
14
32
|
_id
|
15
33
|
_path
|
@@ -37,6 +55,7 @@ module Scrivito
|
|
37
55
|
@_type_computer = nil
|
38
56
|
end
|
39
57
|
|
58
|
+
#
|
40
59
|
# Create a new {Scrivito::BasicObj Obj} in the cms
|
41
60
|
#
|
42
61
|
# This allows you to set the different attributes types of an obj by
|
@@ -92,13 +111,18 @@ module Scrivito
|
|
92
111
|
# @api public
|
93
112
|
# @param [Hash] attributes
|
94
113
|
# @return [Obj] the newly created {Scrivito::BasicObj Obj}
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
114
|
+
#
|
115
|
+
def self.create(attributes = {})
|
116
|
+
if obj_class = extract_obj_class_from_attributes(attributes)
|
117
|
+
obj_class.create(attributes)
|
118
|
+
else
|
119
|
+
attributes = prepare_attributes_for_instantiation(attributes)
|
120
|
+
api_attributes, widget_properties = prepare_attributes_for_rest_api(attributes)
|
121
|
+
json = Workspace.current.api_request(:post, '/objs', obj: api_attributes)
|
122
|
+
obj = find(json['_id'])
|
123
|
+
CmsRestApi::WidgetExtractor.notify_persisted_widgets(obj, widget_properties)
|
124
|
+
obj
|
125
|
+
end
|
102
126
|
end
|
103
127
|
|
104
128
|
# Create a new {Scrivito::BasicObj Obj} instance with the given values and attributes.
|
@@ -171,7 +195,6 @@ module Scrivito
|
|
171
195
|
if self == ::Obj
|
172
196
|
Workspace.current.objs.where(field, operator, value, boost)
|
173
197
|
else
|
174
|
-
assert_has_obj_class('.where')
|
175
198
|
Workspace.current.objs.where(:_obj_class, :equals, name)
|
176
199
|
.and(field, operator, value, boost)
|
177
200
|
end
|
@@ -188,7 +211,6 @@ module Scrivito
|
|
188
211
|
if self == ::Obj
|
189
212
|
Workspace.current.objs.all
|
190
213
|
else
|
191
|
-
assert_has_obj_class('.all')
|
192
214
|
find_all_by_obj_class(name)
|
193
215
|
end
|
194
216
|
end
|
@@ -401,7 +423,6 @@ module Scrivito
|
|
401
423
|
root
|
402
424
|
end
|
403
425
|
|
404
|
-
# @api private
|
405
426
|
def self.generate_widget_pool_id
|
406
427
|
SecureRandom.hex(4)
|
407
428
|
end
|
@@ -489,12 +510,8 @@ module Scrivito
|
|
489
510
|
#
|
490
511
|
# @return true if this Obj represents a binary resource.
|
491
512
|
def binary?
|
492
|
-
|
493
|
-
|
494
|
-
else
|
495
|
-
blob_attribute = obj_class.attributes['blob']
|
496
|
-
blob_attribute && blob_attribute.type == 'binary'
|
497
|
-
end
|
513
|
+
blob_attribute_definition = attribute_definitions['blob']
|
514
|
+
blob_attribute_definition.present? && blob_attribute_definition.type == 'binary'
|
498
515
|
end
|
499
516
|
|
500
517
|
# Returns true if this object is the root object.
|
@@ -523,7 +540,7 @@ module Scrivito
|
|
523
540
|
end
|
524
541
|
|
525
542
|
# This should be a SET, because it's faster in this particular case.
|
526
|
-
|
543
|
+
SYSTEM_KEYS = Set.new(%w[
|
527
544
|
body
|
528
545
|
_id
|
529
546
|
_last_changed
|
@@ -533,12 +550,12 @@ module Scrivito
|
|
533
550
|
title
|
534
551
|
])
|
535
552
|
|
536
|
-
# Returns the value of an
|
553
|
+
# Returns the value of an system or custom attribute specified by its name.
|
537
554
|
# Passing an invalid key will not raise an error, but return +nil+.
|
538
555
|
# @api public
|
539
556
|
def [](key)
|
540
557
|
key = key.to_s
|
541
|
-
if
|
558
|
+
if SYSTEM_KEYS.include?(key)
|
542
559
|
read_attribute(key)
|
543
560
|
else
|
544
561
|
super
|
@@ -548,7 +565,7 @@ module Scrivito
|
|
548
565
|
def has_attribute?(key)
|
549
566
|
key = key.to_s
|
550
567
|
|
551
|
-
if
|
568
|
+
if SYSTEM_KEYS.include?(key)
|
552
569
|
true
|
553
570
|
else
|
554
571
|
super
|
@@ -596,14 +613,14 @@ module Scrivito
|
|
596
613
|
|
597
614
|
def modification(revision=workspace.base_revision)
|
598
615
|
return Modification::UNMODIFIED unless revision
|
599
|
-
|
600
|
-
obj_data_from_revision = cms_data_for_revision(revision)
|
616
|
+
return read_attribute('_modification') if revision == workspace.base_revision
|
601
617
|
|
602
618
|
if deleted?(revision)
|
603
619
|
Modification::DELETED
|
604
620
|
elsif new?(revision)
|
605
621
|
Modification::NEW
|
606
622
|
else # Edited
|
623
|
+
obj_data_from_revision = cms_data_for_revision(revision)
|
607
624
|
if obj_data_from_revision.present?
|
608
625
|
if data_from_cms == obj_data_from_revision
|
609
626
|
Modification::UNMODIFIED
|
@@ -789,10 +806,10 @@ module Scrivito
|
|
789
806
|
end
|
790
807
|
|
791
808
|
widget_copy = widget.copy_for_restore(read_widget_pool.keys)
|
792
|
-
|
809
|
+
attribute_name = widget.container_attribute_name
|
793
810
|
current_container = widgets[container.id] || self
|
794
|
-
current_container.update(
|
795
|
-
current_container[
|
811
|
+
current_container.update(attribute_name =>
|
812
|
+
current_container[attribute_name].insert(widget.container_attribute_index, widget_copy))
|
796
813
|
end
|
797
814
|
|
798
815
|
def mark_resolved
|
@@ -800,16 +817,17 @@ module Scrivito
|
|
800
817
|
reload
|
801
818
|
end
|
802
819
|
|
803
|
-
def
|
804
|
-
if
|
805
|
-
return [self,
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
820
|
+
def find_container_and_attribute_name_for_widget(widget_id)
|
821
|
+
if attribute_name = find_attribute_containing_widget(widget_id)
|
822
|
+
return [self, attribute_name]
|
823
|
+
end
|
824
|
+
|
825
|
+
all_widgets_from_pool.each do |container_widget|
|
826
|
+
if attribute_name = container_widget.find_attribute_containing_widget(widget_id)
|
827
|
+
return [container_widget, attribute_name]
|
811
828
|
end
|
812
829
|
end
|
830
|
+
|
813
831
|
[nil, nil]
|
814
832
|
end
|
815
833
|
|
@@ -841,34 +859,16 @@ module Scrivito
|
|
841
859
|
raise ScrivitoError.new('Could not generate a new unused widget id')
|
842
860
|
end
|
843
861
|
|
844
|
-
#
|
845
|
-
# Generates a +Hash+ containing all public attributes. The hash will _not_ include attributes,
|
846
|
-
# which are read-only or used for internal purpose.
|
847
|
-
#
|
848
|
-
# @api private
|
849
|
-
# @return [Hash] a hash containing all public attributes.
|
850
|
-
#
|
851
|
-
# @example
|
852
|
-
# old_obj = Obj.homepage
|
853
|
-
# attrs = old_obj.to_h
|
854
|
-
# puts attrs
|
855
|
-
# #=> {"_obj_class"=>"Publication", "_path"=>"/", "_id"=>"f64a68258ca15faf", "_widget_pool"=>{}}
|
856
|
-
# old_obj.destroy
|
857
|
-
#
|
858
|
-
# new_obj = Obj.create(attrs)
|
859
|
-
# puts new_obj == old_obj
|
860
|
-
# #=> true
|
861
|
-
#
|
862
|
-
def to_h
|
863
|
-
data_from_cms.to_h.except(*GENERATED_ATTRIBUTES)
|
864
|
-
end
|
865
|
-
|
866
862
|
def parent_path
|
867
863
|
unless root? || path.nil?
|
868
864
|
path.gsub(/\/[^\/]+$/, '').presence || '/'
|
869
865
|
end
|
870
866
|
end
|
871
867
|
|
868
|
+
def as_client_json
|
869
|
+
data_from_cms.to_h.except(*GENERATED_ATTRIBUTES)
|
870
|
+
end
|
871
|
+
|
872
872
|
private
|
873
873
|
|
874
874
|
def cms_data_for_revision(revision)
|
@@ -877,13 +877,6 @@ module Scrivito
|
|
877
877
|
end
|
878
878
|
end
|
879
879
|
|
880
|
-
def field_name_in_data_for_widget(data, widget_id)
|
881
|
-
data.all_custom_attributes.find do |attribute_name|
|
882
|
-
(value, type) = data.value_and_type_of(attribute_name)
|
883
|
-
type == "widget" && value.include?(widget_id)
|
884
|
-
end
|
885
|
-
end
|
886
|
-
|
887
880
|
def read_widget_pool
|
888
881
|
read_attribute('_widget_pool')
|
889
882
|
end
|
@@ -940,8 +933,24 @@ module Scrivito
|
|
940
933
|
"#{obj_class_name.underscore}/#{view_name}"
|
941
934
|
end
|
942
935
|
|
943
|
-
|
936
|
+
def has_system_attribute?(attribute_name)
|
937
|
+
!!SYSTEM_ATTRIBUTES[attribute_name]
|
938
|
+
end
|
939
|
+
|
940
|
+
def has_public_system_attribute?(attribute_name)
|
941
|
+
SYSTEM_ATTRIBUTES[attribute_name].is_a?(PublicSystemAttributeDefinition)
|
942
|
+
end
|
944
943
|
|
944
|
+
def type_of_system_attribute(attribute_name)
|
945
|
+
SYSTEM_ATTRIBUTES[attribute_name].try(:type)
|
946
|
+
end
|
947
|
+
|
948
|
+
def value_of_system_attribute(attribute_name)
|
949
|
+
attribute_value = data_from_cms.value_of(attribute_name)
|
950
|
+
attribute_name == '_last_changed' ? DateAttribute.parse(attribute_value) : attribute_value
|
951
|
+
end
|
952
|
+
|
953
|
+
class << self
|
945
954
|
def assert_not_basic_obj(method_name)
|
946
955
|
if self == Scrivito::BasicObj
|
947
956
|
raise ScrivitoError, "Can not call #{method_name} on Scrivito::BasicObj."
|
@@ -949,13 +958,6 @@ module Scrivito
|
|
949
958
|
end
|
950
959
|
end
|
951
960
|
|
952
|
-
def assert_has_obj_class(method_name)
|
953
|
-
unless Workspace.current.obj_classes[name].present?
|
954
|
-
raise ScrivitoError, "#{name} has no corresponding ObjClass."
|
955
|
-
+ " Please use Obj.#{method_name} instead."
|
956
|
-
end
|
957
|
-
end
|
958
|
-
|
959
961
|
#
|
960
962
|
# Restores a previously deleted +Obj+.
|
961
963
|
#
|
@@ -971,21 +973,54 @@ module Scrivito
|
|
971
973
|
Workspace.current.api_request(:post, '/objs', obj: obj_attributes)
|
972
974
|
end
|
973
975
|
|
974
|
-
def prepare_attributes_for_rest_api(
|
975
|
-
|
976
|
-
|
977
|
-
api_attributes
|
978
|
-
CmsRestApi::AttributeSerializer.generate_widget_pool_changes(widget_properties)
|
976
|
+
def prepare_attributes_for_rest_api(obj_attributes, obj = nil)
|
977
|
+
widget_pool_attributes = CmsRestApi::WidgetExtractor.call(obj_attributes, obj)
|
978
|
+
workspace = obj ? obj.revision.workspace : Workspace.current
|
979
|
+
api_attributes = serialize_attributes(obj_attributes, widget_pool_attributes, workspace)
|
979
980
|
|
980
981
|
if obj
|
981
982
|
widget_pool = api_attributes['_widget_pool']
|
982
|
-
widget_gc = WidgetGarbageCollection.new(obj,
|
983
|
+
widget_gc = WidgetGarbageCollection.new(obj,
|
984
|
+
{obj => obj_attributes}.merge(widget_pool_attributes))
|
983
985
|
widget_gc.widgets_to_delete.each { |widget| widget_pool[widget.id] = nil }
|
984
986
|
end
|
985
987
|
|
986
|
-
[api_attributes,
|
988
|
+
[api_attributes, widget_pool_attributes]
|
989
|
+
end
|
990
|
+
|
991
|
+
def serialize_attributes(obj_attributes, widget_pool_attributes, workspace)
|
992
|
+
if workspace.uses_obj_classes
|
993
|
+
serialized_attributes = CmsRestApi::LegacyAttributeSerializer.convert(obj_attributes)
|
994
|
+
serialized_attributes['_widget_pool'] = CmsRestApi::LegacyAttributeSerializer
|
995
|
+
.generate_widget_pool_changes(widget_pool_attributes)
|
996
|
+
serialized_attributes
|
997
|
+
else
|
998
|
+
serializer = AttributeSerializer.new
|
999
|
+
serialized_attributes = serialize_obj_attributes(serializer, obj_attributes)
|
1000
|
+
serialized_attributes['_widget_pool'] =
|
1001
|
+
serialize_widget_pool_attributes(serializer, widget_pool_attributes)
|
1002
|
+
serialized_attributes
|
1003
|
+
end
|
1004
|
+
end
|
1005
|
+
|
1006
|
+
def serialize_obj_attributes(serializer, obj_attributes)
|
1007
|
+
serializer.serialize(obj_attributes,
|
1008
|
+
find_attribute_definitions(obj_attributes['_obj_class']) || attribute_definitions)
|
1009
|
+
end
|
1010
|
+
|
1011
|
+
def serialize_widget_pool_attributes(serializer, widget_pool_attributes)
|
1012
|
+
{}.tap do |serialized_attributes|
|
1013
|
+
widget_pool_attributes.each_pair do |widget, widget_attributes|
|
1014
|
+
obj_class = widget_attributes['_obj_class']
|
1015
|
+
serialized_attributes[widget.id] = serializer.serialize(widget_attributes,
|
1016
|
+
find_attribute_definitions(obj_class, BasicWidget) || widget.attribute_definitions)
|
1017
|
+
end
|
1018
|
+
end
|
1019
|
+
end
|
1020
|
+
|
1021
|
+
def find_attribute_definitions(obj_class, basic_class = self)
|
1022
|
+
basic_class.type_computer.compute_type(obj_class).attribute_definitions if obj_class
|
987
1023
|
end
|
988
1024
|
end
|
989
1025
|
end
|
990
|
-
|
991
1026
|
end
|