scrivito_sdk 0.60.0 → 0.65.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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -0
  3. data/app/controllers/scrivito/objs_controller.rb +10 -2
  4. data/app/controllers/scrivito/workspaces_controller.rb +2 -1
  5. data/app/helpers/scrivito_helper.rb +32 -1
  6. data/app/views/scrivito/page_details.html.erb +1 -0
  7. data/app/views/scrivito/{workspaces → webservice}/_workspace.json.jbuilder +0 -0
  8. data/app/views/scrivito/webservice/error.json.jbuilder +1 -1
  9. data/app/views/scrivito/{workspaces/index.json.jbuilder → webservice/workspaces.json.jbuilder} +0 -0
  10. data/config/ca-bundle.crt +1 -1
  11. data/config/precedence_routes.rb +1 -0
  12. data/config/routes.rb +4 -5
  13. data/lib/assets/javascripts/scrivito_ui.js +1875 -1065
  14. data/lib/assets/stylesheets/scrivito_sdk.css +1 -1
  15. data/lib/assets/stylesheets/scrivito_ui.css +1 -1
  16. data/lib/generators/scrivito/install/templates/app/views/page/details.html.erb +3 -1
  17. data/lib/generators/scrivito/page/templates/details.html.erb +3 -1
  18. data/lib/scrivito/attribute_content.rb +159 -17
  19. data/lib/scrivito/attribute_serializer.rb +7 -3
  20. data/lib/scrivito/backend/content_state_node.rb +68 -0
  21. data/lib/scrivito/backend/index.rb +45 -0
  22. data/lib/scrivito/backend/obj_data_cache.rb +68 -0
  23. data/lib/scrivito/backend/obj_data_from_rest.rb +64 -0
  24. data/lib/scrivito/backend/obj_load.rb +45 -0
  25. data/lib/scrivito/backend/obj_query.rb +63 -0
  26. data/lib/scrivito/backend/parent_path_index.rb +21 -0
  27. data/lib/scrivito/backend/path_index.rb +27 -0
  28. data/lib/scrivito/backend/permalink_index.rb +17 -0
  29. data/lib/scrivito/basic_obj.rb +67 -39
  30. data/lib/scrivito/basic_widget.rb +19 -3
  31. data/lib/scrivito/cache_middleware.rb +9 -3
  32. data/lib/scrivito/client_error.rb +3 -3
  33. data/lib/scrivito/cms_backend.rb +64 -18
  34. data/lib/scrivito/cms_data_cache.rb +33 -30
  35. data/lib/scrivito/cms_dispatch_controller.rb +18 -0
  36. data/lib/scrivito/cms_field_tag.rb +3 -2
  37. data/lib/scrivito/cms_rest_api.rb +18 -12
  38. data/lib/scrivito/cms_rest_api/rate_limit.rb +40 -0
  39. data/lib/scrivito/cms_routing.rb +9 -8
  40. data/lib/scrivito/configuration.rb +8 -1
  41. data/lib/scrivito/controller_actions.rb +6 -1
  42. data/lib/scrivito/errors.rb +5 -0
  43. data/lib/scrivito/migrations/cms_backend.rb +2 -0
  44. data/lib/scrivito/named_link.rb +9 -45
  45. data/lib/scrivito/obj_collection.rb +14 -15
  46. data/lib/scrivito/obj_create_params_parser.rb +3 -5
  47. data/lib/scrivito/obj_params_parser.rb +2 -2
  48. data/lib/scrivito/obj_update_params_parser.rb +5 -3
  49. data/lib/scrivito/revision.rb +62 -2
  50. data/lib/scrivito/type_computer.rb +6 -2
  51. data/lib/scrivito/widget_tag.rb +4 -4
  52. data/lib/scrivito/workspace.rb +19 -3
  53. data/lib/scrivito/workspace_data.rb +23 -0
  54. data/lib/scrivito/workspace_data_from_service.rb +11 -28
  55. metadata +31 -7
  56. data/lib/scrivito/workspace_data_from_rest_api.rb +0 -6
@@ -39,19 +39,24 @@ module ControllerActions
39
39
 
40
40
  def show_widget
41
41
  widget = load_widget
42
- render text: Scrivito::WidgetTag.new(view_context, widget).render, layout: false
42
+ widget_tag = Scrivito::WidgetTag.new(view_context, widget, nil, params[:template_name])
43
+ render text: widget_tag.render, layout: false
43
44
  end
44
45
 
45
46
  def widget_details
46
47
  assert_dialog_layout
47
48
  widget = load_widget
48
49
  template_path = "#{widget.obj_class_name.underscore}/details"
50
+ @scrivito_default_widget_template = :details
49
51
  render template_path, layout: 'scrivito_dialog', locals: {widget: widget}
50
52
  end
51
53
 
52
54
  def page_details
53
55
  assert_dialog_layout
56
+ @scrivito_default_widget_template = :details
54
57
  render @obj.details_view_path, layout: 'scrivito_dialog'
58
+ rescue ActionView::MissingTemplate
59
+ render 'scrivito/page_details', layout: 'scrivito_dialog'
55
60
  end
56
61
 
57
62
  def resource_details
@@ -12,4 +12,9 @@ end
12
12
  class ObjClassNotFound < ResourceNotFound
13
13
  end
14
14
 
15
+ # this error is raised if scrivito detects an internal problem.
16
+ # these errors should never occur when using the public api of the SDK.
17
+ class InternalError < ScrivitoError
18
+ end
19
+
15
20
  end # module Scrivito
@@ -16,6 +16,7 @@ module Scrivito
16
16
  CmsRestApi.put(endpoint("objs/#{migration_store_obj.id}"),
17
17
  obj: {versions: ['string', value]})
18
18
  end
19
+ Workspace.current.reload
19
20
  end
20
21
 
21
22
  private
@@ -28,6 +29,7 @@ module Scrivito
28
29
  CmsRestApi.post(endpoint('objs'),
29
30
  obj: {_path: path, _obj_class: 'MigrationStore', versions: ['string', '']})
30
31
  end
32
+ Workspace.current.reload
31
33
  end
32
34
 
33
35
  def migration_store_obj
@@ -9,28 +9,6 @@ module Scrivito
9
9
  class NotFound < StandardError
10
10
  end
11
11
 
12
- @@named_links_cache = nil
13
-
14
- # Generates a cache of named links based on the CMS objects related link list.
15
- # Raises exceptions if zero or more than one objects have this obj_class
16
- def self.generate_named_links_cache
17
- return if @@named_links_cache
18
-
19
- found_object = find_named_link_obj
20
- if found_object.nil?
21
- raise NotFound, "Couldn't find NamedLink CMS Object!"
22
- else
23
- @@named_links_cache = found_object.
24
- related_links.
25
- flatten(1).
26
- each_with_object({}) do |link_obj, temp|
27
- temp[link_obj.title] = link_obj.obj
28
- end
29
- end
30
-
31
- return nil
32
- end
33
-
34
12
  # This method will be called to retrieve the NamedLink {BasicObj Obj}.
35
13
  # By default it will look for the Obj at the path "_named_links".
36
14
  # Overwrite this method only if you know what you are doing.
@@ -39,37 +17,23 @@ module Scrivito
39
17
  BasicObj.find_by_path("/_named_links")
40
18
  end
41
19
 
42
- def self.cache_expiry_time=(value)
43
- raise "NamedLink.cache_expiry_time is deprecated. NamedLink no longer has a separate cache."
44
- end
45
-
46
- def self.cache_expired?
47
- true
48
- end
49
-
50
20
  # Returns the CMS object mapped to the given title or nil.
51
21
  # The title can be a string of symbol.
52
22
  # @api public
53
23
  def self.get_object(title, options = {})
54
- object = named_links[title.to_s]
55
- raise NotFound, "The NamedLink '#{title.to_s}' does not exist" if object.nil?
56
- object
57
- end
58
-
59
- def self.reset_cache
60
- @@named_links_cache = nil
24
+ link = named_links.find { |link| link.title == title.to_s }
25
+ raise NotFound, "The NamedLink '#{title.to_s}' does not exist" unless link
26
+ link.obj
61
27
  end
62
28
 
63
29
  def self.named_links
64
- reset_cache if cache_expired?
65
- generate_named_links_cache unless named_links_cache
66
- named_links_cache
67
- end
30
+ named_link_obj = find_named_link_obj
68
31
 
69
- def self.named_links_cache
70
- @@named_links_cache
32
+ if named_link_obj
33
+ named_link_obj.related_links
34
+ else
35
+ raise NotFound, "Couldn't find NamedLink CMS Object!"
36
+ end
71
37
  end
72
-
73
38
  end
74
-
75
39
  end
@@ -15,7 +15,7 @@ module Scrivito
15
15
  # @return [Obj, Array<Obj>]
16
16
  # @api public
17
17
  def find(id_or_list)
18
- find_filtering_deleted(id_or_list, false)
18
+ find_using(id_or_list, :find_by)
19
19
  end
20
20
 
21
21
  # Find a {BasicObj Obj} by its id.
@@ -25,7 +25,7 @@ module Scrivito
25
25
  # @return [Obj, Array<Obj>]
26
26
  # @api public
27
27
  def find_including_deleted(id_or_list)
28
- find_filtering_deleted(id_or_list, true)
28
+ find_using(id_or_list, :find_by_including_deleted)
29
29
  end
30
30
 
31
31
  # Find the {BasicObj Obj} with the given path.
@@ -98,28 +98,27 @@ module Scrivito
98
98
 
99
99
  private
100
100
 
101
- def find_filtering_deleted(id_or_list, include_deleted)
101
+ def find_using(id_or_list, method)
102
102
  case id_or_list
103
103
  when Array
104
- find_by(:id, id_or_list, include_deleted).map(&:first).compact
104
+ send(method, :id, id_or_list).map(&:first).compact
105
105
  else
106
- obj = find_by(:id, [id_or_list.to_s], include_deleted).first.first
106
+ obj = send(method, :id, [id_or_list.to_s]).first.first
107
+
107
108
  obj or raise ResourceNotFound, "Could not find Obj with id #{id_or_list}"
108
109
  end
109
110
  end
110
111
 
111
- # accepts the name of an "obj_by" - view, a list of keys
112
- # and an "include_deleted" flag
113
- # returns a list of lists of Objs: a list of Objs for each given keys.
114
- def find_by(view, keys, include_deleted = false)
115
- if include_deleted
116
- finder_method_name = :find_obj_data_including_deleted_by
117
- else
118
- finder_method_name = :find_obj_data_by
112
+ def find_by(view, keys)
113
+ find_by_including_deleted(view, keys).map do |list|
114
+ list.reject(&:deleted?)
119
115
  end
116
+ end
120
117
 
121
- result = CmsBackend.instance.public_send(
122
- finder_method_name,
118
+ # accepts the name of an "obj_by" - view, a list of keys
119
+ # returns a list of lists of Objs: a list of Objs for each given keys.
120
+ def find_by_including_deleted(view, keys)
121
+ result = CmsBackend.instance.find_obj_data_by(
123
122
  workspace.revision,
124
123
  view,
125
124
  keys)
@@ -1,13 +1,11 @@
1
1
  module Scrivito
2
- class ObjCreateParamsParser < Struct.new(:host, :port)
3
- include ObjParamsParser
4
-
2
+ class ObjCreateParamsParser < ObjParamsParser
5
3
  private
6
4
 
7
5
  def convert_params(params)
8
6
  if obj_class_name = params['_obj_class']
9
- model_class = Obj.type_computer.compute_type(obj_class_name)
10
- convert_field_params(params, model_class.attribute_definitions)
7
+ obj_class = Obj.type_computer.compute_type_without_fallback(obj_class_name)
8
+ convert_field_params(params, obj_class.attribute_definitions)
11
9
  else
12
10
  raise ArgumentError, 'Missing "_obj_class" param'
13
11
  end
@@ -1,5 +1,5 @@
1
1
  module Scrivito
2
- module ObjParamsParser
2
+ class ObjParamsParser < Struct.new(:host, :port, :context)
3
3
  class UnknownWidgetAction < ScrivitoError
4
4
  end
5
5
 
@@ -30,7 +30,7 @@ module Scrivito
30
30
  if widget_id_or_params.is_a?(Hash)
31
31
  action, widget_params = widget_id_or_params.flatten
32
32
  case action
33
- when 'create' then Widget.new(widget_params)
33
+ when 'create' then Widget.new(widget_params, context.slice(:scrivito_user))
34
34
  when 'copy'
35
35
  widget_id = widget_params['widget_id']
36
36
  widget = Workspace.find(widget_params['workspace_id'])
@@ -1,9 +1,11 @@
1
1
  module Scrivito
2
- class ObjUpdateParamsParser < Struct.new(:obj, :host, :port)
3
- include ObjParamsParser
4
-
2
+ class ObjUpdateParamsParser < ObjParamsParser
5
3
  private
6
4
 
5
+ def obj
6
+ context[:current_obj]
7
+ end
8
+
7
9
  def convert_params(params)
8
10
  convert_field_params(params, obj.attribute_definitions)
9
11
  convert_widget_pool_params(params)
@@ -1,13 +1,73 @@
1
1
  module Scrivito
2
2
 
3
- class Revision < Struct.new(:id, :content_state, :workspace, :base)
3
+ class Revision < Struct.new(:id, :workspace, :base)
4
4
  def initialize(options)
5
- super(*options.values_at(:id, :content_state, :workspace, :base))
5
+ super(*options.values_at(:id, :workspace, :base))
6
+ end
7
+
8
+ def content_state
9
+ base ? workspace.base_content_state : workspace.content_state
10
+ end
11
+
12
+ def content_state_id
13
+ base ? workspace.base_content_state_id : workspace.content_state_id
14
+ end
15
+
16
+ def content_state_node
17
+ @node ||= Backend::ContentStateNode.new(content_state_id)
18
+ end
19
+
20
+ # returns a list of raw rest-api hashes, one for each given id
21
+ def obj_mget_request(ids)
22
+ internal_obj_mget(ids, [])
23
+ end
24
+
25
+ # performs given search-query, returns the search-hits as a list of obj-ids
26
+ def obj_search_request(query)
27
+ if base
28
+ raise Scrivito::InternalError, "search not possible for base revision"
29
+ end
30
+
31
+ internal_obj_search(query, [])
32
+ end
33
+
34
+ def api_request(verb, path, payload = nil)
35
+ CmsRestApi.public_send(verb, "revisions/#{id}#{path}", payload)
6
36
  end
7
37
 
8
38
  def base?
9
39
  !!base
10
40
  end
41
+
42
+ private
43
+
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
+ })
48
+
49
+ cur_result = response['results'].map { |obj_data| obj_data['id'] }
50
+ result += cur_result
51
+
52
+ if result.size >= response['total'].to_i
53
+ result
54
+ else
55
+ internal_obj_search(query, result)
56
+ end
57
+ end
58
+
59
+ def internal_obj_mget(ids, result)
60
+ response = api_request(
61
+ :get, "/objs/mget", ids: ids, include_deleted: true)
62
+
63
+ current_result = result + response["results"]
64
+
65
+ if response["results"].size < ids.size
66
+ internal_obj_mget(ids.drop(response["results"].size), current_result)
67
+ else
68
+ current_result
69
+ end
70
+ end
11
71
  end
12
72
 
13
73
  end
@@ -6,11 +6,15 @@ class TypeComputer
6
6
  end
7
7
 
8
8
  def compute_type(obj_class_name)
9
- compute_type_without_fallback(obj_class_name) || @fallback_class
9
+ load_class(obj_class_name) || @fallback_class
10
10
  end
11
11
 
12
12
  def compute_type_without_fallback(obj_class_name)
13
- load_class(obj_class_name)
13
+ if obj_class = load_class(obj_class_name)
14
+ obj_class
15
+ else
16
+ raise ObjClassNotFound
17
+ end
14
18
  end
15
19
 
16
20
  def special_class?(klass)
@@ -1,6 +1,6 @@
1
1
  module Scrivito
2
2
 
3
- class WidgetTag < Struct.new(:view, :widget, :placement_modification)
3
+ class WidgetTag < Struct.new(:view, :widget, :placement_modification, :template_name)
4
4
  include TagRenderer
5
5
 
6
6
  def tag_name
@@ -8,7 +8,7 @@ class WidgetTag < Struct.new(:view, :widget, :placement_modification)
8
8
  end
9
9
 
10
10
  def content
11
- view.render(template: template, locals: {widget: widget})
11
+ view.render(template: template_path, locals: {widget: widget})
12
12
  rescue ActionView::MissingTemplate
13
13
  %{Missing Ruby model class for "#{widget.obj_class_name}"} if view.scrivito_user
14
14
  end
@@ -46,8 +46,8 @@ class WidgetTag < Struct.new(:view, :widget, :placement_modification)
46
46
 
47
47
  private
48
48
 
49
- def template
50
- widget.to_show_view_path
49
+ def template_path
50
+ widget.public_send("to_#{template_name}_view_path")
51
51
  end
52
52
 
53
53
  def has_widget_details_view?(widget)
@@ -79,7 +79,8 @@ class Workspace
79
79
  all.detect { |workspace| workspace.title == title }
80
80
  end
81
81
 
82
- delegate :content_state, :base_revision_id, :base_content_state, :uses_obj_classes, to: :data
82
+ delegate :content_state_id, :base_content_state_id, :content_state,
83
+ :base_revision_id, :base_content_state, :uses_obj_classes, to: :data
83
84
 
84
85
  # Create a new workspace
85
86
  # @api public
@@ -203,14 +204,13 @@ class Workspace
203
204
  end
204
205
 
205
206
  def revision
206
- @revision ||= Revision.new(id: revision_id, content_state: content_state, workspace: self)
207
+ @revision ||= Revision.new(id: revision_id, workspace: self)
207
208
  end
208
209
 
209
210
  def base_revision
210
211
  if base_revision_id
211
212
  @base_revision ||= Revision.new(
212
213
  id: base_revision_id,
213
- content_state: base_content_state,
214
214
  workspace: self,
215
215
  base: true
216
216
  )
@@ -258,6 +258,22 @@ class Workspace
258
258
  "<#{self.class} id=\"#{id}\" title=\"#{title}\">"
259
259
  end
260
260
 
261
+ def has_modification_for?(obj_id)
262
+ !!objs.find_including_deleted(obj_id).modification
263
+ rescue Scrivito::ResourceNotFound
264
+ false
265
+ end
266
+
267
+ def self.all_with_modification_for(obj_id)
268
+ all.select do |ws|
269
+ ws.has_modification_for?(obj_id)
270
+ end
271
+ end
272
+
273
+ def conflict_warning_for(obj_id)
274
+ self.class.all_with_modification_for(obj_id) - [self]
275
+ end
276
+
261
277
  private
262
278
 
263
279
  def backend_url
@@ -11,9 +11,32 @@ module Scrivito
11
11
  data_attr_reader :id
12
12
  data_attr_reader :title
13
13
  data_attr_reader :memberships
14
+ data_attr_reader :revision_id
15
+ data_attr_reader :content_state_id
16
+ data_attr_reader :uses_obj_classes
17
+ data_attr_reader :base_revision_id
18
+ data_attr_reader :base_content_state_id
14
19
 
15
20
  def initialize(data)
16
21
  @data = data
17
22
  end
23
+
24
+ def store_in_cache
25
+ CmsDataCache.write_workspace_data(id, to_hash)
26
+ end
27
+
28
+ private
29
+
30
+ def to_hash
31
+ {
32
+ 'id' => id,
33
+ 'revision_id' => revision_id,
34
+ 'title' => title,
35
+ 'content_state_id' => content_state_id,
36
+ 'base_revision_id' => base_revision_id,
37
+ 'base_content_state_id' => base_content_state_id,
38
+ 'memberships' => memberships,
39
+ }
40
+ end
18
41
  end
19
42
  end
@@ -11,22 +11,7 @@ class WorkspaceDataFromService < WorkspaceData
11
11
  end
12
12
  end
13
13
 
14
- data_attr_reader :revision_id
15
- data_attr_reader :content_state_id
16
- data_attr_reader :base_revision_id
17
- data_attr_reader :base_content_state_id
18
14
  data_attr_reader :diff
19
- data_attr_reader :uses_obj_classes
20
-
21
- def content_state
22
- @content_state ||= ContentState.find_or_create(content_state_id) if content_state_id
23
- end
24
-
25
- def base_content_state
26
- if base_content_state_id
27
- @base_content_state ||= ContentState.find_or_create(base_content_state_id)
28
- end
29
- end
30
15
 
31
16
  def changes
32
17
  diff && diff['changes']
@@ -42,25 +27,23 @@ class WorkspaceDataFromService < WorkspaceData
42
27
 
43
28
  # Serializes and stores a workspace data in cache storage.
44
29
  # Also creates an appropriate content state if needed.
45
- def store_in_cache
30
+ def store_in_cache_and_create_content_state
46
31
  create_content_state if content_state_id
47
- CmsDataCache.write_workspace_data(id, to_hash)
32
+ store_in_cache
48
33
  end
49
34
 
50
- private
35
+ def content_state
36
+ @content_state ||= ContentState.find_or_create(content_state_id) if content_state_id
37
+ end
51
38
 
52
- def to_hash
53
- {
54
- 'id' => id,
55
- 'revision_id' => revision_id,
56
- 'title' => title,
57
- 'content_state_id' => content_state_id,
58
- 'base_revision_id' => base_revision_id,
59
- 'base_content_state_id' => base_content_state_id,
60
- 'memberships' => memberships,
61
- }
39
+ def base_content_state
40
+ if base_content_state_id
41
+ @base_content_state ||= ContentState.find_or_create(base_content_state_id)
42
+ end
62
43
  end
63
44
 
45
+ private
46
+
64
47
  def create_content_state
65
48
  ContentState.create(content_state_id: to_content_state_id, changes: changes,
66
49
  from_content_state_id: from_content_state_id)