scrivito_sdk 0.60.0 → 0.65.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardopts +1 -0
- data/app/controllers/scrivito/objs_controller.rb +10 -2
- data/app/controllers/scrivito/workspaces_controller.rb +2 -1
- data/app/helpers/scrivito_helper.rb +32 -1
- data/app/views/scrivito/page_details.html.erb +1 -0
- data/app/views/scrivito/{workspaces → webservice}/_workspace.json.jbuilder +0 -0
- data/app/views/scrivito/webservice/error.json.jbuilder +1 -1
- data/app/views/scrivito/{workspaces/index.json.jbuilder → webservice/workspaces.json.jbuilder} +0 -0
- data/config/ca-bundle.crt +1 -1
- data/config/precedence_routes.rb +1 -0
- data/config/routes.rb +4 -5
- data/lib/assets/javascripts/scrivito_ui.js +1875 -1065
- data/lib/assets/stylesheets/scrivito_sdk.css +1 -1
- data/lib/assets/stylesheets/scrivito_ui.css +1 -1
- data/lib/generators/scrivito/install/templates/app/views/page/details.html.erb +3 -1
- data/lib/generators/scrivito/page/templates/details.html.erb +3 -1
- data/lib/scrivito/attribute_content.rb +159 -17
- data/lib/scrivito/attribute_serializer.rb +7 -3
- data/lib/scrivito/backend/content_state_node.rb +68 -0
- data/lib/scrivito/backend/index.rb +45 -0
- data/lib/scrivito/backend/obj_data_cache.rb +68 -0
- data/lib/scrivito/backend/obj_data_from_rest.rb +64 -0
- data/lib/scrivito/backend/obj_load.rb +45 -0
- data/lib/scrivito/backend/obj_query.rb +63 -0
- data/lib/scrivito/backend/parent_path_index.rb +21 -0
- data/lib/scrivito/backend/path_index.rb +27 -0
- data/lib/scrivito/backend/permalink_index.rb +17 -0
- data/lib/scrivito/basic_obj.rb +67 -39
- data/lib/scrivito/basic_widget.rb +19 -3
- data/lib/scrivito/cache_middleware.rb +9 -3
- data/lib/scrivito/client_error.rb +3 -3
- data/lib/scrivito/cms_backend.rb +64 -18
- data/lib/scrivito/cms_data_cache.rb +33 -30
- data/lib/scrivito/cms_dispatch_controller.rb +18 -0
- data/lib/scrivito/cms_field_tag.rb +3 -2
- data/lib/scrivito/cms_rest_api.rb +18 -12
- data/lib/scrivito/cms_rest_api/rate_limit.rb +40 -0
- data/lib/scrivito/cms_routing.rb +9 -8
- data/lib/scrivito/configuration.rb +8 -1
- data/lib/scrivito/controller_actions.rb +6 -1
- data/lib/scrivito/errors.rb +5 -0
- data/lib/scrivito/migrations/cms_backend.rb +2 -0
- data/lib/scrivito/named_link.rb +9 -45
- data/lib/scrivito/obj_collection.rb +14 -15
- data/lib/scrivito/obj_create_params_parser.rb +3 -5
- data/lib/scrivito/obj_params_parser.rb +2 -2
- data/lib/scrivito/obj_update_params_parser.rb +5 -3
- data/lib/scrivito/revision.rb +62 -2
- data/lib/scrivito/type_computer.rb +6 -2
- data/lib/scrivito/widget_tag.rb +4 -4
- data/lib/scrivito/workspace.rb +19 -3
- data/lib/scrivito/workspace_data.rb +23 -0
- data/lib/scrivito/workspace_data_from_service.rb +11 -28
- metadata +31 -7
- 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
|
-
|
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
|
data/lib/scrivito/errors.rb
CHANGED
@@ -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
|
data/lib/scrivito/named_link.rb
CHANGED
@@ -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
|
-
|
55
|
-
raise NotFound, "The NamedLink '#{title.to_s}' does not exist"
|
56
|
-
|
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
|
-
|
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
|
-
|
70
|
-
|
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
|
-
|
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
|
-
|
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
|
101
|
+
def find_using(id_or_list, method)
|
102
102
|
case id_or_list
|
103
103
|
when Array
|
104
|
-
|
104
|
+
send(method, :id, id_or_list).map(&:first).compact
|
105
105
|
else
|
106
|
-
obj =
|
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
|
-
|
112
|
-
|
113
|
-
|
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
|
-
|
122
|
-
|
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 <
|
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
|
-
|
10
|
-
convert_field_params(params,
|
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
|
-
|
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 <
|
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)
|
data/lib/scrivito/revision.rb
CHANGED
@@ -1,13 +1,73 @@
|
|
1
1
|
module Scrivito
|
2
2
|
|
3
|
-
class Revision < Struct.new(:id, :
|
3
|
+
class Revision < Struct.new(:id, :workspace, :base)
|
4
4
|
def initialize(options)
|
5
|
-
super(*options.values_at(:id, :
|
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
|
-
|
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)
|
data/lib/scrivito/widget_tag.rb
CHANGED
@@ -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:
|
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
|
50
|
-
widget.
|
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)
|
data/lib/scrivito/workspace.rb
CHANGED
@@ -79,7 +79,8 @@ class Workspace
|
|
79
79
|
all.detect { |workspace| workspace.title == title }
|
80
80
|
end
|
81
81
|
|
82
|
-
delegate :
|
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,
|
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
|
30
|
+
def store_in_cache_and_create_content_state
|
46
31
|
create_content_state if content_state_id
|
47
|
-
|
32
|
+
store_in_cache
|
48
33
|
end
|
49
34
|
|
50
|
-
|
35
|
+
def content_state
|
36
|
+
@content_state ||= ContentState.find_or_create(content_state_id) if content_state_id
|
37
|
+
end
|
51
38
|
|
52
|
-
def
|
53
|
-
|
54
|
-
|
55
|
-
|
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)
|