scrivito_sdk 1.0.0 → 1.1.0.rc1

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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -0
  3. data/README.md +1 -1
  4. data/app/controllers/scrivito/legacy_redirect_controller.rb +11 -0
  5. data/app/controllers/scrivito/objs_controller.rb +15 -5
  6. data/app/controllers/scrivito/webservice_controller.rb +4 -3
  7. data/app/controllers/scrivito/workspaces_controller.rb +24 -29
  8. data/app/helpers/scrivito_helper.rb +82 -37
  9. data/app/views/scrivito/objs/binary_no_cache.json.jbuilder +1 -0
  10. data/app/views/scrivito/objs/search.json.jbuilder +5 -5
  11. data/app/views/scrivito/webservice/error.json.jbuilder +2 -2
  12. data/config/ca-bundle.crt +1 -1
  13. data/config/precedence_routes.rb +51 -48
  14. data/config/routes.rb +1 -14
  15. data/lib/assets/javascripts/scrivito_ui.js +2447 -266
  16. data/lib/assets/stylesheets/scrivito.css +1 -1
  17. data/lib/assets/stylesheets/scrivito_ui.css +1 -1
  18. data/lib/generators/scrivito/install/install_generator.rb +12 -0
  19. data/lib/generators/scrivito/install/templates/config/initializers/scrivito.rb +3 -0
  20. data/lib/scrivito/attribute_definition.rb +13 -1
  21. data/lib/scrivito/attribute_deserializer.rb +4 -4
  22. data/lib/scrivito/attribute_value_renderer.rb +79 -0
  23. data/lib/scrivito/backend/obj_data_cache.rb +15 -15
  24. data/lib/scrivito/backend/obj_data_from_rest.rb +1 -21
  25. data/lib/scrivito/backend/obj_query.rb +2 -2
  26. data/lib/scrivito/basic_obj.rb +17 -6
  27. data/lib/scrivito/binary.rb +13 -5
  28. data/lib/scrivito/cache/chainable.rb +10 -5
  29. data/lib/scrivito/cache/file_store.rb +4 -0
  30. data/lib/scrivito/cache/ram_store.rb +4 -0
  31. data/lib/scrivito/child_list_tag.rb +16 -14
  32. data/lib/scrivito/client_error.rb +10 -0
  33. data/lib/scrivito/cms_backend.rb +66 -291
  34. data/lib/scrivito/cms_data_cache.rb +7 -9
  35. data/lib/scrivito/cms_dispatch_controller.rb +1 -10
  36. data/lib/scrivito/cms_field_tag.rb +2 -1
  37. data/lib/scrivito/cms_rest_api.rb +2 -0
  38. data/lib/scrivito/cms_routing.rb +26 -22
  39. data/lib/scrivito/configuration.rb +38 -0
  40. data/lib/scrivito/connection_manager.rb +69 -0
  41. data/lib/scrivito/controller_actions.rb +2 -2
  42. data/lib/scrivito/controller_helper.rb +2 -2
  43. data/lib/scrivito/date_attribute.rb +4 -1
  44. data/lib/scrivito/deprecation.rb +5 -4
  45. data/lib/scrivito/editing_context.rb +2 -2
  46. data/lib/scrivito/errors.rb +17 -0
  47. data/lib/scrivito/image_tag.rb +2 -2
  48. data/lib/scrivito/link_parser.rb +10 -5
  49. data/lib/scrivito/meta_data_collection.rb +11 -0
  50. data/lib/scrivito/obj_collection.rb +1 -1
  51. data/lib/scrivito/obj_facet_value.rb +19 -7
  52. data/lib/scrivito/obj_params_parser.rb +43 -14
  53. data/lib/scrivito/obj_search_builder.rb +1 -2
  54. data/lib/scrivito/obj_search_enumerator/batch.rb +22 -0
  55. data/lib/scrivito/obj_search_enumerator/batch_iterator.rb +36 -0
  56. data/lib/scrivito/obj_search_enumerator/query_executor.rb +35 -0
  57. data/lib/scrivito/obj_search_enumerator.rb +218 -93
  58. data/lib/scrivito/obj_update_params_parser.rb +1 -0
  59. data/lib/scrivito/preset_routes.rb +25 -0
  60. data/lib/scrivito/revision.rb +13 -11
  61. data/lib/scrivito/route.rb +62 -0
  62. data/lib/scrivito/routing_extensions.rb +92 -0
  63. data/lib/scrivito/sdk_engine.rb +4 -0
  64. data/lib/scrivito/task.rb +77 -0
  65. data/lib/scrivito/type_computer.rb +3 -3
  66. data/lib/scrivito/ui_config.rb +4 -0
  67. data/lib/scrivito/user.rb +24 -18
  68. data/lib/scrivito/workspace/publish_checker.rb +2 -3
  69. data/lib/scrivito/workspace.rb +38 -6
  70. data/lib/scrivito/workspace_data.rb +0 -14
  71. metadata +14 -10
  72. data/lib/scrivito/content_service.rb +0 -121
  73. data/lib/scrivito/content_state.rb +0 -109
  74. data/lib/scrivito/content_state_caching.rb +0 -47
  75. data/lib/scrivito/content_state_visitor.rb +0 -19
  76. data/lib/scrivito/obj_data_from_service.rb +0 -63
  77. data/lib/scrivito/workspace_data_from_service.rb +0 -43
@@ -2,7 +2,7 @@
2
2
  module Scrivito
3
3
  # Provides an enumerator for iterating over the results of searches for CMS objects to retrieve
4
4
  # instances of these objects. This is achieved through the
5
- # {http://ruby-doc.org/core-2.1.3/Enumerable.html +Enumerable+ mixin}, which provides methods such
5
+ # {http://ruby-doc.org/core/Enumerable.html +Enumerable+ mixin}, which provides methods such
6
6
  # as +map+, +select+ or +take+.
7
7
  #
8
8
  # This enumerator is lazy. If, for example, you are looking for {Scrivito::BasicObj Obj}s whose
@@ -13,7 +13,7 @@ module Scrivito
13
13
  #
14
14
  # To start searching, use one of the {Scrivito::BasicObj Obj} methods that return an
15
15
  # {Scrivito::ObjSearchEnumerator}. The preferred way is to start with
16
- # {Scrivito::BasicObj.where Obj.where}.
16
+ # {Scrivito::BasicObj.where Obj.where} or {Scrivito::BasicObj.all Obj.all}.
17
17
  #
18
18
  # == Currently available fields and their values
19
19
  #
@@ -24,9 +24,18 @@ module Scrivito
24
24
  # [+:_name+] Name of an {Scrivito::BasicObj Obj}. This is a +string+ field.
25
25
  # [+:_obj_class+] Object class of an {Scrivito::BasicObj Obj}. This is a +string+ field.
26
26
  # [+:_permalink+] Permalink of an {Scrivito::BasicObj Obj}. This is a +string+ field.
27
- # [+:_last_changed+] Date of last change of an {Scrivito::BasicObj Obj}.
27
+ # [+:_last_changed+] Date of last change to an {Scrivito::BasicObj Obj}.
28
28
  # [every +_:custom_attribute_+] Custom attribute of an {Scrivito::BasicObj Obj}. Note that depending on the attribute type (e.g. an +html+ field), some operators cannot be applied.
29
29
  #
30
+ # === Meta Data
31
+ #
32
+ # If an {Scrivito::BasicObj Obj} has a +binary+ attribute named +blob+, the meta data of this
33
+ # attribute is searchable. For a full list of the available meta data attributes, see the
34
+ # documentation of the {Scrivito::MetaDataCollection MetaDataCollection}. The meta data
35
+ # attribute name needs to be prefixed with +blob:+ when searching for it. So, for example,
36
+ # when searching for the width, you need to specify the attribute name using +blob:width+.
37
+ # Binary attributes other than +blob+ are not searchable.
38
+ #
30
39
  # == Currently available operators
31
40
  #
32
41
  # === +contains+ and +contains_prefix+
@@ -54,24 +63,15 @@ module Scrivito
54
63
  #
55
64
  # ✔ "Every" (case insensitive)
56
65
  #
57
- # === +equals+ and +starts_with+
66
+ # === +equals+
58
67
  #
59
- # These operators are intended for programmatic comparions of string and date values.
68
+ # The +equals+ operator is intended for programmatic comparisons of string and date values.
60
69
  #
61
- # The +equals+ and +prefix+ operators have some limits with regard to string length.
70
+ # The operator has some limits with regard to string length.
62
71
  # String values are only guaranteed to be considered if they are at most 1000 characters in length.
63
72
  # String values of more than 1000 characters may be ignored by these operators.
64
73
  #
65
- # The +prefix+ operator also has a precision limit:
66
- # Only prefixes of up to 20 characters are guaranteed to be matched.
67
- # If you supply a prefix of more than 20 characters, the additional characters may be ignored.
68
- #
69
- # When combined with the system attribute +_path+, the operator +prefix+ has some special functionality:
70
- # There is not precision limit, i.e. a prefix of arbitrary length may be used to match on +_path+.
71
- # Also, prefix matching on +_path+ automatically matches entire path components,
72
- # i.e. the prefix matching is delimited by slashes (the character +'/'+).
73
- #
74
- # For +equals+ and +starts_with+, the examples are based on the following field value:
74
+ # For +equals+, the examples are based on the following field value:
75
75
  # "Some content."
76
76
  #
77
77
  # [+:equals+] The +field+ value needs to be identical to the +value+ of this subquery.
@@ -84,6 +84,22 @@ module Scrivito
84
84
  #
85
85
  # ✘ "Some" (not exact value)
86
86
  #
87
+ # === +starts_with+
88
+ #
89
+ # The +starts_with+ is intended for programmatic comparions of string values.
90
+ #
91
+ # The +starts_with+ operator has a precision limit:
92
+ # Only prefixes of up to 20 characters are guaranteed to be matched.
93
+ # If you supply a prefix of more than 20 characters, the additional characters may be ignored.
94
+ #
95
+ # When combined with the system attribute +_path+, the operator +starts_with+ has some special functionality:
96
+ # There is not precision limit, i.e. a prefix of arbitrary length may be used to match on +_path+.
97
+ # Also, prefix matching on +_path+ automatically matches entire path components,
98
+ # i.e. the prefix matching is delimited by slashes (the character +'/'+).
99
+ #
100
+ # For +starts_with+, the examples are based on the following field value:
101
+ # "Some content."
102
+ #
87
103
  # [+:starts_with+] The +field+ value needs to start exactly with the +value+ of this subquery.
88
104
  #
89
105
  # Applicable to +string+, +stringlist+, +enum+ and +multienum+ fields.
@@ -98,7 +114,9 @@ module Scrivito
98
114
  #
99
115
  # === +is_less_than+ and +is_greater_than+
100
116
  #
101
- # These operators are intended for comparions on +date+ attributes and on numerical metadata, for example the width of an image.
117
+ # These operators are intended for comparing +date+ values or numerical metadata, for example the width of an image.
118
+ # It only considers attributes of {Scrivito::BasicObj Obj}s and _not_ of {Scrivito::BasicWidget Widget}s.
119
+ # Therefore, {Scrivito::BasicWidget Widget} attributes are not searchable using the +is_less_than+ and +is_greater_than+ operators.
102
120
  #
103
121
  # For +is_less_than+ and +is_greater_than+, the examples are based on the following date value:
104
122
  # +Time.new(2000,01,01,00,00,00)+
@@ -133,9 +151,10 @@ module Scrivito
133
151
  attr_reader :workspace
134
152
 
135
153
  attr_reader :query
136
- def initialize(workspace)
154
+ def initialize(workspace, batch_size = nil)
137
155
  @workspace = workspace
138
- @options = {}
156
+ @batch_size = batch_size
157
+ @options = { offset: 0 }
139
158
  end
140
159
 
141
160
  # @group Chainable methods
@@ -168,7 +187,7 @@ module Scrivito
168
187
  "Valid operators are: #{valid_boost_operators.join(', ')}"
169
188
  end
170
189
  end
171
- @size = nil
190
+ reset_for_changed_query
172
191
  @query = (query || []) + [subquery]
173
192
 
174
193
  self
@@ -197,7 +216,7 @@ module Scrivito
197
216
  end
198
217
  subquery = {:field => field, :operator => real_operator, :value => convert_value(value),
199
218
  :negate => true}
200
- @size = nil
219
+ reset_for_changed_query
201
220
  @query = (query || []) + [subquery]
202
221
 
203
222
  self
@@ -210,39 +229,66 @@ module Scrivito
210
229
  # There is a precision limit when sorting string values:
211
230
  # Only the first 50 characters of a string are guaranteed to be considered when sorting search results.
212
231
  #
213
- # @param [Symbol, String] field_name This parameter specifies the field by which the hits are
214
- # sorted (e.g. +:_path+).
232
+ # @overload order(field_name)
233
+ # @param [Symbol, String] field_name This parameter specifies the field by
234
+ # which the hits are sorted (e.g. +:_path+).
235
+ #
236
+ # @overload order(field_and_direction)
237
+ # @param [Hash] field_and_direction The field name and sort direction can be
238
+ # specfied as the key and value of a hash. Valid directions are
239
+ # +:asc+ and +:desc+. The default is +:asc+.
240
+ #
241
+ # @example Sorting descending
242
+ # Obj.all.order(_last_changed: :desc)
243
+ #
215
244
  # @return [Scrivito::ObjSearchEnumerator]
216
245
  # @api public
217
246
  def order(field_name)
247
+ field_name, direction = if field_name.is_a?(Hash)
248
+ field_name.to_a.first
249
+ else
250
+ [field_name, :asc]
251
+ end
252
+
218
253
  options[:sort_by] = field_name
254
+ options[:sort_order] = direction.to_sym
219
255
 
220
256
  self
221
257
  end
222
258
 
223
259
  # Reverses the order of the results. Requires {#order} to be applied before.
224
260
  # @return [Scrivito::ObjSearchEnumerator]
261
+ # @deprecated This method is deprecated and will be removed in the next major
262
+ # version. Please specify the direction using {#order}.
225
263
  # @api public
226
264
  def reverse_order
265
+ Scrivito::Deprecation.warn_method("reverse_order", "order")
227
266
  options[:sort_by].present? or raise "A search order has to be specified"\
228
267
  " before reverse_order can be applied."
229
- @reverse_order = !@reverse_order
268
+ options[:sort_order] = options[:sort_order] == :asc ? :desc : :asc
230
269
 
231
270
  self
232
271
  end
233
272
 
273
+ #
234
274
  # Number of search results to be returned by each of the internal search requests.
275
+ #
276
+ # @api public
277
+ #
235
278
  # The default is +10+.
236
279
  #
237
- # Scrivito makes a best effort to return the given number of search results,
238
- # but may under certain circumstances return larger or smaller batches due to technical
239
- # reasons.
280
+ # Scrivito makes a best effort to return the given number of search results, but may under
281
+ # certain circumstances return larger or smaller batches due to technical reasons.
282
+ #
283
+ # @param [Integer] size number of search results to be returned by each of the internal search
284
+ # requests. Scrivito tries to honor the requested +size+ as much as possible, but there is no
285
+ # guarantee. At the time of writing, +size+ is capped at +100+, for example.
240
286
  #
241
- # @param [Integer] size A value in the range from +1+ to +100+.
242
287
  # @return [Scrivito::ObjSearchEnumerator]
243
- # @api public
288
+ #
244
289
  def batch_size(size)
245
- options[:size] = size
290
+ @batch_size = size
291
+ @preload_search_result = true
246
292
 
247
293
  self
248
294
  end
@@ -253,7 +299,6 @@ module Scrivito
253
299
  # @return [Scrivito::ObjSearchEnumerator]
254
300
  # @api public
255
301
  def offset(amount)
256
- options[:offset] ||= 0
257
302
  options[:offset] += amount
258
303
 
259
304
  self
@@ -272,21 +317,15 @@ module Scrivito
272
317
  # @return [void]
273
318
  # @api public
274
319
  def each
275
- offset = options[:offset] || 0
276
- current_batch, total = fetch_next_batch(offset)
277
- loop do
278
- if current_batch.size == 0
279
- if offset < total
280
- current_batch, total = fetch_next_batch(offset)
281
- else
282
- raise StopIteration
283
- end
284
- end
320
+ iterator = BatchIterator.new(workspace, search_dsl_params, @preloaded_batch)
285
321
 
286
- offset += 1
287
- hit = current_batch.shift
288
- yield hit
322
+ iterator.each do |batch|
323
+ batch.objs.each do |obj|
324
+ yield obj
325
+ end
289
326
  end
327
+
328
+ @size = iterator.total
290
329
  end
291
330
 
292
331
  # The total number of hits.
@@ -321,9 +360,7 @@ module Scrivito
321
360
  # methods from +Enumerable+, for example +take+.
322
361
  # @api public
323
362
  def load_batch
324
- next_batch = fetch_next_batch(options[:offset] || 0)
325
-
326
- next_batch.first
363
+ fetch_batch.objs
327
364
  end
328
365
 
329
366
  # @api public
@@ -332,25 +369,38 @@ module Scrivito
332
369
  # @api public
333
370
  alias_method :count, :size
334
371
 
335
- # @api public beta
336
- # Perform a faceted search over an attribute to retrieve structured results for individual values of this attribute.
372
+ # @api public
373
+ # Perform a faceted search over up to ten attributes to retrieve structured results for individual values of these attributes.
337
374
  #
338
- # Applicable to attribute types +string+, +stringlist+, +enum+, +multienum+.
375
+ # Applicable to attributes of the following types: +string+, +stringlist+, +enum+, +multienum+.
339
376
  #
340
377
  # Please note that there is a precision limit for faceting:
341
378
  # Only the first 50 characters of a string are guaranteed to be considered for faceting.
342
379
  # If two string values have the same first 50 characters, they may be grouped into the same facet value.
343
380
  #
344
- # @param [String] attribute_name the name of an attribute
345
- # @param [Hash] options the options to facet a request with.
346
- # @option options [Integer] :limit maximum number of unique values to return. Defaults to 20.
347
- # @option options [Integer] :include_objs number of Objs to fetch for each unique value. Defaults to 0.
381
+ # Please note that by default {Scrivito::ObjSearchEnumerator#facet} does not preload the first batch of the search results.
382
+ # In order to reduce the number of search requests, +batch_size+ can be explicitly set using the {Scrivito::ObjSearchEnumerator#batch_size} method.
383
+ # This causes Scrivito to preload the first batch of the search results.
348
384
  #
349
- # @return [Array<Scrivito::ObjFacetValue>]
350
- # A list of unique values that were found for the given attribute name. The list is
351
- # ordered by frequency, i.e. values occurring more frequently come first.
385
+ # @overload facet(attribute, options={})
386
+ # Single-attribute faceting request.
387
+ # @param [String] attribute the name of an attribute.
388
+ # @param [Hash] options the options to facet a request with.
389
+ # @option options [Integer] :limit maximum number of unique values to return. Defaults to 20.
390
+ # @option options [Integer] :include_objs number of Objs to fetch for each unique value. Defaults to 0.
391
+ # @return [Array<Scrivito::ObjFacetValue>]
392
+ # in the ++facets++ parameter.
393
+ # The values of the hash are lists of {Scrivito::ObjFacetValue}.
394
+ # @overload facet(facets)
395
+ # Multi-attribute faceting request. The maximum number of attributes that may be specified is 10.
396
+ # @param [Hash] facets a hash where the keys are attribute names and the values are options.
397
+ # The available options are identical to the options for single faceting requests.
398
+ # @return [Hash] a hash where the keys are identical to the keys given.
399
+ # A list of unique values that were found for the given attribute name. The list is
400
+ # ordered by frequency, i.e. values occurring more frequently come first.
401
+ # @raise [Scrivito::ClientError] If the number of attributes exceeds 10.
352
402
  #
353
- # @example Faceted request: colors of _big_ balloons.
403
+ # @example Faceted request: colors of _big_ balloons:
354
404
  # facets = Balloon.where(:size, :equals, "big").facet("color")
355
405
  #
356
406
  # # Big balloons come in 3 colors:
@@ -371,14 +421,14 @@ module Scrivito
371
421
  # blue_balloons.name #=> "blue"
372
422
  # blue_balloons.count #=> 1
373
423
  #
374
- # @example Faceted request with limit: at most 2 colors of big balloons.
424
+ # @example Faceted request with limit: at most 2 colors of big balloons:
375
425
  # facets = Balloon.where(:size, :equals, "big").facet("color", limit: 2)
376
426
  #
377
427
  # # Although there are 3 different colors of big balloons,
378
428
  # # only the first 2 colors will be taken into account.
379
429
  # facets.count # => 2
380
430
  #
381
- # @example Faceted request with included Objs.
431
+ # @example Faceted request with included Objs:
382
432
  # facets = Balloon.where(:size, :equals, "big").facet("color", include_objs: 2)
383
433
  #
384
434
  # facets.each do |facet|
@@ -387,7 +437,7 @@ module Scrivito
387
437
  # end
388
438
  # end
389
439
  #
390
- # # If there are 3 big red balloons, 2 big green balloons and 1 big blue balloon,
440
+ # # If there are 2 big red balloons, 2 big green balloons and 1 big blue balloon,
391
441
  # # then this will produce:
392
442
  #
393
443
  # "big red Balloon"
@@ -396,19 +446,90 @@ module Scrivito
396
446
  # "big green Balloon"
397
447
  # "big blue Balloon"
398
448
  #
449
+ # @example Multiple faceting request:
450
+ # facets = Balloon.where(:size, :equals, "big").facet(
451
+ # color: {limit: 3, include_objs: 5},
452
+ # motif: {limit: 3, include_objs: 5}
453
+ # )
454
+ #
455
+ # color_facet_obj_values = facets[:color]
456
+ # motif_facet_obj_values = facets[:motif]
457
+ #
458
+ # color_facet_obj_values.each do |facet|
459
+ # facet.included_objs.each do |obj|
460
+ # puts "#{obj.size} #{obj.color} #{obj.class}"
461
+ # end
462
+ # end
463
+ #
464
+ # motif_facet_obj_values.each do |facet|
465
+ # facet.included_objs.each do |obj|
466
+ # puts "#{obj.size} #{obj.motif} #{obj.class}"
467
+ # end
468
+ # end
469
+ #
470
+ # # If there are 2 big red balloons, 2 big green balloons and 1 big blue balloon,
471
+ # # this will produce:
472
+ #
473
+ # "big red Balloon"
474
+ # "big red Balloon"
475
+ # "big green Balloon"
476
+ # "big green Balloon"
477
+ # "big blue Balloon"
478
+ #
479
+ # # If there are 1 big birthday balloon and 1 big wedding balloon,
480
+ # # this will produce:
481
+ #
482
+ # "big birthday Balloon"
483
+ # "big wedding Balloon"
484
+ #
485
+ # @example Faceted +where+ query with +batch_size+:
486
+ # big_balloons = Balloon.where(:size, :equals, "big")
487
+ #
488
+ # # Without preloading
489
+ # balloon_colors = big_balloons.facet("color")
490
+ # first_ten_balloons = big_balloons.take(10) # This will cause a search request.
491
+ #
492
+ # # With preloading
493
+ # big_balloons.batch_size(10) # Make Scrivito preload the first ten balloons.
494
+ # balloon_colors = big_balloons.facet("color")
495
+ # first_ten_balloons = big_balloons.take(10) # This will cause _no_ search request.
496
+ #
399
497
  # @raise [Scrivito::ClientError] If the maximum number of results has been exceeded.
400
- # The maximum number of results is limited to 100 with respect to the facets themselves and the included objs.
498
+ # The number of results is limited to 100 with respect to the facets themselves and the included Objs.
401
499
  #
402
- def facet(attribute_name, options = {})
403
- facets_params = [{ attribute: attribute_name }.merge!(options)]
404
- result = get_facet_value_objs(facets_params, [attribute_name])
405
- result[attribute_name]
500
+ def facet(*args)
501
+ if args.length == 1 && args[0].is_a?(Hash)
502
+ return {} if args[0].empty?
503
+ facets_params = multiple_facet_params(args)
504
+ get_facet_value_objs(facets_params, args[0].keys)
505
+ else
506
+ facets_params = [single_facet_params(*args)]
507
+ attribute_name = args[0]
508
+ result = get_facet_value_objs(facets_params, [attribute_name])
509
+ result[attribute_name]
510
+ end
511
+ end
512
+
513
+ def fetch_batch(continuation=nil)
514
+ batch =
515
+ if @preloaded_batch && !continuation
516
+ @preloaded_batch
517
+ else
518
+ QueryExecutor.new(workspace).call(search_dsl_params, continuation)
519
+ end
520
+
521
+ @size = batch.total
522
+ batch
406
523
  end
407
524
 
408
525
  private
409
526
 
410
527
  attr_reader :options
411
528
 
529
+ def reset_for_changed_query
530
+ @size, @preloaded_batch = nil
531
+ end
532
+
412
533
  def convert_value(value)
413
534
  if value.kind_of?(Array)
414
535
  value.map{ |v| convert_single_value(v) }
@@ -449,6 +570,18 @@ module Scrivito
449
570
  result
450
571
  end
451
572
 
573
+ def multiple_facet_params(facets)
574
+ facet_params = []
575
+ facets.first.map do |k, v|
576
+ facet_params << single_facet_params(k, v)
577
+ end
578
+ facet_params
579
+ end
580
+
581
+ def single_facet_params(attribute, options = {})
582
+ { attribute: attribute }.merge!(options)
583
+ end
584
+
452
585
  def get_objs_facet_ids(facet)
453
586
  result = []
454
587
  if included_ids = facet["results"]
@@ -460,27 +593,33 @@ module Scrivito
460
593
  def get_facet_value_objs(facets_params, attributes_list = [])
461
594
  result = attributes_list.each_with_object({}) { |v,h| h[v] = [] }
462
595
  included_objs_ids = []
463
- params = { facets: facets_params }
464
596
 
465
- if query
466
- offset = options[:offset] || 0
467
- params.merge! search_dsl(offset)
468
- end
597
+ params = prepare_facet_search_params facets_params
469
598
 
470
- params.reverse_merge!(size: 0)
599
+ batch = QueryExecutor.new(workspace).call(params)
600
+ @preloaded_batch = batch if @preload_search_result
471
601
 
472
- request_result = CmsBackend.instance.search_objs(workspace, params)
473
- request_result['facets'].each do |facets_array|
602
+ batch.facets.each do |facets_array|
474
603
  included_objs_ids += get_all_facets_ids(facets_array)
475
604
  end
476
605
 
477
- obj_collection = Scrivito::BasicObj.find(included_objs_ids)
478
- request_result['facets'].each_with_index do |facets_array, index|
606
+ obj_collection = workspace.objs.find(included_objs_ids)
607
+ batch.facets.each_with_index do |facets_array, index|
479
608
  result[result.keys[index]] += create_facet_value_objs(facets_array, obj_collection)
480
609
  end
481
610
  result
482
611
  end
483
612
 
613
+ def prepare_facet_search_params(facet_params)
614
+ params = { facets: facet_params }
615
+
616
+ if query
617
+ params.merge! search_dsl_params
618
+ end
619
+
620
+ params.reverse_merge(size: 0)
621
+ end
622
+
484
623
  def operator_mapping(operator)
485
624
  case operator.to_sym
486
625
  when :contains
@@ -496,29 +635,15 @@ module Scrivito
496
635
  when :is_less_than
497
636
  :less_than
498
637
  else
499
- raise "Operator '#{operator}'' is not valid!"
638
+ raise "Operator '#{operator}' is not valid!"
500
639
  end
501
640
  end
502
641
 
503
- def fetch_next_batch(offset)
504
- request_result = CmsBackend.instance.search_objs(workspace, search_dsl(offset))
505
-
506
- obj_ids = request_result['results'].map { |result| result['id'] || result['_id'] }
507
- objs = workspace.objs.find_including_deleted(obj_ids)
508
-
509
- @size = request_result['total'].to_i
510
-
511
- [objs, @size]
512
- end
513
-
514
- def search_dsl(offset)
642
+ def search_dsl_params
515
643
  patches = {
516
- offset: offset,
517
644
  query: query,
518
645
  }
519
- if @reverse_order
520
- patches[:sort_order] = options[:sort_by].present? ? :desc : :asc
521
- end
646
+ patches[:size] = @batch_size if @batch_size
522
647
 
523
648
  if @include_deleted
524
649
  patches[:options] = {
@@ -18,6 +18,7 @@ module Scrivito
18
18
  params['_widget_pool'].each_pair do |widget_id, widget_params|
19
19
  if widget_params.present?
20
20
  widget = obj.widgets[widget_id]
21
+ raise_widget_not_found_error(widget_id) unless widget
21
22
  widget_pool[widget] = convert_field_params(widget_params, widget.attribute_definitions)
22
23
  end
23
24
  end
@@ -0,0 +1,25 @@
1
+ module Scrivito
2
+ module PresetRoutes
3
+ def self.install_into(route_set)
4
+ return if Scrivito::Configuration.scrivito_route_enabled?
5
+
6
+ Scrivito::Configuration.with_scrivito_route_enabled do
7
+ route_set.draw do
8
+ scrivito_route '/', using: "homepage", via: :all
9
+
10
+ if Scrivito::Configuration.legacy_routing
11
+ scrivito_route ':id(/*slug)', using: "slug_id", via: :all
12
+ else
13
+ scrivito_route '(/)(*slug-):id', using: "slug_id", via: :all
14
+
15
+ match ':id(/*slug)', to: 'scrivito/legacy_redirect#index', via: :all, constraints: {
16
+ id: /[a-z0-9]{16}/
17
+ }
18
+ end
19
+
20
+ scrivito_route '/*permalink', using: "permalink", format: false, via: :all
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -5,10 +5,6 @@ class Revision < Struct.new(:id, :workspace, :base)
5
5
  super(*options.values_at(:id, :workspace, :base))
6
6
  end
7
7
 
8
- def content_state
9
- base ? workspace.base_content_state : workspace.content_state
10
- end
11
-
12
8
  def content_state_id
13
9
  base ? workspace.base_content_state_id : workspace.content_state_id
14
10
  end
@@ -41,18 +37,24 @@ class Revision < Struct.new(:id, :workspace, :base)
41
37
 
42
38
  private
43
39
 
44
- def internal_obj_search(query, result)
45
- response = workspace.api_request(:get, "/objs/search", {
46
- query: query, include_deleted: true, size: 100, offset: result.size
47
- })
40
+ def internal_obj_search(query, result, continuation=nil, tentative=false)
41
+ response = workspace.api_request(:get, "/objs/search",
42
+ query: query,
43
+ include_deleted: true,
44
+ size: 100,
45
+ continuation: continuation,
46
+ consistent_with: content_state_id
47
+ )
48
48
 
49
49
  cur_result = response['results'].map { |obj_data| obj_data['id'] }
50
50
  result += cur_result
51
+ continuation = response['continuation']
52
+ tentative ||= !!response['tentative']
51
53
 
52
- if result.size >= response['total'].to_i
53
- result
54
+ if continuation
55
+ internal_obj_search(query, result, continuation, tentative)
54
56
  else
55
- internal_obj_search(query, result)
57
+ [result.uniq, tentative]
56
58
  end
57
59
  end
58
60
 
@@ -0,0 +1,62 @@
1
+ module Scrivito
2
+
3
+ class Route
4
+
5
+ class << self
6
+ VALID_ROUTE_NAMES = %i(homepage slug_id permalink).freeze
7
+
8
+ def register(route_set, name)
9
+ assert_valid_route_name(name)
10
+ registry(route_set)[name] = new(name)
11
+ end
12
+
13
+ def defined?(route_set, name)
14
+ !!registry(route_set)[name]
15
+ end
16
+
17
+ def find(route_set, name)
18
+ registry(route_set)[name] or raise InternalError, "route #{name} not found"
19
+ end
20
+
21
+ private
22
+
23
+ def registry(route_set)
24
+ @registry ||= {}
25
+ @registry[route_set] ||= {}
26
+ end
27
+
28
+ def assert_valid_route_name(name)
29
+ unless VALID_ROUTE_NAMES.include?(name)
30
+ valid_values = VALID_ROUTE_NAMES.join(', ')
31
+ raise ScrivitoError,
32
+ %("#{name}" is not a valid scrivito route name. Valid values are: #{valid_values}.)
33
+ end
34
+ end
35
+ end
36
+
37
+ def initialize(name)
38
+ @name = name
39
+ end
40
+
41
+ def helper_name
42
+ # use a random name to prevent users from getting the idea
43
+ # that the internally unsed routing helpers might be a stable API
44
+ random_segment = Digest::SHA1.hexdigest(@name.to_s)[0..15]
45
+ @helper_name ||= "scrivito_#{random_segment}"
46
+ end
47
+
48
+ def generate_path(context, options = {})
49
+ generate(context, :path, options)
50
+ end
51
+
52
+ def generate_url(context, options = {})
53
+ generate(context, :url, options)
54
+ end
55
+
56
+ def generate(context, path_or_url, options = {})
57
+ context.public_send("#{helper_name}_#{path_or_url}", options)
58
+ end
59
+
60
+ end
61
+
62
+ end