7.2. Writing an Authoring Kit

7.2.1. Authoring Interface Overview

The Red Hat CMS authoring kit provides a powerful framework for registering Bebop components to create and edit attributes of your object type. With the authoring kit, it is easy to create and reuse Bebop components that display your attributes of your object and allow users to manipulate those attributes. There is nothing magic going on behind the scenes. The authoring kit organizes your Bebop components, and integrates them into the CMS UI. You are still responsible for writing the Bebop components that manipulate the attributes of your object.

To create an authoring kit for your website, follow the general process outlined here. Continue reading through the rest of this section for more information.

  1. Break up the attributes/associations you want to edit into a sequence of steps. For example, the Red Hat CMS Article content type defines four steps: (1) Basic Properties (edit the name, title, and description); (2) Edit the body; (3) Categorize the article; and (4) Add an image. Note that there may be additional Article authoring steps depending on whether any of the optional ContentAsset packages have been loaded.

  2. For each of these steps, create a Bebop component that lets you edit that particular attribute. In many cases, the Red Hat CMS PropertyEditor class will be a useful base class.

  3. Create one Bebop component that is responsible for actually creating an instance of your object type. This component may share the form used in the first step of the kit. In many cases, the already-defined com.arsdigita.cms.ui.authoring.PageCreate authoring step will work as-is for the creation component.

  4. Register the authoring kit with the Red Hat CMS content type by creating the appropriate XML file. The \ has been added to break a line for printing purposes:

<?xml version="1.0" encoding="utf-8"?>
<ctd:content-types xmlns:ctd="http://xmlns.redhat.com/cms/content-types"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" \
xsi:schemaLocation="http://xmlns.redhat.com/cms/content-types \
content-types.xsd">
  <ctd:content-type label="Birdwatch" description="Birdwatch"
          objectType="com.arsdigita.package1.Birdwatch" \
classname="com.arsdigita.cms.contenttypes.Birdwatch">
    <ctd:authoring-kit createComponent="com.arsdigita.cms.ui.authoring\
.PageCreate">
     <ctd:authoring-step
         labelKey="cms.contenttypes.shared.basic_properties.title"
         labelBundle="com.arsdigita.cms.ui.CMSResources"
         descriptionKey="cms.contenttypes.shared.basic_properties\
.description"
         descriptionBundle="com.arsdigita.cms.ui.CMSResources"
         component="com.arsdigita.package1.ui.BirdwatchPropertiesStep"/>

     <ctd:include href="/WEB-INF/content-types/edit-body-text-step.xml"/>

     <ctd:include href="/WEB-INF/content-types/shared.xml"/>

    </ctd:authoring-kit>
  </ctd:content-type>
</ctd:content-types>

7.2.2. Authoring Kits Prerequisites

Before reading this section, the reader should have some basic understanding of Bebop; specifically, the reader should be able to use the PageState, write basic Forms, derive components from simple Containers and have a general idea of how the generateXML method works.

In addition, the reader should be familiar with Red Hat CMS ContentItems and DomainObjects in general.

You may find it useful to read about Bebop in the Red Hat Web Application Framework Developer Guide.

7.2.3. Authoring Kits Overview

Since Red Hat CMS could potentially support a wide variety of content types, it would be impractical to create a "one size fits all" UI for instantiating and editing items. Instead, the CMS provides the abstraction of an authoring kit.

An authoring kit is a set of Bebop components that should be used to instantiate and edit items of a particular content type. The authoring kit consists of two parts:

Except for the restrictions placed on the constructor, the Bebop components used by the authoring kit may be arbitrarily complex.

An AuthoringKit domain object will always be associated with a ContentType. All components which are part of the authoring kit are expected to produce content items with the specified ContentType.

7.2.4. Authoring Kits Page Flow

The CMS authoring UI will display the creation component when it receives a request to create a new item. The creation component will temporarily replace the section listing on the Workspace page.

Once the user enters all the necessary information and clicks the Save button, the authoring UI will hide the creation component, and enable the editing steps in the wizard.

For more information about the UI, refer to the Red Hat Content Management System Administrator Guide.

7.2.5. Writing an Authoring Kit

This section covers the activities involved in writing an authoring kit for a content type.

7.2.5.1. Understanding the DomainObjectSelectionModel

All authoring kit components and their associated utility classes rely on the com.arsdigita.cms.DomainObjectSelectionModel interface. This interface is used to abstract away the retrieval of a single DomainObject from the database. Whenever a Bebop component needs to retrieve the current content item, the only method it needs to call is:

DomainObject myObject = m_selectionModel.getSelectedObject(state);

In this call, m_selectionModel is the DomainObjectSelectionModel which was passed to the component in its constructor, and state is the current page state. The component does not know or care where the actual DomainObject is stored — it could be stored in a state parameter, a RequestLocal variable, a session attribute, an XML file, etc. Thus, the Bebop component becomes more autonomous; the implementation of the actual DomainObjectSelectionModel could be changed at any time, without the need to rewrite the code of every Bebop component in the CMS.

In addition to calling getSelectedObject, the Bebop component may call setSelectedObject(PageState state, DomainObject object) in order to substitute a different ACSObject into the model. Typically, this method will be called from a process listener in a Bebop form whose purpose is to instantiate a new content item. The DomainObjectSelectionModel interface actually extends Bebop's SingleSelectionModel interface; the exact nature of their relationship is documented in the Javadoc.

The DomainObjectSelectionModel interface has two implementing classes: ACSObjectSelectionModel and ItemSelectionModel. The ItemSelectionModel class is specifically designed for authoring kit components; the DomainObject that it returns can always be safely cast to the proper subclass of ContentItem. In addition, ItemSelectionModel has a convenience method with the signature.

public ContentItem createItem(BigDecimal id) throws ServletException

This method can be used to automatically instantiate the correct subclass of ContentItem.

7.2.5.2. Write the Creation Component

The creation component must be a Bebop component with the following constructor:

public MyCreationComponent (ItemSelectionModel model, 
CreationSelector parent) { 
// ...}

  • itemModel: The ItemSelectionModel for the component. The creation component is required to eventually instantiate a new ContentItem, and then call the setSelectedObject method of its ItemSelectionModel in order to inform the authoring UI of the new item. Until the creation component explicitly sets the item, the getSelectedObject method is guaranteed to return null.

  • parent: The com.arsdigita.cms.ui.authoring.CreationSelector which serves as the parent for the creation component. When the creation component has successfully created the item, it is required to call

    m_parent.editItem(state,  item)

    in order to notify the parent that it should switch to editing mode (in the call above, state). Alternatively, the creation component may call

    m_parent.redirectBack(state)

    if the user chooses to abort the creation process. The authoring UI will then redirect the user to his previous page.

    In addition, the creation component should call

    newItem.setParent(m_parent.getFolder(state));

    in order to set the correct parent folder for the item. In addition, the creation component may call

    Section sec = m_parent.getContentSection(state);

    in order to obtain the content section in which the new item will be created.

Most of the time, the creation component for an item will be a simple form. The following template can be used to write the form:

public class CreationComponent extends Form
implements FormInitListener, FormProcessListener, FormSubmissionListener {

private CreationSelector m_parent;
private ItemSelectionModel m_itemModel;
private ACSObjectSelectionModel m_sectionModel;

public CreationComponent (
  ItemSelectionModel itemModel, CreationSelector parent
) {
  super("CreationComponent");
  m_itemModel = itemModel;
  m_parent = parent;

  // Add some widgets to the form
    
  addInitListener(this);
  addSubmissionListener(this);
  addProcessListener(this);
}

public void init(FormSectionEvent e) throws FormProcessException {
  // Set default values for some of the widgets
}

public void submitted(FormSectionEvent e) throws FormProcessException {
  PageState state = e.getPageState();
  if(/* User clicked the Cancel button */) {
    m_parent.redirectBack(state);
    throw new FormProcessException("Submission Cancelled");
  } 
}

public void process(FormSectionEvent e) throws FormProcessException {
  PageState state = e.getPageState();
  FunkyContentItem item = 
    (FunkyContentItem)m_itemModel.createItem(new BigDecimal(42));

  // Set some properties of the item based on user input
   
  item.setParent(m_parent.getFolder(state));
    
  item.save();
  m_itemModel.setSelectedObject(state, item);
  m_parent.editItem(state, item);
}
}

For another example, refer to the source for com.arsdigita.cms.ui.authoring.PageCreate.

Example 7-1. Creation Example

7.2.5.3. Write the Authoring Step Components

There must be at least one authoring step associated with the authoring kit. The Bebop component which implements the step must provide the following constructor:

public AuthoringStepComponent(ItemSelectionModel model, 
AuthoringKitWizard parent) {
  // ...
}

  • itemModel: The ItemSelectionModel for the step. When the authoring step component becomes activated, the model.getSelectedObject(state) method is guaranteed to return a non-null ContentItem. It is safe to cast this instance to the ContentItem subclass specified in the authoring kit's content type.

  • parent: The AuthoringKitWizard which will act as a parent for the step. By default, all the steps in the wizard will be enabled.

The authoring step component may embody an arbitrarily complex UI; there are no restrictions on its behavior.

Most of the time, an authoring step component would be a plain old form. The following template can be used to write the form:

public class AuthoringStepComponent extends Form
implements FormInitListener, FormProcessListener, FormSubmissionListener {

private AuthoringKitWizard m_parent;
private ItemSelectionModel m_model;

public AuthoringStepComponent(ItemSelectionModel model, AuthoringKitWizard \
parent) {
  super("AuthoringStepComponent"); 
  m_model = model;
  m_parent = parent;

  // Add some widgets to the form
    
  addInitListener(this);
  addSubmissionListener(this);
  addProcessListener(this);
} 

public void init(FormSectionEvent e) throws FormProcessException {
  PageState state = e.getPageState();
  FunkyContentItem item = (FunkyContentItem)m_model\
.getSelectedObject(state);

  // Set default values for some of the widgets
  //  based on the item's properties
  }

public void submitted(FormSectionEvent e) throws FormProcessException {
  PageState state = e.getPageState();
  if(/* User clicked the Cancel button */) {
    throw new FormProcessException("Submission Cancelled");
  }
} 

public void process(FormSectionEvent e) throws FormProcessException {
  PageState state = e.getPageState();
  FunkyContentItem item = (FunkyContentItem)m_model.getSelectedObject(state);

  // Set some properties of the item based on user input
    
  item.save();
}
}

Example 7-2. Step Components Example

7.2.6. Pre-Built authoring kit steps

Red Hat CMS contains a set of pre-built authoring kit components. The exact functionality of the components is described in the Javadoc; a short summary is provided in Table 7-1. All authoring kit components reside in the com.arsdigita.cms.ui.authoring package. Each component is designed to handle a certain domain object subtype; all domain objects in the CMS reside in the com.arsdigita.cms package.

NameTypePurposeDomainObject Subclass
PageCreateCreation ComponentInstantiates a new ContentPage and fills in the name and title attributes.ContentPage
PageEditStep ComponentEdits the name and title of an existing ContentPage.ContentPage
TextPageBodyStep ComponentSupplies a TextPage with its text body by uploading a file or by asking the user to type in the body. Automatically guesses the mime-type of the uploaded file.TextPage
ArticleImageStep ComponentManages an image which is associated with the Article, and the caption of the image. Automatically guesses the mime-type of the image, and the image size (only works for JPEG right now). Theoretically, this component should also allow the user to browse existing images, but this function is not yet implementedArticle
ItemCategoryStepStep ComponentManages categories for a single content item (i.e., pretty much anything in the CMS).ContentItem

Table 7-1. Pre-Built Authoring Kit Components

In addition to those in Table 7-1, a sample initializer for the Article content item can be found in the com.arsdigita.cms.install.ArticleInitializer class.

7.2.7. Utility Classes

In addition to pre-built authoring kit steps, Red Hat CMS provides several utility classes which could simplify the coding of authoring kit components. The exact usage of these classes is explained in the Javadoc and summarized here.

com.arsdigita.cms.ItemSelectionModel

The purpose of this class has been discussed in Section 7.2.5.1 Understanding the DomainObjectSelectionModel. Most utility classes take the ItemSelectionModel in the constructor and use it to obtain an instance of the ContentItem.

com.arsdigita.bebop.PropertySheet

This is the superclass for com.arsdigita.cms.ui.authoring.ItemPropertySheet. The sole purpose of this component is to simplify com.arsdigita.bebop.Table. The Table class is immensely more powerful, and should be used wherever a simple two-column table is not sufficient.

com.arsdigita.bebop.PropertyEditor

Maintains a display component and a set of forms. Initially, the display component is shown on the screen. When the user clicks on a link which leads to a form, the PropertyEditor will automatically hide the display component and show the form. When the form is submitted or cancelled, the PropertyEditor will hide the form and show the display component. Most pre-built authoring kit components use the PropertyEditor in some way; for example, the TextPageBody class extends PropertyEditor. The superclass handles all the state transitions.

Note that this component does not automatically generate the forms and the display component; it merely manages them.

com.arsdigita.cms.ui.authoring.BasicPageForm

This abstract class is used by the PageCreate and PageEdit components. It displays the name/title widgets and two submit buttons: Save and Cancel. While the class does not actually perform any database interaction, it has utility methods for saving and initializing the name and title of the article. The class also has some auxiliary methods: createContentPage can be used to create a new subclass of ContentPage with the correct parameters, and validateNameUniqueness should be used to ensure that an item's name is unique within its parent folder.

com.arsdigita.cms.ui.authoring.BasicImageForm

This abstract class is similar to BasicPageForm, but it is intended for uploading images. It provides a textbox for the image's caption, and utility functions to initialize and process the textbox. This class is used in the image uploading form of the ArticleImage component.

com.arsdigita.cms.ui.SaveCancelSection

Just a simple form section, with the Save and Cancel submit buttons. There is no actual functionality in this class. Most forms in the authoring kit UI use this section, for convenience.

com.arsdigita.cms.ui.FileUploadSection

Provides the widgets for uploading a file and selecting the file's MIME-type. In addition, provides utility methods for retrieving the file and guessing its MIME-type. Note that this section will not work correctly unless the parent form is multipart. The parent form should therefore execute the following code:

setMethod(Form.POST); setEncType("multipart/form-data");

The FileUploadSection class is used in the ArticleImage component in order to construct the image upload form.

com.arsdigita.cms.ui.DataTable

This component displays a DataQuery on the screen as a sortable table. The component extends the Bebop Table class, and simplifies its methods. It uses a DataQueryBuilder in order to construct the query during each request. For example, the following code will produce a sortable table with three columns:

DataTable table = new DataTable(new DataQueryBuilder() {
public DataQuery makeQuery(DataTable t, PageState s) {
return <emphasis>/* a new DataQuery */</emphasis>;
 }

 public String getKeyColumn() {
     return /* the name of the column which is the primary key for the \
query */;
  }
});

table.addColumn("Name", "name", true);
table.addColumn("Title", "title", true);
table.addColumn("Type", "type_label", true);

In addition, the addQueryListener method may be used to dynamically append filters to the query.