scrivito_sdk 1.0.0 → 1.1.0.rc1

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