scrivito_sdk 0.17.0 → 0.18.0
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.
- checksums.yaml +4 -4
- data/.yardopts +1 -0
- data/app/controllers/scrivito/default_cms_controller.rb +15 -3
- data/app/controllers/scrivito/objs_controller.rb +61 -25
- data/app/controllers/scrivito/users_controller.rb +7 -0
- data/app/controllers/scrivito/webservice_controller.rb +17 -7
- data/app/controllers/scrivito/workspaces_controller.rb +85 -17
- data/app/helpers/scrivito/default_cms_routing_helper.rb +3 -3
- data/config/routes.rb +4 -1
- data/lib/assets/javascripts/scrivito_editing.js +4182 -508
- data/lib/assets/stylesheets/scrivito_editing.css +489 -13
- data/lib/generators/cms/migration/templates/migration.erb +4 -4
- data/lib/scrivito/attribute_collection.rb +1 -6
- data/lib/scrivito/attribute_content.rb +29 -7
- data/lib/scrivito/basic_obj.rb +91 -61
- data/lib/scrivito/basic_widget.rb +0 -11
- data/lib/scrivito/client_config.rb +40 -25
- data/lib/scrivito/cms_backend.rb +54 -0
- data/lib/scrivito/cms_cache_storage.rb +8 -0
- data/lib/scrivito/cms_field_tag.rb +2 -1
- data/lib/scrivito/cms_rest_api.rb +9 -0
- data/lib/scrivito/configuration.rb +4 -2
- data/lib/scrivito/content_state.rb +8 -0
- data/lib/scrivito/content_state_caching.rb +20 -0
- data/lib/scrivito/editing_context.rb +35 -34
- data/lib/scrivito/membership.rb +22 -3
- data/lib/scrivito/memberships_collection.rb +8 -4
- data/lib/scrivito/obj_class.rb +45 -100
- data/lib/scrivito/obj_class_collection.rb +53 -0
- data/lib/scrivito/obj_class_data.rb +33 -0
- data/lib/scrivito/obj_data.rb +26 -48
- data/lib/scrivito/obj_data_from_hash.rb +5 -5
- data/lib/scrivito/obj_data_from_service.rb +9 -3
- data/lib/scrivito/obj_search_builder.rb +0 -5
- data/lib/scrivito/obj_search_enumerator.rb +3 -20
- data/lib/scrivito/objs_collection.rb +7 -0
- data/lib/scrivito/restriction_set.rb +2 -2
- data/lib/scrivito/user.rb +89 -23
- data/lib/scrivito/user_definition.rb +73 -70
- data/lib/scrivito/workspace.rb +52 -8
- data/lib/scrivito/workspace/publish_checker.rb +126 -0
- metadata +6 -2
@@ -14,16 +14,16 @@ class <%= class_name %> < ::Scrivito::Migration
|
|
14
14
|
#
|
15
15
|
# To get you started, here is a list of the most common SDK methods to alter the CMS content.
|
16
16
|
#
|
17
|
-
# @example Create a new obj class named "Homepage"
|
18
|
-
# Scrivito::ObjClass.create(name: 'Homepage',
|
17
|
+
# @example Create a new non-binary obj class named "Homepage" that has a "string" attribute named "locale".
|
18
|
+
# Scrivito::ObjClass.create(name: 'Homepage', is_binary: false, attributes: [
|
19
19
|
# {name: 'locale', type: :string}
|
20
20
|
# ])
|
21
21
|
#
|
22
22
|
# @example Find the obj class named "Homepage".
|
23
23
|
# Scrivito::ObjClass.find('Homepage')
|
24
24
|
#
|
25
|
-
# @example Update the "
|
26
|
-
# Scrivito::ObjClass.find('Homepage').update(
|
25
|
+
# @example Update the "is_active" attribute of the obj class named "Homepage".
|
26
|
+
# Scrivito::ObjClass.find('Homepage').update(is_active: false)
|
27
27
|
#
|
28
28
|
# @example Add an "enum" attribute named "category" to the obj class named "Homepage".
|
29
29
|
# Scrivito::ObjClass.find('Homepage').attributes.add(name: 'category', type: :enum, values: %w(tech, social))
|
@@ -54,14 +54,9 @@ module Scrivito
|
|
54
54
|
# instance or an attribute property hash.
|
55
55
|
# @return [Scrivito::AttributeCollection]
|
56
56
|
def add(attribute)
|
57
|
-
unless attribute.respond_to?(:to_cms_rest_api)
|
58
|
-
attribute = Attribute.new(attribute)
|
59
|
-
end
|
60
|
-
|
57
|
+
attribute = Attribute.new(attribute) unless attribute.respond_to?(:to_cms_rest_api)
|
61
58
|
@attributes << attribute
|
62
|
-
|
63
59
|
@obj_class.update(attributes: @attributes)
|
64
|
-
|
65
60
|
self
|
66
61
|
end
|
67
62
|
alias_method :<<, :add
|
@@ -40,7 +40,8 @@ module AttributeContent
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def has_custom_attribute?(name)
|
43
|
-
|
43
|
+
name = name.to_s
|
44
|
+
name != 'blob' && data_from_cms.has_custom_attribute?(name)
|
44
45
|
end
|
45
46
|
alias_method :has_attribute?, :has_custom_attribute?
|
46
47
|
|
@@ -54,7 +55,12 @@ module AttributeContent
|
|
54
55
|
# @api public
|
55
56
|
def [](key)
|
56
57
|
key = key.to_s
|
57
|
-
|
58
|
+
|
59
|
+
if key == '_obj_class'
|
60
|
+
obj_class
|
61
|
+
else
|
62
|
+
has_attribute?(key) ? read_attribute(key) : nil
|
63
|
+
end
|
58
64
|
end
|
59
65
|
|
60
66
|
# Hook method to control which widget classes should be available for this page.
|
@@ -85,8 +91,8 @@ module AttributeContent
|
|
85
91
|
cms_data_in_revision = cms_data_for_revision(revision)
|
86
92
|
|
87
93
|
if cms_data_in_revision
|
88
|
-
other_value = cms_data_in_revision.
|
89
|
-
if data_from_cms.
|
94
|
+
other_value = cms_data_in_revision.value_without_default_of(attribute_name.to_s)
|
95
|
+
if data_from_cms.value_without_default_of(attribute_name.to_s) == other_value
|
90
96
|
Modification::UNMODIFIED
|
91
97
|
else
|
92
98
|
Modification::EDITED
|
@@ -102,6 +108,22 @@ module AttributeContent
|
|
102
108
|
@attribute_cache = {}
|
103
109
|
end
|
104
110
|
|
111
|
+
# Returns the obj class name of this object.
|
112
|
+
# @api public
|
113
|
+
# @return [String]
|
114
|
+
def obj_class_name
|
115
|
+
data_from_cms.value_of('_obj_class')
|
116
|
+
end
|
117
|
+
|
118
|
+
# Returns the obj class of this object.
|
119
|
+
# @api public
|
120
|
+
# @return [ObjClass]
|
121
|
+
def obj_class
|
122
|
+
if obj_class_data = CmsBackend.instance.find_obj_class_data_by_name(revision, obj_class_name)
|
123
|
+
ObjClass.new(obj_class_data, revision.workspace)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
105
127
|
private
|
106
128
|
|
107
129
|
attr_writer :data_from_cms
|
@@ -140,7 +162,7 @@ module AttributeContent
|
|
140
162
|
link_definitions = link_definitions.map(&:with_indifferent_access)
|
141
163
|
|
142
164
|
object_ids = link_definitions.map { |link_data| link_data[:destination] }.compact.uniq
|
143
|
-
objects = object_ids.empty? ? [] :
|
165
|
+
objects = object_ids.empty? ? [] : BasicObj.find(object_ids)
|
144
166
|
link_definitions.each_with_object([]) do |link_data, links|
|
145
167
|
obj = objects.detect { |o| o && o.id == link_data[:destination] }
|
146
168
|
link = Link.new(link_data.merge(obj: obj))
|
@@ -153,7 +175,7 @@ module AttributeContent
|
|
153
175
|
|
154
176
|
def build_link(attribute_value)
|
155
177
|
return unless attribute_value
|
156
|
-
|
178
|
+
|
157
179
|
if attribute_value['destination']
|
158
180
|
build_internal_link(attribute_value)
|
159
181
|
else
|
@@ -163,7 +185,7 @@ module AttributeContent
|
|
163
185
|
|
164
186
|
def build_internal_link(attribute_value)
|
165
187
|
properties = {
|
166
|
-
obj:
|
188
|
+
obj: BasicObj.find(attribute_value['destination']),
|
167
189
|
title: attribute_value['title'],
|
168
190
|
query: attribute_value['query'],
|
169
191
|
fragment: attribute_value['fragment'],
|
data/lib/scrivito/basic_obj.rb
CHANGED
@@ -6,6 +6,20 @@ module Scrivito
|
|
6
6
|
# The CMS file class
|
7
7
|
# @api public
|
8
8
|
class BasicObj
|
9
|
+
UNIQ_ATTRIBUTES = %w[
|
10
|
+
_id
|
11
|
+
_path
|
12
|
+
_permalink
|
13
|
+
].freeze
|
14
|
+
|
15
|
+
GENERATED_ATTRIBUTES = %w[
|
16
|
+
_conflicts
|
17
|
+
_last_changed
|
18
|
+
_modification
|
19
|
+
_obj_type
|
20
|
+
_text_links
|
21
|
+
].freeze
|
22
|
+
|
9
23
|
extend ActiveModel::Naming
|
10
24
|
|
11
25
|
include AttributeContent
|
@@ -77,10 +91,9 @@ module Scrivito
|
|
77
91
|
def self.create(attributes)
|
78
92
|
attributes = with_default_obj_class(attributes)
|
79
93
|
api_attributes, widget_properties = prepare_attributes_for_rest_api(attributes, nil)
|
80
|
-
json =
|
81
|
-
obj = find(json['
|
94
|
+
json = Workspace.current.api_request(:post, '/objs', obj: api_attributes)
|
95
|
+
obj = find(json['_id'])
|
82
96
|
CmsRestApi::WidgetExtractor.notify_persisted_widgets(obj, widget_properties)
|
83
|
-
Workspace.current.reload
|
84
97
|
obj
|
85
98
|
end
|
86
99
|
|
@@ -263,23 +276,36 @@ module Scrivito
|
|
263
276
|
# )
|
264
277
|
def update(attributes)
|
265
278
|
api_attributes, widget_properties = prepare_attributes_for_rest_api(attributes)
|
266
|
-
|
267
|
-
|
268
|
-
reload
|
279
|
+
workspace.api_request(:put, "/objs/#{id}", obj: api_attributes)
|
280
|
+
reload_data
|
269
281
|
CmsRestApi::WidgetExtractor.notify_persisted_widgets(self, widget_properties)
|
270
282
|
self
|
271
283
|
end
|
272
284
|
|
285
|
+
# Copies itself to a given path.
|
286
|
+
# @api public
|
287
|
+
# @param [Hash] options
|
288
|
+
# @option options [String,Symbol] :_path (nil) the path of the copy.
|
289
|
+
# @option options [String,Symbol] :_id (nil) the id of the copy.
|
290
|
+
# @option options [String,Symbol] :_permalink (nil) the permalink of the copy.
|
291
|
+
# @raise [ArgumentError] if +options+ includes invalid keys.
|
292
|
+
# @return [Obj] the created copy
|
293
|
+
# @example Copy a blog post.
|
294
|
+
# blog_post = Obj.find_by_path('/blog/first_post')
|
295
|
+
# blog_post.copy(_path: '/blog/second_post')
|
296
|
+
def copy(options)
|
297
|
+
options = options.stringify_keys.assert_valid_keys('_path', '_id', '_permalink')
|
298
|
+
json = workspace.api_request(:post, '/objs', obj: copyable_attributes.merge(options))
|
299
|
+
self.class.find(json['_id'])
|
300
|
+
end
|
301
|
+
|
273
302
|
# Destroys the {BasicObj Obj} in the current {Workspace}
|
274
303
|
# @api public
|
275
304
|
def destroy
|
276
305
|
if children.any?
|
277
306
|
raise ClientError.new(I18n.t('scrivito.errors.models.basic_obj.has_children'), 412)
|
278
307
|
end
|
279
|
-
|
280
|
-
CmsRestApi.delete(cms_rest_api_path)
|
281
|
-
|
282
|
-
workspace.reload
|
308
|
+
workspace.api_request(:delete, "/objs/#{id}")
|
283
309
|
end
|
284
310
|
|
285
311
|
def to_param
|
@@ -486,26 +512,15 @@ module Scrivito
|
|
486
512
|
# even if the obj_class in the database has changed.
|
487
513
|
# @api public
|
488
514
|
def reload
|
489
|
-
|
490
|
-
|
491
|
-
reload_data = Proc.new do
|
492
|
-
CmsBackend.instance.find_obj_data_by(workspace.revision, :id, [id]).first.first
|
493
|
-
end
|
494
|
-
|
495
|
-
update_data(reload_data)
|
496
|
-
end
|
497
|
-
|
498
|
-
# @return [String]
|
499
|
-
# @api public
|
500
|
-
def obj_class_name
|
501
|
-
read_attribute('_obj_class')
|
515
|
+
workspace.reload
|
516
|
+
reload_data
|
502
517
|
end
|
503
518
|
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
519
|
+
def reload_data
|
520
|
+
id = self.id.to_s
|
521
|
+
update_data -> {
|
522
|
+
CmsBackend.instance.find_obj_data_by(workspace.revision, :id, [id]).first.first
|
523
|
+
}
|
509
524
|
end
|
510
525
|
|
511
526
|
# @api public
|
@@ -665,35 +680,23 @@ module Scrivito
|
|
665
680
|
# This method does not work with +new+ or +deleted+ objects.
|
666
681
|
# This method also does also not work for the +published+ workspace or the +rtc+ working copy.
|
667
682
|
def revert
|
668
|
-
|
683
|
+
assert_revertable
|
669
684
|
|
670
|
-
if
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
when Modification::UNMODIFIED
|
675
|
-
# don't do anything
|
676
|
-
when Modification::EDITED
|
677
|
-
previous_content = CmsRestApi.get(
|
678
|
-
"revisions/#{workspace.base_revision_id}/objs/#{id}")
|
679
|
-
updated_content = previous_content.except('id', '_id')
|
680
|
-
|
681
|
-
added_widget_ids = read_widget_pool.keys - previous_content['_widget_pool'].keys
|
682
|
-
added_widget_ids.each do |added_widget_id|
|
683
|
-
updated_content['_widget_pool'][added_widget_id] = nil
|
684
|
-
end
|
685
|
+
if modification == Modification::EDITED
|
686
|
+
base_revision_path = "revisions/#{workspace.base_revision_id}/objs/#{id}"
|
687
|
+
previous_attributes = CmsRestApi.get(base_revision_path).except('_id')
|
688
|
+
previous_widget_pool = previous_attributes['_widget_pool']
|
685
689
|
|
686
|
-
|
690
|
+
ids_of_new_widgets = read_widget_pool.keys - previous_widget_pool.keys
|
691
|
+
ids_of_new_widgets.each { |widget_id| previous_widget_pool[widget_id] = nil }
|
687
692
|
|
688
|
-
|
689
|
-
|
690
|
-
raise ScrivitoError, "cannot revert changes, since obj is #{modification}."
|
691
|
-
end
|
693
|
+
workspace.api_request(:put, "/objs/#{id}", obj: previous_attributes)
|
694
|
+
reload
|
692
695
|
end
|
693
696
|
end
|
694
697
|
|
695
698
|
def mark_resolved
|
696
|
-
|
699
|
+
workspace.api_request(:put, "/objs/#{id}", obj: {_conflicts: nil})
|
697
700
|
reload
|
698
701
|
end
|
699
702
|
|
@@ -718,6 +721,10 @@ module Scrivito
|
|
718
721
|
read_attribute('_conflicts') != nil
|
719
722
|
end
|
720
723
|
|
724
|
+
def publishable?
|
725
|
+
!has_conflict?
|
726
|
+
end
|
727
|
+
|
721
728
|
def all_widgets_from_pool
|
722
729
|
read_widget_pool.keys.map do |widget_id|
|
723
730
|
widget_from_pool(widget_id)
|
@@ -734,6 +741,24 @@ module Scrivito
|
|
734
741
|
raise ScrivitoError.new('Could not generate a new unused widget id')
|
735
742
|
end
|
736
743
|
|
744
|
+
# Generates a +Hash+ containing all public attributes. The hash will _not_ include attributes,
|
745
|
+
# which are read-only or used for internal purpose.
|
746
|
+
# @api public
|
747
|
+
# @return [Hash] a hash containing all public attributes.
|
748
|
+
# @example
|
749
|
+
# old_obj = Obj.homepage
|
750
|
+
# attrs = old_obj.to_h
|
751
|
+
# puts attrs
|
752
|
+
# #=> {"_obj_class"=>"Publication", "_path"=>"/", "_id"=>"f64a68258ca15faf", "_widget_pool"=>{}}
|
753
|
+
# old_obj.destroy
|
754
|
+
#
|
755
|
+
# new_obj = Obj.create(attrs)
|
756
|
+
# puts new_obj == old_obj
|
757
|
+
# #=> true
|
758
|
+
def to_h
|
759
|
+
data_from_cms.to_h.except(*GENERATED_ATTRIBUTES)
|
760
|
+
end
|
761
|
+
|
737
762
|
private
|
738
763
|
|
739
764
|
def cms_data_for_revision(revision)
|
@@ -774,10 +799,6 @@ module Scrivito
|
|
774
799
|
Blob.find(blob_spec["id"]) if blob_spec
|
775
800
|
end
|
776
801
|
|
777
|
-
def cms_rest_api_path(obj_id = id)
|
778
|
-
"#{self.class.cms_rest_api_path(workspace)}/#{obj_id}"
|
779
|
-
end
|
780
|
-
|
781
802
|
def workspace
|
782
803
|
if revision.workspace
|
783
804
|
revision.workspace
|
@@ -794,17 +815,26 @@ module Scrivito
|
|
794
815
|
self.class.prepare_attributes_for_rest_api(attributes, self)
|
795
816
|
end
|
796
817
|
|
818
|
+
def copyable_attributes
|
819
|
+
workspace.api_request(:get, "/objs/#{id}")
|
820
|
+
.except(*GENERATED_ATTRIBUTES)
|
821
|
+
.except(*UNIQ_ATTRIBUTES)
|
822
|
+
end
|
823
|
+
|
824
|
+
def assert_revertable
|
825
|
+
workspace.assert_revertable
|
826
|
+
raise "revert not supported for binary objs" if binary?
|
827
|
+
if modification == Modification::NEW || modification == Modification::DELETED
|
828
|
+
raise ScrivitoError, "cannot revert changes, since obj is #{modification}."
|
829
|
+
end
|
830
|
+
end
|
831
|
+
|
797
832
|
class << self
|
798
833
|
def restore(obj_id)
|
799
834
|
Workspace.current.assert_revertable
|
800
|
-
|
801
835
|
base_revision_path = "revisions/#{Workspace.current.base_revision_id}/objs/#{obj_id}"
|
802
|
-
obj_attributes = CmsRestApi.get(base_revision_path).
|
803
|
-
|
804
|
-
end
|
805
|
-
|
806
|
-
def cms_rest_api_path(workspace = Workspace.current)
|
807
|
-
"workspaces/#{workspace.id}/objs"
|
836
|
+
obj_attributes = CmsRestApi.get(base_revision_path).merge('_id' => obj_id)
|
837
|
+
Workspace.current.api_request(:post, '/objs', obj: obj_attributes)
|
808
838
|
end
|
809
839
|
|
810
840
|
def prepare_attributes_for_rest_api(attributes, obj)
|
@@ -99,17 +99,6 @@ class BasicWidget
|
|
99
99
|
@attributes_to_be_saved.nil?
|
100
100
|
end
|
101
101
|
|
102
|
-
def obj_class_name
|
103
|
-
data_from_cms.value_of('_obj_class')
|
104
|
-
end
|
105
|
-
|
106
|
-
# Returns the {Scrivito::ObjClass} of this widget.
|
107
|
-
# @return [Scrivito::ObjClass]
|
108
|
-
# @api public
|
109
|
-
def obj_class
|
110
|
-
ObjClass.find(obj_class_name)
|
111
|
-
end
|
112
|
-
|
113
102
|
def ==(other)
|
114
103
|
other.respond_to?(:obj) && obj == other.obj && other.respond_to?(:id) && id == other.id
|
115
104
|
end
|
@@ -7,46 +7,33 @@ class ClientConfig < Struct.new(:obj, :editing_context, :lookup_context, :resour
|
|
7
7
|
|
8
8
|
def to_json
|
9
9
|
config = {}
|
10
|
-
config[:editing_context]
|
10
|
+
config[:editing_context] = editing_context_config
|
11
|
+
config[:i18n] = i18n_config
|
12
|
+
config[:obj] = obj_config
|
13
|
+
config[:resource_dialog] = resource_dialog_config
|
14
|
+
config[:user] = user_config
|
11
15
|
config[:user_permissions] = user_permissions_config
|
12
|
-
config[:i18n] = i18n_config
|
13
|
-
config[:obj] = obj_config
|
14
|
-
config[:resource_dialog] = resource_dialog_config
|
15
16
|
config.to_json
|
16
17
|
end
|
17
18
|
|
18
19
|
private
|
19
20
|
|
20
|
-
def user_permissions_config
|
21
|
-
{
|
22
|
-
publish_workspace: can_publish_workspace
|
23
|
-
}
|
24
|
-
end
|
25
|
-
|
26
21
|
def editing_context_config
|
27
22
|
{
|
28
23
|
display_mode: editing_context.display_mode,
|
29
24
|
|
30
25
|
selected_workspace: {
|
31
|
-
id:
|
32
|
-
title:
|
26
|
+
id: selected_workspace.id,
|
27
|
+
title: selected_workspace.title
|
33
28
|
},
|
34
29
|
|
35
30
|
visible_workspace: {
|
36
|
-
id:
|
37
|
-
title:
|
31
|
+
id: visible_workspace.id,
|
32
|
+
title: visible_workspace.title
|
38
33
|
}
|
39
34
|
}
|
40
35
|
end
|
41
36
|
|
42
|
-
def can_publish_workspace
|
43
|
-
if editing_context.editor
|
44
|
-
editing_context.editor.able_to?(:publish_workspace)
|
45
|
-
else
|
46
|
-
false
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
37
|
def i18n_config
|
51
38
|
{locale: I18n.locale}
|
52
39
|
end
|
@@ -61,6 +48,7 @@ class ClientConfig < Struct.new(:obj, :editing_context, :lookup_context, :resour
|
|
61
48
|
has_conflict: obj.has_conflict?,
|
62
49
|
has_details_view: obj_has_details_view?,
|
63
50
|
modification: modification(obj),
|
51
|
+
restriction_messages: editor.restriction_messages_for(obj),
|
64
52
|
}
|
65
53
|
}
|
66
54
|
else
|
@@ -77,7 +65,7 @@ class ClientConfig < Struct.new(:obj, :editing_context, :lookup_context, :resour
|
|
77
65
|
def resource_dialog_config
|
78
66
|
if resource
|
79
67
|
{
|
80
|
-
obj: Configuration.obj_formats.fetch('_default').call(resource),
|
68
|
+
obj: Configuration.obj_formats.fetch('_default').call(resource, editor),
|
81
69
|
return_to: return_to,
|
82
70
|
}
|
83
71
|
else
|
@@ -85,13 +73,40 @@ class ClientConfig < Struct.new(:obj, :editing_context, :lookup_context, :resour
|
|
85
73
|
end
|
86
74
|
end
|
87
75
|
|
76
|
+
def user_permissions_config
|
77
|
+
{
|
78
|
+
publish_workspace: editor.can?(:publish, selected_workspace)
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
def user_config
|
83
|
+
{
|
84
|
+
current: {
|
85
|
+
id: editor.id
|
86
|
+
}
|
87
|
+
}
|
88
|
+
end
|
89
|
+
|
88
90
|
def modification(obj_or_resource)
|
89
|
-
|
90
|
-
|
91
|
+
comparison = editing_context.comparison
|
92
|
+
if comparison.active?
|
93
|
+
comparison.modification(obj_or_resource)
|
91
94
|
else
|
92
95
|
obj_or_resource.modification
|
93
96
|
end
|
94
97
|
end
|
98
|
+
|
99
|
+
def editor
|
100
|
+
editing_context.editor
|
101
|
+
end
|
102
|
+
|
103
|
+
def selected_workspace
|
104
|
+
editing_context.selected_workspace
|
105
|
+
end
|
106
|
+
|
107
|
+
def visible_workspace
|
108
|
+
editing_context.visible_workspace
|
109
|
+
end
|
95
110
|
end
|
96
111
|
|
97
112
|
end
|