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.
- 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
|