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,5 @@
1
+ module Scrivito
2
+ class RateLimitExceeded < CommunicationError
3
+
4
+ end
5
+ end
@@ -0,0 +1,9 @@
1
+ module Scrivito
2
+
3
+ class Revision < Struct.new(:id, :content_state, :workspace)
4
+ def initialize(options)
5
+ super(*options.values_at(:id, :content_state, :workspace))
6
+ end
7
+ end
8
+
9
+ end
@@ -0,0 +1,18 @@
1
+ module Scrivito
2
+
3
+ # adds flavour to strings
4
+ module StringTagging
5
+
6
+ # html flavour
7
+ def self.tag_as_html(string)
8
+ unless string.blank?
9
+ class << string
10
+ include Scrivito::HtmlString
11
+ end
12
+ end
13
+ string
14
+ end
15
+
16
+ end
17
+
18
+ end
@@ -0,0 +1,52 @@
1
+ module Scrivito
2
+
3
+ class TextLink
4
+
5
+ def initialize(link_data)
6
+ @data = link_data
7
+ end
8
+
9
+ def query_and_fragment
10
+ query = @data["query"]
11
+ fragment = @data["fragment"]
12
+
13
+ str = ''
14
+ str << "?#{query}" if query.present?
15
+ str << "##{fragment}" if fragment.present?
16
+ str
17
+ end
18
+
19
+ def internal?
20
+ !url
21
+ end
22
+
23
+ def external_url
24
+ url =~ /(external:)?(.*)/
25
+ $2
26
+ end
27
+
28
+ def obj_id
29
+ @data["destination"]
30
+ end
31
+
32
+ def html_attribute_snippet
33
+ tag_name = @data["tag_name"]
34
+ title = @data["title"]
35
+ target = @data["target"]
36
+
37
+ parts = []
38
+ parts << %{alt="#{title}"} if tag_name == 'img' || tag_name == 'input'
39
+ parts << %{title="#{title}"} if (tag_name == 'a' || tag_name == 'link') && title.present?
40
+ parts << %{target="#{target}"} if target.present?
41
+ parts.join(' ')
42
+ end
43
+
44
+ private
45
+
46
+ def url
47
+ @data["url"]
48
+ end
49
+
50
+ end
51
+
52
+ end
@@ -0,0 +1,52 @@
1
+ module Scrivito
2
+
3
+ class TextLinkConversion
4
+
5
+ def initialize(text_links)
6
+ @text_links = text_links
7
+ end
8
+
9
+ def convert(html)
10
+ return html unless @text_links
11
+
12
+ if html
13
+ html.gsub(%r{<?\binternallink:(\d+)\b>?(['"]?)}) do
14
+ link = @text_links[$1]
15
+ postfix = $2
16
+ if link
17
+ attach_snippet("#{convert_link(link)}#{postfix}", link)
18
+ else
19
+ "#__text_link_missing#{postfix}"
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def convert_link(link)
28
+ link.internal? ? convert_internal_link(link) : convert_external_link(link)
29
+ end
30
+
31
+ def attach_snippet(text, link)
32
+ snippet = link.html_attribute_snippet
33
+ if snippet.present?
34
+ "#{text} #{snippet}"
35
+ else
36
+ text
37
+ end
38
+ end
39
+
40
+ def convert_internal_link(link)
41
+ if link.obj_id
42
+ "objid:#{link.obj_id}#{link.query_and_fragment}"
43
+ end
44
+ end
45
+
46
+ def convert_external_link(link)
47
+ link.external_url
48
+ end
49
+
50
+ end
51
+
52
+ end
@@ -0,0 +1,34 @@
1
+ module Scrivito
2
+
3
+ class TypeComputer
4
+ def initialize(base_class, fallback_class)
5
+ @base_class, @fallback_class = base_class, fallback_class
6
+ @type_cache = {}
7
+ end
8
+
9
+ def compute_type(obj_class)
10
+ @type_cache.fetch(obj_class) { calculate_type(obj_class) }
11
+ end
12
+
13
+ def special_class?(klass)
14
+ klass == @base_class || klass == @fallback_class
15
+ end
16
+
17
+ private
18
+
19
+ def calculate_type(obj_class)
20
+ load_class(obj_class) || @fallback_class
21
+ end
22
+
23
+ def load_class(obj_class)
24
+ klass = obj_class.constantize
25
+ klass if inherits_from_base?(klass)
26
+ rescue NameError
27
+ end
28
+
29
+ def inherits_from_base?(klass)
30
+ klass.ancestors.include?(@base_class) && klass != @base_class
31
+ end
32
+ end
33
+
34
+ end
@@ -0,0 +1,61 @@
1
+ module Scrivito
2
+
3
+ class WidgetFieldParams
4
+ class UnknownWidgetAction < ScrivitoError
5
+ end
6
+
7
+ attr_reader :pool_params
8
+
9
+ def initialize
10
+ @pool_params = {}
11
+ end
12
+
13
+ def convert(params)
14
+ widget_ids = []
15
+
16
+ params.each do |widget_id_or_spec|
17
+ if widget_id_or_spec.is_a?(Hash)
18
+ action, spec = parse_action_and_spec(widget_id_or_spec)
19
+ case action
20
+ when 'create' then widget_ids << create_widget(spec)
21
+ when 'copy' then widget_ids << copy_widget(*spec.values_at(:src_obj_id, :src_widget_id))
22
+ else raise UnknownWidgetAction
23
+ end
24
+ else
25
+ widget_ids << widget_id_or_spec
26
+ end
27
+ end
28
+
29
+ {'list' => widget_ids.map { |widget_id| {'widget' => widget_id} }}
30
+ end
31
+
32
+ private
33
+
34
+ def parse_action_and_spec(params)
35
+ action, spec = params.flatten
36
+ [action.to_s, spec.with_indifferent_access]
37
+ end
38
+
39
+ def create_widget(params)
40
+ widget_pool_id = BasicObj.generate_widget_pool_id
41
+ # At the moment the 'create' action supports only the '_obj_class' field.
42
+ @pool_params[widget_pool_id] = {'_obj_class' => params['_obj_class']}
43
+ widget_pool_id
44
+ end
45
+
46
+ def copy_widget(src_obj_id, src_widget_id)
47
+ src_obj_content = CmsRestApi.get("workspaces/#{Workspace.current.id}/objs/#{src_obj_id}")
48
+ widget_params = src_obj_content['_widget_pool']["#{src_widget_id}"]
49
+
50
+ raise ResourceNotFound, "could not find widget with id #{src_widget_id}" unless widget_params
51
+
52
+ src_widget = BasicObj.find(src_obj_id).widget_from_pool(src_widget_id)
53
+ widget_params.delete_if { |field_name, _| src_widget.type_of_attribute(field_name) == 'widget' }
54
+
55
+ widget_pool_id = BasicObj.generate_widget_pool_id
56
+ @pool_params[widget_pool_id] = widget_params
57
+ widget_pool_id
58
+ end
59
+ end
60
+
61
+ end
@@ -0,0 +1,97 @@
1
+ module Scrivito
2
+
3
+ class WidgetGarbageCollection
4
+
5
+ # @param [BasicObj] obj the Obj that the update applies to
6
+ # @param [Hash] update the keys are content objects (Obj or Widget)
7
+ # and the values are their update params.
8
+ # update params are accepted in the same format as accepted by Obj.create or Obj#update.
9
+ # each content object must either be the obj itself or a widget that lives inside the obj.
10
+ def initialize(obj, update)
11
+ if update.blank?
12
+ raise ScrivitoError, "blank update!"
13
+ end
14
+
15
+ update.keys.each do |content|
16
+ if content.is_a?(BasicObj)
17
+ raise "invalid obj given" unless content == obj
18
+ elsif content.is_a?(BasicWidget)
19
+ raise "invalid widget given" unless content.obj == obj
20
+ else
21
+ raise "invalid key"
22
+ end
23
+ end
24
+
25
+ @update = update
26
+ @obj = obj
27
+ end
28
+
29
+ # @return [Array] list containing the widgets that are no longer needed after the update.
30
+ def widgets_to_delete
31
+ dereferenced = []
32
+ newreferenced = []
33
+
34
+ @update.each do |content, params|
35
+ (deref, newref) = changed_references_from(content, params)
36
+ dereferenced += deref
37
+ newreferenced += newref
38
+ end
39
+
40
+ deleted_by_dereference = dereferenced - newreferenced
41
+
42
+ recursivly_deleted = []
43
+ deleted_by_dereference.each do |widget|
44
+ recursivly_deleted += all_widgets_contained_in_except(widget, newreferenced)
45
+ end
46
+
47
+ # we dont want to slow dont every request by scanning for orphans, so
48
+ # we only do this when widgets are being deleted
49
+ if deleted_by_dereference.present?
50
+ orphans = @obj.all_widgets_from_pool - @obj.contained_widgets - newreferenced
51
+ else
52
+ orphans = []
53
+ end
54
+
55
+ deleted_by_dereference + recursivly_deleted + orphans
56
+ end
57
+
58
+ private
59
+
60
+ def all_widgets_contained_in_except(widget, exceptions)
61
+ widget_children = widget.referenced_widgets - exceptions
62
+
63
+ widget_descendants = widget_children.map do |child|
64
+ all_widgets_contained_in_except(child, exceptions)
65
+ end.flatten
66
+
67
+ widget_children + widget_descendants
68
+ end
69
+
70
+ def changed_references_from(content, params)
71
+ dereferenced_ids = []
72
+ newreferenced_ids = []
73
+
74
+ if content.kind_of?(BasicWidget) && !content.persisted?
75
+ params.each_value do |value|
76
+ if value.kind_of?(Array)
77
+ newreferenced_ids += value.select { |v| v.kind_of?(BasicWidget) }
78
+ end
79
+ end
80
+ else
81
+ params.each do |key, value|
82
+ if content.type_of_attribute(key) == "widget"
83
+ current = content[key]
84
+ new = value
85
+
86
+ dereferenced_ids += current - new
87
+ newreferenced_ids += new - current
88
+ end
89
+ end
90
+ end
91
+
92
+ [dereferenced_ids, newreferenced_ids]
93
+ end
94
+
95
+ end
96
+
97
+ end
@@ -0,0 +1,222 @@
1
+ module Scrivito
2
+
3
+ # This class represents a CMS workspace
4
+ # @api public
5
+ class Workspace
6
+ PUBLISHED_ID = 'published'
7
+
8
+ extend ActiveModel::Naming
9
+
10
+ include ModelIdentity
11
+
12
+ # Set the currently used workspace
13
+ # @api public
14
+ # @param [Scrivito::Workspace] workspace
15
+ def self.current=(workspace)
16
+ @current = workspace
17
+ end
18
+
19
+ def self.current_using_proc=(workspace_proc)
20
+ @current = workspace_proc
21
+ end
22
+
23
+ # Returns the currently used workspace
24
+ # @api public
25
+ # @return [Scrivito::Workspace]
26
+ def self.current
27
+ if @current.respond_to? :call
28
+ @current = @current.call
29
+ else
30
+ @current ||= default
31
+ end
32
+ end
33
+
34
+ # Returns all the workspaces
35
+ # @api public
36
+ # @return [Array<Scrivito::Workspace>]
37
+ def self.all
38
+ result_json = CmsRestApi.get('/workspaces')
39
+
40
+ result_json['results'].map do |workspace_json|
41
+ Workspace.find(workspace_json['id'])
42
+ end
43
+ end
44
+
45
+ def self.default
46
+ published
47
+ end
48
+
49
+ def self.published
50
+ cache.fetch(PUBLISHED_ID) do
51
+ cache[PUBLISHED_ID] = find(PUBLISHED_ID)
52
+ end
53
+ end
54
+
55
+ # Find a workspace by its id
56
+ # @api public
57
+ # @param [String] id
58
+ # @return [Scrivito::Workspace]
59
+ # @raise [Scrivito::ResourceNotFound]
60
+ def self.find(id)
61
+ if workspace_data = CmsBackend.instance.find_workspace_data_by_id(id)
62
+ Workspace.new workspace_data
63
+ else
64
+ raise ResourceNotFound, "Could not find #{self} with id #{id}"
65
+ end
66
+ end
67
+
68
+ # Find a workspace by its title.
69
+ # If multiple workspaces share the same title, one of them is returned.
70
+ # If no workspace with the given title can be found, nil is returned.
71
+ # @api public
72
+ # @param [String] title
73
+ # @return [Scrivito::Workspace]
74
+ def self.find_by_title(title)
75
+ all.detect { |workspace| workspace.title == title }
76
+ end
77
+
78
+ delegate :content_state, :base_revision_id, :base_content_state, to: :data
79
+
80
+ # Create a new workspace
81
+ # @api public
82
+ # @param [Hash] attributes
83
+ # @return [Scrivito::Workspace]
84
+ def self.create(attributes)
85
+ workspace_json = CmsRestApi.post("/workspaces", workspace: attributes)
86
+
87
+ self.find(workspace_json["id"])
88
+ end
89
+
90
+ # reloads the current workspace to reflect any changes to it that may have happened concurrently
91
+ # since it was loaded
92
+ # @api public
93
+ def self.reload
94
+ id = current.id
95
+ self.current_using_proc = proc { find(id) }
96
+ end
97
+
98
+ def self.cache
99
+ @cache ||= {}
100
+ end
101
+
102
+ def initialize(workspace_data)
103
+ @workspace_data = workspace_data
104
+ end
105
+
106
+ # Reloads this workspace to reflect any changes to it that may have happened concurrently since
107
+ # it was loaded
108
+ # @api public
109
+ def reload
110
+ @workspace_data = CmsBackend.instance.find_workspace_data_by_id(self.id)
111
+ @revision = @base_revision = nil
112
+ end
113
+
114
+ # Updates this workspace's attributes
115
+ # @api public
116
+ # @param [Hash] attributes
117
+ # @return [Scrivito::Workspace]
118
+ def update(attributes)
119
+ CmsRestApi.put(backend_url, workspace: attributes)
120
+
121
+ self.reload
122
+ end
123
+
124
+ # Destroy this workspace
125
+ # @api public
126
+ def destroy
127
+ reset_workspace_if_current
128
+ CmsRestApi.delete(backend_url)
129
+ end
130
+
131
+ # Publish the changes of this workspace
132
+ # @api public
133
+ def publish
134
+ CmsRestApi.put("#{backend_url}/publish", {})
135
+
136
+ reset_workspace_if_current
137
+ end
138
+
139
+ # Rebases the current workspace on the published content
140
+ # @api public
141
+ def rebase
142
+ CmsRestApi.put("#{backend_url}/rebase", {})
143
+
144
+ self.reload
145
+ end
146
+
147
+ # Returns the id of the workspace
148
+ # @api public
149
+ # @return [String]
150
+ def id
151
+ @workspace_data.id
152
+ end
153
+
154
+ def revision_id
155
+ @workspace_data.revision_id
156
+ end
157
+
158
+ # Returns the title of the workspace
159
+ # @api public
160
+ # @return [String]
161
+ def title
162
+ @workspace_data.title
163
+ end
164
+
165
+ def data
166
+ @workspace_data
167
+ end
168
+
169
+ def published?
170
+ self.id == 'published'
171
+ end
172
+
173
+ def rtc?
174
+ self.id == 'rtc'
175
+ end
176
+
177
+ def revision
178
+ @revision ||= Revision.new(id: revision_id, content_state: content_state, workspace: self)
179
+ end
180
+
181
+ def base_revision
182
+ if base_revision_id
183
+ @base_revision ||= Revision.new(id: base_revision_id, content_state: base_content_state)
184
+ end
185
+ end
186
+
187
+ def as_current(&block)
188
+ old_workspace = Workspace.current
189
+
190
+ begin
191
+ Workspace.current = self
192
+
193
+ yield
194
+ ensure
195
+ Workspace.current = old_workspace
196
+ end
197
+ end
198
+
199
+ def assert_revertable
200
+ raise ScrivitoError, 'published workspace is not modifiable' if published?
201
+ raise ScrivitoError, 'rtc workspace may contain attribute and class changes' if rtc?
202
+ end
203
+
204
+ def inspect
205
+ "<#{self.class} id=\"#{id}\" title=\"#{title}\">"
206
+ end
207
+
208
+ private
209
+
210
+ def backend_url
211
+ "/workspaces/#{id}"
212
+ end
213
+
214
+ def reset_workspace_if_current
215
+ if Workspace.current == self
216
+ Workspace.current = Workspace.default
217
+ end
218
+ end
219
+
220
+ end
221
+
222
+ end