scrivito_sdk 0.30.0 → 0.40.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/scrivito/objs_controller.rb +29 -20
  3. data/app/controllers/scrivito/ui_controller.rb +18 -0
  4. data/app/helpers/scrivito_helper.rb +21 -4
  5. data/app/views/scrivito/objs/obj.json.jbuilder +1 -0
  6. data/app/views/scrivito/objs/update.json.jbuilder +1 -1
  7. data/app/views/scrivito/ui/index.html.erb +13 -0
  8. data/config/ca-bundle.crt +565 -3
  9. data/config/routes.rb +4 -0
  10. data/lib/assets/javascripts/scrivito_sdk.js +28 -19545
  11. data/lib/assets/javascripts/scrivito_ui.js +30398 -0
  12. data/lib/assets/stylesheets/scrivito_sdk.css +228 -232
  13. data/lib/assets/stylesheets/scrivito_ui.css +3148 -0
  14. data/lib/generators/scrivito/install/install_generator.rb +4 -0
  15. data/lib/generators/scrivito/install/templates/app/views/layouts/scrivito_dialog.html.erb +13 -0
  16. data/lib/generators/scrivito/page/templates/details.html.erb +1 -1
  17. data/lib/generators/scrivito/widget/templates/details.html.erb +1 -1
  18. data/lib/generators/scrivito/widget/templates/show.html.erb +1 -1
  19. data/lib/generators/scrivito/widget/templates/thumbnail.html.erb +2 -2
  20. data/lib/generators/scrivito/widget/widget_generator.rb +12 -0
  21. data/lib/scrivito/attribute_content.rb +4 -5
  22. data/lib/scrivito/basic_obj.rb +47 -8
  23. data/lib/scrivito/basic_widget.rb +41 -14
  24. data/lib/scrivito/cache.rb +2 -0
  25. data/lib/scrivito/child_list_tag.rb +1 -1
  26. data/lib/scrivito/client_config.rb +9 -11
  27. data/lib/scrivito/cms_backend.rb +5 -4
  28. data/lib/scrivito/cms_field_tag.rb +6 -2
  29. data/lib/scrivito/cms_rest_api/attribute_serializer.rb +2 -0
  30. data/lib/scrivito/cms_rest_api/widget_extractor.rb +5 -5
  31. data/lib/scrivito/connection_manager.rb +1 -0
  32. data/lib/scrivito/controller_actions.rb +24 -3
  33. data/lib/scrivito/image_tag_helper.rb +0 -12
  34. data/lib/scrivito/layout_tags.rb +1 -8
  35. data/lib/scrivito/link_parser.rb +5 -0
  36. data/lib/scrivito/{memberships_collection.rb → membership_collection.rb} +2 -2
  37. data/lib/scrivito/obj_class.rb +3 -6
  38. data/lib/scrivito/{objs_collection.rb → obj_collection.rb} +1 -1
  39. data/lib/scrivito/obj_create_params_parser.rb +15 -0
  40. data/lib/scrivito/obj_params_parser.rb +37 -45
  41. data/lib/scrivito/obj_update_params_parser.rb +25 -0
  42. data/lib/scrivito/revision.rb +6 -2
  43. data/lib/scrivito/sdk_engine.rb +6 -1
  44. data/lib/scrivito/test_request.rb +4 -0
  45. data/lib/scrivito/uploaded_binary.rb +4 -0
  46. data/lib/scrivito/user.rb +8 -0
  47. data/lib/scrivito/widget_collection.rb +22 -0
  48. data/lib/scrivito/workspace.rb +11 -6
  49. metadata +17 -11
  50. data/app/views/scrivito/objs/copy.json.jbuilder +0 -1
  51. data/app/views/scrivito/objs/create.json.jbuilder +0 -1
  52. data/app/views/scrivito/objs/details.json.jbuilder +0 -1
  53. data/lib/scrivito/widget_field_params.rb +0 -61
@@ -22,6 +22,10 @@ module Scrivito
22
22
  copy_file 'app/controllers/cms_controller.rb'
23
23
  end
24
24
 
25
+ def copy_dialog_layout
26
+ copy_file 'app/views/layouts/scrivito_dialog.html.erb'
27
+ end
28
+
25
29
  def copy_migration
26
30
  migration_template 'scrivito/migrate/install_scrivito_migration.rb',
27
31
  File.join(Scrivito::Configuration.migration_path, 'install_scrivito_migration.rb')
@@ -0,0 +1,13 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <%= scrivito_head_tags %>
5
+ <%= stylesheet_link_tag 'application', media: 'all' %>
6
+ <%= csrf_meta_tags %>
7
+ </head>
8
+ <body class="scrivito_dialog">
9
+ <%= scrivito_body_tags %>
10
+ <%= yield %>
11
+ <%= javascript_include_tag 'application' %>
12
+ </body>
13
+ </html>
@@ -1 +1 @@
1
- <%= scrivito_tag :div, @obj, :title %>
1
+ <b>Title:</b> <%= scrivito_tag :div, @obj, :title %>
@@ -1,2 +1,2 @@
1
- This is the details view of the <%= class_name %>.
1
+ This is the details view of the <%= human_name %>.
2
2
  Edit "app/views/<%= file_name %>/details.html.erb" to change its appearance.
@@ -1,2 +1,2 @@
1
- This is the show view of the <%= class_name %>.
1
+ This is the show view of the <%= human_name %>.
2
2
  Edit "app/views/<%= file_name %>/show.html.erb" to change its appearance.
@@ -1,3 +1,3 @@
1
- <%%= scrivito_thumbnail '<%= class_name %>' do %>
2
- <%= class_name %> Widget
1
+ <%%= scrivito_thumbnail '<%= human_name %>' do %>
2
+ <!-- Add the detailed description of the widget here. -->
3
3
  <%% end %>
@@ -23,6 +23,18 @@ module Scrivito
23
23
  migration_template 'migration.erb',
24
24
  File.join(Scrivito::Configuration.migration_path, "create_#{file_name}_migration.rb")
25
25
  end
26
+
27
+ def file_name
28
+ super.ends_with?('_widget') ? super : "#{super}_widget"
29
+ end
30
+
31
+ def class_name
32
+ super.ends_with?('Widget') ? super : "#{super}Widget"
33
+ end
34
+
35
+ def human_name
36
+ class_name.underscore.humanize
37
+ end
26
38
  end
27
39
  end
28
40
  end
@@ -59,16 +59,15 @@ module AttributeContent
59
59
  has_attribute?(key) ? read_attribute(key) : nil
60
60
  end
61
61
 
62
- # Hook method to control which widget classes should be available for this page.
63
- # Override it to allow only certain classes or none.
62
+ # Hook method to control which widget classes should be available for this page
63
+ # or widget. Override it to allow only certain classes or none.
64
64
  # Must return either +NilClass+, or +Array+.
65
65
  #
66
- # If +nil+ is returned (default), then all widget classes will be available for this page.
66
+ # If +nil+ is returned (default), then all widget classes will be available for this page or widget.
67
67
  #
68
68
  # If +Array+ is returned, then it should include desired class names.
69
69
  # Each class name must be either a +String+ or a +Symbol+.
70
- # Only this class names will be available for this page.
71
- # Order of the class names will be preserved.
70
+ # Only these class names will be available and their order will be preserved.
72
71
  #
73
72
  # @param [String] field_name Name of the widget field.
74
73
  # @return [nil, Array<Symbol, String>]
@@ -76,12 +76,12 @@ module Scrivito
76
76
  # @example Arrays of {String Strings} allow you to set multi enum fields
77
77
  # Obj.create(:tags => ["ruby", "rails"])
78
78
  #
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.
79
+ # @example Simply pass an Array of {Scrivito::BasicWidget Widgets} to change a widget field. See {Scrivito::BasicWidget#copy Widget#copy} on how to copy a widget.
80
80
  # # Add new widgets
81
81
  # Obj.create(:widgets => [Widget.new(_obj_class: 'TitleWidget', title: 'My Title')])
82
82
  #
83
- # # Add a widget clone
84
- # Obj.create(:widgets => [another_obj.widgets.first.clone])
83
+ # # Add a widget copy
84
+ # Obj.create(:widgets => [another_obj.widgets.first.copy])
85
85
  #
86
86
  # # Changing a widget field
87
87
  # obj.update(:widgets => [obj.widgets.first])
@@ -304,7 +304,7 @@ module Scrivito
304
304
  self
305
305
  end
306
306
 
307
- # Copies itself to a given path.
307
+ # Creates a copy of the +Obj+.
308
308
  # @api public
309
309
  # @param [Hash] options
310
310
  # @option options [String,Symbol] :_path (nil) the path of the copy.
@@ -315,7 +315,7 @@ module Scrivito
315
315
  # @example Copy a blog post.
316
316
  # blog_post = Obj.find_by_path('/blog/first_post')
317
317
  # blog_post.copy(_path: '/blog/second_post')
318
- def copy(options)
318
+ def copy(options={})
319
319
  options = options.stringify_keys.assert_valid_keys('_path', '_id', '_permalink')
320
320
  json = workspace.api_request(:post, '/objs', obj: copyable_attributes.merge(options))
321
321
  self.class.find(json['_id'])
@@ -390,7 +390,7 @@ module Scrivito
390
390
  BasicObj.find_by_path('/') or raise ResourceNotFound,
391
391
  '"Obj.root" not found: There is no "Obj" with path "/". '\
392
392
  'Maybe you forgot the migration when setting up your Scrivito application? '\
393
- 'Try "rake scrivito:migrate" and "rake scrivito:migrate:publish".'
393
+ 'Try "bundle exec rake scrivito:migrate scrivito:migrate:publish".'
394
394
  end
395
395
 
396
396
  # Returns the homepage obj. This can be overwritten in your application's +Obj+.
@@ -720,6 +720,17 @@ module Scrivito
720
720
  instantiate_widget(widget_id, widget_data) if widget_data
721
721
  end
722
722
 
723
+ # @api public
724
+ # Allows accessing the {Scrivito::BasicWidget Widgets} of this Obj
725
+ #
726
+ # @example Access a widget by its id
727
+ # obj.widgets['widget_id']
728
+ #
729
+ # @return [Scrivito::WidgetCollection]
730
+ def widgets
731
+ @widgets ||= WidgetCollection.new(self)
732
+ end
733
+
723
734
  # for internal testing purposes only
724
735
  def blob_id
725
736
  find_blob.try(:id)
@@ -756,6 +767,30 @@ module Scrivito
756
767
  end
757
768
  end
758
769
 
770
+ def restore_widget(widget_id)
771
+ Workspace.current.assert_revertable
772
+
773
+ return if modification == Modification::UNMODIFIED
774
+ if modification == Modification::DELETED
775
+ raise ScrivitoError, 'Can not restore a widget inside a deleted obj'
776
+ end
777
+
778
+ unless widget = in_revision(Workspace.current.base_revision).widgets[widget_id]
779
+ raise ResourceNotFound, "Could not find widget with id #{widget_id}"
780
+ end
781
+
782
+ container = widget.container
783
+ if container.kind_of?(BasicWidget) && !widgets[container.id]
784
+ raise ScrivitoError, 'Can not restore a widget inside a deleted widget'
785
+ end
786
+
787
+ widget_copy = widget.copy_for_restore(read_widget_pool.keys)
788
+ field_name = widget.container_field_name
789
+ current_container = widgets[container.id] || self
790
+ current_container.update(field_name =>
791
+ current_container[field_name].insert(widget.container_field_index, widget_copy))
792
+ end
793
+
759
794
  def mark_resolved
760
795
  workspace.api_request(:put, "/objs/#{id}", obj: {_conflicts: nil})
761
796
  reload
@@ -802,10 +837,13 @@ module Scrivito
802
837
  raise ScrivitoError.new('Could not generate a new unused widget id')
803
838
  end
804
839
 
840
+ #
805
841
  # Generates a +Hash+ containing all public attributes. The hash will _not_ include attributes,
806
842
  # which are read-only or used for internal purpose.
807
- # @api public
843
+ #
844
+ # @api private
808
845
  # @return [Hash] a hash containing all public attributes.
846
+ #
809
847
  # @example
810
848
  # old_obj = Obj.homepage
811
849
  # attrs = old_obj.to_h
@@ -816,12 +854,13 @@ module Scrivito
816
854
  # new_obj = Obj.create(attrs)
817
855
  # puts new_obj == old_obj
818
856
  # #=> true
857
+ #
819
858
  def to_h
820
859
  data_from_cms.to_h.except(*GENERATED_ATTRIBUTES)
821
860
  end
822
861
 
823
862
  def parent_path
824
- unless root?
863
+ unless root? || path.nil?
825
864
  path.gsub(/\/[^\/]+$/, '').presence || '/'
826
865
  end
827
866
  end
@@ -66,25 +66,42 @@ class BasicWidget
66
66
  container.update(container_field_name => new_widget_list)
67
67
  end
68
68
 
69
- # Clones the {Scrivito::BasicWidget Widget}. The clone gets all
70
- # attributes of the original widget except nested widget attributes.
71
- # The clone is not attached to an {Scrivito::BasicObj Obj} initially.
72
- # It only becomes usable by assigning it to a widget attribute of an Obj.
73
69
  #
74
- # @example
75
- # # From another_obj, take the first widget in my_widgets,
76
- # # and put a clone in obj's my_widgets.
77
- # obj.update(my_widgets: [another_obj.my_widgets.first.clone])
70
+ # Create a copy of a {Scrivito::BasicWidget Widget}.
78
71
  #
79
72
  # @api public
80
- def clone
81
- attributes = {}
82
- data_from_cms.all_custom_attributes.each do |attr_name|
83
- if type_of_attribute(attr_name) != 'widget'
84
- attributes[attr_name] = read_attribute(attr_name)
73
+ #
74
+ # The copy will have all attributes of the original widget including its widgets.
75
+ # Its attributes can be accessed only after it has been stored in a widget field of an
76
+ # {Scrivito::BasicObj Obj}, since initially the copy is not stored in any widget field.
77
+ #
78
+ # @example Duplicate the first widget in field +my_widget+
79
+ # obj.update(my_widgets: obj.my_widgets.push(obj.my_widgets.first.copy))
80
+ #
81
+ def copy
82
+ attrs = {}
83
+ each_custom_attribute do |attr_name, attr_value, attr_type|
84
+ attrs[attr_name] = attr_type == 'widget' ? attr_value.map(&:copy) : attr_value
85
+ end
86
+ self.class.new(attrs)
87
+ end
88
+
89
+ def copy_for_restore(referenced_ids)
90
+ attrs = {}
91
+ each_custom_attribute do |attr_name, attr_value, attr_type|
92
+ attrs[attr_name] = if attr_type == 'widget'
93
+ attr_value.reject { |widget| referenced_ids.include?(widget.id) }
94
+ .map { |widget| widget.copy_for_restore(referenced_ids) }
95
+ else
96
+ attr_value
85
97
  end
86
98
  end
87
- self.class.new(attributes)
99
+ attrs['_id'] = id
100
+ self.class.new(attrs)
101
+ end
102
+
103
+ def clone
104
+ raise "The method `clone' was removed. Please use `copy' instead"
88
105
  end
89
106
 
90
107
  def id
@@ -193,6 +210,10 @@ class BasicWidget
193
210
  @container_field_name || cache_container_and_field_name_for_widget.second
194
211
  end
195
212
 
213
+ def container_field_index
214
+ container[container_field_name].index(self)
215
+ end
216
+
196
217
  def has_attribute?(key)
197
218
  key.to_s == '_obj_class' || super
198
219
  end
@@ -265,6 +286,12 @@ class BasicWidget
265
286
  def cache_container_and_field_name_for_widget
266
287
  @cache_container_and_field_name_for_widget ||= obj.container_and_field_name_for_widget(id)
267
288
  end
289
+
290
+ def each_custom_attribute
291
+ data_from_cms.all_custom_attributes.each do |attr_name|
292
+ yield attr_name, read_attribute(attr_name), type_of_attribute(attr_name)
293
+ end
294
+ end
268
295
  end
269
296
 
270
297
  end
@@ -33,6 +33,8 @@ class Cache
33
33
  private
34
34
 
35
35
  def cache_key_for_fallback_backend(key)
36
+ # Workaround for rails/rails#15616. Will be fixed in Rails 4.2.
37
+ key = Digest::SHA1.hexdigest(key)
36
38
  @cache_prefix ? "#{@cache_prefix}/#{key}" : key
37
39
  end
38
40
  end
@@ -11,7 +11,7 @@ class ChildListTag < Struct.new(:tag_name, :obj, :field_name, :view)
11
11
  end
12
12
 
13
13
  def options
14
- return {} unless user_present?
14
+ return {} unless user_present? || obj.path.nil?
15
15
 
16
16
  options = {
17
17
  'private-child-list-allowed-classes' => Obj.valid_page_classes_beneath(obj.path).to_json,
@@ -7,9 +7,9 @@ class ClientConfig < Struct.new(:obj, :editing_context, :lookup_context, :resour
7
7
 
8
8
  def to_json
9
9
  config = {}
10
+ config[:current_page] = current_page_config
10
11
  config[:editing_context] = editing_context_config
11
12
  config[:i18n] = i18n_config
12
- config[:obj] = obj_config
13
13
  config[:resource_dialog] = resource_dialog_config
14
14
  config[:user] = user_config
15
15
  config[:user_permissions] = user_permissions_config
@@ -39,18 +39,16 @@ class ClientConfig < Struct.new(:obj, :editing_context, :lookup_context, :resour
39
39
  {locale: I18n.locale}
40
40
  end
41
41
 
42
- def obj_config
42
+ def current_page_config
43
43
  if obj
44
44
  {
45
- current_page: {
46
- id: obj.id,
47
- obj_class_name: obj.obj_class_name,
48
- has_children: obj.children.any?,
49
- has_conflict: obj.has_conflict?,
50
- has_details_view: obj_has_details_view?,
51
- modification: modification(obj),
52
- restriction_messages: editor.restriction_messages_for(obj),
53
- }
45
+ id: obj.id,
46
+ obj_class_name: obj.obj_class_name,
47
+ has_children: obj.children.any?,
48
+ has_conflict: obj.has_conflict?,
49
+ has_details_view: obj_has_details_view?,
50
+ modification: modification(obj),
51
+ restriction_messages: editor.restriction_messages_for(obj),
54
52
  }
55
53
  else
56
54
  {}
@@ -246,7 +246,7 @@ module Scrivito
246
246
  end
247
247
 
248
248
  def blob_metadata_cache_key(id)
249
- "blob_metadata/#{Digest::SHA1.hexdigest(id)}" # id of a blob is a URI.
249
+ "blob_metadata/#{id}"
250
250
  end
251
251
 
252
252
  def find_obj_data_filtering_deleted_by(revision, index, keys, include_deleted)
@@ -312,9 +312,10 @@ module Scrivito
312
312
  include_deleted: true
313
313
  }
314
314
 
315
- # Not every revision has a workspace,
316
- # e.g. when requesting with the base revision of the current workspace.
317
- params[:workspace_id] = revision.workspace.id if revision.workspace
315
+ # A base revision doesn't have a directly corresponding workspace. Instead it uses its
316
+ # derivative workspace as fallback to access the contents. Thus fallback workspace of a base
317
+ # revision may not be used for backend requests.
318
+ params[:workspace_id] = revision.workspace.id unless revision.base?
318
319
 
319
320
  params
320
321
  end
@@ -43,7 +43,7 @@ class CmsFieldTag < Struct.new(
43
43
 
44
44
  if FIELD_TYPES_WITH_ORIGINAL_CONTENT.include?(field_type)
45
45
  original_value = view.scrivito_value(current_value)
46
- original_content = original_content(field_type, original_value)
46
+ original_content = original_content(field_type, original_value, current_value)
47
47
  encoded_content = Base64.strict_encode64(MultiJson.encode(original_content))
48
48
  options['private-field-original-content'] = encoded_content
49
49
  end
@@ -99,6 +99,8 @@ class CmsFieldTag < Struct.new(
99
99
  referencelist
100
100
  string
101
101
  text
102
+ binary
103
+ date
102
104
  ]
103
105
 
104
106
  def authenticated_editor?
@@ -109,10 +111,12 @@ class CmsFieldTag < Struct.new(
109
111
  EditingContextMiddleware.from_request(view.request)
110
112
  end
111
113
 
112
- def original_content(field_type, field_value)
114
+ def original_content(field_type, field_value, raw_value)
113
115
  case field_type
114
116
  when 'reference' then field_value.try(:id)
115
117
  when 'referencelist' then field_value.map(&:id)
118
+ when 'binary' then field_value && {}
119
+ when 'date' then raw_value.try(:utc).try(:iso8601)
116
120
  else field_value
117
121
  end
118
122
  end
@@ -41,6 +41,8 @@ module Scrivito
41
41
  value.map(&:to_s)
42
42
  elsif value.is_a?(File)
43
43
  Scrivito::CmsRestApi.upload_file(value)
44
+ elsif value.is_a?(UploadedBinary)
45
+ value.params
44
46
  else
45
47
  value.to_s
46
48
  end
@@ -31,11 +31,7 @@ module Scrivito
31
31
  end
32
32
 
33
33
  def self.attach_widget_to_obj(widget, obj)
34
- widget.id = if obj.present?
35
- obj.generate_widget_pool_id
36
- else
37
- BasicObj.generate_widget_pool_id
38
- end
34
+ widget.id = widget.attributes_to_be_saved.delete('_id') || generate_widget_pool_id(obj)
39
35
  widget.obj = obj
40
36
  end
41
37
 
@@ -47,6 +43,10 @@ module Scrivito
47
43
  end
48
44
  end
49
45
  end
46
+
47
+ def self.generate_widget_pool_id(obj)
48
+ obj.presence ? obj.generate_widget_pool_id : BasicObj.generate_widget_pool_id
49
+ end
50
50
  end
51
51
  end
52
52
  end
@@ -34,6 +34,7 @@ module Scrivito
34
34
  # For test purpose only.
35
35
  def clear_cache!
36
36
  @instances = nil
37
+ @cert_store = nil
37
38
  end
38
39
 
39
40
  def cert_store