scrivito_sdk 0.30.0 → 0.40.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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