provideEditingConfig(name, editingConfig)

Configures user-interface-related aspects of a CMS object or widget class.

Using this API, the localization and the appearance of page and widget classes can be configured. This includes selection dialogs, details views, initial content of newly created pages and widgets, as well as content validation checks.

Params

  • name (String) – name if the object or or widget class for which the editing configuration is provided.
  • editingConfig (Object) – User-interface-related configuration:
    • title (String) – The title of the object or widget class.
    • thumbnail (String) – The thumbnail for the object or widget class.
    • description (String) – The description of the object or widget class.
    • titleForContent (Function) – Defines a callback that determines the title of an Obj or a Widget instance. The instance concerned is passed to the function. See below for details.
    • descriptionForContent (Function) – Defines a callback that determines the description of an Obj instance. The instance concerned is passed to the function. See below for details.
    • attributes (Object) – The localizations of the Obj or Widget class attributes. See below for details and an example.
    • properties (Array<String>) – List of attributes to be shown on the “General” tab. See below for details and an example.
    • propertiesGroups (Array<Object>) – List of custom tabs onto which any subset of properties can be placed. See below for details.
    • hideInSelectionDialogs (Boolean) – Hides the object or widget class in selection dialogs from editors. The default is false.
    • initialContent (Object) – Provides automatically preset content to UI users when creating new pages or widgets. See below for details and some examples.
    • [New in 1.7.0]validations (Array<Function | Array>) – Specifies rules for content validation. See below for details and examples.

Throws

  •  ArgumentError - Passing in custom propTypes is not allowed.

Appearance of widget and page types in selection dialogs

Using the class configuration options, title, thumbnail, and description, you can control how the Scrivito UI presents CMS object and widget classes, for example as an editor creates new pages and widgets. The following example provides a title, an icon and a description for a LandingPage class:

Copy
Scrivito.provideEditingConfig('LandingPage', {
  title: "Landing page",
  description: "General lead capture page for our products and services.",
  thumbnail: "https://www.example.com/page-icon.png"
});

As an editor creates a new page, the LandingPage type is offered nicely in the selection dialog:

Appearance of individual pages and resources in dialogs

The titleForContent and descriptionForContent functions define callbacks that determine the title of a CMS object or widget instance for display in the details view or the Content Browser. The instance concerned is passed to the function. In the following example the title and the description of an instance are derived from its equally named attributes.

If the title attribute of the selected page reads “Why you should use our service”, the page will be presented in the Content Browser like shown above.

Copy
Scrivito.provideEditingConfig('LandingPage', {
  titleForContent(obj) {
    return obj.get("title");
  },

  descriptionForContent(obj) {
    return obj.get("description");
  }
});

The Scrivito UI uses the text provided through descriptionForContent in places where a more verbose description of an item is appropriate. 

Populating the “General” tab of details views

The properties option lets you specify the list of attributes to be included on the “General” tab. If this configuration has not been provided, the tab will not be displayed for widgets, while for CMS objects the tab will only contain some pieces of system information. The order of the attribute names is acknowledged. If a title has been provided via attributes, it will be used. Otherwise, the title is derived from the attribute’s name; “backgroundColor” becomes “Background color”, for example.

Using the attributes definition, a title and a description can be provided for each attribute to specify how it should be displayed on the details view of a page or a widget. For an enum or multienum attribute, a values key can be provided to localize the available attribute values. Note that, for an attribute to show up on the “General” tab, it still needs to be included in the properties list.

Here's an example of a LandingPage whose tags and backgroundColor attributes should be displayed on the “General” tab. The type of the backgroundColor attribute is enum, so we can also localize its values:

Copy
Scrivito.provideEditingConfig('LandingPage', {
  properties: ["backgroundColor", "tags"],

  attributes: {
    tags: {
      title: "Search tags",
      description: "Please provide some tags for the search engines."
    },

    backgroundColor: {
      title: "Background color",
      description: "Select the background color of the page.",

      values: [
        { value: "#f44141", title: "Red" },
        { value: "#4286f4", title: "Blue" },
        { value: "#41f44c", title: "Green" }
      ]
    },
  }
});

The above configuration includes the attributes on the “General” tab of the page properties view, using the provided titles, descriptions and values.

Adding custom tabs to property views

The propertiesGroups option lets you provide a list of custom tabs containing attribute properties. Each group should have a title key (the title of the resulting tab) and the properties key specifying the names of the attributes to be displayed on the tab. The order of the attribute names is acknowledged.

As with the properties option, if an attribute’s title has been specified in the attributes definition, it will be used in propertiesGroups. Otherwise, the title is derived from the attribute’s name; “backgroundColor” becomes “Background color”, for example.

In the following example, we want Scrivito to display the facebookUrl and twitterUrl attributes on a dedicated “Social media” tab:

Copy
Scrivito.provideEditingConfig('LandingPage', {
  // ...

  propertiesGroups: [
    {
      title: "Social media",
      properties: ["facebookUrl", "twitterUrl"]
    }
  ]
});

Using Scrivito extensions in property views

A Scrivito extension is a component your application provides for the purpose of integrating it into the Scrivito UI. The propertiesGroups option not only lets you specify a list of custom tabs populated with attribute properties, but alternatively lets you define custom tabs on which Scrivito extensions are used (you cannot have both). For a properties group to use an extension, provide it with a title key and a component key, the latter specifying the name (String) of the component to be rendered, like so:

Copy
Scrivito.provideEditingConfig('LandingPage', {
  // ...

  propertiesGroups: [
    {
      title: "Social media",
      component: "SocialMediaTab",
    }
  ]
});

The component to be rendered must be registered using Scrivito.registerComponent. It will receive either a widget or a page prop, depending on whether it is meant to be shown in the details view of a widget or, respectively, a page. If, however, the component is to be displayed in the details view of an Obj not representing a page but, for example, an image, an obj prop will be passed to it.

Here’s an example of a component that adds two string attributes to the above SocialMediaTab properties view tab.

Copy
function SocialMediaTab(props) {
  return [
    <p className="socialTabItem">
      <Scrivito.ContentTag content={props.page} attribute="facebookUrl" />
      <a href={props.page.get("facebookUrl")} target="_blank">Visit Facebook</a>
    </p>,

    <p className="socialTabItem">
      <Scrivito.ContentTag content={props.page} attribute="twitterUrl" />
      <a href={props.page.get("twitterUrl")} target="_blank">Visit Twitter</a>
    </p>,
  ];
}

Scrivito.registerComponent("SocialMediaTab", SocialMediaTab);

For a more extensive example, take a look at our tutorial on Customizing page and widget property editing.

When using extensions in combination with content validation, you can have the Scrivito UI highlight custom tabs containing invalid content. For this, specify the list of attributes to be validated using the properties key:

Copy
Scrivito.provideEditingConfig('LandingPage', {
  propertiesGroups: [
    {
      title: "Social media",
      component: "SocialMediaTab",
      properties: ["tcCreator", "tcImage"]
    }
  ]
});

Providing initial content

The initialContent option lets you specify the default initial content when creating new pages or widgets via the user interface.

Each key inside initialContent refers to an attribute in the Obj or Widget. Each value is either a value that fits the attribute's type (compare Obj.create, for example), or a callback that returns such a value. If a callback is given, it is executed each time an Obj or Widget is initialized, and the returned value is used.

Copy
Scrivito.provideEditingConfig("LandingPage", {
  properties: ["backgroundColor", "tags", "campaignStartDate"],

  // ...

  initialContent: {
    body: [
      new HeadlineWidget({ headline: "New Landing Page!" }),
      new TextWidget({ text: "What do you want visitors to know and to do?" }),
    ],

    backgroundColor: "#41f44c",

    // Each time a new landing page is created, it is supposed to be
    // part of a campaign that starts immediately. 
    // If necessary, the editor can adjust the start date afterwards.

    campaignStartDate() {
      return new Date();
    }
  }
});

The provided initial content will be used:

  • When a new Widget or Obj is created using the UI (regardless of how this is done: menus, buttons, Content Browser – all apply the initialContent).
  • When a new Widget or Obj is created programmatically by the customer's app using the Obj.create or new Widget API.

Initial content will not be applied if a new Widget or Obj is created by copying (or duplicating) an existing one.

When creating Objs or Widgets via the API, initialContent values are only given to attributes whose values aren’t set by the API call. In other words: If a Widget or Obj is created with an explicitly given value, the corresponding initialContent value is ignored.

Initial content may be provided for any custom attribute but not for system attributes.

Full example

Copy
Scrivito.provideObjClass("LandingPage", {
  attributes: {
    // The main content should be edited in place.
    body: "widgetlist",

    // Properties to be edited in the properties dialog:
    tags: "stringlist",
    backgroundColor: ["enum", { values: ["#f44141", "#4286f4", "#41f44c"] }],
    campaignStartDate: "date",
    
    // Properties for the "Social media" tab:
    facebookUrl: "string",
    twitterUrl: "string"
  }
});

Scrivito.provideEditingConfig("LandingPage", {
  title: "Landing Page",
  description: "A Landing Page description.",
  thumbnail: "http://www.example.com/page_icon.png",

  properties: ["backgroundColor", "tags", "campaignStartDate"],

  attributes: {
    tags: {
      title: "Search tags",
      description: "Please provide some tags for the search engines."
    },

    backgroundColor: {
      title: "Background color",
      description: "Select the background color of the page.",
      values: [
        { value: "#f44141", title: "Red" },
        { value: "#4286f4", title: "Blue" },
        { value: "#41f44c", title: "Green" }
      ]
    }
  },

  propertiesGroups: [
    {
      title: "Social media",
      properties: ["facebookUrl", "twitterUrl"]
    }
  ],
  
  titleForContent(obj) {
    return obj.get("title");
  },

  descriptionForContent(obj) {
    return obj.get("description");
  },

  initialContent: {
    body: [
      new HeadlineWidget({ headline: "New Landing Page!" }),
      new TextWidget({ text: "What do you want visitors to know and to do?" }),
    ],

    backgroundColor: "#41f44c",

    // Each time a new landing page is created, it is supposed to be
    // part of a campaign that starts immediately. 
    // If necessary, the editor can adjust the start date afterwards.

    campaignStartDate() {
      return new Date();
    }
  }
});

[New in 1.7.0]

Defining validations

With Scrivito-based applications, you can have the UI ensure that CMS objects or widgets (of a specific type) and their custom attributes contain valid content, based on the application's business logic. If content doesn’t validate, the Scrivito UI displays corresponding notification messages and also prevents the working copy concerned from being published.

The validation logic can be provided in the editing configuration under the validations key. The validation configuration is basically an array containing the validation definitions.

Since a single validation can either be focused on the state of a whole model (an Obj or a Widget), or be only relevant in the scope of a single custom attribute (e.g. title), we differentiate between class-based validations (validations for the whole content model) and attribute validations (validations for an attribute value).

Callback-based validation

The most basic form of a validation is the validation callback. A validation callback can be specified for both class-based and attribute validations. For invalid content, the callback should return a string or an array of strings representing error messages. If the content is considered valid, it should return a falsy value (e.g. undefined, null or "").

Class-based validations using callbacks

For a class-based validation, the callback can be placed anywhere in the validations array. It receives the current content model (an Obj or a Widget):

Copy
Scrivito.provideEditingConfig("BlogPost", {
  validations: [
    obj => {
      const imageWidgets = obj
        .widgets()
        .filter(w => w.objClass() === "ImageWidget").length;

      if (imageWidgets < 1 || imageWidgets > 3) {
        return "A blog post must include 1 to 3 images";
      }
    },
    // Further validation callbacks may go here...
  ]
});

The code inside a validation callback can access models and their attributes without being wrapped in Scrivito.load. Also, the callback will be executed several times, so it must be light-weight, idempotent and free from side effects.

Callback-based attribute validations

In case of an attribute validation, the callback receives the value (representing the current attribute value) and an options object with the following keys:

  • obj, if the subject of validation is an Obj.
  • widget, if the subject of validation is a Widget.
  • content also provides the subject of validation (for convenience).
  • name – the name of the attribute.

An attribute validation callback is specified as an array, with the first element being the attribute name, followed by one or several validation callbacks:

Copy
Scrivito.provideEditingConfig("BlogPost", {
  validations: [
    [
      "title",

      title => {
        if (BlogPost.where("title", "equals", title).count() > 1) {
          return "A blog post with this title already exists.";
        }
      }

      // Further title validation callbacks may go here...
    ],

    [
      "summary",

      (summary, { obj }) => {
        if (obj.get("title") === summary) {
          return (
            "The summary must not be the same as the title. " +
            "Please provide a proper summary."
          );
        }
      }

      // Further summary validation callbacks may go here...
    ]
  ]
});

Constraint-based attribute validation

In order to validate an attribute based on higher-level constraints, a constraint validation callback can be configured.

The structure of the constraints depends solely on the DSL of the library used. With Validate.js, it looks like this:

Copy
Scrivito.provideEditingConfig("BlogPost", {
  validations: [
    ["headline", { presence: true, length: { minimum: 10, maximum: 100 } }],
    ["summary", { presence: true }, { length: { minimum: 100, maximum: 500 } }]
  ]
});