scrivito_sdk 1.2.0 → 1.3.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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/scrivito/blobs_controller.rb +5 -3
  3. data/app/controllers/scrivito/obj_class_controller.rb +58 -0
  4. data/app/controllers/scrivito/objs_controller.rb +1 -6
  5. data/app/controllers/scrivito/proxy/objs_controller.rb +34 -0
  6. data/app/controllers/scrivito/resolve_paths_controller.rb +21 -0
  7. data/app/controllers/scrivito/sessions_controller.rb +7 -0
  8. data/app/controllers/scrivito/webservice_controller.rb +11 -0
  9. data/app/controllers/scrivito/workspaces_controller.rb +7 -9
  10. data/app/helpers/scrivito_helper.rb +1 -1
  11. data/app/views/scrivito/obj_class/defaults.json.jbuilder +1 -0
  12. data/app/views/scrivito/proxy/index.json.jbuilder +1 -0
  13. data/app/views/scrivito/resolve_paths/resolve.json.jbuilder +1 -0
  14. data/app/views/scrivito/sessions/update.json.jbuilder +1 -0
  15. data/app/views/scrivito/ui/index.html.erb +1 -0
  16. data/app/views/scrivito/webservice/_workspace.json.jbuilder +0 -2
  17. data/config/ca-bundle.crt +55 -669
  18. data/config/precedence_routes.rb +7 -1
  19. data/lib/assets/images/scrivito/image_placeholder.gif +0 -0
  20. data/lib/assets/javascripts/scrivito_ui.js +6259 -3918
  21. data/lib/assets/stylesheets/scrivito.css +1 -1
  22. data/lib/assets/stylesheets/scrivito_ui.css +1 -1
  23. data/lib/scrivito/attribute_content.rb +43 -8
  24. data/lib/scrivito/attribute_deserializer.rb +11 -1
  25. data/lib/scrivito/attribute_serializer.rb +44 -8
  26. data/lib/scrivito/basic_obj.rb +3 -3
  27. data/lib/scrivito/binary.rb +17 -3
  28. data/lib/scrivito/binary_param_verifier.rb +2 -2
  29. data/lib/scrivito/client_attribute_serializer.rb +11 -1
  30. data/lib/scrivito/client_error.rb +1 -1
  31. data/lib/scrivito/cms_field_tag.rb +2 -0
  32. data/lib/scrivito/cms_rest_api.rb +6 -13
  33. data/lib/scrivito/{date_attribute.rb → date_conversion.rb} +2 -4
  34. data/lib/scrivito/float_conversion.rb +29 -0
  35. data/lib/scrivito/image_tag.rb +1 -1
  36. data/lib/scrivito/integer_conversion.rb +29 -0
  37. data/lib/scrivito/obj_params_parser.rb +1 -1
  38. data/lib/scrivito/obj_search_builder.rb +1 -1
  39. data/lib/scrivito/obj_search_enumerator.rb +19 -99
  40. data/lib/scrivito/obj_search_enumerator/facet_query.rb +105 -0
  41. data/lib/scrivito/page_config.rb +1 -0
  42. data/lib/scrivito/session.rb +23 -0
  43. data/lib/scrivito/task.rb +1 -1
  44. data/lib/scrivito/ui_config.rb +6 -0
  45. data/lib/scrivito/user.rb +4 -3
  46. data/lib/scrivito/user_definition.rb +7 -7
  47. data/lib/scrivito/workspace.rb +14 -3
  48. data/lib/scrivito/workspace_data.rb +5 -0
  49. metadata +18 -6
  50. data/lib/assets/images/scrivito/image_placeholder.png +0 -0
@@ -1,7 +1,7 @@
1
1
  module Scrivito
2
2
 
3
3
  # Adds support for string columns which contain ISO dates
4
- module DateAttribute
4
+ module DateConversion
5
5
  def self.deserialize_from_backend(iso_date_time)
6
6
  return nil unless iso_date_time
7
7
 
@@ -18,9 +18,7 @@ module Scrivito
18
18
  DateTime.iso8601(iso8601_date_time).in_time_zone
19
19
  rescue ArgumentError
20
20
  raise Scrivito::ClientError.new(
21
- "The value is not a valid ISO 8601 date time: #{iso8601_date_time.inspect}",
22
- 400
23
- )
21
+ "The value is not a valid ISO 8601 date time: #{iso8601_date_time.inspect}")
24
22
  end
25
23
 
26
24
  def self.serialize_for_backend(attribute_value)
@@ -0,0 +1,29 @@
1
+ module Scrivito
2
+ module FloatConversion
3
+ FLOAT_STRING_REGEX = /\A-?\d+(\.\d+)?\z/.freeze
4
+ DEFAULT_VALUE = 0.0
5
+
6
+ def self.deserialize_from_backend(backend_value)
7
+ case backend_value
8
+ when Integer, Float, FLOAT_STRING_REGEX
9
+ value = backend_value.to_f
10
+ value.finite? ? value : DEFAULT_VALUE
11
+ when nil, String
12
+ DEFAULT_VALUE
13
+ else
14
+ raise Scrivito::InternalError,
15
+ "invalid value #{backend_value} (class: #{backend_value.class})"
16
+ end
17
+ end
18
+
19
+ def self.serialize_for_backend(value, attr_name)
20
+ serialized_value = value.to_f
21
+ unless serialized_value.finite?
22
+ raise ClientError.new(
23
+ "Value #{value} for attribute #{attr_name} is not supported.")
24
+ end
25
+
26
+ value
27
+ end
28
+ end
29
+ end
@@ -13,7 +13,7 @@ class ImageTag < Struct.new(:view_context)
13
13
  def src(obj_or_widget, attribute_name, editing_options)
14
14
  path(obj_or_widget, attribute_name, editing_options) ||
15
15
  editing_options[:placeholder] ||
16
- view_context.image_path('scrivito/image_placeholder.png')
16
+ view_context.image_path('scrivito/image_placeholder.gif')
17
17
  end
18
18
 
19
19
  def alt(obj_or_widget, attribute_name)
@@ -0,0 +1,29 @@
1
+ module Scrivito
2
+ module IntegerConversion
3
+ INTEGER_STRING_REGEX = /\A-?\d+\z/.freeze
4
+ DEFAULT_VALUE = 0
5
+ RANGE = Range.new(-(2**53 - 1), (2**53 - 1)).freeze
6
+
7
+ def self.deserialize_from_backend(backend_value)
8
+ case backend_value
9
+ when Integer, Float, INTEGER_STRING_REGEX
10
+ value = backend_value.to_i
11
+ RANGE.include?(value) ? value : DEFAULT_VALUE
12
+ when nil, String
13
+ DEFAULT_VALUE
14
+ else
15
+ raise Scrivito::InternalError,
16
+ "invalid value #{backend_value} (class: #{backend_value.class})"
17
+ end
18
+ end
19
+
20
+ def self.serialize_for_backend(value, attr_name)
21
+ unless RANGE.include?(value)
22
+ raise ClientError.new(
23
+ "Value #{value} for attribute #{attr_name} is out of range: #{RANGE}")
24
+ end
25
+
26
+ value
27
+ end
28
+ end
29
+ end
@@ -19,7 +19,7 @@ module Scrivito
19
19
  when 'link' then ContentConversion.convert_link(value, host, port)
20
20
  when 'linklist' then ContentConversion.convert_linklist_urls(value, host, port)
21
21
  when 'widgetlist' then parse_widgetlist_params(value)
22
- when 'date' then DateAttribute.deserialize_from_client(value)
22
+ when 'date' then DateConversion.deserialize_from_client(value)
23
23
  else value
24
24
  end
25
25
  end
@@ -54,7 +54,7 @@ class ObjSearchBuilder < Struct.new(:query)
54
54
  def deserialize_values(values)
55
55
  values.map do |(type, value)|
56
56
  if type == 'date'
57
- DateAttribute.deserialize_from_client(value)
57
+ DateConversion.deserialize_from_client(value)
58
58
  else
59
59
  value
60
60
  end
@@ -149,8 +149,8 @@ module Scrivito
149
149
  include Enumerable
150
150
 
151
151
  attr_reader :workspace
152
-
153
152
  attr_reader :query
153
+
154
154
  def initialize(workspace, batch_size = nil)
155
155
  @workspace = workspace
156
156
  @batch_size = batch_size
@@ -288,7 +288,7 @@ module Scrivito
288
288
  #
289
289
  def batch_size(size)
290
290
  @batch_size = size
291
- @preload_search_result = true
291
+ @preload_batch = true
292
292
 
293
293
  self
294
294
  end
@@ -497,17 +497,16 @@ module Scrivito
497
497
  # @raise [Scrivito::ClientError] If the maximum number of results has been exceeded.
498
498
  # The number of results is limited to 100 with respect to the facets themselves and the included Objs.
499
499
  #
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
500
+ def facet(*facet_params)
501
+ search_params = search_dsl_params
502
+ search_params[:size] = 0 unless @preload_batch
503
+
504
+ facet_query = FacetQuery.new(facet_params, search_params, workspace)
505
+ facet_query.execute!
506
+
507
+ @preloaded_batch = facet_query.batch if @preload_batch
508
+
509
+ facet_query.result
511
510
  end
512
511
 
513
512
  def fetch_batch(continuation=nil)
@@ -539,87 +538,16 @@ module Scrivito
539
538
  end
540
539
 
541
540
  def convert_single_value(value)
542
- if value.is_a?(Time) || value.is_a?(Date)
543
- DateAttribute.serialize_for_backend(value)
541
+ case value
542
+ when Time, Date
543
+ DateConversion.serialize_for_backend(value)
544
+ when Integer, Float
545
+ value
544
546
  else
545
547
  value.to_s
546
548
  end
547
549
  end
548
550
 
549
- def create_facet_value_objs(facet_arrays, obj_collection)
550
- result = []
551
- facet_arrays.map do |facet|
552
- included_objs = []
553
- if included_ids = get_objs_facet_ids(facet)
554
- obj_collection.each do |basic_obj|
555
- if included_ids.include? basic_obj.id
556
- included_objs << basic_obj unless included_objs.include? basic_obj
557
- end
558
- end
559
- end
560
- result << ObjFacetValue.new(facet["value"], facet["total"], included_objs)
561
- end
562
- result
563
- end
564
-
565
- def get_all_facets_ids(facet_array)
566
- result = []
567
- facet_array.map do |facet|
568
- result += get_objs_facet_ids(facet)
569
- end
570
- result
571
- end
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
-
585
- def get_objs_facet_ids(facet)
586
- result = []
587
- if included_ids = facet["results"]
588
- included_ids.each { |obj| result << obj["id"] }
589
- end
590
- result
591
- end
592
-
593
- def get_facet_value_objs(facets_params, attributes_list = [])
594
- result = attributes_list.each_with_object({}) { |v,h| h[v] = [] }
595
- included_objs_ids = []
596
-
597
- params = prepare_facet_search_params facets_params
598
-
599
- batch = QueryExecutor.new(workspace).call(params)
600
- @preloaded_batch = batch if @preload_search_result
601
-
602
- batch.facets.each do |facets_array|
603
- included_objs_ids += get_all_facets_ids(facets_array)
604
- end
605
-
606
- obj_collection = workspace.objs.find(included_objs_ids)
607
- batch.facets.each_with_index do |facets_array, index|
608
- result[result.keys[index]] += create_facet_value_objs(facets_array, obj_collection)
609
- end
610
- result
611
- end
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
-
623
551
  def operator_mapping(operator)
624
552
  case operator.to_sym
625
553
  when :contains
@@ -640,17 +568,9 @@ module Scrivito
640
568
  end
641
569
 
642
570
  def search_dsl_params
643
- patches = {
644
- query: query,
645
- }
571
+ patches = {query: query}
646
572
  patches[:size] = @batch_size if @batch_size
647
-
648
- if @include_deleted
649
- patches[:options] = {
650
- include_deleted: true
651
- }
652
- end
653
-
573
+ patches[:options] = {include_deleted: true} if @include_deleted
654
574
  options.merge(patches)
655
575
  end
656
576
 
@@ -0,0 +1,105 @@
1
+ module Scrivito
2
+ class ObjSearchEnumerator
3
+ #
4
+ # Response format (assuming we've requested facets for attributes "color" and "size"):
5
+ #
6
+ # [
7
+ # # Facets for attribute "color"
8
+ # [
9
+ # {value: "red", total: 2, results: [{id: "xxx"}, {id: "yyy"}]},
10
+ # {value: "blue", total: 1, results: [{id: "xxx"}]},
11
+ # {value: "green", total: 0, results: []},
12
+ # ],
13
+ #
14
+ # # Facets for attribute "size"
15
+ # [
16
+ # {value: "large", total: 2, results: [{id: "xxx"}, {id: "yyy"}]},
17
+ # {value: "medium", total: 1, results: [{id: "xxx"}]},
18
+ # {value: "small", total: 0, results: []},
19
+ # ]
20
+ # ]
21
+ #
22
+ #
23
+ class FacetQuery < Struct.new(:facet_params, :search_params, :workspace)
24
+ attr_reader :batch, :result
25
+
26
+ def execute!
27
+ @result = case
28
+ when facet_params.first.empty?
29
+ {}
30
+ when facet_params.one? && facet_params.first.is_a?(Hash)
31
+ fetch_result(multi_facet_params, facet_params.first.keys)
32
+ else
33
+ attribute_name = facet_params.first
34
+ fetch_result([single_facet_params(*facet_params)], [attribute_name])[attribute_name]
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def fetch_result(params, attribute_names)
41
+ result = attribute_names.each_with_object({}) { |v, h| h[v] = [] }
42
+
43
+ @batch = QueryExecutor.new(workspace).call(build_search_params(params))
44
+ objs = workspace.objs.find(obj_ids_from_batch)
45
+
46
+ @batch.facets.each_with_index do |facets_per_attribute, i|
47
+ result[result.keys[i]] += obj_facet_values(facets_per_attribute, objs)
48
+ end
49
+
50
+ result
51
+ end
52
+
53
+ def obj_ids_from_batch
54
+ ids = []
55
+
56
+ @batch.facets.each do |facets_per_attribute|
57
+ facets_per_attribute.each do |facet_hash|
58
+ ids += obj_ids_from_facet_hash(facet_hash)
59
+ end
60
+ end
61
+
62
+ ids
63
+ end
64
+
65
+ def multi_facet_params
66
+ facet_params.first.map do |attribute_name, options|
67
+ single_facet_params(attribute_name, options)
68
+ end
69
+ end
70
+
71
+ def single_facet_params(attribute, options = {})
72
+ {attribute: attribute}.merge!(options)
73
+ end
74
+
75
+ def obj_facet_values(facets_per_attribute, objs)
76
+ results = []
77
+
78
+ facets_per_attribute.map do |facet_hash|
79
+ obj_ids = obj_ids_from_facet_hash(facet_hash)
80
+ included_objs = []
81
+
82
+ objs.each do |obj|
83
+ if obj_ids.include?(obj.id) && !included_objs.include?(obj)
84
+ included_objs << obj
85
+ end
86
+ end
87
+
88
+ results << ObjFacetValue.new(facet_hash['value'], facet_hash['total'], included_objs)
89
+ end
90
+
91
+ results
92
+ end
93
+
94
+ def obj_ids_from_facet_hash(facet_hash)
95
+ facet_hash['results'].map { |obj_hash| obj_hash['id'] }
96
+ end
97
+
98
+ def build_search_params(params)
99
+ params = {facets: params}
100
+ params.merge!(search_params) if search_params
101
+ params.reverse_merge(size: 0)
102
+ end
103
+ end
104
+ end
105
+ end
@@ -24,6 +24,7 @@ class PageConfig < Struct.new(:obj, :editing_context, :lookup_context)
24
24
  {
25
25
  id: obj.id,
26
26
  obj_class: obj.obj_class,
27
+ description_for_editor: obj.description_for_editor,
27
28
  has_children: obj.children.any?,
28
29
  has_conflict: obj.has_conflict?,
29
30
  has_details_view: obj_has_details_view?,
@@ -0,0 +1,23 @@
1
+ module Scrivito
2
+ class Session
3
+ def self.renew(id, user)
4
+ payload = {
5
+ session: {
6
+ role: 'editor',
7
+ user_id: user.id,
8
+ permissions: permissions(user),
9
+ },
10
+ }
11
+
12
+ CmsRestApi.task_unaware_request(:put, "sessions/#{id}", payload)
13
+ end
14
+
15
+ def self.permissions(user)
16
+ Hash[user.explicit_rules.map do |permission, verb, _, _|
17
+ [verb, permission.to_s]
18
+ end]
19
+ end
20
+
21
+ private_class_method :permissions
22
+ end
23
+ end
data/lib/scrivito/task.rb CHANGED
@@ -34,7 +34,7 @@ module Scrivito
34
34
  def result
35
35
  case
36
36
  when success? then @result
37
- when error? then raise ClientError.new(@message, 400, @code)
37
+ when error? then raise ClientError.new(@message, backend_code: @code)
38
38
  else
39
39
  poll
40
40
  result
@@ -10,6 +10,7 @@ class UiConfig < Struct.new(:editing_context, :resource, :return_to, :app_extens
10
10
  {
11
11
  app_extension_tags: app_extension_tags,
12
12
  editing_context: editing_context_config,
13
+ session: session_config,
13
14
  i18n: i18n_config,
14
15
  is_development_mode: Rails.env.development?,
15
16
  resource_dialog: resource_dialog_config,
@@ -30,6 +31,10 @@ class UiConfig < Struct.new(:editing_context, :resource, :return_to, :app_extens
30
31
  }
31
32
  end
32
33
 
34
+ def session_config
35
+ Session.renew(SecureRandom.hex(8), editor)
36
+ end
37
+
33
38
  def workspace_config(workspace)
34
39
  {
35
40
  id: workspace.id,
@@ -41,6 +46,7 @@ class UiConfig < Struct.new(:editing_context, :resource, :return_to, :app_extens
41
46
  }
42
47
  end,
43
48
  outdated: workspace.outdated?,
49
+ auto_update: workspace.auto_update?,
44
50
  title: workspace.title,
45
51
  }
46
52
  end