scrivito_sdk 0.60.0 → 0.65.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/.yardopts +1 -0
- data/app/controllers/scrivito/objs_controller.rb +10 -2
- data/app/controllers/scrivito/workspaces_controller.rb +2 -1
- data/app/helpers/scrivito_helper.rb +32 -1
- data/app/views/scrivito/page_details.html.erb +1 -0
- data/app/views/scrivito/{workspaces → webservice}/_workspace.json.jbuilder +0 -0
- data/app/views/scrivito/webservice/error.json.jbuilder +1 -1
- data/app/views/scrivito/{workspaces/index.json.jbuilder → webservice/workspaces.json.jbuilder} +0 -0
- data/config/ca-bundle.crt +1 -1
- data/config/precedence_routes.rb +1 -0
- data/config/routes.rb +4 -5
- data/lib/assets/javascripts/scrivito_ui.js +1875 -1065
- 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/views/page/details.html.erb +3 -1
- data/lib/generators/scrivito/page/templates/details.html.erb +3 -1
- data/lib/scrivito/attribute_content.rb +159 -17
- data/lib/scrivito/attribute_serializer.rb +7 -3
- data/lib/scrivito/backend/content_state_node.rb +68 -0
- data/lib/scrivito/backend/index.rb +45 -0
- data/lib/scrivito/backend/obj_data_cache.rb +68 -0
- data/lib/scrivito/backend/obj_data_from_rest.rb +64 -0
- data/lib/scrivito/backend/obj_load.rb +45 -0
- data/lib/scrivito/backend/obj_query.rb +63 -0
- data/lib/scrivito/backend/parent_path_index.rb +21 -0
- data/lib/scrivito/backend/path_index.rb +27 -0
- data/lib/scrivito/backend/permalink_index.rb +17 -0
- data/lib/scrivito/basic_obj.rb +67 -39
- data/lib/scrivito/basic_widget.rb +19 -3
- data/lib/scrivito/cache_middleware.rb +9 -3
- data/lib/scrivito/client_error.rb +3 -3
- data/lib/scrivito/cms_backend.rb +64 -18
- data/lib/scrivito/cms_data_cache.rb +33 -30
- data/lib/scrivito/cms_dispatch_controller.rb +18 -0
- data/lib/scrivito/cms_field_tag.rb +3 -2
- data/lib/scrivito/cms_rest_api.rb +18 -12
- data/lib/scrivito/cms_rest_api/rate_limit.rb +40 -0
- data/lib/scrivito/cms_routing.rb +9 -8
- data/lib/scrivito/configuration.rb +8 -1
- data/lib/scrivito/controller_actions.rb +6 -1
- data/lib/scrivito/errors.rb +5 -0
- data/lib/scrivito/migrations/cms_backend.rb +2 -0
- data/lib/scrivito/named_link.rb +9 -45
- data/lib/scrivito/obj_collection.rb +14 -15
- data/lib/scrivito/obj_create_params_parser.rb +3 -5
- data/lib/scrivito/obj_params_parser.rb +2 -2
- data/lib/scrivito/obj_update_params_parser.rb +5 -3
- data/lib/scrivito/revision.rb +62 -2
- data/lib/scrivito/type_computer.rb +6 -2
- data/lib/scrivito/widget_tag.rb +4 -4
- data/lib/scrivito/workspace.rb +19 -3
- data/lib/scrivito/workspace_data.rb +23 -0
- data/lib/scrivito/workspace_data_from_service.rb +11 -28
- metadata +31 -7
- data/lib/scrivito/workspace_data_from_rest_api.rb +0 -6
@@ -0,0 +1,64 @@
|
|
1
|
+
module Scrivito
|
2
|
+
module Backend
|
3
|
+
|
4
|
+
class ObjDataFromRest < ObjData
|
5
|
+
def initialize(data)
|
6
|
+
@data = data
|
7
|
+
end
|
8
|
+
|
9
|
+
def raw_value_and_type_of(attribute_name)
|
10
|
+
return [value_of_widget_pool, nil] if attribute_name == '_widget_pool'
|
11
|
+
|
12
|
+
if attribute_data = @data[attribute_name]
|
13
|
+
if attribute_name.starts_with?('_')
|
14
|
+
[attribute_data, nil]
|
15
|
+
else
|
16
|
+
type = attribute_data[0]
|
17
|
+
[legacy_compatible(attribute_data[1], type), type]
|
18
|
+
end
|
19
|
+
else
|
20
|
+
[nil, nil]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def attribute_names_for_comparison
|
25
|
+
@data.keys
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_h
|
29
|
+
@data
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def legacy_compatible(value, type)
|
35
|
+
if type == "linklist"
|
36
|
+
value.map(&method(:legacy_link))
|
37
|
+
elsif type == "link"
|
38
|
+
legacy_link(value)
|
39
|
+
else
|
40
|
+
value
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def legacy_link(link)
|
45
|
+
return link unless link["obj_id"]
|
46
|
+
|
47
|
+
transformed = link.dup
|
48
|
+
transformed["destination"] = transformed.delete("obj_id")
|
49
|
+
|
50
|
+
transformed
|
51
|
+
end
|
52
|
+
|
53
|
+
def value_of_widget_pool
|
54
|
+
if widget_pool = @data['_widget_pool']
|
55
|
+
# using Hash#merge to "map" to a new Hash
|
56
|
+
widget_pool.merge(widget_pool) do |id, raw_widget_data|
|
57
|
+
self.class.new(raw_widget_data)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Scrivito
|
2
|
+
module Backend
|
3
|
+
|
4
|
+
module ObjLoad
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def load(revision, ids)
|
8
|
+
cache = Backend::ObjDataCache.view_for_revision(revision)
|
9
|
+
|
10
|
+
missing_ids = []
|
11
|
+
results_from_cache = ids.map do |id|
|
12
|
+
result = cache.read_obj(id)
|
13
|
+
|
14
|
+
missing_ids << id unless result
|
15
|
+
|
16
|
+
result
|
17
|
+
end
|
18
|
+
|
19
|
+
overall_results =
|
20
|
+
if missing_ids.blank?
|
21
|
+
results_from_cache
|
22
|
+
else
|
23
|
+
results_from_backend = revision.obj_mget_request(missing_ids)
|
24
|
+
|
25
|
+
results_from_backend.each do |result|
|
26
|
+
if result
|
27
|
+
cache.write_obj(result["_id"], result)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
results_from_cache.map do |result|
|
32
|
+
result || results_from_backend.shift
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
overall_results.map do |raw_data|
|
37
|
+
Backend::ObjDataFromRest.new(raw_data) if raw_data
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Scrivito
|
2
|
+
module Backend
|
3
|
+
|
4
|
+
module ObjQuery
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def query(revision, index, keys)
|
8
|
+
cache = Backend::ObjDataCache.view_for_revision(revision)
|
9
|
+
|
10
|
+
missing_keys = []
|
11
|
+
ids_from_cache = keys.map do |key|
|
12
|
+
result = cache.read_index(index.id, key)
|
13
|
+
|
14
|
+
missing_keys << key unless result
|
15
|
+
|
16
|
+
result
|
17
|
+
end
|
18
|
+
|
19
|
+
if missing_keys.blank?
|
20
|
+
return load_nested_ids(revision, ids_from_cache)
|
21
|
+
end
|
22
|
+
|
23
|
+
backend_ids = revision.obj_search_request(index.query(missing_keys))
|
24
|
+
|
25
|
+
all_obj_datas = load_nested_ids(revision, ids_from_cache + [backend_ids])
|
26
|
+
|
27
|
+
cache_obj_datas = all_obj_datas[0..-2]
|
28
|
+
backend_obj_datas = all_obj_datas.last
|
29
|
+
|
30
|
+
grouped_backend_results = index.group_by(missing_keys, backend_obj_datas)
|
31
|
+
|
32
|
+
grouped_backend_results.each_with_index do |result, i|
|
33
|
+
ids = result.map { |obj_data| obj_data.value_of("_id") }
|
34
|
+
cache.write_index(index.id, missing_keys[i], ids)
|
35
|
+
end
|
36
|
+
|
37
|
+
cache_obj_datas.map do |obj_datas|
|
38
|
+
obj_datas || grouped_backend_results.shift
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
# nested_ids is a list containing elements that are either lists of ids or nil
|
45
|
+
# returns a corresponding list
|
46
|
+
# containing elements that are either lists of obj_datas or nil
|
47
|
+
def load_nested_ids(revision, nested_ids)
|
48
|
+
all_ids = nested_ids.inject([]) do |list, ids|
|
49
|
+
ids ? list + ids : list
|
50
|
+
end
|
51
|
+
|
52
|
+
all_obj_datas = Backend::ObjLoad.load(revision, all_ids)
|
53
|
+
|
54
|
+
nested_ids.map do |ids|
|
55
|
+
all_obj_datas.shift(ids.length) if ids
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Scrivito
|
2
|
+
module Backend
|
3
|
+
|
4
|
+
module ParentPathIndex
|
5
|
+
class << self
|
6
|
+
|
7
|
+
include Backend::Index
|
8
|
+
|
9
|
+
def id
|
10
|
+
"ppath"
|
11
|
+
end
|
12
|
+
|
13
|
+
def query(keys)
|
14
|
+
[{:field => "_parent_path", :operator => :equals, :value => keys}]
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Scrivito
|
2
|
+
module Backend
|
3
|
+
|
4
|
+
module PathIndex
|
5
|
+
class << self
|
6
|
+
|
7
|
+
include Backend::Index
|
8
|
+
|
9
|
+
def id
|
10
|
+
"path"
|
11
|
+
end
|
12
|
+
|
13
|
+
def query(keys)
|
14
|
+
[{:field => "_path", :operator => :equals, :value => keys}]
|
15
|
+
end
|
16
|
+
|
17
|
+
def group_by_multiple(paths, obj_datas)
|
18
|
+
paths.map do |path|
|
19
|
+
obj_datas.select { |obj_data| obj_data.value_of("_path") == path }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
data/lib/scrivito/basic_obj.rb
CHANGED
@@ -55,11 +55,20 @@ module Scrivito
|
|
55
55
|
end
|
56
56
|
|
57
57
|
#
|
58
|
-
# Create a new {Scrivito::BasicObj Obj} in the
|
58
|
+
# Create a new {Scrivito::BasicObj Obj} in the CMS.
|
59
59
|
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
# to set
|
60
|
+
# @api public
|
61
|
+
#
|
62
|
+
# This allows you to set the different attributes types of an obj by providing a hash with the
|
63
|
+
# attributes names as key and the values you want to set as values. It also considers the
|
64
|
+
# defaults set via {Scrivito::AttributeContent::ClassMethods#default_for Obj.default_for}.
|
65
|
+
#
|
66
|
+
# @param [Hash] attributes for the new obj
|
67
|
+
# @param [Hash] context in which the object creating should happen
|
68
|
+
# @option context [Scrivito::User] :scrivito_user current visitor
|
69
|
+
# @return [Obj] the newly created {Scrivito::BasicObj Obj}
|
70
|
+
#
|
71
|
+
# @see Scrivito::AttributeContent::ClassMethods#default_for
|
63
72
|
#
|
64
73
|
# @example Reference lists have to be provided as an Array of {Scrivito::BasicObj Objs}
|
65
74
|
# Obj.create(:reference_list => [other_obj])
|
@@ -107,14 +116,11 @@ module Scrivito
|
|
107
116
|
# # Clear a widget field
|
108
117
|
# obj.update(:widgets => [])
|
109
118
|
#
|
110
|
-
|
111
|
-
# @param [Hash] attributes
|
112
|
-
# @return [Obj] the newly created {Scrivito::BasicObj Obj}
|
113
|
-
#
|
114
|
-
def self.create(attributes = {})
|
119
|
+
def self.create(attributes = {}, context = {})
|
115
120
|
if obj_class = extract_obj_class_from_attributes(attributes)
|
116
|
-
obj_class.create(attributes)
|
121
|
+
obj_class.create(attributes, context)
|
117
122
|
else
|
123
|
+
attributes = build_attributes_with_defaults(attributes, context)
|
118
124
|
attributes = prepare_attributes_for_instantiation(attributes)
|
119
125
|
api_attributes, widget_properties = prepare_attributes_for_rest_api(attributes)
|
120
126
|
json = Workspace.current.api_request(:post, '/objs', obj: api_attributes)
|
@@ -604,42 +610,59 @@ module Scrivito
|
|
604
610
|
read_attribute('_last_changed')
|
605
611
|
end
|
606
612
|
|
607
|
-
def new?(revision=
|
608
|
-
|
613
|
+
def new?(revision=workspace.base_revision)
|
614
|
+
quick_modification(revision) == "new"
|
615
|
+
end
|
609
616
|
|
610
|
-
|
611
|
-
|
612
|
-
else
|
613
|
-
false
|
614
|
-
end
|
617
|
+
def deleted?(revision=workspace.base_revision)
|
618
|
+
quick_modification(revision) == "deleted"
|
615
619
|
end
|
616
620
|
|
617
|
-
def
|
618
|
-
|
621
|
+
def modification(revision=workspace.base_revision)
|
622
|
+
quick_modification = quick_modification(revision)
|
619
623
|
|
620
|
-
if
|
621
|
-
|
624
|
+
if ObjData === quick_modification
|
625
|
+
if data_from_cms == quick_modification
|
626
|
+
Modification::UNMODIFIED
|
627
|
+
else
|
628
|
+
Modification::EDITED
|
629
|
+
end
|
630
|
+
else
|
631
|
+
quick_modification
|
622
632
|
end
|
623
633
|
end
|
624
634
|
|
625
|
-
|
635
|
+
# similar to modification, but faster if you are only interested in
|
636
|
+
# "new" and "deleted".
|
637
|
+
# this method sometimes does not return a string, but an instance of
|
638
|
+
# ObjData instead. this indicates that the modification is either
|
639
|
+
# UNMODIFIED or EDITED. Which one it is can be determined by comparing
|
640
|
+
# the returned ObjData.
|
641
|
+
def quick_modification(revision)
|
626
642
|
return Modification::UNMODIFIED unless revision
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
if
|
636
|
-
|
637
|
-
|
638
|
-
else
|
639
|
-
Modification::EDITED
|
640
|
-
end
|
643
|
+
|
644
|
+
modification_attr = read_attribute('_modification')
|
645
|
+
|
646
|
+
return modification_attr if revision == workspace.base_revision
|
647
|
+
|
648
|
+
data_for_comparison = cms_data_for_revision(revision)
|
649
|
+
|
650
|
+
if data_for_comparison.present?
|
651
|
+
if modification_attr == 'deleted'
|
652
|
+
# Obj exists in comparison revision, but not in current
|
653
|
+
Modification::DELETED
|
641
654
|
else
|
655
|
+
# Obj exists in both revisions, leave the actual comparions
|
656
|
+
# up to the caller
|
657
|
+
data_for_comparison
|
658
|
+
end
|
659
|
+
else
|
660
|
+
if modification_attr == "deleted"
|
661
|
+
# Obj does not exist in either revision
|
642
662
|
Modification::UNMODIFIED
|
663
|
+
else
|
664
|
+
# Obj exists in current, but not in comparision revision
|
665
|
+
Modification::NEW
|
643
666
|
end
|
644
667
|
end
|
645
668
|
end
|
@@ -883,8 +906,13 @@ module Scrivito
|
|
883
906
|
private
|
884
907
|
|
885
908
|
def cms_data_for_revision(revision)
|
886
|
-
|
887
|
-
|
909
|
+
return nil unless revision
|
910
|
+
|
911
|
+
result = CmsBackend.instance.find_obj_data_by(revision, "id", [id])
|
912
|
+
obj_data = result.first.first
|
913
|
+
|
914
|
+
if obj_data && obj_data.value_of("_modification") != "deleted"
|
915
|
+
obj_data
|
888
916
|
end
|
889
917
|
end
|
890
918
|
|
@@ -1006,7 +1034,7 @@ module Scrivito
|
|
1006
1034
|
.generate_widget_pool_changes(widget_pool_attributes)
|
1007
1035
|
serialized_attributes
|
1008
1036
|
else
|
1009
|
-
serializer = AttributeSerializer.new
|
1037
|
+
serializer = AttributeSerializer.new(obj_attributes['_obj_class'] || name)
|
1010
1038
|
serialized_attributes = serialize_obj_attributes(serializer, obj_attributes)
|
1011
1039
|
serialized_attributes['_widget_pool'] =
|
1012
1040
|
serialize_widget_pool_attributes(serializer, widget_pool_attributes)
|
@@ -83,11 +83,27 @@ class BasicWidget
|
|
83
83
|
@attribute_cache = {}
|
84
84
|
end
|
85
85
|
|
86
|
-
|
86
|
+
#
|
87
|
+
# Creates a new {Scrivito::BasicWidget Widget}.
|
88
|
+
#
|
89
|
+
# @api public
|
90
|
+
#
|
91
|
+
# It also considers the defaults set via
|
92
|
+
# {Scrivito::AttributeContent::ClassMethods#default_for Widget.default_for}.
|
93
|
+
#
|
94
|
+
# @param [Hash] attributes for the new widget
|
95
|
+
# @param [Hash] context in which the object creating should happen
|
96
|
+
# @option context [Scrivito::User] :scrivito_user current visitor
|
97
|
+
# @return [Widget] the newly created {Scrivito::BasicWidget Widget}
|
98
|
+
#
|
99
|
+
# @see Scrivito::BasicWidget.new
|
100
|
+
# @see Scrivito::AttributeContent::ClassMethods#default_for
|
101
|
+
#
|
102
|
+
def self.new(attributes = {}, context = {})
|
87
103
|
if obj_class = extract_obj_class_from_attributes(attributes)
|
88
|
-
obj_class.new(attributes)
|
104
|
+
obj_class.new(attributes, context)
|
89
105
|
else
|
90
|
-
super
|
106
|
+
super(build_attributes_with_defaults(attributes, context))
|
91
107
|
end
|
92
108
|
end
|
93
109
|
|
@@ -6,11 +6,17 @@ class CacheMiddleware
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def call(env)
|
9
|
-
|
9
|
+
clear_caches
|
10
|
+
|
10
11
|
@app.call(env)
|
11
|
-
|
12
|
-
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def clear_caches
|
13
18
|
Workspace.cache.clear
|
19
|
+
CmsBackend.instance.clear_cache
|
14
20
|
end
|
15
21
|
end
|
16
22
|
|