scrivito_sdk 0.18.1 → 0.30.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (144) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/scrivito/blobs_controller.rb +1 -2
  3. data/app/controllers/scrivito/default_cms_controller.rb +9 -75
  4. data/app/controllers/scrivito/objs_controller.rb +107 -119
  5. data/app/controllers/scrivito/tasks_controller.rb +1 -1
  6. data/app/controllers/scrivito/users_controller.rb +1 -1
  7. data/app/controllers/scrivito/webservice_controller.rb +6 -2
  8. data/app/controllers/scrivito/workspaces_controller.rb +57 -17
  9. data/app/helpers/scrivito_helper.rb +285 -0
  10. data/app/views/google_maps_widget/show.html.erb +1 -0
  11. data/app/views/scrivito/_editing_auth_warning.html.erb +43 -0
  12. data/app/views/scrivito/blobs/upload_permission.json.jbuilder +1 -0
  13. data/app/views/scrivito/objs/copy.json.jbuilder +1 -0
  14. data/app/views/scrivito/objs/copy_widget.html.erb +1 -1
  15. data/app/views/scrivito/objs/create.json.jbuilder +1 -0
  16. data/app/views/scrivito/objs/create_widget.html.erb +1 -1
  17. data/app/views/scrivito/objs/details.json.jbuilder +1 -0
  18. data/app/views/scrivito/objs/format_missing_error.json.jbuilder +1 -0
  19. data/app/views/scrivito/objs/modification.json.jbuilder +1 -0
  20. data/app/views/scrivito/objs/page_class_selection.json.jbuilder +1 -0
  21. data/app/views/scrivito/objs/search.json.jbuilder +6 -0
  22. data/app/views/scrivito/objs/search_only_size.json.jbuilder +1 -0
  23. data/app/views/scrivito/objs/update.json.jbuilder +1 -0
  24. data/app/views/scrivito/objs/widget_class_selection.json.jbuilder +1 -0
  25. data/app/views/scrivito/objs/widget_modification.json.jbuilder +1 -0
  26. data/app/views/scrivito/tasks/show.json.jbuilder +3 -0
  27. data/app/views/scrivito/users/suggest.json.jbuilder +1 -0
  28. data/app/views/scrivito/webservice/empty.json +1 -0
  29. data/app/views/scrivito/webservice/error.json.jbuilder +2 -0
  30. data/app/views/scrivito/workspaces/_workspace.json.jbuilder +5 -0
  31. data/app/views/scrivito/workspaces/check.json.jbuilder +1 -0
  32. data/app/views/scrivito/workspaces/create.json.jbuilder +1 -0
  33. data/app/views/scrivito/workspaces/index.json.jbuilder +1 -0
  34. data/app/views/scrivito/workspaces/show.json.jbuilder +1 -0
  35. data/app/views/scrivito/workspaces/task.json.jbuilder +5 -0
  36. data/config/ca-bundle.crt +259 -234
  37. data/config/cms_routes.rb +2 -2
  38. data/config/routes.rb +3 -0
  39. data/lib/assets/javascripts/{scrivito_editing.js → scrivito_sdk.js} +1662 -1180
  40. data/lib/assets/stylesheets/{scrivito_editing.css → scrivito_sdk.css} +242 -6
  41. data/lib/generators/scrivito/install/install_generator.rb +62 -0
  42. data/lib/generators/scrivito/install/templates/app/controllers/cms_controller.rb +3 -0
  43. data/lib/generators/scrivito/install/templates/app/controllers/page_controller.rb +2 -0
  44. data/lib/generators/scrivito/install/templates/app/models/headline_widget.rb +7 -0
  45. data/lib/generators/scrivito/install/templates/app/models/image.rb +5 -0
  46. data/lib/generators/scrivito/install/templates/app/models/image_widget.rb +7 -0
  47. data/lib/generators/scrivito/install/templates/app/models/obj.rb +2 -0
  48. data/lib/generators/scrivito/install/templates/app/models/page.rb +8 -0
  49. data/lib/generators/scrivito/install/templates/app/models/text_widget.rb +7 -0
  50. data/lib/generators/scrivito/install/templates/app/models/widget.rb +2 -0
  51. data/lib/generators/scrivito/install/templates/app/views/headline_widget/show.html.erb +1 -0
  52. data/lib/generators/scrivito/install/templates/app/views/headline_widget/thumbnail.html.erb +3 -0
  53. data/lib/generators/scrivito/install/templates/app/views/image_widget/show.html.erb +1 -0
  54. data/lib/generators/scrivito/install/templates/app/views/image_widget/thumbnail.html.erb +3 -0
  55. data/lib/generators/scrivito/install/templates/app/views/page/details.html.erb +1 -0
  56. data/lib/generators/scrivito/install/templates/app/views/page/index.html.erb +16 -0
  57. data/lib/generators/scrivito/install/templates/app/views/page/thumbnail.html.erb +3 -0
  58. data/lib/generators/scrivito/install/templates/app/views/text_widget/show.html.erb +1 -0
  59. data/lib/generators/scrivito/install/templates/app/views/text_widget/thumbnail.html.erb +3 -0
  60. data/lib/generators/scrivito/install/templates/config/initializers/scrivito.rb +10 -0
  61. data/lib/generators/scrivito/install/templates/scrivito/migrate/install_scrivito.rb +49 -0
  62. data/lib/generators/scrivito/migration/USAGE +9 -0
  63. data/lib/generators/{cms → scrivito}/migration/migration_generator.rb +4 -9
  64. data/lib/generators/{cms → scrivito}/migration/templates/migration.erb +3 -3
  65. data/lib/generators/{cms → scrivito}/widget/templates/details.html.erb +0 -0
  66. data/lib/generators/{cms → scrivito}/widget/templates/migration.erb +0 -0
  67. data/lib/generators/{cms → scrivito}/widget/templates/model.erb +0 -0
  68. data/lib/generators/{cms → scrivito}/widget/templates/show.html.erb +0 -0
  69. data/lib/generators/scrivito/widget/templates/thumbnail.html.erb +3 -0
  70. data/lib/generators/{cms → scrivito}/widget/widget_generator.rb +3 -2
  71. data/lib/obj.rb +17 -3
  72. data/lib/scrivito/attribute_collection.rb +7 -12
  73. data/lib/scrivito/attribute_content.rb +31 -12
  74. data/lib/scrivito/basic_obj.rb +186 -96
  75. data/lib/scrivito/basic_widget.rb +25 -10
  76. data/lib/scrivito/binary.rb +75 -0
  77. data/lib/scrivito/cache_garbage_collector.rb +19 -4
  78. data/lib/scrivito/child_list_tag.rb +108 -0
  79. data/lib/scrivito/client_config.rb +2 -1
  80. data/lib/scrivito/client_error.rb +3 -2
  81. data/lib/scrivito/cms_backend.rb +81 -16
  82. data/lib/scrivito/cms_dispatch_controller.rb +7 -0
  83. data/lib/scrivito/cms_env.rb +0 -2
  84. data/lib/scrivito/cms_field_tag.rb +44 -35
  85. data/lib/scrivito/cms_rest_api.rb +23 -6
  86. data/lib/scrivito/cms_rest_api/attribute_serializer.rb +1 -1
  87. data/lib/scrivito/cms_routing.rb +120 -0
  88. data/lib/scrivito/configuration.rb +69 -58
  89. data/lib/scrivito/connection_manager.rb +30 -1
  90. data/lib/scrivito/content_service.rb +10 -7
  91. data/lib/scrivito/controller_actions.rb +108 -0
  92. data/lib/scrivito/diff.rb +30 -15
  93. data/lib/scrivito/editing_context.rb +4 -0
  94. data/lib/scrivito/editing_context_helper.rb +19 -0
  95. data/lib/scrivito/editing_context_middleware.rb +20 -1
  96. data/lib/scrivito/image_tag_helper.rb +44 -0
  97. data/lib/scrivito/layout_tags.rb +33 -0
  98. data/lib/scrivito/link.rb +4 -2
  99. data/lib/scrivito/membership.rb +0 -8
  100. data/lib/scrivito/migrations.rb +0 -1
  101. data/lib/scrivito/migrations/installer.rb +1 -1
  102. data/lib/scrivito/migrations/migration.rb +0 -2
  103. data/lib/scrivito/migrations/migrator.rb +2 -3
  104. data/lib/scrivito/obj_class.rb +64 -16
  105. data/lib/scrivito/obj_class_data.rb +4 -0
  106. data/lib/scrivito/obj_search_enumerator.rb +1 -1
  107. data/lib/scrivito/routing_helper.rb +42 -0
  108. data/lib/scrivito/{engine.rb → sdk_engine.rb} +15 -29
  109. data/lib/scrivito/tag_renderer.rb +40 -0
  110. data/lib/scrivito/test_request.rb +40 -0
  111. data/lib/scrivito/type_computer.rb +1 -6
  112. data/lib/scrivito/user.rb +33 -15
  113. data/lib/scrivito/user_definition.rb +34 -6
  114. data/lib/scrivito/widget_tag.rb +67 -0
  115. data/lib/scrivito/workspace.rb +12 -24
  116. data/lib/scrivito_sdk.rb +17 -1
  117. data/lib/tasks/cache.rake +4 -5
  118. data/lib/tasks/migration.rake +1 -1
  119. data/lib/widget.rb +21 -3
  120. metadata +119 -70
  121. data/README +0 -6
  122. data/app/controllers/cms_controller.rb +0 -7
  123. data/app/helpers/cms_helper.rb +0 -7
  124. data/app/helpers/cms_routing_helper.rb +0 -7
  125. data/app/helpers/scrivito/cms_asset_helper.rb +0 -110
  126. data/app/helpers/scrivito/cms_tag_helper.rb +0 -232
  127. data/app/helpers/scrivito/default_cms_helper.rb +0 -21
  128. data/app/helpers/scrivito/default_cms_routing_helper.rb +0 -128
  129. data/app/helpers/scrivito/display_helper.rb +0 -64
  130. data/app/helpers/scrivito/editing_helper.rb +0 -32
  131. data/app/helpers/scrivito/layout_helper.rb +0 -21
  132. data/app/models/named_link.rb +0 -2
  133. data/app/views/cms/_index.html.erb +0 -7
  134. data/app/views/cms/index.html.erb +0 -1
  135. data/app/views/scrivito/_editing_javascript.html.erb +0 -7
  136. data/app/views/scrivito/default_cms/show_widget.html.erb +0 -1
  137. data/lib/assets/stylesheets/scrivito.css +0 -199
  138. data/lib/generators/cms/migration/USAGE +0 -9
  139. data/lib/generators/cms/widget/templates/thumbnail.html.erb +0 -2
  140. data/lib/scrivito/blob.rb +0 -48
  141. data/lib/scrivito/cms_accessible.rb +0 -30
  142. data/lib/scrivito/cms_rest_api/blob_uploader.rb +0 -18
  143. data/lib/scrivito/cms_test_request.rb +0 -23
  144. data/lib/scrivito/migrations/migration_dsl.rb +0 -180
@@ -0,0 +1,3 @@
1
+ <%= scrivito_thumbnail 'Page', :content do %>
2
+ A simple content page with a headline and content consisting of widgets.
3
+ <% end %>
@@ -0,0 +1 @@
1
+ <%= scrivito_tag :div, widget, :text %>
@@ -0,0 +1,3 @@
1
+ <%= scrivito_thumbnail 'Text Widget', :text do %>
2
+ A widget for text and HTML content.
3
+ <% end %>
@@ -0,0 +1,10 @@
1
+ Scrivito.configure do |config|
2
+ #
3
+ # Uncomment following lines in order to explicitly set the tenant and the API key.
4
+ # If not explicitly set, the tenant and the API key are obtained from the environment variables
5
+ # SCRIVITO_TENANT and SCRIVITO_API_KEY.
6
+ #
7
+ # config.tenant = 'my-tenant-123'
8
+ # config.api_key = 'secret'
9
+ #
10
+ end
@@ -0,0 +1,49 @@
1
+ class InstallScrivito < Scrivito::Migration
2
+ def up
3
+ Scrivito::ObjClass.create(name: 'Page', attributes: [
4
+ {name: 'title', type: 'string'},
5
+ {name: 'body', type: 'widget'},
6
+ {name: 'child_order', type: 'referencelist'},
7
+ ])
8
+
9
+ Scrivito::ObjClass.create(name: 'HeadlineWidget', attributes: [
10
+ {name: 'headline', type: 'string'},
11
+ ])
12
+
13
+ Scrivito::ObjClass.create(name: 'TextWidget', attributes: [
14
+ {name: 'text', type: 'html'},
15
+ ])
16
+
17
+ Scrivito::ObjClass.create(name: 'Image', attributes:[
18
+ {name: 'blob', type: 'binary'},
19
+ ])
20
+
21
+ Scrivito::ObjClass.create(name: 'ImageWidget', attributes: [
22
+ {name: 'image', type: 'reference'}
23
+ ])
24
+
25
+ Page.create(_path: '/', title: 'Welcome to Scrivito!', body: [
26
+ HeadlineWidget.new(headline: 'A professional Cloud CMS built for Ruby on Rails.'),
27
+ TextWidget.new(text: %{
28
+ <h3>Add content management to your website</h3>
29
+ <p>
30
+ With Scrivito, you can develop your website like you always did.
31
+ It just works, and it just works "The Rails Way".
32
+ </p>
33
+ <h3>No training for your editors required.</h3>
34
+ <p>
35
+ Your clients can edit content in-place, directly on the website itself.
36
+ Intuitively, without any training or HTML skills.
37
+ </p>
38
+ <h3>No installation required</h3>
39
+ <p>
40
+ You should focus on the important things:
41
+ Developing the functionality and look of your website.
42
+ That's why we built Scrivito as a cloud service.
43
+ So you don't have to keep on updating your CMS or worry about uptime all the time.
44
+ And you always get the newest features.
45
+ </p>
46
+ })
47
+ ])
48
+ end
49
+ end
@@ -0,0 +1,9 @@
1
+ Description:
2
+ Creates a new CMS migration.
3
+
4
+ Example:
5
+ rails generate scrivito:migration CreateExample
6
+ rails generate scrivito:migration create_example
7
+
8
+ This will create:
9
+ scrivito/migrate/[timestamp]_create_example.rb
@@ -1,21 +1,16 @@
1
- module Cms
1
+ module Scrivito
2
2
  class MigrationGenerator < ::Rails::Generators::NamedBase
3
3
  include ::Rails::Generators::Migration
4
4
 
5
5
  source_root File.expand_path('../templates', __FILE__)
6
6
 
7
- class_option :path,
8
- type: :string,
9
- default: 'cms/migrate',
10
- desc: 'Relative path to Rails.root where to place the migration file. Defaults to "cms/migrate".',
11
- banner: 'PATH'
12
-
13
7
  def self.next_migration_number(dirname)
14
8
  Time.now.utc.strftime('%Y%m%d%H%M%S')
15
9
  end
16
10
 
17
11
  def create_migration_file
18
- migration_template('migration.erb', "#{options[:path]}/#{file_name}.rb")
12
+ base_path = Scrivito::Configuration.migration_path
13
+ migration_template('migration.erb', File.join(base_path, "#{file_name}.rb"))
19
14
  end
20
15
  end
21
- end
16
+ end
@@ -4,12 +4,12 @@ class <%= class_name %> < ::Scrivito::Migration
4
4
  # content programatically. All Scrivito SDK classes and methods are available and arbitrary Ruby
5
5
  # code can be executed.
6
6
  #
7
- # Use `bundle exec rake cms:migrate` to run all migrations that are new to the `rtc` workspace.
7
+ # Use `bundle exec rake scrivito:migrate` to run all migrations that are new to the `rtc` workspace.
8
8
  # Migrations are identified by an ID and only get executed once. If you migrate the CMS an
9
9
  # existing `rtc` workspace is used or a new `rtc` workspace is created. Only one developer at a
10
10
  # time can run migrations, which means to quickly try to publish the current migrations.
11
11
  #
12
- # Use `bundle exec rake cms:migrate:publish` to publish your `rtc` workspace that holds the
12
+ # Use `bundle exec rake scrivito:migrate:publish` to publish your `rtc` workspace that holds the
13
13
  # latest unpublished migrations.
14
14
  #
15
15
  # To get you started, here is a list of the most common SDK methods to alter the CMS content.
@@ -26,7 +26,7 @@ class <%= class_name %> < ::Scrivito::Migration
26
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
- # Scrivito::ObjClass.find('Homepage').attributes.add(name: 'category', type: :enum, values: %w(tech, social))
29
+ # Scrivito::ObjClass.find('Homepage').attributes.add(name: 'category', type: :enum, values: %w(tech social))
30
30
  #
31
31
  # @example Update the "category" attribute and add another value.
32
32
  # attribute = Scrivito::ObjClass.find('Homepage').attributes['category']
@@ -0,0 +1,3 @@
1
+ <%%= scrivito_thumbnail '<%= class_name %>' do %>
2
+ <% class_name %> Widget
3
+ <%% end %>
@@ -1,4 +1,4 @@
1
- module Cms
1
+ module Scrivito
2
2
  module Generators
3
3
  class WidgetGenerator < ::Rails::Generators::NamedBase
4
4
  include ::Rails::Generators::Migration
@@ -23,7 +23,8 @@ module Cms
23
23
  end
24
24
 
25
25
  def generate_migration
26
- migration_template('migration.erb', "cms/migrate/create_#{file_name}.rb")
26
+ base_path = Scrivito::Configuration.migration_path
27
+ migration_template('migration.erb', File.join(base_path, "/create_#{file_name}.rb"))
27
28
  end
28
29
  end
29
30
  end
data/lib/obj.rb CHANGED
@@ -1,3 +1,17 @@
1
- # empty implementation for Obj
2
- # only unsed in case the app does not define Obj itself
3
- ::Obj = Class.new(Scrivito::BasicObj)
1
+ # The class +Obj+ should be overridden by the application (see example).
2
+ # The SDK provides methods for {Obj} through it's abstract base class, {Scrivito::BasicObj}.
3
+ # The Scrivito SDK provides this empty implementation for {Obj}
4
+ # which will only be used in case the app does not define the class {Obj} itself.
5
+ # @example
6
+ # # in app/models/obj.rb
7
+ # class Obj < Scrivito::BasicObj
8
+ # # put your custom extensions that apply to every Obj here
9
+ # end
10
+ #
11
+ # # in app/models/page.rb
12
+ # class Page < Obj
13
+ # # put custom extensions that only apply to Objs with class Page here
14
+ # end
15
+ # @api public
16
+ class Obj < Scrivito::BasicObj
17
+ end
@@ -24,24 +24,19 @@ module Scrivito
24
24
  # @api public
25
25
  def_delegators :@attributes, :each
26
26
 
27
+ #
27
28
  # Finds an attribute in this collection by its name.
28
29
  #
29
30
  # @api public
30
31
  #
31
- # See {Scrivito::ObjClass#attributes} for example of how to find an attribute by name.
32
+ # @param [String] name the name of the attribute
33
+ # @return [Scrivito::Attribute] if there is an attribute with name +name+
34
+ # @return [nil] if there is no attribute with name +name+
35
+ #
36
+ # @see Scrivito::ObjClass#attributes Examples of how to find an attribute by name.
32
37
  #
33
- # @param [String] name
34
- # @return [Scrivito::Attribute]
35
- # @raise [Scrivito::ResourceNotFound] Raised when no attribute with the given +name+ can be found.
36
38
  def [](name)
37
- attribute = @attributes.detect { |attribute| attribute.name == name.to_s }
38
-
39
- unless attribute
40
- raise ResourceNotFound, "Could not find #{Attribute} with name '#{name}' for" \
41
- " obj class '#{@obj_class.name}'."
42
- end
43
-
44
- attribute
39
+ @attributes.detect { |attribute| attribute.name == name.to_s }
45
40
  end
46
41
 
47
42
  # Adds an attribute to this collection and updates the obj class.
@@ -41,7 +41,7 @@ module AttributeContent
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
+ data_from_cms.has_custom_attribute?(name)
45
45
  end
46
46
  alias_method :has_attribute?, :has_custom_attribute?
47
47
 
@@ -56,11 +56,7 @@ module AttributeContent
56
56
  def [](key)
57
57
  key = key.to_s
58
58
 
59
- if key == '_obj_class'
60
- obj_class
61
- else
62
- has_attribute?(key) ? read_attribute(key) : nil
63
- end
59
+ has_attribute?(key) ? read_attribute(key) : nil
64
60
  end
65
61
 
66
62
  # Hook method to control which widget classes should be available for this page.
@@ -124,6 +120,14 @@ module AttributeContent
124
120
  end
125
121
  end
126
122
 
123
+ def to_show_view_path
124
+ to_view_path('show')
125
+ end
126
+
127
+ def to_details_view_path
128
+ to_view_path('details')
129
+ end
130
+
127
131
  private
128
132
 
129
133
  attr_writer :data_from_cms
@@ -137,6 +141,10 @@ module AttributeContent
137
141
  end
138
142
 
139
143
  def prepare_attribute_value(attribute_value, attribute_type, attribute_name)
144
+ if attribute_name == '_obj_class'
145
+ return obj_class
146
+ end
147
+
140
148
  case attribute_type
141
149
  when "html"
142
150
  StringTagging.tag_as_html(attribute_value)
@@ -147,22 +155,30 @@ module AttributeContent
147
155
  when "link"
148
156
  build_link(attribute_value)
149
157
  when "reference"
150
- BasicObj.find([attribute_value]).first
158
+ workspace.objs.find([attribute_value]).first
151
159
  when "referencelist"
152
- BasicObj.find(attribute_value).compact
160
+ workspace.objs.find(attribute_value).compact
153
161
  when "widget"
154
162
  build_widgets(attribute_value, attribute_name)
163
+ when "binary"
164
+ build_binary(attribute_value)
155
165
  else
156
166
  attribute_value
157
167
  end
158
168
  end
159
169
 
170
+ def build_binary(binary_definition)
171
+ if binary_definition && binary_definition['id']
172
+ Binary.new(binary_definition['id'], workspace.published?)
173
+ end
174
+ end
175
+
160
176
  def build_links(link_definitions)
161
177
  if link_definitions.present?
162
178
  link_definitions = link_definitions.map(&:with_indifferent_access)
163
179
 
164
180
  object_ids = link_definitions.map { |link_data| link_data[:destination] }.compact.uniq
165
- objects = object_ids.empty? ? [] : BasicObj.find(object_ids)
181
+ objects = object_ids.empty? ? [] : workspace.objs.find(object_ids)
166
182
  link_definitions.each_with_object([]) do |link_data, links|
167
183
  obj = objects.detect { |o| o && o.id == link_data[:destination] }
168
184
  link = Link.new(link_data.merge(obj: obj))
@@ -185,7 +201,7 @@ module AttributeContent
185
201
 
186
202
  def build_internal_link(attribute_value)
187
203
  properties = {
188
- obj: BasicObj.find(attribute_value['destination']),
204
+ obj: workspace.objs.find(attribute_value['destination']),
189
205
  title: attribute_value['title'],
190
206
  query: attribute_value['query'],
191
207
  fragment: attribute_value['fragment'],
@@ -221,6 +237,10 @@ module AttributeContent
221
237
  end
222
238
  end
223
239
 
240
+ def to_view_path(view_name)
241
+ "#{obj_class_name.underscore}/#{view_name}"
242
+ end
243
+
224
244
  module ClassMethods
225
245
  # Instantiate an Obj or Widget instance from obj_data.
226
246
  # If a subclass of Obj or Widget with the same name as the property +_obj_class+ exists,
@@ -242,8 +262,7 @@ module AttributeContent
242
262
 
243
263
  def descendants
244
264
  type_computer = TypeComputer.new(self, nil)
245
- CmsRestApi.get("workspaces/#{Workspace.current.id}/obj_classes")['results']
246
- .map { |obj_class_spec| obj_class_spec['name'] }
265
+ Workspace.current.obj_classes.map(&:name)
247
266
  .sort
248
267
  .map { |obj_class_name| type_computer.compute_type(obj_class_name) }
249
268
  .compact
@@ -3,7 +3,11 @@ require 'ostruct'
3
3
  require 'active_model/naming'
4
4
 
5
5
  module Scrivito
6
- # The CMS file class
6
+ # The abstract base class for cms objects.
7
+ #
8
+ # @note Please do not use {Scrivito::BasicObj} directly,
9
+ # as it is intended as an abstract class.
10
+ # Always use {Obj} or a subclass of {Obj}.
7
11
  # @api public
8
12
  class BasicObj
9
13
  UNIQ_ATTRIBUTES = %w[
@@ -33,16 +37,16 @@ module Scrivito
33
37
  @_type_computer = nil
34
38
  end
35
39
 
36
- # Create a new {BasicObj Obj} in the cms
40
+ # Create a new {Scrivito::BasicObj Obj} in the cms
37
41
  #
38
42
  # This allows you to set the different attributes types of an obj by
39
43
  # providing a hash with the attributes names as key and the values you want
40
44
  # to set as values
41
45
  #
42
- # @example Reference lists have to be provided as an Array of {BasicObj Objs}
46
+ # @example Reference lists have to be provided as an Array of {Scrivito::BasicObj Objs}
43
47
  # Obj.create(:reference_list => [other_obj])
44
48
  #
45
- # @example Passing an {BasicObj Obj} allows you to set a reference
49
+ # @example Passing an {Scrivito::BasicObj Obj} allows you to set a reference
46
50
  # Obj.create(:reference => other_obj)
47
51
  #
48
52
  # @example you can upload files by passing a ruby File object
@@ -72,7 +76,7 @@ module Scrivito
72
76
  # @example Arrays of {String Strings} allow you to set multi enum fields
73
77
  # Obj.create(:tags => ["ruby", "rails"])
74
78
  #
75
- # @example Simply pass an Array of {BasicWidget Widgets} to change a widget field. See {BasicWidget#clone Widget#clone} on how to clone a widget.
79
+ # @example Simply pass an Array of {Scrivito::BasicWidget Widgets} to change a widget field. See {Scrivito::BasicWidget#clone Widget#clone} on how to clone a widget.
76
80
  # # Add new widgets
77
81
  # Obj.create(:widgets => [Widget.new(_obj_class: 'TitleWidget', title: 'My Title')])
78
82
  #
@@ -87,7 +91,7 @@ module Scrivito
87
91
  #
88
92
  # @api public
89
93
  # @param [Hash] attributes
90
- # @return [Obj] the newly created {BasicObj Obj}
94
+ # @return [Obj] the newly created {Scrivito::BasicObj Obj}
91
95
  def self.create(attributes)
92
96
  attributes = with_default_obj_class(attributes)
93
97
  api_attributes, widget_properties = prepare_attributes_for_rest_api(attributes, nil)
@@ -97,7 +101,7 @@ module Scrivito
97
101
  obj
98
102
  end
99
103
 
100
- # Create a new {BasicObj Obj} instance with the given values and attributes.
104
+ # Create a new {Scrivito::BasicObj Obj} instance with the given values and attributes.
101
105
  # Normally this method should not be used.
102
106
  # Instead Objs should be loaded from the cms database.
103
107
  def initialize(attributes = {})
@@ -120,7 +124,7 @@ module Scrivito
120
124
 
121
125
  ### FINDERS ####################
122
126
 
123
- # Find a {BasicObj Obj} by its id.
127
+ # Find a {Scrivito::BasicObj Obj} by its id.
124
128
  # If the parameter is an Array containing ids, return a list of corresponding Objs.
125
129
  # @param [String, Integer, Array<String, Integer>]id_or_list
126
130
  # @return [Obj, Array<Obj>]
@@ -133,7 +137,7 @@ module Scrivito
133
137
  Workspace.current.objs.find_by_id(id)
134
138
  end
135
139
 
136
- # Find a {BasicObj Obj} by its id.
140
+ # Find a {Scrivito::BasicObj Obj} by its id.
137
141
  # If the parameter is an Array containing ids, return a list of corresponding Objs.
138
142
  # The results include deleted objects as well.
139
143
  # @param [String, Integer, Array<String, Integer>]id_or_list
@@ -147,7 +151,10 @@ module Scrivito
147
151
  #
148
152
  # Note that +field+ and +value+ can also be arrays for searching several fields or searching for several values.
149
153
  #
150
- # {ObjSearchEnumerator}s can be chained using one of the chainable methods (e.g. {ObjSearchEnumerator#and} and {ObjSearchEnumerator#and_not}).
154
+ # @note If invoked on a subclass of Obj, the result will be restricted to instances of that subclass.
155
+ #
156
+ # {ObjSearchEnumerator}s can be chained using one of the chainable methods
157
+ # (e.g. {ObjSearchEnumerator#and} and {ObjSearchEnumerator#and_not}).
151
158
  #
152
159
  # @example Look for the first 10 Objs whose ObjClass is "Pressrelease" and whose title contains "quarterly":
153
160
  # Obj.where(:_obj_class, :equals, 'Pressrelease').and(:title, :contains, 'quarterly').take(10)
@@ -155,20 +162,33 @@ module Scrivito
155
162
  # @param [Symbol, String] operator See {ObjSearchEnumerator#and} for details
156
163
  # @param [String, Array<String>] value See {ObjSearchEnumerator#and} for details
157
164
  # @param [Hash] boost See {ObjSearchEnumerator#and} for details
165
+ # @raise [ScrivitoError] if called on a subclass of +Obj+ with no corresponding {ObjClass}
166
+ # @raise [ScrivitoError] if called directly on +BasicObj+. Use +Obj.where+ instead.
158
167
  # @return [ObjSearchEnumerator]
159
168
  # @api public
160
169
  def self.where(field, operator, value, boost = nil)
161
- Workspace.current.objs.where(field, operator, value, boost)
170
+ assert_not_basic_obj('.where')
171
+ if self == ::Obj
172
+ Workspace.current.objs.where(field, operator, value, boost)
173
+ else
174
+ assert_has_obj_class('.where')
175
+ Workspace.current.objs.where(:_obj_class, :equals, name)
176
+ .and(field, operator, value, boost)
177
+ end
162
178
  end
163
179
 
164
- # Returns a {ObjSearchEnumerator} of all {BasicObj Obj}s.
180
+ # Returns a {ObjSearchEnumerator} of all {Scrivito::BasicObj Obj}s.
165
181
  # If invoked on a subclass of Obj, the result will be restricted to instances of that subclass.
166
182
  # @return [ObjSearchEnumerator]
183
+ # @raise [ScrivitoError] if called on a subclass of +Obj+ with no corresponding {ObjClass}
184
+ # @raise [ScrivitoError] if called directly on +BasicObj+. Use +Obj.all+ instead.
167
185
  # @api public
168
186
  def self.all
169
- if superclass == Scrivito::BasicObj
187
+ assert_not_basic_obj('.all')
188
+ if self == ::Obj
170
189
  Workspace.current.objs.all
171
190
  else
191
+ assert_has_obj_class('.all')
172
192
  find_all_by_obj_class(name)
173
193
  end
174
194
  end
@@ -181,19 +201,19 @@ module Scrivito
181
201
  Workspace.current.objs.find_all_by_obj_class(obj_class)
182
202
  end
183
203
 
184
- # Find the {BasicObj Obj} with the given path.
204
+ # Find the {Scrivito::BasicObj Obj} with the given path.
185
205
  # Returns +nil+ if no matching Obj exists.
186
- # @param [String] path Path of the {BasicObj Obj}.
206
+ # @param [String] path Path of the {Scrivito::BasicObj Obj}.
187
207
  # @return [Obj]
188
208
  # @api public
189
209
  def self.find_by_path(path)
190
210
  Workspace.current.objs.find_by_path(path)
191
211
  end
192
212
 
193
- # Find an {BasicObj Obj} with the given name.
213
+ # Find an {Scrivito::BasicObj Obj} with the given name.
194
214
  # If several Objs with the given name exist, an arbitrary one of these Objs is chosen and returned.
195
215
  # If no Obj with the name exits, +nil+ is returned.
196
- # @param [String] name Name of the {BasicObj Obj}.
216
+ # @param [String] name Name of the {Scrivito::BasicObj Obj}.
197
217
  # @return [Obj]
198
218
  # @api public
199
219
  def self.find_by_name(name)
@@ -201,23 +221,25 @@ module Scrivito
201
221
  end
202
222
 
203
223
  # Returns a {ObjSearchEnumerator} of all Objs with the given name.
204
- # @param [String] name Name of the {BasicObj Obj}.
224
+ # @param [String] name Name of the {Scrivito::BasicObj Obj}.
205
225
  # @return [ObjSearchEnumerator]
206
226
  # @api public
207
227
  def self.find_all_by_name(name)
208
228
  where(:_name, :equals, name)
209
229
  end
210
230
 
211
- # Returns the {BasicObj Obj} with the given permalink, or +nil+ if no matching Obj exists.
212
- # @param [String] permalink The permalink of the {BasicObj Obj}.
231
+ # Returns the {Scrivito::BasicObj Obj} with the given permalink, or +nil+ if no matching Obj
232
+ # exists.
233
+ # @param [String] permalink The permalink of the {Scrivito::BasicObj Obj}.
213
234
  # @return [Obj]
214
235
  # @api public
215
236
  def self.find_by_permalink(permalink)
216
237
  Workspace.current.objs.find_by_permalink(permalink)
217
238
  end
218
239
 
219
- # Returns the {BasicObj Obj} with the given permalink, or raise ResourceNotFound if no matching Obj exists.
220
- # @param [String] permalink The permalink of the {BasicObj Obj}.
240
+ # Returns the {Scrivito::BasicObj Obj} with the given permalink, or raise ResourceNotFound if
241
+ # no matching Obj exists.
242
+ # @param [String] permalink The permalink of the {Scrivito::BasicObj Obj}.
221
243
  # @return [Obj]
222
244
  # @api public
223
245
  def self.find_by_permalink!(permalink)
@@ -245,10 +267,10 @@ module Scrivito
245
267
  def self.valid_page_classes_beneath(parent_path)
246
268
  end
247
269
 
248
- # Update the {BasicObj Obj} with the attributes provided.
270
+ # Update the {Scrivito::BasicObj Obj} with the attributes provided.
249
271
  #
250
272
  # For an overview of which values you can set via this method see the
251
- # documentation of {BasicObj.create Obj.create}.
273
+ # documentation of {Scrivito::BasicObj.create Obj.create}.
252
274
  #
253
275
  # Additionally, +update+ accepts a +_widget_pool+ hash in +attributes+ to modify widgets.
254
276
  # The keys of +_widget_pool+ are widget instances, the values are the modified attributes of
@@ -299,7 +321,7 @@ module Scrivito
299
321
  self.class.find(json['_id'])
300
322
  end
301
323
 
302
- # Destroys the {BasicObj Obj} in the current {Workspace}
324
+ # Destroys the {Scrivito::BasicObj Obj} in the current {Workspace}
303
325
  # @api public
304
326
  def destroy
305
327
  if children.any?
@@ -312,12 +334,12 @@ module Scrivito
312
334
  id
313
335
  end
314
336
 
315
- # return the {BasicObj Obj} that is the parent of this Obj.
337
+ # return the {Scrivito::BasicObj Obj} that is the parent of this Obj.
316
338
  # returns +nil+ for the root Obj.
317
339
  # @api public
318
340
  def parent
319
341
  if child_path?
320
- BasicObj.find_by_path(parent_path)
342
+ workspace.objs.find_by_path(parent_path)
321
343
  end
322
344
  end
323
345
 
@@ -334,7 +356,7 @@ module Scrivito
334
356
  Workspace.current.objs.find_by_paths(ancestor_paths)
335
357
  end
336
358
 
337
- # return a list of all child {BasicObj Obj}s.
359
+ # return a list of all child {Scrivito::BasicObj Obj}s.
338
360
  # @return [Array<Obj>]
339
361
  # @api public
340
362
  def children
@@ -345,13 +367,13 @@ module Scrivito
345
367
 
346
368
  ### ATTRIBUTES #################
347
369
 
348
- # returns the {BasicObj Obj}'s path as a String.
370
+ # returns the {Scrivito::BasicObj Obj}'s path as a String.
349
371
  # @api public
350
372
  def path
351
373
  read_attribute('_path')
352
374
  end
353
375
 
354
- # returns the {BasicObj Obj}'s name, i.e. the last component of the path.
376
+ # returns the {Scrivito::BasicObj Obj}'s name, i.e. the last component of the path.
355
377
  # @api public
356
378
  def name
357
379
  if child_path?
@@ -361,12 +383,14 @@ module Scrivito
361
383
  end
362
384
  end
363
385
 
364
- # Returns the root {BasicObj Obj}, i.e. the Obj with the path "/"
386
+ # Returns the root {Scrivito::BasicObj Obj}, i.e. the Obj with the path "/"
365
387
  # @return [Obj]
366
388
  # @api public
367
389
  def self.root
368
- BasicObj.find_by_path("/") or raise ResourceNotFound,
369
- "Obj.root not found: There is no Obj with path '/'."
390
+ BasicObj.find_by_path('/') or raise ResourceNotFound,
391
+ '"Obj.root" not found: There is no "Obj" with path "/". '\
392
+ 'Maybe you forgot the migration when setting up your Scrivito application? '\
393
+ 'Try "rake scrivito:migrate" and "rake scrivito:migrate:publish".'
370
394
  end
371
395
 
372
396
  # Returns the homepage obj. This can be overwritten in your application's +Obj+.
@@ -427,18 +451,27 @@ module Scrivito
427
451
  (title || '').parameterize
428
452
  end
429
453
 
430
- # This method determines the description that is shown in the changes list.
431
- # It can be overriden by a custom value.
454
+ #
455
+ # This method determines the description that is shown in the UI
456
+ # and defaults to {Scrivito::BasicObj#display_title}. It can be overriden by a custom value.
457
+ #
432
458
  # @api public
459
+ #
433
460
  def description_for_editor
434
- slug.presence || path
461
+ display_title
435
462
  end
436
463
 
437
- # Returns the title of the content or the name.
438
- # @return [String]
464
+ #
465
+ # Calculates appropriate title for an +Obj+.
466
+ #
439
467
  # @api public
468
+ #
469
+ # @return [String] {Scrivito::Binary#filename} if +Obj+ is +binary+ and has a +filename+.
470
+ # @return [String] {Scrivito::BasicObj#title} if +Obj+ has a non-empty +title+.
471
+ # @return [String] a placeholder +<untitled MyClass>+ otherwise.
472
+ #
440
473
  def display_title
441
- self.title || name
474
+ (binary_title || title).presence || "<untitled #{obj_class_name}>"
442
475
  end
443
476
 
444
477
  # @api public
@@ -446,9 +479,25 @@ module Scrivito
446
479
  read_attribute('title')
447
480
  end
448
481
 
449
- # Returns true if image? or generic?
482
+ # @api public
483
+ # This method indicates if the Obj represents binary data. Binaries are
484
+ # handled differently in that they are not rendered using the normal layout
485
+ # but sent as a file. Examples of binary resources are Images or PDFs.
486
+ #
487
+ # By default every Obj that has an attribute +blob+ of the type +binary+ is
488
+ # considered a binary
489
+ #
490
+ # @note you can override this method to indicate that an Obj is binary,
491
+ # if the default behavior doesn't fit your needs
492
+ #
493
+ # @return true if this Obj represents a binary resource.
450
494
  def binary?
451
- [:image, :generic].include?(read_attribute('_obj_type').to_sym)
495
+ if obj_type = read_attribute('_obj_type')
496
+ [:image, :generic].include?(obj_type.to_sym)
497
+ else
498
+ blob_attribute = obj_class.attributes['blob']
499
+ blob_attribute && blob_attribute.type == 'binary'
500
+ end
452
501
  end
453
502
 
454
503
  # Returns true if this object is the root object.
@@ -468,9 +517,10 @@ module Scrivito
468
517
  toclist
469
518
  end
470
519
 
471
- # @param objs_to_be_sorted [Array<BasicObj>] unsorted list of Objs
472
- # @param list [Array<BasicObj>] list of Objs that defines the order
473
- # @return [Array<BasicObj>] a sorted list of Objs. Any objs present in +objs_to_be_sorted+ but not in +list+ are appended at the end, sorted by +Obj#id+
520
+ # @param objs_to_be_sorted [Array<Scrivito::BasicObj>] unsorted list of Objs
521
+ # @param list [Array<Scrivito::BasicObj>] list of Objs that defines the order
522
+ # @return [Array<Scrivito::BasicObj>] a sorted list of Objs. Any objs present in
523
+ # +objs_to_be_sorted+ but not in +list+ are appended at the end, sorted by +Obj#id+
474
524
  def self.sort_by_list(objs_to_be_sorted, list)
475
525
  (list & objs_to_be_sorted) + (objs_to_be_sorted - list).sort_by(&:id)
476
526
  end
@@ -482,6 +532,7 @@ module Scrivito
482
532
  _last_changed
483
533
  _path
484
534
  _permalink
535
+ _obj_class
485
536
  title
486
537
  ])
487
538
 
@@ -583,19 +634,8 @@ module Scrivito
583
634
  end
584
635
  end
585
636
 
586
- # For a binary Obj, the content_type is equal to the content_type of its body (i.e. its data).
587
- # For non-binary Objs, a the default content_type is "text/html".
588
- # Override this method in subclasses to define a different content_type.
589
- # Note that only Objs with content_type "text/html"
590
- # will be rendered with layout and templates by the DefaultCmsController.
591
- # @return [String]
592
- # @api public
593
637
  def content_type
594
- if binary?
595
- body_content_type
596
- else
597
- "text/html"
598
- end
638
+ raise "The method `content_type' and `mime_type' were removed. Please use `binary_content_type' instead"
599
639
  end
600
640
  alias mime_type content_type
601
641
 
@@ -619,42 +659,55 @@ module Scrivito
619
659
  end
620
660
  end
621
661
 
622
- # for binary Objs body_length equals the file size
623
- # for non-binary Objs body_length equals the number of characters in the body (main content)
624
662
  # @api public
625
- def body_length
626
- if binary?
627
- blob = find_blob
628
- blob ? blob.length : 0
629
- else
630
- (body || "").length
631
- end
663
+ # This method is intended for Objs that represent binary resources like
664
+ # images or pdf documents. If this Obj represents a binary file, an instance
665
+ # of {Binary} is returned.
666
+ # The default implementation returns the attribute "blob" (if available and
667
+ # of type +binary+).
668
+ # @note You may override this method in your subclasses.
669
+ # @return [Binary, nil]
670
+ def binary
671
+ self[:blob] if self[:blob].is_a?(Binary)
632
672
  end
633
673
 
634
- # returns an URL to retrieve the Obj's body for binary Objs.
635
- # returns +nil+ for non-binary Objs.
636
- # @return [String]
637
674
  # @api public
638
- def body_data_url
639
- if binary?
640
- blob = find_blob
641
- blob.url if blob
642
- end
675
+ # This method returns the length in bytes of the binary of this obj
676
+ # @return [Fixnum] If no binary is set it will return 0
677
+ def binary_length
678
+ binary.try(:content_length) || 0
643
679
  end
644
680
 
645
- # returns the content type of the Obj's body for binary Objs.
646
- # returns +nil+ for non-binary Objs.
647
- # @return [String]
648
681
  # @api public
682
+ # This method returns the content type of the binary of this obj if it is set.
683
+ # @return [String, nil]
684
+ def binary_content_type
685
+ binary.try(:content_type)
686
+ end
687
+
688
+ # @api public
689
+ # This method returns the url under which the content of this binary is
690
+ # available to the public if the binary is set.
691
+ #
692
+ # See {Binary#url} for details
693
+ # @return [String, nil]
694
+ def binary_url
695
+ binary.try(:url)
696
+ end
697
+
698
+ def body_length
699
+ raise %(
700
+ The method `body_length' was removed. Please use
701
+ `binary_length' or `body.length' instead
702
+ )
703
+ end
704
+
705
+ def body_data_url
706
+ raise "The method `body_data_url' was removed. Please use `binary_url' instead"
707
+ end
708
+
649
709
  def body_content_type
650
- if binary?
651
- blob = find_blob
652
- if blob
653
- blob.content_type
654
- else
655
- "application/octet-stream"
656
- end
657
- end
710
+ raise "The method `body_content_type' was removed. Please use `binary_content_type' instead"
658
711
  end
659
712
 
660
713
  def inspect
@@ -675,10 +728,21 @@ module Scrivito
675
728
  find_blob.try(:id)
676
729
  end
677
730
 
678
- # Reverts changes of this object.
679
- # After calling this method it's as if this object has been never modified in the current working copy.
680
- # This method does not work with +new+ or +deleted+ objects.
681
- # This method also does also not work for the +published+ workspace or the +rtc+ working copy.
731
+ #
732
+ # Reverts all changes made to the +Obj+ in the current workspace.
733
+ #
734
+ # @api public
735
+ #
736
+ # @note This method does not support +Obj+s, which are +new+.
737
+ # Please use {Scrivito::BasicObj#destroy Obj#destroy} to destroy them.
738
+ # @note This method does not support +Obj+s, which are +deleted+.
739
+ # Please use {Scrivito::BasicObj.restore Obj.restore} to restore them.
740
+ #
741
+ # @raise [ScrivitoError] If the current workspace is +published+.
742
+ # @raise [ScrivitoError] If the current workspace is the +rtc+ workspace.
743
+ # @raise [ScrivitoError] If the +Obj+ is +new+.
744
+ # @raise [ScrivitoError] If the +Obj+ is +deleted+.
745
+ #
682
746
  def revert
683
747
  assert_revertable
684
748
 
@@ -759,6 +823,12 @@ module Scrivito
759
823
  data_from_cms.to_h.except(*GENERATED_ATTRIBUTES)
760
824
  end
761
825
 
826
+ def parent_path
827
+ unless root?
828
+ path.gsub(/\/[^\/]+$/, '').presence || '/'
829
+ end
830
+ end
831
+
762
832
  private
763
833
 
764
834
  def cms_data_for_revision(revision)
@@ -785,18 +855,12 @@ module Scrivito
785
855
  end
786
856
  end
787
857
 
788
- def parent_path
789
- raise "parent_path called for root" if root?
790
- path.gsub(/\/[^\/]+$/, "").presence || "/"
791
- end
792
-
793
858
  def as_date(value)
794
859
  DateAttribute.parse(value) unless value.nil?
795
860
  end
796
861
 
797
862
  def find_blob
798
- blob_spec = read_attribute('blob')
799
- Blob.find(blob_spec["id"]) if blob_spec
863
+ read_attribute('blob')
800
864
  end
801
865
 
802
866
  def workspace
@@ -823,13 +887,39 @@ module Scrivito
823
887
 
824
888
  def assert_revertable
825
889
  workspace.assert_revertable
826
- raise "revert not supported for binary objs" if binary?
827
890
  if modification == Modification::NEW || modification == Modification::DELETED
828
891
  raise ScrivitoError, "cannot revert changes, since obj is #{modification}."
829
892
  end
830
893
  end
831
894
 
895
+ def binary_title
896
+ binary.filename if binary? && binary
897
+ end
898
+
832
899
  class << self
900
+
901
+ def assert_not_basic_obj(method_name)
902
+ if self == Scrivito::BasicObj
903
+ raise ScrivitoError, "Can not call #{method_name} on Scrivito::BasicObj."
904
+ + " Only call it on Obj or subclasses of Obj"
905
+ end
906
+ end
907
+
908
+ def assert_has_obj_class(method_name)
909
+ unless Workspace.current.obj_classes[name].present?
910
+ raise ScrivitoError, "#{name} has no corresponding ObjClass."
911
+ + " Please use Obj.#{method_name} instead."
912
+ end
913
+ end
914
+
915
+ #
916
+ # Restores a previously deleted +Obj+.
917
+ #
918
+ # @api public
919
+ #
920
+ # @raise [ScrivitoError] If the current workspace is +published+.
921
+ # @raise [ScrivitoError] If the current workspace is the +rtc+ workspace.
922
+ #
833
923
  def restore(obj_id)
834
924
  Workspace.current.assert_revertable
835
925
  base_revision_path = "revisions/#{Workspace.current.base_revision_id}/objs/#{obj_id}"