Skip to content

Content management

Using data files we know how we can store data inside our bot, without touching our bot's code directly.

You might want to give less technically skilled people access to these files, in order for them to edit them and manage the content. However, there is the risk they break the bot by entering incorrect data, or destroying the entire file.

To address this, the Botsquad platform allows developers to define a a basic content management system (CMS). It is possible to invite users into the studio, giving them just the editor access role. With this role, they cannot see or edit a bot's code, and they also cannot edit any data files directly.

The CMS lives in a dedicated menu entry called Content. It is composed of multiple parts, called Content sections. Each section has a descriptive title and an icon, to indicate its purpose.

Each content section represents either an editable form, or a spreadsheet.

Defining content sections

When you have created your first content definition, a new content tab is enabled in the bot editor, showing the available content sections. It looks like this:

ddl

On the left side, there are the various content sections; in this case there is only one, named "Configuration". In the middle, it shows the actual configuration form. Any data you enter in this form, gets saved in a YAML file in the bot's scripts.

When you change the content, you can try it out directly in the bot by clicking the Run button.

The data edited in the CMS is versioned just like the rest of the publishing cycle of scripts. You'll notice that, as soon as you change any data, you'll see the publish box pop up. Click Publish to put the changes live, or revert to undo any changes to the data.

Form data

Every content section is represented by a single content definition file: a special YAML script which is part of your bot.

To create a content section like the one above, start by creating a Content definition YAML file in your bot:

ddl

Then, hit publish, and immediately the Content section becomes visible in the studio.

The content definition you added looks like the following:

# This is an example "Content Definition" YAML file.
# Files like these define sections of the bot's CMS. Each section
# creates either a form or a data table, which can be edited by users
# (editors) that do not have access to this bot's code.
#
# For more information about the CMS feature:
# http://doc.botsquad.com/customize/cms/
type: form # or 'table'

# Define where the content gets stored
storage:
  type: script # stored inside a script file
  script_title: config  # Please create a YAML file with this title!

# title of the CMS section
title: Configuration

# see http://blueprintjs.com/docs/v2/#icons for possible icon names
icon: cog

# Optional extra description / help text
description: This is a configuration form

# The form definition, defines the data that can be edited
form:
  # the JSON schema, describing the form
  schema:
    type: object
    properties:
      account_name:
        type: string
        title: Account name
      is_advanced:
        type: boolean
        title: Enable advanced features
      is_ludicrous:
        type: boolean
        title: Enable ludicrous mode
  ui_schema:
    # specifies the ordering of the fields
    ui:order:
      - account_name
      - "*" # all the others

It is heavily commented and should be pretty much self explanatory. A content definition like this defines a section in the CMS part of the bot, with a title, an icon, a possible description, and the definition of the actual form. The storage section defines where the content should be stored. For now, content is always stored in a script, so you need to make sure that there is a data script with the same name as given in the storage.script_title key.

JSON Schema

The form.schema key is the most important key of the content definition: the schema defines the shape of the content. For this, we use JSON schema, which is a way to describe how data looks. And in our case, it also describes how the form looks that will be shown in the CMS.

For composing the form, we use the React JSON Schema Form library. So most options described there are functional and usable in the content definition. The Form customization section especially, goes into detail on the available customization options.

Specialized content widgets in forms

By using ui:widget in the UI schema, it is possible to use form widgets that are specialized for editing a specific type of content. The following are supported:

  • ui:widget: image - render an image picker
  • ui:widget: file - render a file uploader
  • ui:widget: location - render a location picker
  • ui:widget: timezone - render a timezone picker
  • ui:widget: integration_token - See "OAuth tokens" below

OAuth tokens

It is possible to render an OAuth connect button inside a form, to be able to configure a bot's OAuth connection to an integration provider from within a CMS form.

For this to work, you need to set up an integration with an OAuth provider in the integrations.yaml file (See API / OAuth integrations on how to set that up).

Specify integration_token as ui:widget, and add the alias to the integration provider to the ui:options, like below:

    google_token:
      ui:widget: integration_token
      ui:options:
        alias: google

In this example we assume that you have an integration configured under the alias "google".

When you have set all of this up, you will see a "Request Authorization" button rendered inline in the form, which triggers the start of the OAuth flow.

Translatable content

By using the ui:field: i18n option in the form's UI schema definition, fields are marked as being translable.

form:
  # the JSON schema, describing the form
  schema:
    type: object
    properties:
      account_name:
        type: string
        title: Account name
  ui_schema:
    account_name:
      ui:field: i18n # mark the account_name as translatable

Next to each field that is translateable, a flag will appear like this:

Language flags are semi-transparent when the corresponding string is still empty.

It is also possible to combine ui:widget with ui:field: i18n; so in that way it is possible to create a multilingual location, file or image picker.

Automatic UUID

By using the ui:field: auto_uuid option in the form's UI schema definition, you get a hidden field which is filled with a unique identifier.

form:
  # the JSON schema, describing the form
  schema:
    type: object
    properties:
      id:
        type: string
  ui_schema:
    id:
      ui:field: auto_uuid

Tag input control

To be able to easily edit a list of (short) string, you can use ui:field: tag_input. This will render a Tag Input control instead of the default RSJF string array:

You would configure a string tokens input like this:

form:
  schema:
    type: array
    title: "Extra locales"
    items:
      type: string
  ui_schema:
    ui:field: tag_input

Collapsible lists

Managing large lists in the CMS can become quite cumbersome, as each form takes up a lot of space. To circumvent this, it is possible to collapse each item in the array, to make more space and hide the details of each item. This is done using ui:field: collapsible_array, incombination with a CSS class. Once configured, this will look like this:

A minimal JSON UI schema configuration for collapsible lists looks like this:

form:
  schema:
    type: array
    items:
      type: object
      properties:
        title:
          type: string
          title: Item title
  ui_schema:
    classNames: "collapsible-array--field"
    items:
      ui:field: collapsible_array
      ui:options:
        caption_field: title
      title:
        ui:autofocus: true

Mind that both the classNames property on the array item UI schema itself is required, and the ui:field option in the items part.

Collapsed caption

To set the caption of the collapsed item, use the caption_field property inside the ui:options. Alternatively, it is possible to render a Mustache template as the field caption, using the caption_template option:

      ui:options:
        caption_template: 'Name: <b>{{ name }}</b><span class="right">{{ title }}</span>'

The caption template returns HTML that is rendered directly inside the item, use with care.

Form layout

It is possible to control the layout of the form using ui:field: layout. This way, it is possible to put multiple fields on a single row, and set the relative widths of the grid columns.

For instance, with a form consisting of a name and an image, the following will put these fields on the same row, in equal width.

      ui:field: layout
      ui:layout:
      - name: { sm: 6 }
        image: { sm: 6 }

Bootstrap grid conventions are used, for each field in a row you can specify sm, md, lg and xl values to control the relative width of the field. In each row, the corresponding values must add up to 12, as the Bootstrap grid is a 12-column grid.

It is also possible to use ui:layout inside a ui:field: collapsible_array item, to control the layout of each form in an array.

Data lookup field

It is possible to add a CMS field that uses information from another CMS section, to create a lookup list of values that is defined in another CMS section.

To use this, you need to create a ui:field with the type data_lookup.

form:
  schema:
    properties:
      folder:
        type: string
  ui_schema:
    folder:
      ui:field: data_lookup
      ui:options:
        source: folders
        id_field: id
        caption_field: title

This definition will create form with one field, folder. It will use the data script called folders as the source to populate a dropdown list from which the user can choose the value for the folder field.

Within the folders, it will use the id_field as the final value for the data, while it will display the data from caption_field.

Data caption template

Instead of using caption_field, you can also use caption_template which allows you to specify a Mustache template for the rendering of the dropdown labels.

caption_template: '{{ first_name }} {{ last_name }}'

Nested data lookup

It is possible to fill the data lookup with a subselection from the data source, by specifying a xpath expression in the ui:options.

For instance if you have the following, nested data:

- category: Sodas
  items:
  - id: cola
    caption: Coca Cola
  - id: fanta
    caption: Fanta
- category: Coffees
  items:
  - id: machiato
    caption: Latte Machiato

You can make a lookup data field on a list of all items, like so:

form:
  schema:
    type: object
    properties:
      drink:
        type: string
  ui_schema:
    drink:
      ui:field: data_lookup
      ui:options:
        source: drinks
        id_field: id
        caption_field: caption
        xpath: "//items/*"

This XPath expression uses the jsel library; you can try out path expressions using the jsel playground.

Dynamic enumeration

A string widget that is part of an array of items can have an automatic lookup field based on its own values from the form.

form:
  schema:
    type: array
    items:
      type: object
      properties:
        item_type:
          type: string
          title: "Item type"
  ui_schema:
    items:
      item_type:
        ui:widget: dynamic_enum

This will render a string input field with a suggestion box for the item_type field. The suggestions come from the form data itself, from the collection of all the different item_type entries in the form's data.

Preview button

By using the ui:field: preview_button option in the form's UI schema, you can create a preview button which runs a part of the bot script in the context of the current CMS.

form:
  # the JSON schema, describing the form
  schema:
    type: object
    properties:
      preview:
        type: string
  ui_schema:
    preview:
      ui:field: preview_button

This will create a preview button in the form which then emits the event $studio_content_preview in Bubblescript.

The event has the following items in the event.payload property:

  • constant - the name of the @constant in the CMS
  • path - the path to the data in the file (an array)
  • data - the data in the file (only the leaf node on the path).

Example script:

dialog event: "$studio_content_preview" when event.payload.constant == "settings" do
  say "You clicked preview button in the settings CMS section."
  log event.payload.data
end

CSV / spreadsheet data

Besides defining forms, there is also a second content management option, which is used to edit CSV files. By specifying type: table in the content definition, the content section presents an Excel-like spreadsheet data:

ddl

The contents of the spreadsheet is fully editable, and the data itself is stored in a CSV file in your bot's scripts.

The names of the columns are stored in the content definition file, as you don't want content editors to change these names.

The corresponding content definition file looks like this:

type: table
storage:
  type: script
  script_title: users
title: Users
icon: people
table:
  columns:
  - id
  - first_name
  - last_name
  - age