scrivito_sdk 0.17.0 → 0.18.0
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/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
|