lib/scrivito/attribute_content.rb in scrivito_sdk-0.60.0 vs lib/scrivito/attribute_content.rb in scrivito_sdk-0.65.0.rc1

- old
+ new

@@ -27,10 +27,15 @@ %w[string html], %w[string text], %w[widgetlist widget], ] + # + # @api public + # + # Default attribute values. + # DEFAULT_ATTRIBUTE_VALUES = { 'binary' => nil, 'date' => nil, 'enum' => nil, 'html' => '', @@ -38,11 +43,10 @@ 'linklist' => [], 'multienum' => [], 'reference' => nil, 'referencelist' => [], 'string' => '', - 'text' => '', 'widgetlist' => [], } delegate :attribute_definitions, to: :class @@ -109,20 +113,24 @@ end def valid_widget_ruby_classes_for(field) computed_classes = valid_widget_classes_for(field) - return Scrivito.models.widgets.to_a unless computed_classes + ruby_classes = + if computed_classes + type_computer = Widget.type_computer + self.class.convert_to_obj_classes(computed_classes, type_computer) do |name| + Rails.logger.warn("Invalid widget class #{name} returned by #valid_widget_classes_for.") + end + else + Scrivito.models.widgets.to_a + end - type_computer = Widget.type_computer - ruby_classes = self.class.convert_to_obj_classes(computed_classes, type_computer) do |name| - Rails.logger.warn("Invalid widget class #{name} returned by #valid_widget_classes_for.") - end - ruby_classes.select { |ruby_class| ruby_class.valid_inside_container?(self.class) } end + def modification_for_attribute(attribute_name, revision=Workspace.current.base_revision) return Modification::UNMODIFIED unless revision if new?(revision) Modification::NEW @@ -288,15 +296,11 @@ end end def extract_obj_class_from_attributes(attributes) if special_class? && (obj_class_name = attributes[:_obj_class] || attributes['_obj_class']) - if obj_class = type_computer.compute_type_without_fallback(obj_class_name) - obj_class - else - raise ObjClassNotFound - end + type_computer.compute_type_without_fallback(obj_class_name) end end # # Defines an attribute. @@ -317,19 +321,24 @@ # +html+, +enum+, +multienum+, +widgetlist+, +reference+, +referencelist+ and +binary+. # @param [Hash] options definition options. # # @option options [Symbol, String] :values allowed values for types +enum+ and +multienum+. # If no values are given for that types, then an empty array will be assumed. + # @option options [Symbol, String] :default custom default value. + # See {Scrivito::AttributeContent::DEFAULT_ATTRIBUTE_VALUES} for factory defaults. + # See {Scrivito::AttributeContent::ClassMethods#default_for} for more advanced default settings. # # @return nil # @raise [Scrivito::ScrivitoError] if the +type+ is unknown + # @see Scrivito::AttributeContent::DEFAULT_ATTRIBUTE_VALUES + # @see Scrivito::AttributeContent::ClassMethods#default_for # # @example Defining attributes # class Page < ::Obj # attribute :my_string, :string # attribute 'my_html', 'my_html' - # attribute :my_enum, :enum, values: %w[a b c] + # attribute :my_enum, :enum, values: %w[a b c], default: 'a' # attribute :my_multienum, :multienum # end # # Page.attribute_definitions # #=> #<Scrivito::AttributeDefinitionCollection> @@ -381,15 +390,119 @@ # def attribute(name, type, options = {}) name, type, options = name, type, options assert_valid_attribute_name(name.to_s) assert_valid_attribute_type(type.to_s) + default = options.delete(:default) || options.delete('default') own_attribute_definitions[name.to_s] = AttributeDefinition.new(name, type, options) + default_for(name) { default } if default nil end # + # Sets a default for an attribute. + # + # @api public + # + # If {Scrivito::BasicObj.create Obj.create} or {Scrivito::BasicWidget.new Widget.new} are called + # with no value provided for attribute +attribute_name+, then the +block+ will be called and its + # return value will be set as the attribute's value. + # + # The +block+ will be called with two parameters. + # + # The first parameter is an +ActiveSupport::HashWithIndifferentAccess+ containing attributes, + # which were provided to {Scrivito::BasicObj.create Obj.create} or + # {Scrivito::BasicWidget.new Widget.new}. + # + # The second parameter is a +Hash+ containing the context, which were provided to + # {Scrivito::BasicObj.create Obj.create} or {Scrivito::BasicWidget.new Widget.new}. If there is + # a current visitor of type {Scrivito::User}, then it can be accessed under the key + # +:scrivito_user+ in the provided context. + # + # @param [Symbol, String] attribute_name the name of the attribute. + # @param [Proc] block that returns the default value. + # @raise [Scrivito::ScrivitoError] if no block given + # @return nil + # + # @see Scrivito::BasicObj.create + # @see Scrivito::BasicWidget.new + # @see Scrivito::Configuration.editing_auth + # + # @example A simple default + # class MyPage < Obj + # attribute :title, :string + # default_for(:title) { 'Spam' } + # end + # + # my_page = MyPage.create + # my_page.title # => 'Spam' + # + # @example A default depending on the given attributes + # class MyPage < Obj + # attribute :title, :string + # + # default_for :title do |attributes| + # if (path = attributes[:_path]) && path.starts_with('/de') + # 'Hier den Titel eingeben' + # else + # 'Your title here' + # end + # end + # end + # + # my_page = MyPage.create(_path: '/en/test') + # my_page.title # => 'Your title here' + # + # my_page = MyPage.create(_path: '/de/test') + # my_page.title # => 'Hier den Titel eingeben' + # + # @example A more complex default depending on the given attributes and the current user + # class MyPage < Obj + # attribute :title, :string + # + # default_for :title do |attributes, context| + # if use_german_title?(context[:scrivito_user], attributes[:_path]) + # 'Hier den Titel eingeben' + # else + # 'Your title here' + # end + # end + # + # private + # + # # + # # Assuming there is a model +MyUser+ with a +preferences+ method returning the preferences + # # of the current user. + # # The +email+ of a +MyUser+ is the +id+ of the corresponding +Scrivito::User+ as set in + # # +Scrivito::Configuration.editing_auth+. + # # + # def use_german_title?(scrivito_user, path) + # scrivito_user && MyUser.find_by(email: scrivito_user.id).preferences[:locale] == 'de' || + # path && path.starts_with?('/de') + # end + # end + # + # alice = Scrivito::User.define('alice@scrivito.com') + # alice.preferences[:locale] # => 'en' + # + # my_page = MyPage.create({_path: '/de/test'}, alice) + # my_page.title # => 'Your title here' + # + # bob = Scrivito::User.define('bob@scrivito.com') + # bob.preferences[:locale] # => 'de' + # + # my_page = MyPage.create({_path: '/en/test'}, bob) + # my_page.title # => 'Hier den Titel eingeben' + # + def default_for(attribute_name, &block) + attribute_name = attribute_name.to_s + raise ScrivitoError, 'No block given' unless block_given? + attribute_defaults[attribute_name] = block + nil + end + + # # This method determines the description that is shown in the UI # and defaults to class name. It can be overriden by a custom value. # # @api public # @@ -406,10 +519,36 @@ # def attribute_definitions AttributeDefinitionCollection.new(all_attribute_definitions) end + # For test purpose only. + def reset_attribute_defaults! + @attribute_defaults = nil + end + + def build_attributes_with_defaults(attributes = {}, context = {}) + attributes_with_indifferent_access = attributes.with_indifferent_access + attributes_with_defaults = attributes_with_indifferent_access.dup + + attribute_definitions.each do |attribute_definition| + attribute_name = attribute_definition.name + attribute_default = attribute_defaults[attribute_name] + if !attributes_with_indifferent_access.has_key?(attribute_name) && attribute_default + attributes_with_defaults[attribute_name] = + attribute_default.call(attributes_with_indifferent_access, context) + end + end + + if superclass.respond_to?(:build_attributes_with_defaults) + attributes_with_defaults.merge!( + superclass.build_attributes_with_defaults(attributes_with_defaults, context)) + end + + attributes_with_defaults + end + # @api public # # This method disables the creation of +Objs+ or +Widgets+ of the given type using the UI. # It does not prevent adding these objects programatically. # @@ -436,15 +575,14 @@ type_computer.special_class?(self) end def convert_to_obj_classes(class_names, type_computer) class_names.inject([]) do |classes, class_name| - widget_class = type_computer.compute_type_without_fallback(class_name.to_s) - - if widget_class + begin + widget_class = type_computer.compute_type_without_fallback(class_name.to_s) classes << widget_class - else + rescue ObjClassNotFound yield class_name end classes end end @@ -486,9 +624,13 @@ def assert_valid_obj_class(obj_class) unless obj_class == to_s raise ScrivitoError, "Cannot set _obj_class to #{obj_class.inspect} when creating #{self}" end + end + + def attribute_defaults + @attribute_defaults ||= {} end end end end