scrivito_sdk 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (123) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +5 -0
  3. data/README +6 -0
  4. data/app/controllers/cms_controller.rb +7 -0
  5. data/app/controllers/scrivito/blobs_controller.rb +10 -0
  6. data/app/controllers/scrivito/default_cms_controller.rb +61 -0
  7. data/app/controllers/scrivito/objs_controller.rb +200 -0
  8. data/app/controllers/scrivito/tasks_controller.rb +11 -0
  9. data/app/controllers/scrivito/webservice_controller.rb +36 -0
  10. data/app/controllers/scrivito/workspaces_controller.rb +41 -0
  11. data/app/helpers/cms_helper.rb +7 -0
  12. data/app/helpers/cms_routing_helper.rb +7 -0
  13. data/app/helpers/scrivito/cms_asset_helper.rb +103 -0
  14. data/app/helpers/scrivito/cms_tag_helper.rb +231 -0
  15. data/app/helpers/scrivito/default_cms_helper.rb +21 -0
  16. data/app/helpers/scrivito/default_cms_routing_helper.rb +130 -0
  17. data/app/helpers/scrivito/display_helper.rb +71 -0
  18. data/app/helpers/scrivito/editing_helper.rb +26 -0
  19. data/app/helpers/scrivito/layout_helper.rb +28 -0
  20. data/app/models/named_link.rb +2 -0
  21. data/app/views/cms/_index.html.erb +7 -0
  22. data/app/views/cms/index.html.erb +1 -0
  23. data/app/views/scrivito/_editing_javascript.html.erb +7 -0
  24. data/app/views/scrivito/default_cms/show_widget.html.erb +1 -0
  25. data/app/views/scrivito/objs/copy_widget.html.erb +1 -0
  26. data/app/views/scrivito/objs/create_widget.html.erb +1 -0
  27. data/app/views/scrivito/widget_thumbnail.html.erb +9 -0
  28. data/config/ca-bundle.crt +3509 -0
  29. data/config/cms_routes.rb +17 -0
  30. data/config/locales/de.scrivito.errors.yml +7 -0
  31. data/config/locales/de.scrivito.lib.yml +6 -0
  32. data/config/locales/de.scrivito.models.yml +6 -0
  33. data/config/locales/en.scrivito.errors.yml +7 -0
  34. data/config/locales/en.scrivito.lib.yml +6 -0
  35. data/config/locales/en.scrivito.models.yml +6 -0
  36. data/config/routes.rb +37 -0
  37. data/lib/assets/images/180x120.gif +0 -0
  38. data/lib/assets/images/scrivito/image_placeholder.png +0 -0
  39. data/lib/assets/javascripts/scrivito_editing.js +14642 -0
  40. data/lib/assets/stylesheets/scrivito.css +180 -0
  41. data/lib/assets/stylesheets/scrivito_editing.css +2213 -0
  42. data/lib/generators/cms/migration/USAGE +9 -0
  43. data/lib/generators/cms/migration/migration_generator.rb +21 -0
  44. data/lib/generators/cms/migration/templates/migration.erb +10 -0
  45. data/lib/obj.rb +3 -0
  46. data/lib/scrivito/access_denied.rb +6 -0
  47. data/lib/scrivito/attribute_content.rb +194 -0
  48. data/lib/scrivito/backend_error.rb +4 -0
  49. data/lib/scrivito/basic_obj.rb +840 -0
  50. data/lib/scrivito/basic_widget.rb +238 -0
  51. data/lib/scrivito/blob.rb +48 -0
  52. data/lib/scrivito/cache.rb +41 -0
  53. data/lib/scrivito/cache_garbage_collector.rb +83 -0
  54. data/lib/scrivito/cache_middleware.rb +17 -0
  55. data/lib/scrivito/client_config.rb +62 -0
  56. data/lib/scrivito/client_error.rb +12 -0
  57. data/lib/scrivito/cms_accessible.rb +30 -0
  58. data/lib/scrivito/cms_backend.rb +238 -0
  59. data/lib/scrivito/cms_cache_storage.rb +51 -0
  60. data/lib/scrivito/cms_dispatch_controller.rb +46 -0
  61. data/lib/scrivito/cms_env.rb +63 -0
  62. data/lib/scrivito/cms_field_tag.rb +112 -0
  63. data/lib/scrivito/cms_rest_api.rb +151 -0
  64. data/lib/scrivito/cms_rest_api/attribute_serializer.rb +98 -0
  65. data/lib/scrivito/cms_rest_api/blob_uploader.rb +18 -0
  66. data/lib/scrivito/cms_rest_api/widget_extractor.rb +42 -0
  67. data/lib/scrivito/cms_test_request.rb +23 -0
  68. data/lib/scrivito/communication_error.rb +17 -0
  69. data/lib/scrivito/comparison.rb +67 -0
  70. data/lib/scrivito/configuration.rb +221 -0
  71. data/lib/scrivito/connection_manager.rb +100 -0
  72. data/lib/scrivito/content_conversion.rb +43 -0
  73. data/lib/scrivito/content_service.rb +118 -0
  74. data/lib/scrivito/content_state.rb +109 -0
  75. data/lib/scrivito/content_state_caching.rb +47 -0
  76. data/lib/scrivito/content_state_visitor.rb +19 -0
  77. data/lib/scrivito/controller_runtime.rb +35 -0
  78. data/lib/scrivito/date_attribute.rb +16 -0
  79. data/lib/scrivito/deprecation.rb +21 -0
  80. data/lib/scrivito/diff.rb +110 -0
  81. data/lib/scrivito/editing_context.rb +106 -0
  82. data/lib/scrivito/editing_context_middleware.rb +60 -0
  83. data/lib/scrivito/engine.rb +65 -0
  84. data/lib/scrivito/errors.rb +11 -0
  85. data/lib/scrivito/html_string.rb +18 -0
  86. data/lib/scrivito/link.rb +187 -0
  87. data/lib/scrivito/link_parser.rb +81 -0
  88. data/lib/scrivito/log_subscriber.rb +29 -0
  89. data/lib/scrivito/migration.rb +2 -0
  90. data/lib/scrivito/migrations.rb +12 -0
  91. data/lib/scrivito/migrations/cms_backend.rb +94 -0
  92. data/lib/scrivito/migrations/installer.rb +45 -0
  93. data/lib/scrivito/migrations/migration.rb +93 -0
  94. data/lib/scrivito/migrations/migration_dsl.rb +143 -0
  95. data/lib/scrivito/migrations/migration_store.rb +23 -0
  96. data/lib/scrivito/migrations/migrator.rb +135 -0
  97. data/lib/scrivito/migrations/workspace_lock.rb +39 -0
  98. data/lib/scrivito/model_identity.rb +13 -0
  99. data/lib/scrivito/modification.rb +8 -0
  100. data/lib/scrivito/named_link.rb +75 -0
  101. data/lib/scrivito/network_error.rb +11 -0
  102. data/lib/scrivito/obj_data.rb +140 -0
  103. data/lib/scrivito/obj_data_from_hash.rb +31 -0
  104. data/lib/scrivito/obj_data_from_service.rb +84 -0
  105. data/lib/scrivito/obj_params_parser.rb +61 -0
  106. data/lib/scrivito/obj_search_builder.rb +62 -0
  107. data/lib/scrivito/obj_search_enumerator.rb +374 -0
  108. data/lib/scrivito/rate_limit_exceeded.rb +5 -0
  109. data/lib/scrivito/revision.rb +9 -0
  110. data/lib/scrivito/string_tagging.rb +18 -0
  111. data/lib/scrivito/text_link.rb +52 -0
  112. data/lib/scrivito/text_link_conversion.rb +52 -0
  113. data/lib/scrivito/type_computer.rb +34 -0
  114. data/lib/scrivito/widget_field_params.rb +61 -0
  115. data/lib/scrivito/widget_garbage_collection.rb +97 -0
  116. data/lib/scrivito/workspace.rb +222 -0
  117. data/lib/scrivito/workspace_data_from_service.rb +80 -0
  118. data/lib/scrivito/workspace_selection_middleware.rb +23 -0
  119. data/lib/scrivito_sdk.rb +19 -0
  120. data/lib/tasks/cache.rake +12 -0
  121. data/lib/tasks/migration.rake +35 -0
  122. data/lib/widget.rb +3 -0
  123. metadata +291 -0
@@ -0,0 +1,238 @@
1
+ module Scrivito
2
+
3
+ # The CMS widget class
4
+ # @api public
5
+ class BasicWidget
6
+ include AttributeContent
7
+
8
+ def self.type_computer
9
+ @_type_computer ||= TypeComputer.new(Scrivito::BasicWidget, ::Widget)
10
+ end
11
+
12
+ def self.reset_type_computer!
13
+ @_type_computer = nil
14
+ end
15
+
16
+ attr_accessor :container, :container_field_name
17
+
18
+ attr_writer :obj, :id
19
+
20
+ attr_reader :attributes_to_be_saved
21
+
22
+ delegate :widget_from_pool, to: :obj
23
+
24
+ # Create a new Widget.
25
+ # The new Widget must be stored inside a container (i.e. an Obj or another Widget)
26
+ # before it can be used.
27
+ #
28
+ # See {BasicObj.create Obj.create} for a detailed overview of how to set attributes.
29
+ # @api public
30
+ # @param [Hash] attributes
31
+ #
32
+ # @example Create a widget using a subclass
33
+ # # you can create widgets by explicitly providing the attribute `_obj_class`
34
+ # new_widget = Widget.new(_obj_class: "MyWidget")
35
+ # # but it is better to simply use the constructor of a subclass
36
+ # new_widget = MyWidget.new
37
+ #
38
+ # @example Create a widget and store it inside an Obj
39
+ # # create the widget
40
+ # new_widget = Widget.new(_obj_class: "MyWidget")
41
+ # # store it inside an obj
42
+ # my_obj(my_widget_field: [new_widget])
43
+ #
44
+ def initialize(attributes = {})
45
+ @attributes_to_be_saved = self.class.with_default_obj_class(attributes)
46
+ end
47
+
48
+ def revision
49
+ obj.revision
50
+ end
51
+
52
+ # Update the attributes of this Widget
53
+ #
54
+ # See {BasicObj.create Obj.create} for a detailed overview of how to set attributes
55
+ # @api public
56
+ # @param [Hash] attributes
57
+ def update(attributes)
58
+ widget_hash = CmsRestApi::WidgetExtractor.call(attributes, obj)
59
+ widget_pool = CmsRestApi::AttributeSerializer.generate_widget_pool_changes(widget_hash)
60
+ .merge(id => CmsRestApi::AttributeSerializer.convert(attributes))
61
+
62
+ obj_attributes = {'_widget_pool' => widget_pool}
63
+
64
+ widget_gc = WidgetGarbageCollection.new(obj, {self => attributes}.merge(widget_hash))
65
+ widget_gc.widgets_to_delete.each { |widget| widget_pool[widget.id] = nil }
66
+
67
+ CmsRestApi.put("workspaces/#{Workspace.current.id}/objs/#{obj.id}", obj: obj_attributes)
68
+
69
+ CmsRestApi::WidgetExtractor.notify_persisted_widgets(self.obj, widget_hash)
70
+
71
+ Workspace.reload
72
+
73
+ reload
74
+ end
75
+
76
+ # Destroys the {BasicWidget Widget} in the current {Workspace}
77
+ # @api public
78
+ def destroy
79
+ new_widget_list = container[container_field_name] - [self]
80
+ container.update(container_field_name => new_widget_list)
81
+ end
82
+
83
+ def id
84
+ if @id
85
+ @id
86
+ else
87
+ raise_not_persisted_errror
88
+ end
89
+ end
90
+
91
+ def persisted?
92
+ @attributes_to_be_saved.nil?
93
+ end
94
+
95
+ def obj_class_name
96
+ data_from_cms.value_of('_obj_class')
97
+ end
98
+
99
+ def obj_class
100
+ raise ScrivitoError, "BasicWidget#obj_class is no longer available"+
101
+ ", please use BasicWidget#obj_class_name instead."
102
+ end
103
+
104
+ def ==(other)
105
+ other.respond_to?(:obj) && obj == other.obj && other.respond_to?(:id) && id == other.id
106
+ end
107
+
108
+ def eql?(other)
109
+ self == other
110
+ end
111
+
112
+ # Reverts content changes of this widget. Widget attributes of this widget are not effected.
113
+ # After calling this method it's as if this widget has been never modified in the current working copy.
114
+ # This method does not work with +new+ or +deleted+ widgets.
115
+ # This method also does also not work for the +published+ workspace or the +rtc+ working copy.
116
+ def revert
117
+ Workspace.current.assert_revertable
118
+
119
+ case modification
120
+ when Modification::UNMODIFIED
121
+ # do nothing
122
+ when Modification::EDITED
123
+ previous_obj_content =
124
+ CmsRestApi.get("revisions/#{Workspace.current.base_revision_id}/objs/#{obj.id}")
125
+ previous_widget_content = previous_obj_content["_widget_pool"]["#{id}"]
126
+ previous_widget_content.delete_if do |attribute_name, _|
127
+ type_of_attribute(attribute_name) == "widget"
128
+ end
129
+ CmsRestApi.put("workspaces/#{Workspace.current.id}/objs/#{obj.id}",
130
+ { obj: {_widget_pool: {id => previous_widget_content}} })
131
+ else
132
+ raise ScrivitoError, "cannot revert changes, since widget is #{modification}."
133
+ end
134
+ end
135
+
136
+ def in_revision(revision)
137
+ obj_in_revision = obj.in_revision(revision)
138
+ obj_in_revision && obj_in_revision.widget_from_pool(id)
139
+ end
140
+
141
+ def new?(revision=Workspace.current.base_revision)
142
+ obj.new?(revision) || cms_data_for_revision(revision).nil?
143
+ end
144
+
145
+ def deleted?(revision=Workspace.current.base_revision)
146
+ obj.deleted?(revision)
147
+ end
148
+
149
+ def modification(revision=Workspace.current.base_revision)
150
+ if deleted?(revision)
151
+ Modification::DELETED
152
+ elsif new?(revision)
153
+ Modification::NEW
154
+ else
155
+ if data_from_cms == cms_data_for_revision(revision)
156
+ Modification::UNMODIFIED
157
+ else
158
+ Modification::EDITED
159
+ end
160
+ end
161
+ end
162
+
163
+ def hash
164
+ if @obj && @id
165
+ (id + obj.id).hash
166
+ else
167
+ super
168
+ end
169
+ end
170
+
171
+ # returns the entity ({BasicObj} or {BasicWidget}) that references this widget
172
+ # @api public
173
+ def container
174
+ @container || cache_container_and_field_name_for_widget.first
175
+ end
176
+
177
+ # returns the name of the widget field that references this widget
178
+ # @api public
179
+ def container_field_name
180
+ @container_field_name || cache_container_and_field_name_for_widget.second
181
+ end
182
+
183
+ def inspect
184
+ if @id
185
+ if @obj
186
+ "<#{self.class} id=\"#{id}\" obj.id=\"#{obj.id}\">"
187
+ else
188
+ "<#{self.class} id=\"#{id}\">"
189
+ end
190
+ else
191
+ "<#{self.class}>"
192
+ end
193
+ end
194
+
195
+ def obj
196
+ if @obj
197
+ @obj
198
+ else
199
+ raise_not_persisted_errror
200
+ end
201
+ end
202
+
203
+ def forget_unsaved_attribtues
204
+ @attributes_to_be_saved = nil
205
+ reload
206
+ end
207
+
208
+ def reload
209
+ obj.reload
210
+
211
+ update_proc = -> { obj.widget_data_from_pool(id) }
212
+ update_data(update_proc)
213
+ end
214
+
215
+ private
216
+
217
+ def data_from_cms
218
+ if persisted?
219
+ super
220
+ else
221
+ raise_not_persisted_errror
222
+ end
223
+ end
224
+
225
+ def raise_not_persisted_errror
226
+ raise ScrivitoError.new('Can not access a new widget before it has been saved')
227
+ end
228
+
229
+ def cms_data_for_revision(revision)
230
+ obj.widget_data_for_revision(id, revision)
231
+ end
232
+
233
+ def cache_container_and_field_name_for_widget
234
+ @cache_container_and_field_name_for_widget ||= obj.container_and_field_name_for_widget(id)
235
+ end
236
+ end
237
+
238
+ end
@@ -0,0 +1,48 @@
1
+ module Scrivito
2
+
3
+ # TODO Caching:
4
+ # unlimited urls: ewig
5
+ # limited urls: zeitlimit - x (x fuer mindestverwendbarkeitszeit)
6
+ class Blob
7
+
8
+ class << self
9
+
10
+ def configure(config)
11
+ end
12
+
13
+ def find(id)
14
+ new(id)
15
+ end
16
+
17
+ end
18
+
19
+ attr_reader :id
20
+
21
+ def initialize(id)
22
+ @id = id
23
+ end
24
+
25
+ def url
26
+ raw_data["url"]
27
+ end
28
+
29
+ def content_type
30
+ meta_data[:content_type]
31
+ end
32
+
33
+ def length
34
+ meta_data[:content_length].to_i
35
+ end
36
+
37
+ private
38
+
39
+ def raw_data
40
+ @raw_data ||= CmsBackend.instance.find_blob_data_by_id(id)
41
+ end
42
+
43
+ def meta_data
44
+ @meta_data ||= RestClient.head(raw_data["meta_url"]).headers
45
+ end
46
+ end
47
+
48
+ end
@@ -0,0 +1,41 @@
1
+ module Scrivito
2
+
3
+ class Cache
4
+ attr_reader :cache_prefix, :fallback_backend
5
+
6
+ def initialize(options = {})
7
+ @fallback_backend, @cache_prefix = options[:fallback_backend], options[:cache_prefix]
8
+ @cache = {}
9
+ end
10
+
11
+ def read(key)
12
+ value = @cache[key]
13
+ if !value && @fallback_backend
14
+ value = @fallback_backend.read(cache_key_for_fallback_backend(key))
15
+ @cache[key] = value
16
+ end
17
+ value
18
+ end
19
+
20
+ def write(key, value, options={})
21
+ @fallback_backend.write(cache_key_for_fallback_backend(key), value, options) if @fallback_backend
22
+ @cache[key] = value
23
+ end
24
+
25
+ def fetch(key, &block)
26
+ read(key) || block.call.tap { |value| write(key, value) }
27
+ end
28
+
29
+ def clear
30
+ @cache = {}
31
+ end
32
+
33
+ private
34
+
35
+ def cache_key_for_fallback_backend(key)
36
+ @cache_prefix ? "#{@cache_prefix}/#{key}" : key
37
+ end
38
+ end
39
+
40
+ end # module Scrivito
41
+
@@ -0,0 +1,83 @@
1
+ module Scrivito
2
+ # The CacheGarbageCollector allows to remove old cache files from the Scrivito cache
3
+ # storage path. By default, a maximum of +DEFAULT_MAX_FILE_INODES+ file inodes that were accessed
4
+ # lately are kept in the cache directory. All other files and empty directories are removed. Log
5
+ # output is written to the Rails environment log file.
6
+ #
7
+ # @example Running the garbage collector with default settings
8
+ #
9
+ # bundle exec rake scrivito:cache:gc
10
+ #
11
+ # @example Changing the maximum number of file inodes
12
+ #
13
+ # bundle exec rake scrivito:cache:gc MAX_FILE_INODES=5000
14
+ class CacheGarbageCollector
15
+ DEFAULT_MAX_FILE_INODES = 100000
16
+
17
+ attr_accessor :max_file_inodes
18
+
19
+ def initialize(max_file_inodes = nil)
20
+ @max_file_inodes = (max_file_inodes || DEFAULT_MAX_FILE_INODES).to_i
21
+ end
22
+
23
+ def run_gc
24
+ announce "Cap number of cache file inodes at #{max_file_inodes} in #{cache_path}."
25
+
26
+ directories = []
27
+ file_inode_count = 0
28
+ delta_disk_space = 0
29
+ delta_count = 0
30
+
31
+ cache_entries.each do |cache_entry|
32
+ begin
33
+ stat = File.stat(cache_entry)
34
+
35
+ if stat.file?
36
+ file_inode_count += 1
37
+ next if file_inode_count <= max_file_inodes
38
+
39
+ File.delete(cache_entry)
40
+
41
+ delta_disk_space += stat.size
42
+ delta_count += 1
43
+ elsif stat.directory?
44
+ directories << cache_entry
45
+ end
46
+ rescue Errno::ENOENT
47
+ end
48
+ end
49
+
50
+ directories.sort.reverse_each do |dirname|
51
+ begin
52
+ Dir.rmdir(dirname)
53
+ delta_count += 1
54
+ rescue Errno::ENOTEMPTY, Errno::ENOENT
55
+ end
56
+ end
57
+
58
+ announce "Done. Removed #{delta_count} entries. Saved at least #{delta_disk_space} Bytes."
59
+ end
60
+
61
+ def cache_path
62
+ CmsCacheStorage.backend_cache.cache_path
63
+ end
64
+
65
+ def cache_entries
66
+ Dir["#{cache_path}/**/*"].sort_by do |file|
67
+ begin
68
+ -File.atime(file).to_i
69
+ rescue Errno::ENOENT
70
+ # If such file or directory does not exist, sort it as if the last
71
+ # access time is very long ago.
72
+ 0
73
+ end
74
+ end
75
+ end
76
+
77
+ def announce(message)
78
+ message = "[#{Time.now.iso8601}] #{message}"
79
+
80
+ Rails.logger.info(message)
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,17 @@
1
+ module Scrivito
2
+
3
+ class CacheMiddleware
4
+ def initialize(app)
5
+ @app = app
6
+ end
7
+
8
+ def call(env)
9
+ CmsBackend.instance.begin_caching
10
+ @app.call(env)
11
+ ensure
12
+ CmsBackend.instance.end_caching
13
+ Workspace.cache.clear
14
+ end
15
+ end
16
+
17
+ end # module Scrivito
@@ -0,0 +1,62 @@
1
+ module Scrivito
2
+
3
+ class ClientConfig < Struct.new(:obj, :editing_context, :lookup_context)
4
+ def to_json
5
+ config = {}
6
+ config[:editing_context] = editing_context_config
7
+ config[:i18n] = i18n_config
8
+ config[:obj] = obj_config
9
+ config.to_json
10
+ end
11
+
12
+ private
13
+
14
+ def editing_context_config
15
+ {
16
+ display_mode: editing_context.display_mode,
17
+
18
+ selected_workspace: {
19
+ id: editing_context.selected_workspace.id,
20
+ title: editing_context.selected_workspace.title
21
+ },
22
+
23
+ visible_workspace: {
24
+ id: editing_context.visible_workspace.id,
25
+ title: editing_context.visible_workspace.title
26
+ }
27
+ }
28
+ end
29
+
30
+ def i18n_config
31
+ {locale: I18n.locale}
32
+ end
33
+
34
+ def obj_config
35
+ if obj
36
+ {current_page: {
37
+ id: obj.id,
38
+ obj_class_name: obj.obj_class_name,
39
+ has_children: obj.children.any?,
40
+ has_conflict: obj.has_conflict?,
41
+ has_details_view: obj_has_details_view?,
42
+ modification: obj_modification,
43
+ }}
44
+ end
45
+ end
46
+
47
+ def obj_has_details_view?
48
+ lookup_context.find(obj.details_view_path).present?
49
+ rescue ActionView::MissingTemplate
50
+ false
51
+ end
52
+
53
+ def obj_modification
54
+ if editing_context.comparison.active?
55
+ editing_context.comparison.modification(obj)
56
+ else
57
+ obj.modification
58
+ end
59
+ end
60
+ end
61
+
62
+ end