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