Class: Scrivito::BasicWidget

Inherits:
Object
  • Object
show all
Extended by:
ActiveSupport::DescendantsTracker, AttributeContent::ClassMethods
Includes:
AttributeContent
Defined in:
app/cms/scrivito/basic_widget.rb

Overview

The CMS widget class. Widget classes let you create widget types for the individual kinds of content editors want to place on pages or into widgets. All widget classes are based on subclasses of Widget. As with CMS objects, the content of a widget is stored in its attributes.

Direct Known Subclasses

Widget

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(attributes = {}) ⇒ BasicWidget

Create a new Widget. The new Widget must be stored inside a container (i.e. an Obj or another Widget) before it can be used.

See Obj.create for a detailed overview of how to set attributes.

Examples:

Create a widget using a subclass:

# you can create widgets by explicitly providing the '_obj_class' attribute
new_widget = Widget.new(_obj_class: "MyWidget")
# but it is better to simply use the constructor of a subclass
new_widget = MyWidget.new

Create a widget and store it inside an Obj:

# create the widget
new_widget = Widget.new(_obj_class: "MyWidget")
# store it inside an obj
my_obj(my_widget_field: [new_widget])

Parameters:

  • attributes (Hash) (defaults to: {})

122
123
124
125
# File 'app/cms/scrivito/basic_widget.rb', line 122

def initialize(attributes = {})
  @attributes_to_be_saved = self.class.prepare_attributes_for_instantiation(attributes)
  @attribute_cache = {}
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class Scrivito::AttributeContent

Instance Attribute Details

#containerScrivito::AttributeContent

Returns the entity (Scrivito::BasicObj or Scrivito::BasicWidget) that references this widget.

Returns:


320
321
322
# File 'app/cms/scrivito/basic_widget.rb', line 320

def container
  @container
end

#container_attribute_nameString

Returns the name of the widgetlist attribute that references this widget.

Returns:

  • (String)

    the name of the attribute that references this widget.


327
328
329
# File 'app/cms/scrivito/basic_widget.rb', line 327

def container_attribute_name
  @container_attribute_name
end

Class Method Details

.attribute(name, type, options = {}) ⇒ Object Originally defined in module AttributeContent::ClassMethods

Defines an attribute.

For the purpose of persisting model data in the CMS, the attributes of the model need to be defined. When defining an attribute, you specify the name under which Scrivito should persist its value as well as the type of content it is meant to contain, and, for the enum and multienum types, the selectable values.

Attributes are inherited. If, for example, the Page model defines a title attribute of the string type, the SpecialPage model, which inherits from Page, is also equipped with title. Inherited attributes can be overridden, i.e. you may redefine title in SpecialPage if you want its type to be html, for example.

Examples:

Defining attributes:

class Page < Obj
  attribute :my_string, :string
  attribute 'my_html', 'my_html'
  attribute :my_enum, :enum, values: %w[a b c], default: 'a'
  attribute :my_multienum, :multienum
end

Page.attribute_definitions
#=> #<Scrivito::AttributeDefinitionCollection>

Page.attribute_definitions[:my_string]
#=> #<Scrivito::AttributeDefinition>

Page.attribute_definitions[:my_string].type
#=> "string"

Page.attribute_definitions[:my_html].type
#=> "html"

Page.attribute_definitions[:my_enum].type
#=> "enum"
Page.attribute_definitions[:my_enum].values
#=> ["a", "b", "c"]

Page.attribute_definitions[:my_multienum].type
#=> "multienum"
Page.attribute_definitions[:my_multienum].values
#=> []

Inheriting attributes:

class Page < Obj
  attribute :title, :string
end

class SpecialPage < Page
end

SpecialPage.attribute_definitions[:title].type
#=> "string"

Overriding inherited attributes:

class Page < Obj
  attribute :title, :string
end

class SpecialPage < Page
  attribute :title, :html
end

Page.attribute_definitions[:title].type
#=> "string"

SpecialPage.attribute_definitions[:title].type
#=> "html"

Specifying valid classes for reference attributes:

class Page < Obj
  attribute :my_reference1, :reference, only: Image
  attribute :my_reference2, :reference, only: [Image, Video]
end

ImageWidget.attribute_definitions[:my_reference1].valid_classes
#=> [Image]

ImageWidget.attribute_definitions[:my_reference2].valid_classes
#=> [Image, Video]

Parameters:

  • name (Symbol, String)

    name of the attribute. The first character must be an ASCII lowercase letter, subsequent characters may also be digits or underscores. Also, the name must not be longer than 50 characters and must not be “id”.

  • type (Symbol, String)

    type of the attribute. Scrivito supports the following types: string, stringlist, html, enum, multienum, widgetlist, reference, referencelist, link, linklist, integer, float and binary.

  • options (Hash) (defaults to: {})

    definition options.

Options Hash (options):

  • :values (Array<Symbol>, Array<String>)

    selectable values for enum and multienum attributes. If no values are provided for attributes of these types, the resulting array of selectable values is empty. Empty string is not allowed as value.

  • :default (Symbol, String)

    custom default value. See DEFAULT_ATTRIBUTE_VALUES for factory defaults. See #default_for for more advanced defaults.

  • :only (Class, Array<Class>)

    specifies (for use in the UI) the valid classes of the values in a reference or a referencelist attribute, e.g. Image or Video. Raises an error if the attribute type is neither reference nor referencelist. If not specified, the UI assumes that any class is valid.

Returns:

  • nil

Raises:

See Also:

.attribute_definitionsScrivito::AttributeDefinitionCollection Originally defined in module AttributeContent::ClassMethods

Returns the attribute definitions.

.default_for(attribute_name, &block) ⇒ Object Originally defined in module AttributeContent::ClassMethods

Sets the default value of an attribute defined by #attribute or for the built-in attributes _path and _permalink.

If Obj.create or Widget.new are called without providing a value for a specific custom attribute, the block is called, and its return value is used as the initial value of this attribute.

The block is called with two parameters.

The first parameter is an ActiveSupport::HashWithIndifferentAccess containing the attributes that were passed to Obj.create or Widget.new.

The second parameter, context, is a hash. If the default_for callback is triggered by a UI user creating a CMS object or a Widget, Scrivito places the :scrivito_user and the :current_page (originating from the UI calling the creation method) into this hash. :current_page won't be present in the context if the user creates the object or widget while viewing a page which isn't a CMS object. The context hash is empty if the object or widget is not created using the UI but, for example, via the console.

Examples:

Setting a simple default:

class MyPage < Obj
  attribute :title, :string
  default_for(:title) { 'Spam' }
end

my_page = MyPage.create
my_page.title # => 'Spam'

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'

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 +MyUser+ model equipped with a +preferences+ method which
  # returns 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

MyUser.find_by(email: '[email protected]').preferences[:locale] # => 'en'
alice = Scrivito::User.define('[email protected]')

my_page = MyPage.create({_path: '/de/test'}, alice)
my_page.title # => 'Your title here'

MyUser.find_by(email: '[email protected]').preferences[:locale] # => 'de'
bob = Scrivito::User.define('[email protected]')

my_page = MyPage.create({_path: '/en/test'}, bob)
my_page.title # => 'Hier den Titel eingeben'

Parameters:

  • attribute_name (Symbol, String)

    the name of the attribute.

  • block (Proc)

    that returns the default value.

Returns:

  • nil

Raises:

See Also:

.description_for_editorString Originally defined in module AttributeContent::ClassMethods

Short description of a CMS object or widget type for the UI.

The description is displayed when adding new pages or widgets, for example. As a general rule, it is used whenever no widget or object instance is available. If there is, the BasicObj#description_for_editor and, respectively, BasicWidget#description_for_editor instance methods are used instead.

This method can be overridden to customize the description displayed to editors.

Returns:

  • (String)

    short description of a CMS object or widget type for the UI

.hide_from_editorObject Originally defined in module AttributeContent::ClassMethods

This method prevents UI users from creating Objs or Widgets of the given type. It does not prevent adding such objects programatically.

By default, hide_from_editor is false.

Examples:

Hiding error pages:

class ErrorPage < Obj
  hide_from_editor
end

Hiding admin widgets:

class AdminWidget < Widget
  hide_from_editor
end

.new(attributes = {}, context = {}) ⇒ Widget

Note:

Note that creating a widget does not implicitly persist it. The widget is only persisted if its creation is part of the creation of the object containing it, or as the object or widget containing it is updated.

Creates a new Widget. The defaults set via Widget.default_for are taken into account.

Parameters:

  • attributes (Hash) (defaults to: {})

    for the new widget

  • context (Hash) (defaults to: {})

    in which the object creating should happen

Options Hash (context):

Returns:

Raises:

  • (ArgumentError)

    if called on Widget without _obj_class attribute.

See Also:


148
149
150
151
152
153
154
# File 'app/cms/scrivito/basic_widget.rb', line 148

def self.new(attributes = {}, context = {})
  if obj_class = extract_obj_class_from_attributes(attributes)
    obj_class.new(attributes, context)
  else
    super(build_attributes_with_defaults(attributes, context))
  end
end

.valid_container_classesNilClass, Array<Class>

This method can be overridden in subclasses to control to which pages or widgets the given widget class can be added. This method can be used to ensure that only a special type of widget can be added to a specific container. An example for this is a TabGroupWidget that should contain widgets of the TabWidget type only, and, vice versa, a TabWidget should only be contained in a TabGroupWidget. A value of NilClass means that no additional restrictions are applied.

This method only further restricts the list of valid classes defined by means of AttributeContent#valid_widget_classes_for.

Examples:

class TabGroupWidget < Widget
  def valid_widget_classes_for(field_name)
    [TabWidget]
  end
end

class TabWidget < Widget
  def self.valid_container_classes
    [TabGroupWidget]
  end
end

Returns:

  • (NilClass, Array<Class>)

50
51
# File 'app/cms/scrivito/basic_widget.rb', line 50

def self.valid_container_classes
end

Instance Method Details

#[](attribute_name) ⇒ Object Originally defined in module AttributeContent

Returns the value of an attribute specified by its name. Passing an invalid key will not raise an error but return nil.

Parameters:

  • attribute_name (Symbol, String)

    the name of the attribute.

Returns:

  • the value of the attribute if it's defined or nil otherwise.

#as_json(options = nil) ⇒ Hash Originally defined in module AttributeContent

Note:

Override it in subclasses to fit your needs.

Returns a hash to be used for the JSON serialization.

Parameters:

  • options (Hash) (defaults to: nil)

Returns:

  • (Hash)

See Also:

#container_field_nameString

Deprecated.

Returns the name of the 'widgetlist' field that references this widget.

Returns:

  • (String)

    the name of the attribute that references this widget.


335
336
337
338
339
340
# File 'app/cms/scrivito/basic_widget.rb', line 335

def container_field_name
  Scrivito::Deprecation.warn('Scrivito::BasicWidget#container_field_name is deprecated'\
    ' and will be removed in a future version.'\
    ' Please use Scrivito::BasicWidget#container_attribute_name instead.')
  container_attribute_name
end

#copyScrivito::BasicWidget

Create a copy of a Widget.

The copy will have all the attributes of the original widget including its widgets. Its attributes can be accessed only after it has been stored in a widgetlist field of an Obj, since initially the copy is not stored in such a field.

Examples:

Duplicate the first widget contained in the my_widget field.

obj.update(my_widgets: obj.my_widgets.push(obj.my_widgets.first.copy))

Returns:


195
196
197
198
199
200
201
202
203
204
# File 'app/cms/scrivito/basic_widget.rb', line 195

def copy
  attributes = {}
  attribute_definitions.each do |attribute_definition|
    attribute_name = attribute_definition.name
    attribute_value = read_attribute(attribute_name)
    attribute_value = attribute_value.map(&:copy) if attribute_definition.widgetlist?
    attributes[attribute_name] = attribute_value
  end
  self.class.new(attributes)
end

#description_for_editorString

This method determines the description to be shown as widget tooltips. By default, it uses the value of the Widget.description_for_editor class method.

This method can be overridden to customize the description displayed to the editor.

Returns:

  • (String)

    description for the editor


392
393
394
# File 'app/cms/scrivito/basic_widget.rb', line 392

def description_for_editor
  self.class.description_for_editor
end

#destroyObject

Destroys the Widget in the current Workspace.


177
178
179
180
# File 'app/cms/scrivito/basic_widget.rb', line 177

def destroy
  new_widget_list = container[container_attribute_name] - [self]
  container.update(container_attribute_name => new_widget_list)
end

#objScrivito::BasicObj

Returns the Obj to which this widget belongs.

Returns:


362
363
364
365
366
367
368
# File 'app/cms/scrivito/basic_widget.rb', line 362

def obj
  if @obj
    @obj
  else
    raise_not_persisted_error
  end
end

#obj_classString Originally defined in module AttributeContent

Returns the object class name of this CMS object.

Examples:

BlogPost.all.first.obj_class
#=> "BlogPost"
FrenchBlog::BlogPost.all.first.obj_class
#=> "FrenchBlog::BlogPost"

GermanBlog::BlogPost.all.first.obj_class
#=> "GermanBlog::BlogPost"

Returns:

  • (String)

#obj_class_nameString Originally defined in module AttributeContent

Deprecated.

Use #obj_class instead.

Returns the object class name of this CMS object.

Returns:

  • (String)

#revertObject

Note:

This method does not support new Widgets. Please use Widget#destroy to destroy them.

Note:

This method does not support deleted Widgets. Please use Obj#revert to restore them.

Reverts all changes made to the Widget in the current workspace.

Raises:


261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
# File 'app/cms/scrivito/basic_widget.rb', line 261

def revert
  workspace.assert_revertable

  case modification
  when Modification::UNMODIFIED
    # do nothing
  when Modification::EDITED
    previous_obj_content =
        CmsRestApi.get("revisions/#{workspace.base_revision_id}/objs/#{obj.id}")
    previous_widget_content = previous_obj_content["_widget_pool"]["#{id}"]
    previous_widget_content = self.class.reset_blank_attributes(previous_widget_content)

    previous_widget_content.delete_if do |attribute_name, _|
      type_of_attribute(attribute_name) == 'widgetlist'
    end
    CmsRestApi.put("workspaces/#{workspace.id}/objs/#{obj.id}",
        { obj: {_widget_pool: {id => previous_widget_content}} })
    reload
  else
    raise ScrivitoError, "cannot revert changes, since widget is #{modification}"
  end
end

#update(attributes) ⇒ Object

Updates the attributes of this Widget. See Obj.create for a detailed overview of how to set attributes.

Examples:

Set the title of a specific widget contained in the 'main_content' attribute of an Obj:

obj.main_content[4].update(title: "New Title")

Add a widget to the 'main_content' widgetlist attribute:

widgets = obj.main_content << MyWidget.new(title: "Fresh")
obj.update(main_content: widgets)

Parameters:

  • attributes (Hash)

170
171
172
173
# File 'app/cms/scrivito/basic_widget.rb', line 170

def update(attributes)
  obj.update(_widget_pool: { self => attributes })
  reload
end

#valid_widget_classes_for(field_name) ⇒ nil, Array<Class> Originally defined in module AttributeContent

Hook method that lets you control the widget classes that are made available for adding instances of them to this page or widget. Override it to allow only specific classes or none at all. Must return either NilClass, or Array.

If nil is returned (default), all widget classes will be available for this page or widget.

If an Array is returned, it is expected to include the permitted classes. Their order is preserved as they are offered to the user via the widget browser.

Parameters:

  • field_name (String)

    Name of the widget attribute.

Returns:

  • (nil, Array<Class>)

See Also: