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.
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 fromContent
. In a more robust implementation, you might create the classReporter
that extends , create the classReporterRole
that extends , and then assign instances ofReporter
toReporterRole
.
-
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.
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.
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.
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 implementingSubstitution
. -
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.
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 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 returnsfalse
and the widget does not appear. See the following illustration.
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.
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: