Please click here if you are not redirected within a few seconds.
CMS Manifest › shanebow.com
CMS Manifest

Many games (e.g. labeler) have several data sets and courses have multiple lessons. Manifests are the way to deliver these different data to a single app.

Each Manifest has several standard fields, with an arbitrarily defined payload (which is an SQL blob type). Here are the fields in a manifest record in the database:

 `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
 `ord` int(10) unsigned COMMENT "old id - use for ordering",
 `otype` smallint(1) unsigned NOT NULL DEFAULT 4096 COMMENT 'obj type',
 `title` varchar(255) NOT NULL,
 `desc` text,
 `mid` int(10) unsigned NOT NULL default 0 COMMENT "image",
 `access` smallint NOT NULL COMMENT "owner-only trial free member purchase",
 `flags` int(3) unsigned NOT NULL default 0 COMMENT "publish draft current featured",
 `payload` blob,
 `owner` int(10) unsigned NOT NULL default 1,
 `uid_lmod` int(10) unsigned NULL COMMENT "author",
 `debut` int(10) unsigned NOT NULL,
 `lmod` int(10) unsigned NULL,

OTYPE Object Types

The otype — short for Object TYPE — field is used to identity what kind of manifest this is and this can be mapped to a set of apis or apps that knows how to encode/decode the payload field.

Pages vs Manifests

There is some overlap in the functionality of manifests and pages. Both can present the user with a choice of related dynamic content. For example if there is a course with multiple lessons, then each lesson could be saved as either a manifest or as a separate page.

Sometimes it's unclear which is the better solution — pages or manifests. Here are some of the key differences to help the choice between for a given situation:

So, back to the course example. We could make a category for Courses and a subcategory for each course. Then we might make pages for each lesson. In this case the lessons would be searchable.

On the other hand we might make on page for each course and pull in the lessons as manifests. There is no right answer.

Selecting Manifests

The image below shows a panel that allows a ThaiDrills user to choose which manifest to load for a labeling game.

Contrast that with the page summaries in the side bar which allow the user to choose from distinct pages all categorized as CMS pages.

Note that there are option to show descriptions and owner for the manifests, so both manifests and pages can be presented to the user in a very similar fashion.

A paged list consisting of manifest summaries — typically, thumbnail images with a title underneath — can easily be fetched into a <div id="man-summaries"></div> filtered by OTYPE via the UBOW.ManifestSummaries class. The following example shows the basic usage:

new UBOW.ManifestSummaries('#man-summaries', {
  per_page: 24,
  extra: {otype: TD.OTYPE.DECK.value, admin:1},
  onClick: (idManifest) => {
   console.log('loading manifest:', idManifest);
   // do something with the selected manifest
   _manifest.load(idManifest);
   },
  });

Paging is handled automatically, and you can do whatever you like with the data when the user clicks on a manifest to select it.

In the above example from ThaiDrills.com we present all of the the manifests that have otype TD.OTYPE.DECK with 24 manifests per page.

Since the payload of the manifest depends on it's otype, you will need to write a parser for each otype.

Editing Manifests

We manage manifests in the back-end. There is a standard form — shown left — for creating and editing manifests that contains all the fields.

The form will populate a <div> whose css selector is passed into the constructor.

A settings object is the second argument passed to the constuctor. There are a wide range of options including the available otypes and urlLoad which specifies the API responsible fetching the data.

Usually, the basic editor is extended or augmented with separate code to process the payload.

For instance, in the case of ThaiDrill's Deck Editor, we are creating decks of flash cards to study Thai vocaulary. Each manifest holds a different word list which is representated as a table where each row has columns for the Thai word and its English translation, as well as an image and links to audio files for the Thai and English pronunciations.

Here's a typical use case, from the ThaiDrills back-end:

 const _manifest = new UBOW.ManifestEditor('#mani-editor', {
  urlLoad: '/api/deck/load',
  otype: TD.OTYPE.DECK,
  isValid: () => true,
  getExtra: () => {
   return {
    payload: tbl.toCSV(), // a csv of the labels
    };
   },
  onReset: reset, // create new manifest
  onLoaded: (mani) => {
   mani.cards.forEach((word) => tbl.append(word));
   },
  });

Notice how the getExtra setting is used to easily override the payload field (which can be hidden in the DOM). It is called after the form is validated, but before it is posted to the server. Any additional fields may be also be posted.

Also, the onLoaded setting is particularly useful for performing client actions after the manifest is retrieved from the server. In the above code we populate the aforementioned table of flash cards from the 'extra' field (cards) that the server adds to the manifest object. The server expands the payload field into an array of cards via a database looking up of ids stored in the payload.

Because this is 'non-standard' behavior, we override the urlLoad setting to point to a controller that knows how to process this particular otype (i.e. TD.OTYPE.DECK in this example).

Using another image

This is an unusual use case, but one that I have used more than once: Sometimes the manifest image (which is displayed in the list of manifests) is also the background image for the game you are editing.

You can take the image out of the form proper and put it someplace else on the page. The following settings for the UBOW.ManifestEditor put the image into <div id="drops></div>, that may be changed by clicking on <button id="img-pick"">Change Background</button>.

The imgChanged setting fixes everything up internally for the image editor to work as expected.

 imgDiv: '#drops',
 btnPickImg: '#img-pick',
 imgChanged: ($img) => {
   const mid = $img.attr('mid');
   $('#img-info').val( `#${mid}, ${$img[0].naturalWidth} x ${$img[0].naturalHeight}`);
   ed.imgChanged($img);
   },

~~~ ~~~


More Shanebow CMS