Skip to content

Properties Schema

Brad Simpson edited this page Nov 10, 2022 · 12 revisions

What is properties.schema?

properties.schema is a JSON file that is provided by a plug-in. It is the point of contact between the authoring tool, the plug-in, and the course author who configures it. It tells the Adapt authoring tool what configuration data is required from the course author in order to make the plug-in work. It specifies the type of field input the authoring tool should use to gather that data. And in some cases, it directs the type of data validation should be performed upon submit. Every property that appears in the plug-in's model that is intended to be configured by the course author must appear in the properties.schema. If a model attribute is not configured by the course author using the authoring tool, the plug-in’s JavaScript code must assign to the attribute a default value.

properties.schema is also used by the Adapt framework to facilitate the localisation of courses. When a field is marked "translatable": true, its text can be exported for translation. Read more about this in Course Localisation.

properties.schema is based on Backbone forms and JSON Schema draft-03. Neither are explained here thoroughly, but both should be referenced for more detail or for situations not covered in this article. Adapt's properties.schema departs from JSON Schema draft-03 in how required fields are identified.

The value of a quality example.json

In the Adapt framework, the course author configures plug-ins (extensions, components, menu, theme) by assigning values in the following course JSON files: config.json, course.json, componentObjects.json, articles.json, blocks.json, and components.json. In which file the property resides is determined largely by the type of plug-in and the scope of its effects. The plug-in’s example.json provides a model of its configurable properties. If necessary, the example.json also specifies which course JSON file the properties should be added to. (maybe an image with a snippet of example.json.)

In the Adapt authoring tool, the course author does not directly edit these course JSON files. The tool uses an interface—based on properties.schema—to capture values for the same properties specified in example.json. For this reason, it is highly recommended that you reference an accurate example.json of your plug-in while constructing its properties.schema.

Schemas for primitives

In JSON, JavaScript primitives such as number, string, and Boolean are represented by a single key/value pair. Both the key and its value are represented by properties.schema. "body" is a typical attribute of an Adapt component. Its value is always string data. In example.json it look like this:

“body”: “This is some body text.

In properties.schema it is typically described like this:

"body": {
    "type": "string",
    "required": true,
    "inputType": "TextArea",
    "title": "Item Body",
    "default": "",
    "validators": ["required"],
    "help": "This is the item body text that is hidden until the item title is clicked on",
    "translatable": true
}

In the example above, "body" is modeled as an object. Some of its properties describe the data value (metadata), and some describe how it will be captured by the authoring tool. This is the model used with JavaScript primitives: numbers, strings, and Booleans.

type (string): The JavaScript data-type of the expected value. One of "string", "number", "boolean", "object", or "array".

required (boolean): Whether or not this property must have a value. If set to true, the user interface forces the course author to provide input. If set to false, no input is required from the author; the default value will be used.

inputType (string): The type of input field that the authoring tool will use to collect the configuration data from the course author. The types of input fields are associated with standard HTML form elements, such as text boxes and drop-down lists. The supplied value must conform to Backbone forms (https://github.com/powmedia/backbone-forms). Further explanation and descriptions are found in Schema Input Types and Validators.

title (string): This is displayed as the label for the input type.

default (data-type varies): This value is used if the course author does not specify a value. Use "" to represent the empty string when a more specific value is not required.

validators (string array): One or more string constants representing the kinds of validation that should be performed on the data when it is submitted. Most common values values include "required", "number", "match", "email", "url", and "regexp". Separate multiple values with a comma. Use [] to represent an empty array when no validators are necessary. Further explanation and descriptions are found in Schema Input Types and Validators.

help (string): Brief text intended to guide the course author in specifying configuration values. It is displayed below the Title.

translatable (boolean): Set to true if this property has text that should be localised when the course is translated. Defaults to false.

Schemas for objects

In JSON, objects and arrays are represented by nesting a key/value pair within a value. Likewise properties.schema makes use of nested descriptions— or schemas and subschemas. "_graphic" is a typical object that is used to collect properties of a component’s image. In example.json it looks like this:

"_graphic": {
    "src": "hot_graphic.jpg",
    "alt": "alt text"
}

In properties.schema it is typically described like this:

"_graphic": {
    "type": "object",
    "required": false,
    "title": "Main hotgraphic",
    "properties": {
        "src": {
            "type": "string",
            "required": true,
            "default": "",
            "inputType": "Asset:image",
            "validators": ["required"],
            "help": "This is the image that appears behind the pins"
        },
        "alt": {
            "type": "string",
            "required": false,
            "default": "",
            "inputType": "Text",
            "validators": [],
            "help": "Alternative text for this image read by screen readers",
            "translatable": true
        }
    }
}

The list of key/value pairs that describe “_graphic” is terminated by the key “properties” which holds object descriptions of "_graphic"’s two properties. Because "src" and "alt" are primitive data-types (string), their descriptions employ the model used above for "body".

Schemas for arrays

Some components, such as the core Text and Graphic components, are comprised of non-repeating attributes. Other core plug-ins, such as Accordion and Narrative, have elements that repeat. Each of its repeating items has the same set of properties. The schema is not concerned with how many items the course author might want. It is only concerned with the properties of the repeating item and with the array that stores them.

Like objects explained above, arrays are represented by nesting a key/value pair within a value.

"_items" is a typical array that is used to store items such as the expandable items of the Accordion. In example.json it looks like this:

"_items": [
    {
        "title": "Heading 1",
        "body": "This is display text 1.",
        "_graphic": {
            "src": "course/en/images/example.jpg",
            "alt": "accordion item 1 graphic alt text here."
        }
    }
]

In properties.schema it is typically described like this:

"_items": {
    "type": "array",
    "required": true,
    "title": "Items",
    "items": {
        "type": "object",
        "required": true,
        "properties": {
            "title": {
                "type": "string",
                "required": true,
                "inputType": "Text",
                "title": "Item Title",
                "default": "",
                "validators": ["required"],
                "help": "This is the item title",
                "translatable": true
            },
            "body": {
                "type": "string",
                "required": true,
                "inputType": "TextArea",
                "title": "Item Body",
                "default": "",
                "validators": ["required"],
                "help": "This is the item body text that is hidden until the item title is clicked on",
                "translatable": true
            },
            "_graphic": {
                "type": "object",
                "required": false,
                "title": "Graphic",
                "properties":{
                    "alt": {
                        "type": "string",
                        "required": false,
                        "default": "",
                        "inputType": "Text",
                        "validators": [],
                        "help": "The alternative text for this image",
                        "translatable": true
                    },
                    "src": {
                        "type": "string",
                        "required": false,
                        "default": "",
                        "inputType": "Asset:image",
                        "validators": [],
                        "help": "Optional image which will be rendered with the item body text"
                    }
                }   
            }
        }
    }
}

Unlike the object explored previously, the array does not have a "properties" attribute. Instead it uses "items".

Caution: Avoid confusing "_items" with "item". "_items" is a key established by the plug-in's source code. The developer could have chosen another identifier such as "_stages", "_widgets", or "_tabs" as long as the same identifier was used in the code to access this value. The "items" that appears in line with the keys that precede it—"type", "required", "title"—is not an arbitrary term. It must be used in conjunction with "type": "array".

The value of "items" is determined by the data type of the item. If each item is simply a word or phrase, it can follow the model set in Schemas for primitives. Most likely the item will be an object and should be described according to the model set in Schemas for objects. In the code example above, "items" is indeed an object. Its "properties" key identifies two string properties ("title" and "body") and one object property ("_graphic"). "_graphic", as an object itself, provides a description of its properties, too.

The nesting involved with these descriptions can get confusing. Remember that "type": "object" is paired with "properties" and "type": "array" with "items". And remember that no description is complete until complex data types have been described by their primitive components.

Constructing a properties.schema

This section focuses on creating a properties.schema for a component. It is applicable to other types of plug-ins. The following section addresses the unique needs of extensions, themes, and menus.

Keep two objectives in mind when creating a properties.schema:

  • constructing the nested descriptions that ultimately terminate in definitions of primitive data types
  • supplying the models with values appropriate for configuring your plug-in

Note: Presenting a complete schema as a universal example is problematic. Instead you are encouraged to reference the properties.schema of the core Adapt plug-ins.

Header

The document begins with the following lines:

{
  "type":"object",
  "$schema": "http://json-schema.org/draft-04/schema",
  "id": "http://jsonschema.net",
  "$ref": "http://localhost/plugins/content/component/model.schema",

The value of “$ref” depends on the type of plug-in. Substitute where appropriate.

  • component: "http://localhost/plugins/content/component/model.schema"
  • menu: "http://localhost/plugins/content/contentobject/model.schema"
  • theme: "http://localhost/plugins/content/theme/model.schema"
  • extension: None. Omit the entire “$ref” key/value pair. Replace it with "required": false,. The Boolean value you supply, true or false, reflects whether or not the extension must be configured by the course author.

"globals"

Accessibility is an important issue in the world today, and Adapt has embraced accessibility solutions within its core modules. We hope every developer of Adapt plug-ins will do so, too.

  "globals": {
    "ariaRegion": {
      "type": "string",
      "required": true,
      "default": "This component is an accordion comprised of collapsible content panels containing display text. Select the item titles to toggle the visibility of these content panels.",
      "inputType": "Text",
      "validators": [],
      "translatable": true
    }
  },

"properties"

The Adapt framework requires that plug-ins have certain properties based on the plug-in's type: component, extension, theme, or menu. These required properties are presented to the course author by the authoring tool itself. ("_supportedLayout" is an exception that will be addressed shortly.) The plug-in developer does not need to include the following component properties in the properties.schema: "_componentType", "_type", "_component", "_layout", "_classes", "_isOptional", "_parentId", "_courseId", "title", "displayTitle", and "body". The most current list is maintained in code in component/model.schema. Properties for other plug-in types can be found by investigating extension/extensiontype.schema, menu/menutype.schema, and theme/model.schema.

After "globals" the schema lists the configurable properties of the plug-in.

"properties": {
    "_supportedLayout": {
      "type": "string",
      "required": true,
      "enum": ["full-width", "half-width", "both"],
      "default": "half-width",
      "editorOnly": true
    },
    "instruction": {
      "type": "string",
      "required": false,
      "default": "",
      "inputType": "Text",
      "validators": [],
      "help": "This is the instruction text",
      "translatable": true
    },
The first property, `"_supportedLayout"`, is required by the authoring tool. It is associated with the `"_layout"` property that appears in the Adapt framework. The values listed in the `"enum"` determine the layout options presented to the course author. `"full-width"` allows the author to position the component to span the width of the page. `"half-width"` gives the author the option of positioning it either on the left or right side. And `"both"` provides the author with all three options. The developer specifies values only for `"enum"` and `"default"`; all other keys and values must remain as presented above.

"_supportedLayout" is followed by the schema for "instruction", if applicable, then by the schemas for all the configurable properties of the plug-in.

The plug-in's properties are described using the models outlined above in Schemas for primitives, Schemas for objects, and Schemas for arrays. More information about input types and validators is provided in [Schema-Input-Types-and-Validators].

What about extensions, menus, and themes

Properties of extensions, menus, and themes may be placed in any of the course JSON files (reference adapt-contrib-pageLevelProgress's example.json). Because of this, the structure of the properties.schema file used with extensions, menus, and themes differs from the structure used with components.

An empty properties.schema file for an extension looks like this:

{
  "type":"object",
  "$schema": "http://json-schema.org/draft-04/schema",
  "id": "http://jsonschema.net",
  "required":false,
  "globals": {
  },
  "properties":{
    "pluginLocations": {
      "type":"object",
      "required":true,
      "properties":{
        "config": {
          "type":"object"
        },
        "course": {
          "type":"object"
        },
        "contentobject": {
          "type":"object"
        },
        "article": {
          "type":"object"
        },
        "block": {
          "type":"object"
        },
        "component": {
          "type":"object"
        }
      }
    }
  }
}

This structure has a pluginLocations element with objects representing each of the course JSON files. Properties of the plug-in are added to one or more of these objects depending on where the plug-in's code expects to find them.

A plug-in's properties must be contained within an element specifically identified with the plug-in. By convention use the plug-in's short name in camel case and preceded by an underscore (e.g., _tutor). Within this element, properties of the plug-in are described in the same way as for components, as explained above for primitives, objects, and arrays.

For example, the Tutor extension has only one property, isEnabled, and it is defined within config.json. In this snippet of Tutor's properties.schema it is described like this:

"config": {
  "type":"object",
  "properties":{
    "_tutor": {
      "legend" : "Tutor",
      "type":"object",
      "required":false,
      "properties": {
        "_isEnabled": {
          "type":"boolean",
          "required":true,
          "title": "Enabled",
          "inputType": { "type": "Boolean", "options": [false, true]},
        }
      }
    }
  }
}

For an example of a plug-in that sets properties in multiple locations (multiple course JSON files), reference pageLevelProgress's properties.schema.

Important: The authoring tool currently supports properties for extensions only.

Properties.schema best practices

Is Enabled

Most extensions will have a "Is Enabled?" attribute to enable or disable an extension quickly. If they are target config or course they should be enabled by default as it means the course creator will instantly be able to use it when they turn it on. For extension that target Article, Block or Component level they should be disabled by default so the course creator can opt-in to where they want to apply it.

Checkbox

For a boolean value such as a "Is enabled?" attribute you can use a checkbox input to reduce the number of clicks the user will need. Just use:

"inputType": "Checkbox"

Oh and don't forget bower.json

In addition to the properties.schema file, a plug-in must have a correctly defined bower.json file. The bower.json file contains properties that are specific to the plug-in and plug-in type (component, extension, menu, theme).

All plug-ins should contain a displayName and description attribute. These will be displayed in the authoring tool and will help identify your plug-in.

If a minimum framework version is defined in bower.json, it will be used by the authoring tool to determine if the plug-in is compatible with the installed framework version. The minimum framework version is described like this:

"framework": "^2.0.0",

An extension, menu, or theme must also define a _targetAttribute (components are exempt). The _targetAttribute property must match the value used to identify the plug-in within pluginLocations. Using _tutor in properties.schema requires you to use it as the value of _targetAttribute.

Tutor's bower.json looks like this:

{
  "name": "adapt-contrib-tutor",
  "version": "2.0.3",
  "framework": "^2.0.0",
  "homepage": "https://github.com/adaptlearning/adapt-contrib-tutor",
  "issues": "https://adaptlearning.atlassian.net/secure/CreateIssueDetails!init.jspa?pid=10100&issuetype=1&priority=6&components=10521",
  "displayName" : "Tutor",
  "extension" : "tutor",
  "description": "An extension which provides a basic feedback overlay for question components",
  "main": "/js/adapt-contrib-tutor.js",
  "keywords": [
    "adapt-plugin",
    "adapt-extension"
  ],
  "license": "GPLv3",
  "targetAttribute": "_tutor"
}
Clone this wiki locally