scrivito_sdk 0.17.0 → 0.18.0

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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -0
  3. data/app/controllers/scrivito/default_cms_controller.rb +15 -3
  4. data/app/controllers/scrivito/objs_controller.rb +61 -25
  5. data/app/controllers/scrivito/users_controller.rb +7 -0
  6. data/app/controllers/scrivito/webservice_controller.rb +17 -7
  7. data/app/controllers/scrivito/workspaces_controller.rb +85 -17
  8. data/app/helpers/scrivito/default_cms_routing_helper.rb +3 -3
  9. data/config/routes.rb +4 -1
  10. data/lib/assets/javascripts/scrivito_editing.js +4182 -508
  11. data/lib/assets/stylesheets/scrivito_editing.css +489 -13
  12. data/lib/generators/cms/migration/templates/migration.erb +4 -4
  13. data/lib/scrivito/attribute_collection.rb +1 -6
  14. data/lib/scrivito/attribute_content.rb +29 -7
  15. data/lib/scrivito/basic_obj.rb +91 -61
  16. data/lib/scrivito/basic_widget.rb +0 -11
  17. data/lib/scrivito/client_config.rb +40 -25
  18. data/lib/scrivito/cms_backend.rb +54 -0
  19. data/lib/scrivito/cms_cache_storage.rb +8 -0
  20. data/lib/scrivito/cms_field_tag.rb +2 -1
  21. data/lib/scrivito/cms_rest_api.rb +9 -0
  22. data/lib/scrivito/configuration.rb +4 -2
  23. data/lib/scrivito/content_state.rb +8 -0
  24. data/lib/scrivito/content_state_caching.rb +20 -0
  25. data/lib/scrivito/editing_context.rb +35 -34
  26. data/lib/scrivito/membership.rb +22 -3
  27. data/lib/scrivito/memberships_collection.rb +8 -4
  28. data/lib/scrivito/obj_class.rb +45 -100
  29. data/lib/scrivito/obj_class_collection.rb +53 -0
  30. data/lib/scrivito/obj_class_data.rb +33 -0
  31. data/lib/scrivito/obj_data.rb +26 -48
  32. data/lib/scrivito/obj_data_from_hash.rb +5 -5
  33. data/lib/scrivito/obj_data_from_service.rb +9 -3
  34. data/lib/scrivito/obj_search_builder.rb +0 -5
  35. data/lib/scrivito/obj_search_enumerator.rb +3 -20
  36. data/lib/scrivito/objs_collection.rb +7 -0
  37. data/lib/scrivito/restriction_set.rb +2 -2
  38. data/lib/scrivito/user.rb +89 -23
  39. data/lib/scrivito/user_definition.rb +73 -70
  40. data/lib/scrivito/workspace.rb +52 -8
  41. data/lib/scrivito/workspace/publish_checker.rb +126 -0
  42. metadata +6 -2
@@ -123,8 +123,62 @@ module Scrivito
123
123
  end
124
124
  end
125
125
 
126
+ def search_objs(workspace, params)
127
+ content_state = workspace.revision.content_state
128
+ cache_index = 'search'
129
+ cache_key = params.to_param
130
+
131
+ if result = fetch_search_result_from_cache(content_state, cache_index, cache_key)
132
+ result
133
+ else
134
+ request_search_result_from_backend(workspace, params).tap do |result|
135
+ store_search_result_in_cache(content_state, cache_index, cache_key, result)
136
+ end
137
+ end
138
+ end
139
+
140
+ def find_obj_class_data_by_name(revision, name)
141
+ find_all_obj_class_data(revision).find { |obj_class_data| obj_class_data.name == name }
142
+ end
143
+
144
+ def find_all_obj_class_data(revision)
145
+ content_state = revision.content_state
146
+ if obj_classes_data = fetch_obj_classes_data_from_cache(content_state)
147
+ obj_classes_data
148
+ else
149
+ request_obj_classes_data_from_backend(revision).tap do |obj_classes_data|
150
+ store_obj_classes_data_in_cache(content_state, obj_classes_data)
151
+ end
152
+ end
153
+ end
154
+
126
155
  private
127
156
 
157
+ def fetch_obj_classes_data_from_cache(content_state)
158
+ ContentStateCaching.find_obj_classes_data(content_state) if caching?
159
+ end
160
+
161
+ def request_obj_classes_data_from_backend(revision)
162
+ response = CmsRestApi.get("revisions/#{revision.id}/obj_classes", include_inactive: true)
163
+ response['results'].map { |raw_data| ObjClassData.new(raw_data) }
164
+ end
165
+
166
+ def store_obj_classes_data_in_cache(content_state, obj_classes_data)
167
+ ContentStateCaching.store_obj_classes_data(content_state, obj_classes_data) if caching?
168
+ end
169
+
170
+ def fetch_search_result_from_cache(content_state, cache_index, cache_key)
171
+ content_state.find_obj_data(cache_index, cache_key) if caching?
172
+ end
173
+
174
+ def request_search_result_from_backend(workspace, params)
175
+ CmsRestApi.get("workspaces/#{workspace.id}/objs/search", params)
176
+ end
177
+
178
+ def store_search_result_in_cache(content_state, cache_index, cache_key, result)
179
+ content_state.save_obj_data(cache_index, cache_key, result) if caching?
180
+ end
181
+
128
182
  def find_obj_data_filtering_deleted_by(revision, index, keys, include_deleted)
129
183
  index = index.to_s
130
184
  assert_valid_index_name(index)
@@ -45,6 +45,14 @@ module CmsCacheStorage
45
45
  def write_obj_data(content_state_id, index, key, data)
46
46
  cache.write("content/#{content_state_id}/obj/#{index}/#{key}", data)
47
47
  end
48
+
49
+ def read_obj_classes_data(content_state_id)
50
+ cache.read("content/#{content_state_id}/obj_classes")
51
+ end
52
+
53
+ def write_obj_classes_data(content_state_id, data)
54
+ cache.write("content/#{content_state_id}/obj_classes", data)
55
+ end
48
56
  end
49
57
  end
50
58
 
@@ -60,7 +60,8 @@ class CmsFieldTag < Struct.new(
60
60
  if FIELD_TYPES_WITH_ORIGINAL_CONTENT.include?(field_type)
61
61
  original_value = view_context.display_value(current_value)
62
62
  original_content = original_content(field_type, original_value)
63
- options['private-field-original-content'] = MultiJson.encode(original_content)
63
+ encoded_content = Base64.strict_encode64(MultiJson.encode(original_content))
64
+ options['private-field-original-content'] = encoded_content
64
65
  end
65
66
 
66
67
  if field_type == 'widget'
@@ -56,11 +56,20 @@ module Scrivito
56
56
  response_for_request_cms_api(method, resource_path, payload)
57
57
  end
58
58
 
59
+ def self.count_requests(path)
60
+ @count_requests = path
61
+ @number_of_requests = 0
62
+ yield
63
+ @count_requests = nil
64
+ @number_of_requests
65
+ end
66
+
59
67
  class << self
60
68
 
61
69
  private
62
70
 
63
71
  def request_cms_api(action, resource_path, payload, options)
72
+ @number_of_requests += 1 if resource_path == @count_requests
64
73
  decoded = response_for_request_cms_api(action, resource_path, payload)
65
74
  return decoded unless Hash === decoded
66
75
  return decoded unless decoded.keys == ["task"]
@@ -178,9 +178,10 @@ module Scrivito
178
178
  self.ca_file = DEFAULT_CA_FILE
179
179
  self.editing_auth { |env| false }
180
180
  self.endpoint = 'api.scrivito.com'
181
+ self.check_batch_size = 100
181
182
  end
182
183
 
183
- attr_accessor :choose_homepage_callback, :activate_users_and_permissions
184
+ attr_accessor :choose_homepage_callback, :check_batch_size
184
185
 
185
186
  # Configure a callback to be invoked when the Scrivito SDK delivers the homepage.
186
187
  # The given callback will receive the rack env
@@ -205,7 +206,7 @@ module Scrivito
205
206
 
206
207
  def obj_formats
207
208
  @obj_formats ||= {
208
- '_default' => proc do |obj|
209
+ '_default' => proc do |obj, user|
209
210
  {
210
211
  id: obj.id,
211
212
  obj_class_name: obj.obj_class_name,
@@ -214,6 +215,7 @@ module Scrivito
214
215
  has_conflict: obj.has_conflict?,
215
216
  last_changed: obj.last_changed.utc.iso8601,
216
217
  is_binary: obj.binary?,
218
+ restriction_messages: user.restriction_messages_for(obj)
217
219
  }
218
220
  end
219
221
  }
@@ -48,6 +48,14 @@ class ContentState < Struct.new(:content_state_id, :changes, :changes_index, :fr
48
48
  CmsCacheStorage.read_obj_data(content_state_id, index, key)
49
49
  end
50
50
 
51
+ def save_obj_classes_data(data)
52
+ CmsCacheStorage.write_obj_classes_data(content_state_id, data)
53
+ end
54
+
55
+ def find_obj_classes_data
56
+ CmsCacheStorage.read_obj_classes_data(content_state_id)
57
+ end
58
+
51
59
  # Fetches and caches the ancestor.
52
60
  # Returns nil if there is no ancestor.
53
61
  def from_content_state
@@ -38,6 +38,26 @@ module ContentStateCaching
38
38
  nil
39
39
  end
40
40
  end
41
+
42
+ def store_obj_classes_data(content_state, data)
43
+ content_state.save_obj_classes_data(data)
44
+ end
45
+
46
+ def find_obj_classes_data(current_content_state)
47
+ visitor = ContentStateVisitor.new(current_content_state)
48
+
49
+ cache_lookup_depth.times do |depth|
50
+ return unless content_state = visitor.visit_next
51
+ if obj_classes_data = content_state.find_obj_classes_data
52
+ if depth >= cache_replication_depth
53
+ current_content_state.save_obj_classes_data(obj_classes_data)
54
+ end
55
+ return obj_classes_data
56
+ end
57
+ end
58
+
59
+ nil
60
+ end
41
61
  end
42
62
 
43
63
  self.cache_replication_depth = 5
@@ -18,15 +18,11 @@ class EditingContext
18
18
  @editor = @editor_callback.call
19
19
 
20
20
  if @editor && !@editor.is_a?(Scrivito::User)
21
- if Scrivito::Configuration.activate_users_and_permissions
22
- raise ScrivitoError.new(
23
- "The editing auth callback has to return a Scrivito::User or falsy."+
24
- " To upgrade please return Scrivito::User.new(id) and set the permissions" +
25
- " of the user. See the documentation for further details."
21
+ raise ScrivitoError.new(
22
+ "The editing auth callback has to return a Scrivito::User or falsy."+
23
+ " To upgrade please return Scrivito::User.new(id) and set the permissions" +
24
+ " of the user. See the documentation for further details."
26
25
  )
27
- else
28
- @editor = User.anonymous_admin
29
- end
30
26
  end
31
27
  end
32
28
 
@@ -50,37 +46,13 @@ class EditingContext
50
46
  # defaults to "published" if no workspace with `selected_workspace_id` can be found.
51
47
  # @return [Workspace]
52
48
  def selected_workspace
53
- unless @selected_workspace
54
- if @selected_workspace_id
55
- begin
56
- @selected_workspace = Workspace.find(@selected_workspace_id)
57
- rescue Scrivito::ResourceNotFound
58
- @selected_workspace = Workspace.default
59
- end
60
- else
61
- @selected_workspace = Workspace.default
62
- end
63
- end
64
-
65
- @selected_workspace
49
+ @selected_workspace ||= find_selected_workspace
66
50
  end
67
51
 
68
52
  # when authenticated_editor? return selected_workspace, otherwise published workspace.
69
53
  # @return [Workspace]
70
54
  def visible_workspace
71
- unless @visible_workspace
72
- if authenticated_editor?
73
- if display_mode == 'deleted'
74
- @visible_workspace = Workspace.default
75
- else
76
- @visible_workspace = selected_workspace
77
- end
78
- else
79
- @visible_workspace = Workspace.default
80
- end
81
- end
82
-
83
- @visible_workspace
55
+ @visible_workspace ||= find_visible_workspace
84
56
  end
85
57
 
86
58
  def comparison
@@ -102,6 +74,35 @@ class EditingContext
102
74
 
103
75
  private
104
76
 
77
+ def find_selected_workspace
78
+ if @selected_workspace_id
79
+ begin
80
+ workspace = Workspace.find(@selected_workspace_id)
81
+ if editor && editor.can?(:read, workspace)
82
+ workspace
83
+ else
84
+ default_workspace
85
+ end
86
+ rescue Scrivito::ResourceNotFound
87
+ default_workspace
88
+ end
89
+ else
90
+ default_workspace
91
+ end
92
+ end
93
+
94
+ def find_visible_workspace
95
+ if authenticated_editor?
96
+ display_mode == 'deleted' ? default_workspace : selected_workspace
97
+ else
98
+ default_workspace
99
+ end
100
+ end
101
+
102
+ def default_workspace
103
+ Workspace.default
104
+ end
105
+
105
106
  # @return [Revision] or +nil+
106
107
  def compare_revision
107
108
  case display_mode
@@ -1,16 +1,16 @@
1
1
  module Scrivito
2
2
 
3
- # @api beta
3
+ # @api public
4
4
  # Represents a Membership of a {User} in a {Workspace}
5
5
  class Membership
6
6
 
7
- # @api beta
7
+ # @api public
8
8
  # The {User User's} id
9
9
  #
10
10
  # @return [String]
11
11
  attr_reader :user_id
12
12
 
13
- # @api beta
13
+ # @api public
14
14
  # The role associated with this membership.
15
15
  #
16
16
  # @note Currently the only available role is "owner".
@@ -22,5 +22,24 @@ module Scrivito
22
22
  @user_id = user_id
23
23
  @role = data.fetch("role")
24
24
  end
25
+
26
+ # Fetches and returns the {User} with the id {Membership#user_id}.
27
+ # Uses the proc set in {Configuration.find_user} to fetch the user.
28
+ # @api public
29
+ # @return The value returned by the proc set in {Configuration.find_user}.
30
+ # @return An unknown user if no proc is set in {Configuration.find_user} or the proc returns a
31
+ # falsy value. The unknown user will have the id of the original user and no abilities.
32
+ # @see Scrivito::Configuration.find_user
33
+ def user
34
+ User.find(user_id) || User.unknown_user(user_id)
35
+ end
36
+
37
+ def as_json(options = nil)
38
+ {
39
+ user_id: user_id,
40
+ role: role,
41
+ description: user.description,
42
+ }
43
+ end
25
44
  end
26
45
  end
@@ -1,5 +1,5 @@
1
1
  module Scrivito
2
- # @api beta
2
+ # @api public
3
3
  # The MembershipsCollection includes all members of a given {Workspace}.
4
4
  # You can access it using {Workspace#memberships} method.
5
5
  class MembershipsCollection
@@ -8,7 +8,7 @@ module Scrivito
8
8
 
9
9
  attr_reader :workspace
10
10
 
11
- # @api beta
11
+ # @api public
12
12
  # @!method each
13
13
  # Iterate over all {Membership Memberships} of a specfic {Workspace}. Allows
14
14
  # you to use all methods defined by ruby's Enumerable module.
@@ -39,7 +39,7 @@ module Scrivito
39
39
  @workspace = workspace
40
40
  end
41
41
 
42
- # @api beta
42
+ # @api public
43
43
  # return a hash where the keys are user_ids and the values are Membership-Instances
44
44
  # @return [Hash<String, Membership>]
45
45
  def to_h
@@ -49,7 +49,7 @@ module Scrivito
49
49
  end
50
50
  end
51
51
 
52
- # @api beta
52
+ # @api public
53
53
  # Returns the membership for a user or nil
54
54
  #
55
55
  # @param [User, String] id_or_user
@@ -64,6 +64,10 @@ module Scrivito
64
64
  to_h[id]
65
65
  end
66
66
 
67
+ def to_a
68
+ memberships.sort_by(&:user_id)
69
+ end
70
+
67
71
  private
68
72
 
69
73
  def memberships
@@ -17,11 +17,7 @@ module Scrivito
17
17
  #
18
18
  # @return [Array<Scrivito::ObjClass>]
19
19
  def all
20
- results = CmsRestApi.get("workspaces/#{Workspace.current.id}/obj_classes")['results']
21
-
22
- results.map do |properties|
23
- new(properties)
24
- end
20
+ Workspace.current.obj_classes.to_a
25
21
  end
26
22
 
27
23
  # Finds an obj class by its name.
@@ -35,13 +31,13 @@ module Scrivito
35
31
  # @return [Scrivito::ObjClass]
36
32
  # @raise [Scrivito::ResourceNotFound] Raised when no obj class with the given +name+ can be found.
37
33
  def find(name)
38
- response = begin
39
- CmsRestApi.get("workspaces/#{Workspace.current.id}/obj_classes/#{name}")
40
- rescue Scrivito::ClientError
41
- raise ResourceNotFound, "Could not find '#{self}' with name '#{name}'."
34
+ obj_class = Workspace.current.obj_classes[name]
35
+
36
+ unless obj_class
37
+ raise ResourceNotFound, "Could not find '#{ObjClass}' with name '#{name}'."
42
38
  end
43
39
 
44
- new(response)
40
+ obj_class
45
41
  end
46
42
 
47
43
  # Creates a new obj class and persists it in the CMS.
@@ -94,21 +90,10 @@ module Scrivito
94
90
  end
95
91
  end
96
92
 
97
- params = format_properties_for_cms(properties)
98
- obj_class = new(params)
99
-
100
- if properties[:attributes]
101
- properties[:attributes].each do |attribute|
102
- attribute.obj_class = obj_class
103
- end
104
- end
105
-
106
- payload = { 'obj_class' => params }
107
- response = CmsRestApi.post("workspaces/#{Workspace.current.id}/obj_classes", payload)
108
-
109
- obj_class.update_instance_properties(response)
110
-
111
- obj_class
93
+ workspace = Workspace.current
94
+ raw_obj_class_data = workspace.api_request(:post, '/obj_classes',
95
+ obj_class: format_properties_for_cms(properties))
96
+ new(ObjClassData.new(raw_obj_class_data), workspace)
112
97
  end
113
98
 
114
99
  private
@@ -124,9 +109,7 @@ module Scrivito
124
109
  end
125
110
 
126
111
  if params.has_key?(:attributes)
127
- params[:attributes] = params[:attributes].map do |attribute|
128
- attribute.to_cms_rest_api
129
- end
112
+ params[:attributes] = params[:attributes].map(&:to_cms_rest_api)
130
113
  end
131
114
 
132
115
  params
@@ -137,47 +120,34 @@ module Scrivito
137
120
  #
138
121
  # See {ObjClass.create} for a detailed overview of how to set properties.
139
122
  #
140
- # @param [Hash] properties
141
- # @return [Scrivito::ObjClass]
123
+ # @param [Hash] obj_class_data
124
+ # @param [Workspace,NilClass] workspace
142
125
  # @raise [Scrivito::ScrivitoError]
143
- def initialize(properties)
144
- update_instance_properties(properties)
145
- end
146
-
147
- # Returns a unique identifier of this obj class. Implements
148
- # the {Scrivito::ModelIdentity} interface.
149
- # @return [String]
150
- def id
151
- @name
126
+ # @return [Scrivito::ObjClass]
127
+ def initialize(obj_class_data, workspace)
128
+ update_obj_class_data(obj_class_data)
129
+ @workspace = workspace
152
130
  end
153
131
 
154
- # Returns the name of this obj class.
155
- # @api public
156
- # @return [String]
157
- def name
158
- @name
159
- end
132
+ # @!attribute [r] id
133
+ # @api public
134
+ # @return [String] unique identifier of this obj class
135
+ # @!attribute [r] name
136
+ # @api public
137
+ # @return [String] the name of this obj class
138
+ # @!attribute [r] is_active
139
+ # @api public
140
+ # @return [Boolean] whether instances can be created with this obj class
141
+ # @!attribute [r] is_binary
142
+ # @api public
143
+ # @return [Boolean] whether instances of this class are binary, e.g. images or PDFs
144
+ delegate :id, :name, :is_active, :is_binary, to: :obj_class_data
160
145
 
161
- # Returns if new {Scrivito::BasicObj} instances can be created with this obj class.
162
- # @api public
163
- # @return [Boolean]
164
- def is_active
165
- @is_active
166
- end
167
146
  alias_method :active?, :is_active
168
-
169
- # Returns if new {Scrivito::BasicObj} instances created with this obj class are
170
- # binary objects like images or PDF documents.
171
- # @api public
172
- # @return [Boolean]
173
- def is_binary
174
- @is_binary
175
- end
176
147
  alias_method :binary?, :is_binary
177
148
 
178
- # Returns the attributes for this obj class.
179
- #
180
149
  # @api public
150
+ # @return [Scrivito::AttributeCollection] the attributes of this obj class.
181
151
  #
182
152
  # @example Find an attribute named "locale" for the obj class "Homepage".
183
153
  # ObjClass.find('Homepage').attributes['locale']
@@ -193,11 +163,7 @@ module Scrivito
193
163
  # ObjClass.find('Homepage').attributes.each do |attribute|
194
164
  # puts "#{attribute.name}:#{attribute.type}"
195
165
  # end
196
- #
197
- # @return [Scrivito::AttributeCollection]
198
- def attributes
199
- @attributes
200
- end
166
+ attr_reader :attributes
201
167
 
202
168
  # Updates this obj class and persists the changes in the CMS. It is not possible to
203
169
  # update the +name+ or +is_binary+ property.
@@ -227,56 +193,35 @@ module Scrivito
227
193
 
228
194
  if params.has_key?(:attributes)
229
195
  params[:attributes].map! do |attribute|
230
- unless attribute.respond_to?(:to_cms_rest_api)
231
- attribute = Attribute.new(attribute)
232
- end
233
-
196
+ attribute = Attribute.new(attribute) unless attribute.respond_to?(:to_cms_rest_api)
234
197
  attribute.obj_class = self
235
198
  attribute.to_cms_rest_api
236
199
  end
237
200
  end
238
201
 
239
- payload = { obj_class: params }
240
- response = CmsRestApi.put("workspaces/#{Workspace.current.id}/obj_classes/#{name}", payload)
241
-
242
- update_instance_properties(response)
202
+ raw_obj_class_data = workspace.api_request(:put, "/obj_classes/#{name}", obj_class: params)
203
+ update_obj_class_data(ObjClassData.new(raw_obj_class_data))
204
+ workspace.reload
243
205
 
244
206
  nil
245
207
  end
246
208
 
247
- # Updates the instance properties of this obj class with +properties+
248
- # given in the CMS REST API format.
249
- #
250
- # @param [Hash] properties
251
- # @return [void]
252
- def update_instance_properties(properties)
253
- properties = properties.with_indifferent_access
254
-
255
- if properties.has_key?(:type)
256
- properties[:is_binary] = %w(image generic).include?(properties[:type])
257
- end
258
-
259
- @name = properties[:name].to_s
260
- @is_active = properties[:is_active]
261
- @is_binary = properties[:is_binary]
262
- @attributes = create_attribute_collection(properties[:attributes] || [])
209
+ def update_obj_class_data(obj_class_data)
210
+ @obj_class_data = obj_class_data
211
+ update_attributes
263
212
  end
264
213
 
265
214
  private
266
215
 
267
- # Creates an attribute collection from an Array of attributes given in the CMS REST API format
268
- # and binds all attributes to this obj class.
269
- #
270
- # @param [Array] attributes
271
- # @return [Scrivito::AttributeCollection]
272
- def create_attribute_collection(attributes)
273
- attributes = attributes.map do |properties|
274
- attribute = Attribute.new(properties)
216
+ attr_reader :obj_class_data, :workspace
217
+
218
+ def update_attributes
219
+ attributes = @obj_class_data.attributes.map do |attribute_data|
220
+ attribute = Attribute.new(attribute_data)
275
221
  attribute.obj_class = self
276
222
  attribute
277
223
  end
278
-
279
- AttributeCollection.new(self, attributes)
224
+ @attributes = AttributeCollection.new(self, attributes)
280
225
  end
281
226
  end
282
227
  end