Brightspot CMS Developer Guide

Content edit widgets


Content edit widgets are widgets that appear on an editor’s content edit page. Brightspot comes with standard widgets such as Revisions and URLs. You can also create your own custom content edit widgets, and you can configure the standard and custom content edit widgets.


You can create widgets that appear in the content edit page by extending the abstract class ContentEditWidget. That abstract class includes three abstract methods that you must override, all of which support displaying read-only data.

The following snippet shows an entire class for displaying the custom widget Current Time Zones in the content edit page. The custom widget lists several cities and their current time.

import com.psddev.cms.tool.*;

public class CurrentTimesWidget extends ContentEditWidget { 

    @Override
    public void display(ToolPageContext page, Object content, ContentEditWidgetPlacement placement) throws IOException { 
        Map<String, String> timeZoneIdentifiers = new HashMap<>(); 
        timeZoneIdentifiers.put("New York", "America/New_York");
        timeZoneIdentifiers.put("Los Angeles", "America/Los_Angeles");
        timeZoneIdentifiers.put("Mexico City", "America/Mexico_City");

        page.writeStart("table");

            page.writeStart("tr"); 
                page.writeStart("th");
                    page.writeHtml("City");
                page.writeEnd(); /* th */
                page.writeStart("th");
                    page.writeHtml("Time");
                page.writeEnd(); /* th */
            page.writeEnd(); /* tr */

            for (String myTimeZone : timeZoneIdentifiers.keySet()) { 
                page.writeStart("tr");
                    page.writeStart("td");
                        page.writeHtml(myTimeZone);
                    page.writeEnd(); /* td */
                    page.writeStart("td");
                        String localTime = displayTime(timeZoneIdentifiers.get(myTimeZone));
                        page.writeHtml(localTime);
                    page.writeEnd(); /* td */
                page.writeEnd(); /* tr */
            }
        page.writeEnd(); /* table */
    }

    public ContentEditWidgetPlacement getPlacement(ToolPageContext page, Object content) { 
        return ContentEditWidgetPlacement.BOTTOM;
    }

    public String getHeading(ToolPageContext page, Object content) { 
        return "Current Times";
    }

    private String displayTime(String timeZoneIdentifier) {
        /* 
            Returns local time in hh:mm format based on
            passed time zone identifier.
        */
    }
}
  • Declares the class CurrentTimesWidget. Objects instantiated from this class appear as Current Times Widget in the content edit page.
  • Instantiates a HashMap of time zones.
  • Writes a table header in the widget’s body.
  • Loops through each time-zone record. For each record, call the method displayTime to retrieve and then print the time zone’s current time.
  • Positions the widget under the content edit form. For details about positioning a widget on the content edit form, see Position.
  • Displays the widget’s heading.

Based on the previous snippet, the custom widget Current Times appears at the bottom of the content edit page.

Custom content edit widget


The content edit page includes an updating URLs widget that contains traditional HTML form tags such as <textarea> and <select>. You can create custom updating widgets that provide web forms in the content edit page by extending UpdatingContentEditWidget—an abstract class that includes the abstract method displayOrUpdate you must override in addition to the abstract methods described in Creating content edit widgets. These widgets respond to events and instantiate custom objects for data validation and posting content to the database.

In the following example you create a widget for attributing an article to a reporter—a typical task in a ghost-writing scenario.

Step 1: Create reporter model

In this step, you create a simple model to hold reporters’ first and last names.

public class Reporter extends Content { 

    private String firstName;
    private String lastName;

    /* Getters and setters */

    public String getFullName() { 
        return getFirstName() + " " + getLastName();
    }
}
  • Declares the class Reporter that extends from Content. In a more robust implementation, you might create the class Reporter that extends ToolUser, create the class ReporterRole that extends ToolRole, and then assign instances of Reporter to ReporterRole.
  • Returns a reporter’s full name as a concatenation of the reporter’s first and last name.

Step 2: Add field to model, hide from content edit form

By default, Brightspot places all of a model’s fields in the content edit form. In this example, you are attributing an article to a reporter, so a reporterCredit field must be part of the article’s model. Because reporterCredit appears in an updating content edit widget, you must use the @ToolUi.Hidden annotation in the model to remove the field from the content edit form.

public class Article extends Content {

    @ToolUi.Hidden
    private Reporter reporterCredit;

    public Reporter getReporterCredit() {
        return reporterCredit;
    }
}

Step 3: Display custom updating widget in content edit page

import com.psddev.cms.tool.ContentEditWidgetPlacement;
import com.psddev.cms.tool.ToolPageContext;
import com.psddev.cms.tool.UpdatingContentEditWidget;
import com.psddev.dari.db.Query;
import content.article.*;

public class ReporterCreditWidget extends UpdatingContentEditWidget { 

    @Override
    public void displayOrUpdate(ToolPageContext page, Object content, ContentEditWidgetPlacement placement) throws IOException {

        Article myArticle = (Article) content; 

        if (placement == null) { 
            UUID reporterID = page.param(UUID.class, "reporter.select"); 
            Reporter creditedReporter = Query.from(Reporter.class).where("id = ?", reporterID).first(); 
            myArticle.setReporterCredit(creditedReporter); 
            return;
        }

        List<Reporter> reporters = Query.from(Reporter.class).selectAll(); 
        Reporter currentReporter = myArticle.getReporterCredit(); 

        page.writeStart("select", "name", "reporter.select"); 
            page.writeStart("option", "value", "").writeEnd(); /* Initial blank option */
            for (Reporter reporter : reporters) { 
                page.writeStart("option", "selected", reporter.equals(currentReporter) ? "selected" : null, "value", reporter.getId()); 
                    page.writeHtml(reporter.getFullName()); 
                page.writeEnd(); /* option */ 
            }
        page.writeEnd(); /* select */
    }

    @Override
    public ContentEditWidgetPlacement getPlacement(ToolPageContext page, Object content) { 
        return ContentEditWidgetPlacement.RIGHT;
    }

    @Override
    public String getHeading(ToolPageContext page, Object content) { 
        return "Reporter Credit";
    }

    @Override
    public boolean shouldDisplay(ToolPageContext page, Object content) { 
        return content instanceof Article;
    }
}
  • Declares the class ReporterCreditWidget as a subclass of an updating content widget.
  • Callback for displaying or updating a content edit widget.
  • Casts the incoming asset as an Article.
  • Traps the widget’s update event.
  • Retrieves the selected reporter’s UUID.
  • Retrieves the Reporter record matching the selected reporter’s UUID.
  • Assigns the retrieved reporter record to the article.
  • Retrieves all of the reporters.
  • Retrieves the current article’s reporter credit.
  • Renders the widget's bgody with an HTML <select> field. The field’s options are a default blank entry and the list of retrieved reporters.
  • Positions the widget in the right rail.
  • Displays the widget’s heading Reporter Credit.
  • Displays the widget if the current content type is an article.

Based on the previous snippet, the custom updating content widget Reporter Credit appears in the right rail of the content edit page.

Reporter Credit widget


The position of a widget refers to its location relative to other widgets with the same placement. The following sections describe Brightspot’s default positioning logic as well as techniques for overriding that logic.

Default positioning

Within the same Placement (top, bottom, right, dedicated tab), Brightspot by default positions custom widgets in alphabetical order based on the class’s fully qualified name. For example, suppose you defined three custom widgets, widgets.AdamsCustomWidget, widgets.JacksCustomWidget, and specials.widgets.ZoesCustomWidget, and all of them appear under the content edit form. By default, Brightspot positions them in alphabetical order as in the following illustration.

Default widget positioning

Custom positioning

You can override the alphabetical sorting for custom widgets by implementing the method ContentEditWidget#getPosition and returning a double value. Values with smaller numbers appear closer to the top, and the default value is zero. Positions with the same value are sorted alphabetically by fully qualified class name.

import com.psddev.cms.tool.ContentEditWidget;
import com.psddev.cms.tool.ContentEditWidgetPlacement;
import com.psddev.cms.tool.ToolPageContext;

public class JillsCustomWidget extends ContentEditWidget {

    @Override
    public ContentEditWidgetPlacement getPlacement(ToolPageContext toolPageContext, Object o) { 
        return ContentEditWidgetPlacement.BOTTOM;
    }

    @Override
    public double getPosition(ToolPageContext page, Object content, ContentEditWidgetPlacement placement) { 
        return 8.0;
    }

    @Override
    public String getHeading(ToolPageContext page, Object content) {
        return "Jill's Custom Widget";
    }

    @Override
    public void display(ToolPageContext page, Object content, ContentEditWidgetPlacement placement) throws IOException {
        page.writeStart("p");
        page.writeHtml("Here is Jill's custom widget.");
        page.writeEnd();
    }
}
  • Places Jill’s widget under the content edit form.
  • Assigns Jill’s widget a relative position of 8.0

Suppose you assign Zoe’s, Adam’s, Jack’s, and Jill’s widgets return values for getPosition as in the following table.

Example values for positioning widgets

Widget
Fully Qualified Class Name
getPlacement
getPosition
Zoe
special.widgets.ZoesCustomWidget
ContentEditWidgetPlacement.BOTTOM
10.0
Adam
widgets.AdamsCustomWidget
ContentEditWidgetPlacement.BOTTOM
10.0
Jack
widgets.JacksCustomWidget
ContentEditWidgetPlacement.BOTTOM
8.0
Jill
widgets.JillsCustomWidget
ContentEditWidgetPlacement.BOTTOM
9.0

Because all the widgets have the same placement, Brightspot lays them out as in the following illustration.

Custom widget positioning

Both Zoe’s and Adam’s widgets have the same return value for getPosition, so Brightspot breaks the tie by sorting by fully qualified class name.

Custom positioning with substitution

If you use a custom widget that is imported from another package, that widget may have a result from getPosition that conflicts with your implementation. Referring to the table "Example values for positioning widgets," Zoe’s custom widget has a position of 10, but you may want to place her widget first, in front of all the other custom widgets.

You can use substitutions to incorporate an imported widget into your project and then override the original value for getPosition.

At run time, the ported custom widget appears first among all other custom widgets under the content edit form.

import com.psddev.cms.tool.ContentEditWidgetPlacement;
import com.psddev.cms.tool.ToolPageContext;
import com.psddev.dari.db.Substitution;
import special.widgets.ZoesCustomWidget; 

public class ZoesPortedCustomWidget extends ZoesCustomWidget implements Substitution { 

    @Override
    public double getPosition(ToolPageContext page, Object content, ContentEditWidgetPlacement placement) { 
        return 6.0;
    }

}
  • Imports the custom widget ZoesCustomWidget from another package.
  • Declares a ported custom widget ZoesPortedCustomWidget by extending the imported custom widget and implementing Substitution.
  • Overrides the imported widget’s position, now assigning it 6 (compared to the original 10 as listed in the table "Example values for positioning widgets"). This position value is the lowest of all other custom widgets.

At run time, the ported custom widget appears first among all other custom widgets under the content edit form.

Rendered custom positioning of widget


The placement of a widget refers to its location relative to the content edit form: top, bottom, right, or in its own tab. You provide a placement for a custom widget by implementing the method ContentEditWidget#getPlacement.

public ContentEditWidgetPlacement getPlacement(ToolPageContext page, Object content) {
    /* Return one of the placement constants. */
}

See the snippet "Implementing a custom widget in content edit page" for an implementation of this method.

The following table lists the available placement constants.

Placement constants

Constant

Description

Example

TOP
Places widget above content edit form.
return ContentEditWidgetPlacement.TOP;
TAB
Places widget in its own tab. If you use this position, the tab does not appear for embedded objects in the content edit form.
return ContentEditWidgetPlacement.TAB;
BOTTOM
Places widget below content edit form.
return ContentEditWidgetPlacement.BOTTOM;
RIGHT
Places widget below URLs widget.
return ContentEditWidgetPlacement.RIGHT;


By default, Brightspot displays the following standard widgets in the content edit page: References, Revisions, SEO, Sites, Template, and URLs. Some of these widgets appear depending on an item’s state. For example, the Revisions widget appears only after you save a draft, change a workflow state, or publish an item. In addition, Brightspot automatically displays any custom widgets created for the content edit page. (For information about creating custom widgets for the content edit page, see Creating content edit widgets and Creating updating content edit widgets.)

You can hide widgets at the content-type level and at the custom widget level.

Hiding widgets at the content-type level

You can hide the standard or custom widgets at the content-type level. For example, you can show the URLs widget on content edit pages for articles, and hide the widget on content edit pages for images.

The interface ContentEditWidgetDisplay contains a method shouldDisplayContentEditWidget that you can override to specify if a widget is visible or not in a content edit page. You implement the interface at the model level, which gives you the flexibility for showing or hiding widgets depending on content type. The following example shows how to hide the standard URLs widget in an article’s content edit page.

import com.psddev.cms.db.Content;
import com.psddev.cms.db.ToolUi;
import com.psddev.cms.tool.ContentEditWidgetDisplay;
import com.psddev.cms.tool.content.UrlsWidget;
import com.psddev.dari.db.Recordable;

public class Article extends Content implements ContentEditWidgetDisplay {

    @Recordable.Required
    private String headline;

    @ToolUi.RichText
    private String body;

    @Override
    public boolean shouldDisplayContentEditWidget(String widgetName) { 
        if (widgetName.equals(UrlsWidget.class.getName())) {
            return false;
        } else {
            return true;
        }
    }
}
  • Implements the method shouldDisplayContentEditWidget. Brightspot calls this method for each widget eligible to appear in the content edit page. If the widget’s name is the same as the URLs widget’s name, the method returns false and the widget does not appear. See the following illustration.

Hiding widgets at content type level

Hiding custom widgets

If you create a custom widget, you can hide it from the content edit page—regardless of the content type—by overriding the shouldDisplay method in the widget’s definition.

import com.psddev.cms.tool.ContentEditWidget;
import com.psddev.cms.tool.ContentEditWidgetPlacement;
import com.psddev.cms.tool.ToolPageContext;

public class CurrentTimesWidget extends ContentEditWidget {

    @Override
    public void display(ToolPageContext page, Object content, ContentEditWidgetPlacement placement) throws IOException { 

        page.writeStart("p");
            page.writeHtml("My widget for personal notes.");
        page.writeEnd(); /* p */
    }

    public ContentEditWidgetPlacement getPlacement(ToolPageContext page, Object content) {
        return ContentEditWidgetPlacement.BOTTOM;
    }

    public String getHeading(ToolPageContext page, Object content) {
        return "My Custom Widget";
    }

    @Override
    public boolean shouldDisplay(ToolPageContext page, Object content) { 
        return false;
    }
}
  • Defines a custom widget that appears under the content edit form.
  • Hides the custom widget from the content edit page. See the following examples.

Custom widget shown
Custom widget shown
Custom widget hidden
Custom widget hidden

You can configure widgets on the content edit form directly from Brightspot. Configuration includes the following features:

  • Creating custom content edit form by content type.
  • Assigning custom content edit forms at the role level or to site-role combinations.
  • Add tabs to content edit forms, and adding fields to those tabs.
  • Assigning read/write permissions at the tab and field level.

For details about these configuration activities, see the following topics in the Editorial Guide:

Previous Topic
Dashboard widgets
Next Topic
Tabs
Was this topic helpful?
Thanks for your feedback.