scrivito_sdk 0.17.0 → 0.18.0

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