scrivito_sdk 0.65.2 → 0.66.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/scrivito/binary_redirect_controller.rb +19 -0
  3. data/app/controllers/scrivito/objs_controller.rb +6 -2
  4. data/app/controllers/scrivito/webservice_controller.rb +1 -1
  5. data/app/controllers/scrivito/workspaces_controller.rb +1 -7
  6. data/app/helpers/scrivito_helper.rb +115 -64
  7. data/app/views/scrivito/objs/is_outdated.json.jbuilder +1 -0
  8. data/app/views/scrivito/ui/index.html.erb +1 -0
  9. data/config/ca-bundle.crt +1 -1
  10. data/config/precedence_routes.rb +6 -4
  11. data/config/routes.rb +1 -1
  12. data/lib/assets/images/scrivito/source_too_large.png +0 -0
  13. data/lib/assets/images/scrivito/source_type_invalid.png +0 -0
  14. data/lib/assets/javascripts/scrivito_ui.js +2975 -357
  15. data/lib/assets/stylesheets/scrivito_sdk.css +1 -3
  16. data/lib/assets/stylesheets/scrivito_ui.css +1 -3
  17. data/lib/generators/scrivito/install/templates/scrivito/migrate/install_scrivito_migration.rb +0 -2
  18. data/lib/generators/scrivito/migration/templates/migration.erb +6 -8
  19. data/lib/scrivito/attribute_content.rb +16 -52
  20. data/lib/scrivito/attribute_deserializer.rb +3 -5
  21. data/lib/scrivito/attribute_serializer.rb +42 -45
  22. data/lib/scrivito/backend/content_state_node.rb +71 -19
  23. data/lib/scrivito/backend/index.rb +20 -0
  24. data/lib/scrivito/backend/obj_data_cache.rb +157 -31
  25. data/lib/scrivito/backend/obj_data_from_rest.rb +1 -0
  26. data/lib/scrivito/backend/obj_load.rb +10 -6
  27. data/lib/scrivito/backend/obj_query.rb +2 -1
  28. data/lib/scrivito/backend/parent_path_index.rb +5 -1
  29. data/lib/scrivito/backend/path_index.rb +5 -1
  30. data/lib/scrivito/backend/permalink_index.rb +18 -10
  31. data/lib/scrivito/basic_obj.rb +62 -42
  32. data/lib/scrivito/basic_widget.rb +5 -5
  33. data/lib/scrivito/binary.rb +154 -13
  34. data/lib/scrivito/binary_param_verifier.rb +31 -0
  35. data/lib/scrivito/binary_rewrite.rb +35 -0
  36. data/lib/scrivito/cache_middleware.rb +5 -0
  37. data/lib/scrivito/child_list_tag.rb +3 -3
  38. data/lib/scrivito/cms_backend.rb +89 -57
  39. data/lib/scrivito/cms_data_cache.rb +21 -4
  40. data/lib/scrivito/cms_dispatch_controller.rb +13 -0
  41. data/lib/scrivito/cms_field_tag.rb +16 -3
  42. data/lib/scrivito/cms_rest_api.rb +2 -2
  43. data/lib/scrivito/cms_routing.rb +41 -18
  44. data/lib/scrivito/configuration.rb +22 -0
  45. data/lib/scrivito/content_state.rb +1 -9
  46. data/lib/scrivito/content_state_caching.rb +0 -20
  47. data/lib/scrivito/controller_actions.rb +3 -2
  48. data/lib/scrivito/controller_runtime.rb +16 -8
  49. data/lib/scrivito/dialog_size_helper.rb +11 -0
  50. data/lib/scrivito/diff.rb +0 -2
  51. data/lib/scrivito/editing_context.rb +24 -13
  52. data/lib/scrivito/editing_context_middleware.rb +6 -1
  53. data/lib/scrivito/errors.rb +18 -0
  54. data/lib/scrivito/image_tag.rb +55 -0
  55. data/lib/scrivito/link.rb +12 -0
  56. data/lib/scrivito/membership_collection.rb +3 -2
  57. data/lib/scrivito/migrations/cms_backend.rb +6 -13
  58. data/lib/scrivito/migrations/migrator.rb +2 -23
  59. data/lib/scrivito/migrations/workspace_lock.rb +23 -11
  60. data/lib/scrivito/named_link.rb +1 -1
  61. data/lib/scrivito/obj_collection.rb +1 -1
  62. data/lib/scrivito/obj_data_from_service.rb +0 -7
  63. data/lib/scrivito/obj_search_enumerator.rb +2 -2
  64. data/lib/scrivito/parent_path.rb +9 -0
  65. data/lib/scrivito/routing_helper.rb +2 -2
  66. data/lib/scrivito/user.rb +2 -2
  67. data/lib/scrivito/user_definition.rb +2 -2
  68. data/lib/scrivito/widget_garbage_collection.rb +2 -2
  69. data/lib/scrivito/widget_tag.rb +6 -3
  70. data/lib/scrivito/workspace.rb +60 -41
  71. data/lib/scrivito/workspace_data.rb +40 -4
  72. data/lib/scrivito/workspace_data_from_service.rb +0 -10
  73. data/lib/scrivito_sdk.rb +0 -16
  74. metadata +12 -12
  75. data/lib/scrivito/attribute.rb +0 -152
  76. data/lib/scrivito/attribute_collection.rb +0 -66
  77. data/lib/scrivito/attribute_definition_migrator.rb +0 -188
  78. data/lib/scrivito/cms_rest_api/legacy_attribute_serializer.rb +0 -105
  79. data/lib/scrivito/image_tag_helper.rb +0 -46
  80. data/lib/scrivito/obj_class.rb +0 -258
  81. data/lib/scrivito/obj_class_collection.rb +0 -71
  82. data/lib/scrivito/obj_class_data.rb +0 -37
  83. data/lib/tasks/migrate_attribute_definitions.rake +0 -6
@@ -21,13 +21,13 @@ class BasicWidget
21
21
  # to ensure that only a special type of widget can be added to a specific
22
22
  # container. An example for this is a +TabGroupWidget+ that should
23
23
  # contain widgets of the +TabWidget+ type only, and, vice versa, a +TabWidget+
24
- # should only be contained in a +TabGroupWidget+. A value of +nil+ means that
24
+ # should only be contained in a +TabGroupWidget+. A value of +NilClass+ means that
25
25
  # no additional restrictions are applied.
26
26
  #
27
27
  # This method only further restricts the list of valid classes defined
28
28
  # by means of {AttributeContent#valid_widget_classes_for}.
29
29
  #
30
- # @return [Array<String, Symbol, Class>, nil]
30
+ # @return [NilClass, Array<Class>]
31
31
  #
32
32
  # @example
33
33
  # class TabGroupWidget < Widget
@@ -46,8 +46,9 @@ class BasicWidget
46
46
  end
47
47
 
48
48
  def self.valid_inside_container?(container_class)
49
- valid_container_classes.nil? ||
50
- valid_container_classes.map(&:to_s).include?(container_class.name)
49
+ computed_classes = assert_classes(valid_container_classes, '.valid_container_classes')
50
+
51
+ computed_classes.nil? || computed_classes.include?(container_class)
51
52
  end
52
53
 
53
54
  attr_accessor :container, :container_attribute_name
@@ -202,7 +203,6 @@ class BasicWidget
202
203
  # Please use {Scrivito::BasicObj#revert Obj#revert} to restore them.
203
204
  #
204
205
  # @raise [ScrivitoError] If the current workspace is +published+.
205
- # @raise [ScrivitoError] If the current workspace is the +rtc+ workspace.
206
206
  # @raise [ScrivitoError] If the +Widget+ is +new+.
207
207
  # @raise [ScrivitoError] If the +Widget+ is +deleted+.
208
208
  #
@@ -4,11 +4,13 @@ module Scrivito
4
4
  # The Binary class represents the data stored in a binary attribute of an Obj
5
5
  # or Widget
6
6
  class Binary
7
- attr_reader :id
7
+ attr_reader :id, :transformation_definition
8
8
 
9
- def initialize(id, public_content)
9
+ def initialize(id, is_public, transformation_definition = nil, original = nil)
10
10
  @id = id
11
- @public_content = public_content
11
+ @is_public = !!is_public
12
+ @transformation_definition = transformation_definition
13
+ @original = original
12
14
  end
13
15
 
14
16
  # @api public
@@ -17,21 +19,35 @@ class Binary
17
19
  # been published yet.
18
20
  # @return [Boolean]
19
21
  def private?
20
- !public_content?
22
+ !@is_public
23
+ end
24
+
25
+ def public?
26
+ @is_public
21
27
  end
22
28
 
23
29
  # @api public
24
- # The URL where this binary data is accessible and can be downloaded using an
25
- # HTTP GET request. Note that urls for private content will have an
26
- # expiration time in order to protect them.
27
- # Therefore the url returned here should be accessed immediately after it has been returned (i.e. within a couple of minutes).
28
- # When accessed after they have expired, an error will occur.
29
- # The urls should not be used for long-term-storage (i.e. they are no longer accessible hours or days after they have been generated).
30
- # @return [String] the URL under which this content is available
30
+ # The URL for accessing the binary data and downloading it using an HTTP GET request.
31
+ #
32
+ # @note URLs for private content have an expiration time in order to protect them.
33
+ # Therefore, the URL should be accessed immediately after it has been returned
34
+ # (i.e. within a couple of minutes). Accessing it after expiration causes an error.
35
+ #
36
+ # The URLs should not be used for long-term storage since they are no longer
37
+ # accessible hours or days after they have been generated.
38
+ # @return [String] the URL at which this content is available.
31
39
  def url
32
40
  find_url('get')
33
41
  end
34
42
 
43
+ def url_from_cache
44
+ blob_data = CmsBackend.instance
45
+ .find_blob_data_from_cache(id, access_type, 'get', transformation_definition)
46
+ if blob_data
47
+ blob_data['url']
48
+ end
49
+ end
50
+
35
51
  # @api public
36
52
  # the filename of this binary data, for example "my_image.jpg"
37
53
  # @return [String] the filename of binary
@@ -53,6 +69,121 @@ class Binary
53
69
  headers[:content_length].to_i
54
70
  end
55
71
 
72
+ #
73
+ # Returns a transformed {Scrivito::Binary}.
74
+ #
75
+ # @api beta
76
+ #
77
+ # Calling this method will not change the binary, but instead return a copy of it,
78
+ # transformed using the +definition+.
79
+ #
80
+ # If the original binary has already been transformed, then the returned binary will be a
81
+ # combination of the transformations. Thus the transformations can be chained (see examples).
82
+ #
83
+ # The transformed data is calculated "lazily", so calling {Scrivito::Binary#transform} will not
84
+ # trigger any calculation. The calculation will be triggered only if data is accessed, for example
85
+ # via {Scrivito::Binary#url}.
86
+ #
87
+ # @param [Hash] definition transformation definition
88
+ #
89
+ # @option definition [Fixnum,String] :width The width in pixels of the output image. Must be a
90
+ # positive integer.
91
+ #
92
+ # If only this dimension is specified, the other dimension will be calculated automatically to
93
+ # preserve the aspect ratio of the input image.
94
+ #
95
+ # If the +fit+ parameter is set to +:clip+, then the actual output one of width and height may
96
+ # be equal to or less than the dimensions you specify to prevent distortion.
97
+ #
98
+ # If neither +width+ nor +height+ is given, the width and height of the input image
99
+ # will be used.
100
+ #
101
+ # The maximum output image size width + height = 4096 pixels. The given width and height may be
102
+ # adjusted to accomodate this limit. The output image will never be larger than the source
103
+ # image, i.e. the given width and height may be adjusted to keep the output image from exceeding
104
+ # the dimensions of the input image.
105
+ #
106
+ # When the given width and height are adjusted, the aspect ratio is preserved.
107
+ #
108
+ # @option definition [Fixnum,String] :height The height in pixels of the output image. Must be a
109
+ # positive integer.
110
+ #
111
+ # If only this dimension is specified, the other dimension will be calculated automatically to
112
+ # preserve the aspect ratio of the input image.
113
+ #
114
+ # If the +fit+ parameter is set to +:clip+, then the actual output one of width and height may
115
+ # be equal to or less than the dimensions you specify to prevent distortion.
116
+ #
117
+ # If neither +width+ nor +height+ is given, the width and height of the input image
118
+ # will be used.
119
+ #
120
+ # The maximum output image size width + height = 4096 pixels. The given width and height may be
121
+ # adjusted to accomodate this limit. The output image will never be larger than the source
122
+ # image, i.e. the given width and height may be adjusted to keep the output image from exceeding
123
+ # the dimensions of the input image.
124
+ #
125
+ # When the given width and height are adjusted, the aspect ratio is preserved.
126
+ #
127
+ # @option definition [Symbol,String] :fit Controls how the tranformed image is fitted to the
128
+ # given width and heigth. Valid values are +:clip+ and +:crop+. The default value is +:clip+.
129
+ #
130
+ # If set to +:clip+ it resizes the image to fit within the width and height boundaries without
131
+ # cropping or distorting the image. The resulting image is assured to match one of the
132
+ # constraining dimensions, while the other dimension is altered to maintain the same aspect
133
+ # ratio of the input image.
134
+ #
135
+ # If set to +:crop+ it resizes the image to fill the given width and height and preserves the
136
+ # aspect ration by cropping any excess image data. The resulting image will match both the given
137
+ # width and height without distorting the image. The crop is done centered, i.e. the center of
138
+ # the image is preserved.
139
+ #
140
+ # @option definition [Fixnum,String] :quality Controls the output quality of lossy file formats.
141
+ # Applies when the format is +"jpg"+. Valid values are in the range from +0+ to +100+.
142
+ # The default value is +75+.
143
+ #
144
+ # @return [Scrivito::Binary] transformed binary
145
+ #
146
+ # @example Crop image to fit into 50 x 50 pixel square
147
+ # @obj.blob.transform(width: 50, height: 50, fit: :crop)
148
+ #
149
+ # @example Convert image to a low quality JPEG
150
+ # @obj.blob.transform(quality: 25)
151
+ #
152
+ # @example Combine the both transformations
153
+ # @obj.blob.transform(width: 50, height: 50, fit: :crop).transform(quality: 25)
154
+ #
155
+ def transform(definition)
156
+ self.class.new(id, public?, (transformation_definition || {}).merge(definition), original)
157
+ end
158
+
159
+ #
160
+ # Returns whether a binary is transformed.
161
+ # @api beta
162
+ #
163
+ def transformed?
164
+ !!transformation_definition
165
+ end
166
+
167
+ #
168
+ # Returns the transformation original of a binary.
169
+ #
170
+ # @api beta
171
+ #
172
+ # If a binary is a result of a transformation, then its original binary is returned.
173
+ # Otherwise +self+.
174
+ #
175
+ # @return [Scrivito::Binary] original binary
176
+ #
177
+ # @example
178
+ # @obj.blob.id # => "abc123"
179
+ # @obj.blob.original.id # => "abc123"
180
+ # @obj.blob.transform(width: 50).original.id # => "abc123"
181
+ # @obj.blob.transform(width: 50).transform(height: 50).original.id # => "abc123"
182
+ #
183
+ def original
184
+ @original || self
185
+ end
186
+
56
187
  private
57
188
 
58
189
  def public_content?
@@ -64,11 +195,21 @@ class Binary
64
195
  end
65
196
 
66
197
  def find_url(verb)
67
- CmsBackend.instance.find_blob_data(@id, access_type, verb)['url']
198
+ CmsBackend.instance.find_blob_data(id, access_type, verb, transformation_definition)['url']
199
+ rescue ClientError => e
200
+ case e.backend_code
201
+ when 'binary.unprocessable.image.transform.source_too_large',
202
+ 'binary.unprocessable.image.transform.source_type_invalid'
203
+ raise TransformationSourceError.new(e.message, e.backend_code)
204
+ when 'binary.unprocessable.image.transform.invalid_config'
205
+ raise TransformationDefinitionError.new(e.message, e.backend_code)
206
+ else
207
+ raise e
208
+ end
68
209
  end
69
210
 
70
211
  def access_type
71
- public_content? ? 'public_access' : 'private_access'
212
+ public? ? 'public_access' : 'private_access'
72
213
  end
73
214
  end
74
215
 
@@ -0,0 +1,31 @@
1
+ module Scrivito
2
+
3
+ module BinaryParamVerifier
4
+ InvalidSignature = Class.new(StandardError)
5
+
6
+ class << self
7
+ def verify(params)
8
+ params = message_verifier.verify(params)
9
+ expires = DateAttribute.parse(params['expires'])
10
+ raise InvalidSignature if expires && expires < Time.zone.now
11
+ Binary.new(params['binary_id'], expires.nil?, params['transformation_definition'])
12
+ rescue ActiveSupport::MessageVerifier::InvalidSignature
13
+ raise InvalidSignature
14
+ end
15
+
16
+ def generate(binary)
17
+ params = {binary_id: binary.id, transformation_definition: binary.transformation_definition}
18
+ params[:expires] = DateAttribute.serialize(Time.zone.now + 1.hour) if binary.private?
19
+ message_verifier.generate(params)
20
+ end
21
+
22
+ private
23
+
24
+ def message_verifier
25
+ ActiveSupport::MessageVerifier.new(
26
+ Rails.application.secrets.secret_key_base, serializer: JSON)
27
+ end
28
+ end
29
+ end
30
+
31
+ end
@@ -0,0 +1,35 @@
1
+ module Scrivito
2
+ #
3
+ # This module lets you change the URL of a binary CMS object for the purpose of
4
+ # fetching the binary data from a different location (e.g. via a proxy or from a mirror).
5
+ #
6
+ #
7
+ # @api public
8
+ #
9
+ module BinaryRewrite
10
+ def self.call(request, url)
11
+ change_url_callback = request.env["SCRIVITO_BINARY_URL_CALLBACK"]
12
+ url = change_url_callback.call(url) if change_url_callback
13
+ url
14
+ end
15
+ #
16
+ # You can, for example, have the URLs of images, etc., point to your desired
17
+ # domain instead of to the CDN Scrivito uses.
18
+ #
19
+ # @api public
20
+ #
21
+ # @param request [ActionDispatch::Request]
22
+ # @param block [Proc] block change default CDN url of current binary obj
23
+ #
24
+ # @example
25
+ # Scrivito::BinaryRewrite.enable_for(request) do |binary_url|
26
+ # binary_url.sub(
27
+ # "https://scrivito-public-cdn.s3-eu-west-1.amazonaws.com/",
28
+ # "https://my.reverse.proxy.com/")
29
+ # end
30
+ #
31
+ def self.enable_for(request, &block)
32
+ request.env["SCRIVITO_BINARY_URL_CALLBACK"] = block
33
+ end
34
+ end
35
+ end
@@ -7,6 +7,7 @@ class CacheMiddleware
7
7
 
8
8
  def call(env)
9
9
  clear_caches
10
+ clear_runtime
10
11
 
11
12
  @app.call(env)
12
13
  end
@@ -14,6 +15,10 @@ class CacheMiddleware
14
15
 
15
16
  private
16
17
 
18
+ def clear_runtime
19
+ Scrivito::LogSubscriber.reset_runtime
20
+ end
21
+
17
22
  def clear_caches
18
23
  Workspace.cache.clear
19
24
  CmsBackend.instance.clear_cache
@@ -65,9 +65,9 @@ class ChildListTag < Struct.new(:tag_name, :obj, :field_name, :view)
65
65
  # @return [String] The rendered html tag
66
66
  #
67
67
  # @example Render a <div> tag containing the text "random content" and assigns the tag a css class called +very_important+.
68
- # <%= list.tag :div, class: "very_important" do %>
69
- # random content
70
- # <% end %>
68
+ # list.tag(:div, class: "very_important") do
69
+ # "random content"
70
+ # end
71
71
  #
72
72
  def tag(tag_name, html_options = {}, &block)
73
73
  raise '"list.tag" can only be called once per iteration!' if @rendered
@@ -90,30 +90,36 @@ module Scrivito
90
90
 
91
91
  def find_workspace_data_by_id(id)
92
92
  if die_content_service
93
- from_csid = CmsDataCache.read_workspace_csid(id)
93
+ begin
94
+ cached_workspace_state = CmsDataCache.read_workspace_state(id)
94
95
 
95
- changes = CmsRestApi.get("/workspaces/#{id}/changes", from: from_csid)
96
+ cached_csid = cached_workspace_state.try(:first)
97
+ cached_workspace_data_tag = cached_workspace_state.try(:second)
96
98
 
97
- objs = changes["objs"]
98
- if objs.present?
99
- if objs != "*"
100
- last_state = Backend::ContentStateNode.find(from_csid)
99
+ changes = CmsRestApi.get("/workspaces/#{id}/changes", from: cached_csid)
101
100
 
102
- successor_state = changes["to"]
103
- current_state = last_state.create_successor(successor_state, objs)
104
- end
101
+ update_obj_cache(id, cached_csid, changes)
105
102
 
106
- # TODO what if workspace data is missing?
107
- # have the backend team include a new key `current`
108
- current_csid = changes["workspace"]["content_state_id"]
109
- CmsDataCache.write_workspace_csid(id, current_csid)
110
- end
103
+ workspace_data, workspace_data_tag = update_workspace_cache(
104
+ id, cached_workspace_data_tag, changes["workspace"])
111
105
 
112
- # TODO what if workspace data is missing?
113
- # implement workspace data caching
114
- workspace_data = changes["workspace"]
106
+ current_csid = changes["current"]
107
+ current_workspace_state = [current_csid, workspace_data_tag]
108
+
109
+ if current_workspace_state != cached_workspace_state
110
+ CmsDataCache.write_workspace_state(id, current_workspace_state)
111
+ end
115
112
 
116
- return WorkspaceData.new(workspace_data)
113
+ return WorkspaceData.new(workspace_data.merge(
114
+ "content_state_id" => current_csid))
115
+
116
+ rescue Scrivito::ClientError => client_error
117
+ if client_error.http_code == 404
118
+ return nil
119
+ else
120
+ raise
121
+ end
122
+ end
117
123
  end
118
124
 
119
125
  workspace_data_from_cache = WorkspaceDataFromService.find_from_cache(id)
@@ -168,17 +174,22 @@ module Scrivito
168
174
  end
169
175
  end
170
176
 
171
- def find_blob_data(id, access, verb)
172
- id = Addressable::URI.normalize_component(id, Addressable::URI::CharacterClasses::UNRESERVED)
173
- if blob_data = fetch_blob_data_from_cache(id, access, verb)
177
+ def find_blob_data(id, access, verb, transformation_definition)
178
+ if blob_data = find_blob_data_from_cache(id, access, verb, transformation_definition)
174
179
  blob_data
175
180
  else
176
- blob_datas = request_blob_datas_from_backend(id)
177
- store_blob_datas_in_cache(id, blob_datas)
181
+ id = normalize_blob_id(id)
182
+ blob_datas = request_blob_datas_from_backend(id, transformation_definition)
183
+ store_blob_datas_in_cache(id, transformation_definition, blob_datas)
178
184
  blob_datas[access][verb]
179
185
  end
180
186
  end
181
187
 
188
+ def find_blob_data_from_cache(id, access, verb, transformation_definition)
189
+ cache_key = blob_data_cache_key(normalize_blob_id(id), access, verb, transformation_definition)
190
+ CmsDataCache.cache.read(cache_key)
191
+ end
192
+
182
193
  def find_blob_metadata(id, url)
183
194
  if blob_metadata = fetch_blob_metadata_from_cache(id)
184
195
  blob_metadata
@@ -190,14 +201,24 @@ module Scrivito
190
201
  end
191
202
 
192
203
  def search_objs(workspace, params)
204
+ cache_index = 'search'
205
+ cache_key = params.to_param
206
+
193
207
  if die_content_service
194
- # TODO caching
195
- return request_search_result_from_backend(workspace, params)
208
+ cache = Backend::ObjDataCache.view_for_revision(workspace.revision)
209
+
210
+ if hit = cache.read_index(cache_index, cache_key)
211
+ return hit
212
+ end
213
+
214
+ result = request_search_result_from_backend(workspace, params)
215
+
216
+ cache.write_index(cache_index, cache_key, result)
217
+
218
+ return result
196
219
  end
197
220
 
198
221
  content_state = workspace.revision.content_state
199
- cache_index = 'search'
200
- cache_key = params.to_param
201
222
 
202
223
  if result = fetch_search_result_from_cache(content_state, cache_index, cache_key)
203
224
  result
@@ -208,34 +229,39 @@ module Scrivito
208
229
  end
209
230
  end
210
231
 
211
- def find_obj_class_data_by_name(revision, name)
212
- find_all_obj_class_data(revision).find { |obj_class_data| obj_class_data.name == name }
213
- end
232
+ private
214
233
 
215
- def find_all_obj_class_data(revision)
216
- content_state = revision.content_state
217
- if obj_classes_data = fetch_obj_classes_data_from_cache(content_state)
218
- obj_classes_data
234
+ def update_workspace_cache(id, cached_data_tag, changed_workspace)
235
+ if changed_workspace
236
+ workspace_data = changed_workspace
219
237
  else
220
- request_obj_classes_data_from_backend(revision).tap do |obj_classes_data|
221
- store_obj_classes_data_in_cache(content_state, obj_classes_data)
238
+ cached_workspace_data = CmsDataCache.read_data_from_tag(cached_data_tag)
239
+
240
+ if cached_workspace_data
241
+ workspace_data = cached_workspace_data
242
+ workspace_data_tag = cached_data_tag
243
+ else
244
+ workspace_data = CmsRestApi.get("/workspaces/#{id}")
222
245
  end
223
246
  end
224
- end
225
247
 
226
- private
248
+ workspace_data_tag ||= CmsDataCache.write_data_to_tag(workspace_data)
227
249
 
228
- def fetch_obj_classes_data_from_cache(content_state)
229
- ContentStateCaching.find_obj_classes_data(content_state) if caching?
250
+ [workspace_data, workspace_data_tag]
230
251
  end
231
252
 
232
- def request_obj_classes_data_from_backend(revision)
233
- response = CmsRestApi.get("revisions/#{revision.id}/obj_classes", include_inactive: true)
234
- response['results'].map { |raw_data| ObjClassData.new(raw_data) }
235
- end
253
+ def update_obj_cache(workspace_id, cached_csid, changes)
254
+ objs = changes["objs"]
255
+ if objs.present? && objs != "*"
256
+ last_state = Backend::ContentStateNode.find(cached_csid)
257
+ changes_index = Backend::ObjDataCache.changes_index_from(objs)
258
+ successor = last_state.create_successor(changes["to"], changes_index)
236
259
 
237
- def store_obj_classes_data_in_cache(content_state, obj_classes_data)
238
- ContentStateCaching.store_obj_classes_data(content_state, obj_classes_data) if caching?
260
+ cache = Backend::ObjDataCache.view_for_workspace(workspace_id, successor)
261
+ changes_index.each do |id, tag|
262
+ cache.write_obj_tag(id, tag)
263
+ end
264
+ end
239
265
  end
240
266
 
241
267
  def fetch_search_result_from_cache(content_state, cache_index, cache_key)
@@ -250,27 +276,33 @@ module Scrivito
250
276
  content_state.save_obj_data(cache_index, cache_key, result) if caching?
251
277
  end
252
278
 
253
- def fetch_blob_data_from_cache(id, access, verb)
254
- CmsDataCache.cache.read(blob_data_cache_key(id, access, verb))
255
- end
256
-
257
- def request_blob_datas_from_backend(id)
279
+ def request_blob_datas_from_backend(id, transformation_definition)
258
280
  @query_counter += 1
259
- CmsRestApi.get("blobs/#{id}")
281
+ if transformation_definition
282
+ CmsRestApi.get("blobs/#{id}/transform", transformation: transformation_definition)
283
+ else
284
+ CmsRestApi.get("blobs/#{id}")
285
+ end
260
286
  end
261
287
 
262
- def store_blob_datas_in_cache(id, blob_datas)
288
+ def store_blob_datas_in_cache(id, transformation_definition, blob_datas)
263
289
  %w[public_access private_access].each do |access|
264
290
  %w[get head].each do |verb|
265
291
  blob_data = blob_datas[access][verb]
266
- CmsDataCache.cache.write(blob_data_cache_key(id, access, verb),
267
- blob_data, blob_data['maxage'])
292
+ cache_key = blob_data_cache_key(id, access, verb, transformation_definition)
293
+ CmsDataCache.cache.write(cache_key, blob_data, blob_data['maxage'])
268
294
  end
269
295
  end
270
296
  end
271
297
 
272
- def blob_data_cache_key(id, access, verb)
273
- "blob_data/#{id}/#{access}/#{verb}"
298
+ def blob_data_cache_key(id, access, verb, transformation_definition)
299
+ cache_key = "blob_data/#{id}/#{access}/#{verb}"
300
+ cache_key << "/#{transformation_definition.to_query}" if transformation_definition
301
+ cache_key
302
+ end
303
+
304
+ def normalize_blob_id(id)
305
+ Addressable::URI.normalize_component(id, Addressable::URI::CharacterClasses::UNRESERVED)
274
306
  end
275
307
 
276
308
  def fetch_blob_metadata_from_cache(id)