scrivito_sdk 0.18.1 → 0.30.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (144) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/scrivito/blobs_controller.rb +1 -2
  3. data/app/controllers/scrivito/default_cms_controller.rb +9 -75
  4. data/app/controllers/scrivito/objs_controller.rb +107 -119
  5. data/app/controllers/scrivito/tasks_controller.rb +1 -1
  6. data/app/controllers/scrivito/users_controller.rb +1 -1
  7. data/app/controllers/scrivito/webservice_controller.rb +6 -2
  8. data/app/controllers/scrivito/workspaces_controller.rb +57 -17
  9. data/app/helpers/scrivito_helper.rb +285 -0
  10. data/app/views/google_maps_widget/show.html.erb +1 -0
  11. data/app/views/scrivito/_editing_auth_warning.html.erb +43 -0
  12. data/app/views/scrivito/blobs/upload_permission.json.jbuilder +1 -0
  13. data/app/views/scrivito/objs/copy.json.jbuilder +1 -0
  14. data/app/views/scrivito/objs/copy_widget.html.erb +1 -1
  15. data/app/views/scrivito/objs/create.json.jbuilder +1 -0
  16. data/app/views/scrivito/objs/create_widget.html.erb +1 -1
  17. data/app/views/scrivito/objs/details.json.jbuilder +1 -0
  18. data/app/views/scrivito/objs/format_missing_error.json.jbuilder +1 -0
  19. data/app/views/scrivito/objs/modification.json.jbuilder +1 -0
  20. data/app/views/scrivito/objs/page_class_selection.json.jbuilder +1 -0
  21. data/app/views/scrivito/objs/search.json.jbuilder +6 -0
  22. data/app/views/scrivito/objs/search_only_size.json.jbuilder +1 -0
  23. data/app/views/scrivito/objs/update.json.jbuilder +1 -0
  24. data/app/views/scrivito/objs/widget_class_selection.json.jbuilder +1 -0
  25. data/app/views/scrivito/objs/widget_modification.json.jbuilder +1 -0
  26. data/app/views/scrivito/tasks/show.json.jbuilder +3 -0
  27. data/app/views/scrivito/users/suggest.json.jbuilder +1 -0
  28. data/app/views/scrivito/webservice/empty.json +1 -0
  29. data/app/views/scrivito/webservice/error.json.jbuilder +2 -0
  30. data/app/views/scrivito/workspaces/_workspace.json.jbuilder +5 -0
  31. data/app/views/scrivito/workspaces/check.json.jbuilder +1 -0
  32. data/app/views/scrivito/workspaces/create.json.jbuilder +1 -0
  33. data/app/views/scrivito/workspaces/index.json.jbuilder +1 -0
  34. data/app/views/scrivito/workspaces/show.json.jbuilder +1 -0
  35. data/app/views/scrivito/workspaces/task.json.jbuilder +5 -0
  36. data/config/ca-bundle.crt +259 -234
  37. data/config/cms_routes.rb +2 -2
  38. data/config/routes.rb +3 -0
  39. data/lib/assets/javascripts/{scrivito_editing.js → scrivito_sdk.js} +1662 -1180
  40. data/lib/assets/stylesheets/{scrivito_editing.css → scrivito_sdk.css} +242 -6
  41. data/lib/generators/scrivito/install/install_generator.rb +62 -0
  42. data/lib/generators/scrivito/install/templates/app/controllers/cms_controller.rb +3 -0
  43. data/lib/generators/scrivito/install/templates/app/controllers/page_controller.rb +2 -0
  44. data/lib/generators/scrivito/install/templates/app/models/headline_widget.rb +7 -0
  45. data/lib/generators/scrivito/install/templates/app/models/image.rb +5 -0
  46. data/lib/generators/scrivito/install/templates/app/models/image_widget.rb +7 -0
  47. data/lib/generators/scrivito/install/templates/app/models/obj.rb +2 -0
  48. data/lib/generators/scrivito/install/templates/app/models/page.rb +8 -0
  49. data/lib/generators/scrivito/install/templates/app/models/text_widget.rb +7 -0
  50. data/lib/generators/scrivito/install/templates/app/models/widget.rb +2 -0
  51. data/lib/generators/scrivito/install/templates/app/views/headline_widget/show.html.erb +1 -0
  52. data/lib/generators/scrivito/install/templates/app/views/headline_widget/thumbnail.html.erb +3 -0
  53. data/lib/generators/scrivito/install/templates/app/views/image_widget/show.html.erb +1 -0
  54. data/lib/generators/scrivito/install/templates/app/views/image_widget/thumbnail.html.erb +3 -0
  55. data/lib/generators/scrivito/install/templates/app/views/page/details.html.erb +1 -0
  56. data/lib/generators/scrivito/install/templates/app/views/page/index.html.erb +16 -0
  57. data/lib/generators/scrivito/install/templates/app/views/page/thumbnail.html.erb +3 -0
  58. data/lib/generators/scrivito/install/templates/app/views/text_widget/show.html.erb +1 -0
  59. data/lib/generators/scrivito/install/templates/app/views/text_widget/thumbnail.html.erb +3 -0
  60. data/lib/generators/scrivito/install/templates/config/initializers/scrivito.rb +10 -0
  61. data/lib/generators/scrivito/install/templates/scrivito/migrate/install_scrivito.rb +49 -0
  62. data/lib/generators/scrivito/migration/USAGE +9 -0
  63. data/lib/generators/{cms → scrivito}/migration/migration_generator.rb +4 -9
  64. data/lib/generators/{cms → scrivito}/migration/templates/migration.erb +3 -3
  65. data/lib/generators/{cms → scrivito}/widget/templates/details.html.erb +0 -0
  66. data/lib/generators/{cms → scrivito}/widget/templates/migration.erb +0 -0
  67. data/lib/generators/{cms → scrivito}/widget/templates/model.erb +0 -0
  68. data/lib/generators/{cms → scrivito}/widget/templates/show.html.erb +0 -0
  69. data/lib/generators/scrivito/widget/templates/thumbnail.html.erb +3 -0
  70. data/lib/generators/{cms → scrivito}/widget/widget_generator.rb +3 -2
  71. data/lib/obj.rb +17 -3
  72. data/lib/scrivito/attribute_collection.rb +7 -12
  73. data/lib/scrivito/attribute_content.rb +31 -12
  74. data/lib/scrivito/basic_obj.rb +186 -96
  75. data/lib/scrivito/basic_widget.rb +25 -10
  76. data/lib/scrivito/binary.rb +75 -0
  77. data/lib/scrivito/cache_garbage_collector.rb +19 -4
  78. data/lib/scrivito/child_list_tag.rb +108 -0
  79. data/lib/scrivito/client_config.rb +2 -1
  80. data/lib/scrivito/client_error.rb +3 -2
  81. data/lib/scrivito/cms_backend.rb +81 -16
  82. data/lib/scrivito/cms_dispatch_controller.rb +7 -0
  83. data/lib/scrivito/cms_env.rb +0 -2
  84. data/lib/scrivito/cms_field_tag.rb +44 -35
  85. data/lib/scrivito/cms_rest_api.rb +23 -6
  86. data/lib/scrivito/cms_rest_api/attribute_serializer.rb +1 -1
  87. data/lib/scrivito/cms_routing.rb +120 -0
  88. data/lib/scrivito/configuration.rb +69 -58
  89. data/lib/scrivito/connection_manager.rb +30 -1
  90. data/lib/scrivito/content_service.rb +10 -7
  91. data/lib/scrivito/controller_actions.rb +108 -0
  92. data/lib/scrivito/diff.rb +30 -15
  93. data/lib/scrivito/editing_context.rb +4 -0
  94. data/lib/scrivito/editing_context_helper.rb +19 -0
  95. data/lib/scrivito/editing_context_middleware.rb +20 -1
  96. data/lib/scrivito/image_tag_helper.rb +44 -0
  97. data/lib/scrivito/layout_tags.rb +33 -0
  98. data/lib/scrivito/link.rb +4 -2
  99. data/lib/scrivito/membership.rb +0 -8
  100. data/lib/scrivito/migrations.rb +0 -1
  101. data/lib/scrivito/migrations/installer.rb +1 -1
  102. data/lib/scrivito/migrations/migration.rb +0 -2
  103. data/lib/scrivito/migrations/migrator.rb +2 -3
  104. data/lib/scrivito/obj_class.rb +64 -16
  105. data/lib/scrivito/obj_class_data.rb +4 -0
  106. data/lib/scrivito/obj_search_enumerator.rb +1 -1
  107. data/lib/scrivito/routing_helper.rb +42 -0
  108. data/lib/scrivito/{engine.rb → sdk_engine.rb} +15 -29
  109. data/lib/scrivito/tag_renderer.rb +40 -0
  110. data/lib/scrivito/test_request.rb +40 -0
  111. data/lib/scrivito/type_computer.rb +1 -6
  112. data/lib/scrivito/user.rb +33 -15
  113. data/lib/scrivito/user_definition.rb +34 -6
  114. data/lib/scrivito/widget_tag.rb +67 -0
  115. data/lib/scrivito/workspace.rb +12 -24
  116. data/lib/scrivito_sdk.rb +17 -1
  117. data/lib/tasks/cache.rake +4 -5
  118. data/lib/tasks/migration.rake +1 -1
  119. data/lib/widget.rb +21 -3
  120. metadata +119 -70
  121. data/README +0 -6
  122. data/app/controllers/cms_controller.rb +0 -7
  123. data/app/helpers/cms_helper.rb +0 -7
  124. data/app/helpers/cms_routing_helper.rb +0 -7
  125. data/app/helpers/scrivito/cms_asset_helper.rb +0 -110
  126. data/app/helpers/scrivito/cms_tag_helper.rb +0 -232
  127. data/app/helpers/scrivito/default_cms_helper.rb +0 -21
  128. data/app/helpers/scrivito/default_cms_routing_helper.rb +0 -128
  129. data/app/helpers/scrivito/display_helper.rb +0 -64
  130. data/app/helpers/scrivito/editing_helper.rb +0 -32
  131. data/app/helpers/scrivito/layout_helper.rb +0 -21
  132. data/app/models/named_link.rb +0 -2
  133. data/app/views/cms/_index.html.erb +0 -7
  134. data/app/views/cms/index.html.erb +0 -1
  135. data/app/views/scrivito/_editing_javascript.html.erb +0 -7
  136. data/app/views/scrivito/default_cms/show_widget.html.erb +0 -1
  137. data/lib/assets/stylesheets/scrivito.css +0 -199
  138. data/lib/generators/cms/migration/USAGE +0 -9
  139. data/lib/generators/cms/widget/templates/thumbnail.html.erb +0 -2
  140. data/lib/scrivito/blob.rb +0 -48
  141. data/lib/scrivito/cms_accessible.rb +0 -30
  142. data/lib/scrivito/cms_rest_api/blob_uploader.rb +0 -18
  143. data/lib/scrivito/cms_test_request.rb +0 -23
  144. data/lib/scrivito/migrations/migration_dsl.rb +0 -180
@@ -25,7 +25,7 @@ class BasicWidget
25
25
  # The new Widget must be stored inside a container (i.e. an Obj or another Widget)
26
26
  # before it can be used.
27
27
  #
28
- # See {BasicObj.create Obj.create} for a detailed overview of how to set attributes.
28
+ # See {Scrivito::BasicObj.create Obj.create} for a detailed overview of how to set attributes.
29
29
  # @api public
30
30
  # @param [Hash] attributes
31
31
  #
@@ -51,7 +51,7 @@ class BasicWidget
51
51
 
52
52
  # Update the attributes of this Widget
53
53
  #
54
- # See {BasicObj.create Obj.create} for a detailed overview of how to set attributes
54
+ # See {Scrivito::BasicObj.create Obj.create} for a detailed overview of how to set attributes
55
55
  # @api public
56
56
  # @param [Hash] attributes
57
57
  def update(attributes)
@@ -59,16 +59,16 @@ class BasicWidget
59
59
  reload
60
60
  end
61
61
 
62
- # Destroys the {BasicWidget Widget} in the current {Workspace}
62
+ # Destroys the {Scrivito::BasicWidget Widget} in the current {Workspace}
63
63
  # @api public
64
64
  def destroy
65
65
  new_widget_list = container[container_field_name] - [self]
66
66
  container.update(container_field_name => new_widget_list)
67
67
  end
68
68
 
69
- # Clones the {BasicWidget Widget}. The clone gets all
69
+ # Clones the {Scrivito::BasicWidget Widget}. The clone gets all
70
70
  # attributes of the original widget except nested widget attributes.
71
- # The clone is not attached to an {BasicObj Obj} initially.
71
+ # The clone is not attached to an {Scrivito::BasicObj Obj} initially.
72
72
  # It only becomes usable by assigning it to a widget attribute of an Obj.
73
73
  #
74
74
  # @example
@@ -107,10 +107,21 @@ class BasicWidget
107
107
  self == other
108
108
  end
109
109
 
110
- # Reverts content changes of this widget. Widget attributes of this widget are not effected.
111
- # After calling this method it's as if this widget has been never modified in the current working copy.
112
- # This method does not work with +new+ or +deleted+ widgets.
113
- # This method also does also not work for the +published+ workspace or the +rtc+ working copy.
110
+ #
111
+ # Reverts all changes made to the +Widget+ in the current workspace.
112
+ #
113
+ # @api public
114
+ #
115
+ # @note This method does not support +Widget+s, which are +new+.
116
+ # Please use {Scrivito::BasicWidget#destroy Widget#destroy} to destroy them.
117
+ # @note This method does not support +Widget+s, which are +deleted+.
118
+ # Please use {Scrivito::BasicObj#revert Obj#revert} to restore them.
119
+ #
120
+ # @raise [ScrivitoError] If the current workspace is +published+.
121
+ # @raise [ScrivitoError] If the current workspace is the +rtc+ workspace.
122
+ # @raise [ScrivitoError] If the +Widget+ is +new+.
123
+ # @raise [ScrivitoError] If the +Widget+ is +deleted+.
124
+ #
114
125
  def revert
115
126
  workspace.assert_revertable
116
127
 
@@ -170,7 +181,7 @@ class BasicWidget
170
181
  end
171
182
  end
172
183
 
173
- # returns the entity ({BasicObj} or {BasicWidget}) that references this widget
184
+ # returns the entity ({Scrivito::BasicObj} or {Scrivito::BasicWidget}) that references this widget
174
185
  # @api public
175
186
  def container
176
187
  @container || cache_container_and_field_name_for_widget.first
@@ -182,6 +193,10 @@ class BasicWidget
182
193
  @container_field_name || cache_container_and_field_name_for_widget.second
183
194
  end
184
195
 
196
+ def has_attribute?(key)
197
+ key.to_s == '_obj_class' || super
198
+ end
199
+
185
200
  def inspect
186
201
  if @id
187
202
  if @obj
@@ -0,0 +1,75 @@
1
+ module Scrivito
2
+
3
+ # @api public
4
+ # The Binary class represents the data stored in a binary attribute of an Obj
5
+ # or Widget
6
+ class Binary
7
+ def initialize(id, public_content)
8
+ @id = id
9
+ @public_content = public_content
10
+ end
11
+
12
+ # @api public
13
+ # Some Scrivito data is considered private, i.e. it is not currently intended
14
+ # for the general public, for example content in a workspace that has not
15
+ # been published yet.
16
+ # @return [Boolean]
17
+ def private?
18
+ !public_content?
19
+ end
20
+
21
+ # @api public
22
+ # The URL where this binary data is accessible and can be downloaded using an
23
+ # HTTP GET request. Note that urls for private content will have an
24
+ # expiration time in order to protect them.
25
+ # Therefore the url returned here should be accessed immediately after it has been returned (i.e. within a couple of minutes).
26
+ # When accessed after they have expired, an error will occur.
27
+ # 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).
28
+ # @return [String] the URL under which this content is available
29
+ def url
30
+ find_url('get')
31
+ end
32
+
33
+ # @api public
34
+ # the filename of this binary data, for example "my_image.jpg"
35
+ # @return [String] the filename of binary
36
+ def filename
37
+ File.basename(URI(url).path)
38
+ end
39
+
40
+ # @api public
41
+ # the content type of this binary data, for example "image/jpeg"
42
+ # @return [String] content type
43
+ def content_type
44
+ headers[:content_type]
45
+ end
46
+
47
+ # @api public
48
+ # the length of this binary data, in bytes.
49
+ # @return [Integer] number of bytes
50
+ def content_length
51
+ headers[:content_length].to_i
52
+ end
53
+
54
+ private
55
+
56
+ attr_reader :id
57
+
58
+ def public_content?
59
+ !!@public_content
60
+ end
61
+
62
+ def headers
63
+ CmsBackend.instance.find_blob_metadata(id, find_url('head'))
64
+ end
65
+
66
+ def find_url(verb)
67
+ CmsBackend.instance.find_blob_data(@id, access_type, verb)['url']
68
+ end
69
+
70
+ def access_type
71
+ public_content? ? 'public_access' : 'private_access'
72
+ end
73
+ end
74
+
75
+ end
@@ -1,8 +1,12 @@
1
1
  module Scrivito
2
- # The CacheGarbageCollector allows to remove old cache files from the Scrivito cache
3
- # storage path. By default, a maximum of +DEFAULT_MAX_FILE_INODES+ file inodes that were accessed
4
- # lately are kept in the cache directory. All other files and empty directories are removed. Log
5
- # output is written to the Rails environment log file.
2
+ #
3
+ # The CacheGarbageCollector allows to remove old cache files from the Scrivito cache storage path.
4
+ # By default, a maximum of {Scrivito::CacheGarbageCollector::DEFAULT_MAX_FILE_INODES} file inodes
5
+ # that were accessed lately are kept in the cache directory.
6
+ # All other files and empty directories are removed.
7
+ # Log output is written to the Rails environment log file.
8
+ #
9
+ # @api public
6
10
  #
7
11
  # @example Running the garbage collector with default settings
8
12
  #
@@ -11,7 +15,13 @@ module Scrivito
11
15
  # @example Changing the maximum number of file inodes
12
16
  #
13
17
  # bundle exec rake scrivito:cache:gc MAX_FILE_INODES=5000
18
+ #
14
19
  class CacheGarbageCollector
20
+ #
21
+ # Default maximum number of file inodes that are kept in the cache directory.
22
+ #
23
+ # @api public
24
+ #
15
25
  DEFAULT_MAX_FILE_INODES = 100000
16
26
 
17
27
  attr_accessor :max_file_inodes
@@ -20,6 +30,9 @@ module Scrivito
20
30
  @max_file_inodes = (max_file_inodes || DEFAULT_MAX_FILE_INODES).to_i
21
31
  end
22
32
 
33
+ #
34
+ # @api public
35
+ #
23
36
  def run_gc
24
37
  announce "Cap number of cache file inodes at #{max_file_inodes} in #{cache_path}."
25
38
 
@@ -58,6 +71,8 @@ module Scrivito
58
71
  announce "Done. Removed #{delta_count} entries. Saved at least #{delta_disk_space} Bytes."
59
72
  end
60
73
 
74
+ private
75
+
61
76
  def cache_path
62
77
  CmsCacheStorage.backend_cache.cache_path
63
78
  end
@@ -0,0 +1,108 @@
1
+ module Scrivito
2
+
3
+ class ChildListTag < Struct.new(:tag_name, :obj, :field_name, :view)
4
+ include TagRenderer
5
+
6
+ def initialize(tag_name, obj, field_name, view)
7
+ unless field_name == 'toclist'
8
+ raise "#{field_name} is not (yet) supported. Currently only toclist is supported."
9
+ end
10
+ super
11
+ end
12
+
13
+ def options
14
+ return {} unless user_present?
15
+
16
+ options = {
17
+ 'private-child-list-allowed-classes' => Obj.valid_page_classes_beneath(obj.path).to_json,
18
+ 'private-child-list-path' => obj.path,
19
+ 'private-obj-description-for-editor' => obj.description_for_editor,
20
+ }
21
+
22
+ if obj.has_attribute?(:child_order)
23
+ options['private-child-list-order-obj-id'] = obj.id
24
+ modification = comparison.modification_for_attribute(obj, :child_order)
25
+ if modification == Modification::EDITED
26
+ options['private-child-list-modification'] = modification
27
+ end
28
+ end
29
+
30
+ options
31
+ end
32
+
33
+ def content(&block)
34
+ return unless block_given?
35
+
36
+ is_sortable = obj.has_attribute?(:child_order) && user_present?
37
+ rendered_children = sorted_children.map do |child|
38
+ obj_tag = ObjTag.new(child, is_sortable, comparison.modification(child), view)
39
+ yield obj_tag, child
40
+ obj_tag.rendered
41
+ end
42
+ view.safe_join(rendered_children)
43
+ end
44
+
45
+ #
46
+ # This is a helper class for {ScrivitoHelper#scrivito_tag_list}.
47
+ # @api public
48
+ #
49
+ class ObjTag < Struct.new(:obj, :is_sortable, :modification, :view)
50
+ include TagRenderer
51
+
52
+ attr_reader :tag_name, :rendered
53
+
54
+ #
55
+ # @api public
56
+ #
57
+ # @param tag_name [String, Symbol] Name of the html tag (e.g. +:div+ or +:span+).
58
+ # @param html_options [Hash] Additional options, which are passed to +content_tag+.
59
+ # Use them to add HTML attributes to the tag.
60
+ #
61
+ # @return [String] The rendered html tag
62
+ #
63
+ # @example Render a <div> tag containing the text "random content" and assigns the tag a css class called +very_important+.
64
+ # <%= list.tag :div, class: "very_important" do %>
65
+ # random content
66
+ # <% end %>
67
+ #
68
+ def tag(tag_name, html_options = {}, &block)
69
+ raise '"list.tag" can only be called once per iteration!' if @rendered
70
+ @tag_name = tag_name
71
+ @rendered = render(html_options, &block)
72
+ nil
73
+ end
74
+
75
+ def options
76
+ return {} unless is_sortable
77
+ options = {
78
+ 'private-obj-id' => obj.id,
79
+ 'private-obj-description-for-editor' => obj.description_for_editor,
80
+ }
81
+ if modification == Modification::NEW || modification == Modification::DELETED
82
+ options['private-obj-modification'] = modification
83
+ end
84
+ options
85
+ end
86
+
87
+ def content(&block)
88
+ view.capture { yield } if block_given?
89
+ end
90
+ end
91
+
92
+ private
93
+
94
+ def user_present?
95
+ view.scrivito_user.present?
96
+ end
97
+
98
+ def comparison
99
+ EditingContextMiddleware.from_request(view.request).comparison
100
+ end
101
+
102
+ def sorted_children
103
+ children = obj.toclist
104
+ obj.has_attribute?(:child_order) ? Obj.sort_by_list(children, obj.child_order) : children
105
+ end
106
+ end
107
+
108
+ end
@@ -24,7 +24,8 @@ class ClientConfig < Struct.new(:obj, :editing_context, :lookup_context, :resour
24
24
 
25
25
  selected_workspace: {
26
26
  id: selected_workspace.id,
27
- title: selected_workspace.title
27
+ title: selected_workspace.title,
28
+ outdated: selected_workspace.outdated?
28
29
  },
29
30
 
30
31
  visible_workspace: {
@@ -1,10 +1,11 @@
1
1
  module Scrivito
2
2
 
3
3
  class ClientError < StandardError
4
- attr_reader :http_code
4
+ attr_reader :http_code, :error_code
5
5
 
6
- def initialize(message, http_code)
6
+ def initialize(message, http_code, error_code=nil)
7
7
  @http_code = http_code
8
+ @error_code = error_code
8
9
  super(message)
9
10
  end
10
11
  end
@@ -1,4 +1,4 @@
1
- require 'rest_client'
1
+ require 'addressable/uri'
2
2
 
3
3
  module Scrivito
4
4
 
@@ -47,7 +47,6 @@ module Scrivito
47
47
  end
48
48
 
49
49
  class CmsBackend
50
- BLOB_DATA_CACHE_PREFIX = 'blob_data'.freeze
51
50
  VALID_INDEX_NAMES = %w[id path ppath permalink].freeze
52
51
 
53
52
  class << self
@@ -77,6 +76,11 @@ module Scrivito
77
76
  @query_counter
78
77
  end
79
78
 
79
+ # For test purpose only.
80
+ def reset_query_counter!
81
+ @query_counter = 0
82
+ end
83
+
80
84
  def find_workspace_data_by_id(id)
81
85
  workspace_data_from_cache = WorkspaceDataFromService.find_from_cache(id)
82
86
  from_content_state_id = workspace_data_from_cache.try(:content_state_id)
@@ -110,16 +114,24 @@ module Scrivito
110
114
  find_obj_data_filtering_deleted_by(revision, index, keys, true)
111
115
  end
112
116
 
113
- def find_blob_data_by_id(id)
114
- cache_key = "#{BLOB_DATA_CACHE_PREFIX}/#{id}"
115
- if data_from_cache = CmsCacheStorage.cache.read(cache_key)
116
- data_from_cache
117
+ def find_blob_data(id, access, verb)
118
+ id = Addressable::URI.normalize_component(id, Addressable::URI::CharacterClasses::UNRESERVED)
119
+ if blob_data = fetch_blob_data_from_cache(id, access, verb)
120
+ blob_data
117
121
  else
118
- data_from_database = find_blob_data_from_database_by(id)
119
- if maxage = data_from_database['maxage']
120
- CmsCacheStorage.cache.write(cache_key, data_from_database, :expires_in => maxage)
121
- end
122
- data_from_database
122
+ blob_datas = request_blob_datas_from_backend(id)
123
+ store_blob_datas_in_cache(id, blob_datas)
124
+ blob_datas[access][verb]
125
+ end
126
+ end
127
+
128
+ def find_blob_metadata(id, url)
129
+ if blob_metadata = fetch_blob_metadata_from_cache(id)
130
+ blob_metadata
131
+ else
132
+ blob_metadata = request_blob_metadata_from_s3(url)
133
+ store_blob_metadata_in_cache(id, blob_metadata)
134
+ blob_metadata
123
135
  end
124
136
  end
125
137
 
@@ -179,6 +191,64 @@ module Scrivito
179
191
  content_state.save_obj_data(cache_index, cache_key, result) if caching?
180
192
  end
181
193
 
194
+ def fetch_blob_data_from_cache(id, access, verb)
195
+ CmsCacheStorage.cache.read(blob_data_cache_key(id, access, verb))
196
+ end
197
+
198
+ def request_blob_datas_from_backend(id)
199
+ @query_counter += 1
200
+ CmsRestApi.get("blobs/#{id}")
201
+ end
202
+
203
+ def store_blob_datas_in_cache(id, blob_datas)
204
+ %w[public_access private_access].each do |access|
205
+ %w[get head].each do |verb|
206
+ blob_data = blob_datas[access][verb]
207
+ CmsCacheStorage.cache.write(blob_data_cache_key(id, access, verb),
208
+ blob_data, expires_in: blob_data['maxage'])
209
+ end
210
+ end
211
+ end
212
+
213
+ def blob_data_cache_key(id, access, verb)
214
+ "blob_data/#{id}/#{access}/#{verb}"
215
+ end
216
+
217
+ def fetch_blob_metadata_from_cache(id)
218
+ CmsCacheStorage.cache.read(blob_metadata_cache_key(id))
219
+ end
220
+
221
+ def request_blob_metadata_from_s3(url)
222
+ uri = URI.parse(url)
223
+ retried = false
224
+ begin
225
+ response = ConnectionManager.request(uri, Net::HTTP::Head.new(uri.path))
226
+ @query_counter += 1
227
+ rescue NetworkError
228
+ raise if retried
229
+ retried = true
230
+ retry
231
+ end
232
+
233
+ raise ScrivitoError, "S3 responded with #{reponse.code}" unless response.code == '200'
234
+
235
+ {
236
+ content_length: response['content-length'],
237
+ content_type: response['content-type'],
238
+ cache_control: response['cache-control'],
239
+ }
240
+ end
241
+
242
+ def store_blob_metadata_in_cache(id, blob_metadata)
243
+ max_age = blob_metadata.delete(:cache_control) =~ /max-age=(.*),/ && $1
244
+ max_age = max_age.to_i if max_age
245
+ CmsCacheStorage.cache.write(blob_metadata_cache_key(id), blob_metadata, expires_in: max_age)
246
+ end
247
+
248
+ def blob_metadata_cache_key(id)
249
+ "blob_metadata/#{Digest::SHA1.hexdigest(id)}" # id of a blob is a URI.
250
+ end
251
+
182
252
  def find_obj_data_filtering_deleted_by(revision, index, keys, include_deleted)
183
253
  index = index.to_s
184
254
  assert_valid_index_name(index)
@@ -269,11 +339,6 @@ module Scrivito
269
339
  ContentStateCaching.store_obj_data(revision.content_state, index, key, item)
270
340
  end
271
341
 
272
- def find_blob_data_from_database_by(id)
273
- @query_counter += 1
274
- ContentService.query('blobs/query', :blob_ids => [id])['blobs'][id]
275
- end
276
-
277
342
  def assert_valid_index_name(index)
278
343
  raise ArgumentError, "invalid index name '#{index}'" unless VALID_INDEX_NAMES.include?(index)
279
344
  end