lib/scrivito/basic_obj.rb in scrivito_sdk-0.70.2 vs lib/scrivito/basic_obj.rb in scrivito_sdk-0.71.0.rc1

- old
+ new

@@ -130,12 +130,16 @@ obj_class.create(attributes, context) else attributes = build_attributes_with_defaults(attributes, context) attributes = prepare_attributes_for_instantiation(attributes) api_attributes, widget_properties = prepare_attributes_for_rest_api(attributes) - json = Workspace.current.api_request(:post, '/objs', obj: api_attributes) - obj = find(json['_id']) + + workspace = Workspace.current + obj_data = workspace.create_obj(obj: api_attributes) + obj = BasicObj.instantiate(obj_data) + obj.revision = workspace.revision + CmsRestApi::WidgetExtractor.notify_persisted_widgets(obj, widget_properties) obj end end @@ -185,18 +189,21 @@ # @api public def self.find_including_deleted(id_or_list) Workspace.current.objs.find_including_deleted(id_or_list) end - # Returns an {ObjSearchEnumerator} with the given initial subquery consisting of the four arguments. + # Returns an {Scrivito::ObjSearchEnumerator} with the given initial subquery consisting of the + # four arguments. # - # Note that +field+ and +value+ can also be arrays for searching several fields or searching for several values. + # Note that +field+ and +value+ can also be arrays for searching several fields or searching for + # several values. # - # @note If invoked on a subclass of Obj, the result will be restricted to instances of this subclass. + # @note If invoked on a subclass of Obj, the result will be restricted to instances of this + # subclass. # - # {ObjSearchEnumerator}s can be chained using one of the chainable methods - # (e.g. {ObjSearchEnumerator#and} and {ObjSearchEnumerator#and_not}). + # {Scrivito::ObjSearchEnumerator}s can be chained using one of the chainable methods + # (e.g. {Scrivito::ObjSearchEnumerator#and} and {Scrivito::ObjSearchEnumerator#and_not}). # # @example Look for objects containing "Lorem", boosting headline matches: # Obj.where(:*, :contains, 'Lorem', headline: 2).to_a # # @example Look for the first 10 objects whose object class is "Pressrelease" and whose title contains "quarterly": @@ -205,16 +212,17 @@ # @example Look for all objects whose class is "Item". The path should start with a defined location. Furthermore, select only items of a particular category: # Obj.where(:_obj_class, :equals, 'Item').and(:_path, :starts_with, '/en/items/').select do |item| # item.categories.include?(category) # end # - # @param [Symbol, String, Array<Symbol, String>] field See {ObjSearchEnumerator#and} for details - # @param [Symbol, String] operator See {ObjSearchEnumerator#and} for details - # @param [String, Array<String>] value See {ObjSearchEnumerator#and} for details - # @param [Hash] boost See {ObjSearchEnumerator#and} for details - # @raise [ScrivitoError] if called directly on +BasicObj+. Use +Obj.where+ instead. - # @return [ObjSearchEnumerator] + # @param [Symbol, String, Array<Symbol, String>] field See {Scrivito::ObjSearchEnumerator#and} + # for details + # @param [Symbol, String] operator See {Scrivito::ObjSearchEnumerator#and} for details + # @param [String, Array<String>] value See {Scrivito::ObjSearchEnumerator#and} for details + # @param [Hash] boost See {Scrivito::ObjSearchEnumerator#and} for details + # @raise [ScrivitoError] if called directly on {Scrivito::BasicObj}. Use +Obj.where+ instead. + # @return [Scrivito::ObjSearchEnumerator] # @api public def self.where(field, operator, value, boost = nil) assert_not_basic_obj('.where') if self == ::Obj Workspace.current.objs.where(field, operator, value, boost) @@ -222,27 +230,27 @@ Workspace.current.objs.where(:_obj_class, :equals, name) .and(field, operator, value, boost) end end - # Returns an {ObjSearchEnumerator} of all {Scrivito::BasicObj Obj}s. + # Returns an {Scrivito::ObjSearchEnumerator} of all {Scrivito::BasicObj Obj}s. # If invoked on a subclass of Obj, the result is restricted to instances of this subclass. - # @return [ObjSearchEnumerator] - # @raise [ScrivitoError] if called directly on +BasicObj+. Use +Obj.all+ instead. + # @return [Scrivito::ObjSearchEnumerator] + # @raise [ScrivitoError] if called directly on +Scrivito::BasicObj+. Use +Obj.all+ instead. # @api public def self.all assert_not_basic_obj('.all') if self == ::Obj Workspace.current.objs.all else find_all_by_obj_class(name) end end - # Returns an {ObjSearchEnumerator} of all CMS objects with the given +obj_class+. + # Returns an {Scrivito::ObjSearchEnumerator} of all CMS objects with the given +obj_class+. # @param [String] obj_class name of the object class. - # @return [ObjSearchEnumerator] + # @return [Scrivito::ObjSearchEnumerator] # @api public def self.find_all_by_obj_class(obj_class) Workspace.current.objs.find_all_by_obj_class(obj_class) end @@ -263,13 +271,13 @@ # @api public def self.find_by_name(name) where(:_name, :equals, name).batch_size(1).first end - # Returns an {ObjSearchEnumerator} of all CMS objects with the given name. + # Returns an {Scrivito::ObjSearchEnumerator} of all CMS objects with the given name. # @param [String] name Name of the {Scrivito::BasicObj Obj}. - # @return [ObjSearchEnumerator] + # @return [Scrivito::ObjSearchEnumerator] # @api public def self.find_all_by_name(name) where(:_name, :equals, name) end @@ -280,12 +288,13 @@ # @api public def self.find_by_permalink(permalink) Workspace.current.objs.find_by_permalink(permalink) end - # Returns the {Scrivito::BasicObj Obj} with the given permalink, or raises ResourceNotFound if - # no matching Obj exists. + # Returns the {Scrivito::BasicObj Obj} with the given permalink, or raises + # {Scrivito::ResourceNotFound} if no matching Obj exists. + # # @param [String] permalink The permalink of the {Scrivito::BasicObj Obj}. # @return [Obj] # @api public def self.find_by_permalink!(permalink) find_by_permalink(permalink) or @@ -311,12 +320,12 @@ # @api public def self.valid_page_classes_beneath(parent_path) end def self.valid_page_ruby_classes_beneath(parent_path) - assert_classes(valid_page_classes_beneath(parent_path), '.valid_page_classes_beneath') || - Scrivito.models.pages.to_a + result = assert_classes(valid_page_classes_beneath(parent_path), '.valid_page_classes_beneath') + (result || Scrivito.models.pages.to_a).uniq end # Update the {Scrivito::BasicObj Obj} using the attributes provided. # # For an overview of the values you can set via this method, see the @@ -360,12 +369,11 @@ # } # } # ) def update(attributes) api_attributes, widget_properties = prepare_attributes_for_rest_api(attributes) - workspace.api_request(:put, "/objs/#{id}", obj: api_attributes) - reload_data + update_data(workspace.update_obj(id, obj: api_attributes)) CmsRestApi::WidgetExtractor.notify_persisted_widgets(self, widget_properties) self end # Creates a copy of the {Scrivito::BasicObj Obj}. @@ -386,11 +394,11 @@ json = workspace.api_request(:post, '/objs', obj: attributes_for_copy.merge(options)) self.class.find(json['_id']) end - # Destroys the {Scrivito::BasicObj Obj} in the current {Workspace}. + # Destroys the {Scrivito::BasicObj Obj} in the current {Scrivito::Workspace}. # @api public def destroy if children.any? raise ClientError.new(I18n.t('scrivito.errors.models.basic_obj.has_children'), 412) end @@ -475,11 +483,11 @@ # If the controller does not exist, the CmsController is used as a fallback. # Override this method to force a different controller to be used. # @return [String] # @api public def controller_name - obj_class_name + obj_class end # This method determines the action to be invoked when the +Obj+ is requested. # The default action is 'index'. # Override this method to force a different action to be used. @@ -778,11 +786,11 @@ # @api public # This method returns the URL under which the content of this binary is # available to the public if the binary is set. # - # See {Binary#url} for details + # See {Scrivito::Binary#url} for details # @return [String, nil] def binary_url binary.try(:url) end @@ -803,18 +811,10 @@ def inspect "<#{self.class} id=\"#{id}\" path=\"#{path}\">" end - def details_view_path - view_path('details') - end - - def embed_view_path - view_path('embed') - end - def widget_from_pool(widget_id) widget_data = widget_data_from_pool(widget_id) instantiate_widget(widget_id, widget_data) if widget_data end @@ -857,10 +857,12 @@ previous_widget_pool = previous_attributes['_widget_pool'] ids_of_new_widgets = read_widget_pool.keys - previous_widget_pool.keys ids_of_new_widgets.each { |widget_id| previous_widget_pool[widget_id] = nil } + previous_attributes = reset_blank_attributes(previous_attributes) + workspace.api_request(:put, "/objs/#{id}", obj: previous_attributes) reload end end @@ -951,12 +953,17 @@ cms_data_for_revision(base_revision) != cms_data_for_revision(published_revision) end def transfer_modifications_to(target_workspace) return unless modification + if has_conflict? || future_conflict?(target_workspace) + raise TransferModificationsConflictError, "Transfer will result in a conflict. " \ + "Please update the current and target workspace and ensure they are conflict free." + end if in_revision(target_workspace.revision).try(:modification) - raise TransferModificationsError, "Already modified in workspace #{target_workspace.id}" + raise TransferModificationsModifiedError, + "Already modified in workspace #{target_workspace.id}" end copy_modifications_to(target_workspace) reset_modifications end @@ -968,41 +975,31 @@ when deleted? then destroy_in(target_workspace) else update_in(target_workspace) end end + def future_conflict?(target_workspace) + base_revision = workspace.base_revision + target_base_revision = target_workspace.base_revision + + base_revision != target_base_revision && + cms_data_for_revision(base_revision) != cms_data_for_revision(target_base_revision) + end + def create_in(target_workspace) target_workspace.api_request(:post, '/objs', obj: get_attributes) end def update_in(target_workspace) - update_attributes = fill_in_missing_attributes_as_nil(copyable_attributes) + update_attributes = reset_blank_attributes(copyable_attributes) target_workspace.api_request(:put, "/objs/#{id}", obj: update_attributes) end def destroy_in(target_workspace) target_workspace.api_request(:delete, "/objs/#{id}") end - def fill_in_missing_attributes_as_nil(attributes, type_computer=Obj.type_computer) - obj_class = type_computer.compute_type_without_fallback(attributes['_obj_class']) - missing_attributes = obj_class.attribute_definitions.map(&:name) - attributes.keys - - if attributes['_widget_pool'] - attributes['_widget_pool'].each do |id, widget_attributes| - attributes['_widget_pool'][id] = fill_in_missing_attributes_as_nil( - widget_attributes, Widget.type_computer) - end - end - - missing_attributes.each do |attribute_name| - attributes[attribute_name] = nil - end - - attributes - end - def reset_modifications case when new? then destroy when deleted? then self.class.restore(id) else revert @@ -1030,11 +1027,11 @@ widget.obj = self end end def as_date(value) - DateAttribute.deserialize_from_backend(value) unless value.nil? + DateAttribute.parse(value) unless value.nil? end def find_blob read_attribute('blob') end @@ -1062,11 +1059,11 @@ def get_attributes workspace.api_request(:get, "/objs/#{id}") end def copy_binaries(attributes) - attribute_defintions = self.class.find_attribute_definitions(obj_class_name) + attribute_defintions = self.class.find_attribute_definitions(obj_class) destination_obj_id = attributes.fetch(:_id) Hash[attributes.map do |name, value| if value && attribute_defintions[name].try(:type) == 'binary' binary = self[name] @@ -1095,14 +1092,10 @@ def binary_title binary.filename if binary? && binary end - def view_path(view_name) - "#{obj_class_name.underscore}/#{view_name}" - end - def has_system_attribute?(attribute_name) !!SYSTEM_ATTRIBUTES[attribute_name] end def has_public_system_attribute?(attribute_name) @@ -1113,14 +1106,21 @@ SYSTEM_ATTRIBUTES[attribute_name].try(:type) end def value_of_system_attribute(attribute_name) attribute_value = data_from_cms.value_of(attribute_name) - if attribute_name == '_last_changed' - DateAttribute.deserialize_from_backend(attribute_value) - else - attribute_value + attribute_name == '_last_changed' ? DateAttribute.parse(attribute_value) : attribute_value + end + + def reset_blank_attributes(attributes) + widget_pool = attributes['_widget_pool'] + widget_pool.each do |id, widget_attributes| + if widget_attributes + widget_pool[id] = Widget.reset_blank_attributes(widget_attributes) + end end + + self.class.reset_blank_attributes(attributes) end class << self def assert_not_basic_obj(method_name) if self == Scrivito::BasicObj