Adding 4.5 Features Guide
Brightspot version 4.5 introduces multiple new features. For a complete list, see the Brightspot 4.5 Product Guide. While many features are automatically included with the new version, others require development implementation. This guide outlines steps for adding boilerplate implementations of those features, which apply to data models and code patterns common to many applications built on Brightspot. Specific applications built on Brightspot may have differences from the examples contained in this document, but the approaches outlined below are intended to be easily extrapolated to include other data models and situations. For information on 4.5 features requiring development that are not covered in this guide, please contact your account representative.
Pre-Publish Actions
To implement this feature, implement the interface Suggestible on noted types.
Your project may already have versions of these classes implementing Suggestable. This is NOT a typo and serves different functionality than Suggestible.
Implement on below classes if applicable to project:
- Article
- Listicle
- Gallery
- Story
Example implementations of getSuggestibleFields()
Ensure that the field names listed in your implementation of getSuggestibleFields correctly match the field names including internal prefixes as specified on your data model. Verify that the specified fields are presented in the pre-publish action diaglog as described in the user documentation.
1// --- Suggestible support ---23@Override4public List<String> getSuggestibleFields() {5return Stream.of(6"cms.seo.title",7"cms.seo.description",8"promotable.promoTitle",9"promotable.promoDescription",10"promotable.promoImage",11"shareable.shareTitle",12"shareable.shareDescription",13"shareable.shareImage"14).collect(Collectors.toList());15}
1// --- Suggestible support ---23@Override4public List<String> getSuggestibleFields() {5return Stream.of(6"seo.title",7"seo.description",8"pagePromotable.promoTitle",9"pagePromotable.promoDescription",10"pagePromotable.promoImage",11"shareable.shareTitle",12"shareable.shareDescription",13"shareable.shareImage"14).collect(Collectors.toList());15}
Pre-Publish Actions is disabled by default. To enable it, go into Sites & Settings > CMS (tab) > UI (cluster) > Enable Pre-Publish Actions (toggle on). See Enabling pre-publish actions
Convert & Copy
Convert & Copy is enabled by default.
The standard implementation of Convert & Copy relies on concepts of "shared" and "inline" object placements. The implementation consists of implementing Interchangeable on both the "shared" and "inline" placement classes in the data model. If your project contains multiple pairs of shared / inline placement objects, Interchangeable will need to be implemented on each of those pairings.
The methods required to be implemented from Interchangeable for Convert & Copy functionality are loadTo() and loadableTypes which will vary greatly among multiple classes as applicable to your codebase. Recommended implementations can be found below.
If your project contains Module.java, Shared.java , or ModuleType.java
If your project contains either of these classes, implement Interchangeable on the classes exactly as detailed below.
Shared.java
1// --- Interchangeable Support ---2@Override3public boolean loadTo(Object target) {4if (target instanceof ModuleType && module != null) {5State.getInstance(target).setValues(module.getType().getState().getSimpleValues());67return true;8}9return false;10}1112@Override13public List<UUID> loadableTypes(Recordable recordable) {14Module module = getModule();15if (module != null) {16return new ImmutableList.Builder<UUID>()17.add(ObjectType.getInstance(module.getType().getClass()).getId())18.build();19} else {20return Collections.emptyList();21}22}
ModuleType.java
1// --- Interchangeable Support ---2@Override3public boolean loadTo(Object target) {4WebRequest current = WebRequest.getCurrent();5if (target instanceof Shared && Boolean.TRUE.equals(current.getParameter(boolean.class, "isConvert"))) {6UUID itemTypeId = this.getState().getTypeId();7Object currentModuleType = ObjectType.getInstance(itemTypeId).createObject(null);89if (currentModuleType instanceof ModuleType) {10State moduleTypeState = this.getState();11String moduleLabel = moduleTypeState.getLabel();12String convertedModuleLabel = ToolLocalization.text(new LocalizationContext(13Module.class,14ImmutableMap.of(15"label",16ObjectUtils.to(UUID.class, moduleLabel) != null17? ToolLocalization.text(moduleTypeState.getType(), "label.untitled")18: moduleLabel,19"date",20ToolLocalization.dateTime(new Date().getTime()))), "convertLabel");2122Shared sharedModule = (Shared) target;23Module newModule = new Module();24newModule.setName(convertedModuleLabel);25newModule.setType(this);26sharedModule.setModule(newModule);27State newModuleState = newModule.getState();2829if (newModuleState.validate()) {30// Publish converted module31Content.Static.publish(32newModuleState,33current.as(ToolRequest.class).getCurrentSite(),34current.as(ToolRequest.class).getCurrentUser());3536return true;37}38}3940}41return false;42}4344@Override45public List<UUID> loadableTypes(Recordable recordable) {46if (Module.hasPermission()) {47return new ImmutableList.Builder<UUID>()48.add(ObjectType.getInstance(Shared.class).getId())49.build();50} else {51return ImmutableList.of();52}53}
Module.java
You need to implement hasPermission on this class to ensure that users have the correct permission to convert between the different module types.
1static boolean hasPermission() {2return WebRequest.getCurrent()3.as(ToolRequest.class)4.hasPermission("type/" + ObjectType.getInstance(Module.class).getId() + "/publish");5}
If your project has inline/shared list implementations that implement ModulePlacement
These classes commonly occur in pairs. Some common examples of classes that will implement ModulePlacement are PageListModulePlacementInline and PageListModulePlacementShared
If your project has classes like this, you will need to implement Interchangeable on these list types, and Copyable on the inline type. This will drive the conversion between a shared module and an inline module.
1@Recordable.DisplayName("List Module")2@Recordable.Embedded3public class PageListModulePlacementInline extends AbstractAuthorListModule implements ModulePlacement,4Copyable,5Interchangeable {67// --- Copyable support ---89@Override10public void onCopy(Object source) {11if (source instanceof PageListModulePlacementShared) {12getState().remove("internalName");13}14}1516// --- Interchangeable support ---1718@Override19public boolean loadTo(Object target) {2021if (target instanceof PageListModulePlacementShared) {2223PageListModule sharedModule = Copyable.copy(ObjectType.getInstance(PageListModule.class), this);24String inlineLabel = getLabel();25String sharedLabel = ToolLocalization.text(new LocalizationContext(26ModulePlacement.class,27ImmutableMap.of(28"label",29ObjectUtils.to(UUID.class, inlineLabel) != null30? ToolLocalization.text(this.getClass(), "label.untitled")31: inlineLabel,32"date",33ToolLocalization.dateTime(new Date().getTime()))), "convertLabel");34// Internal Name field of the shared module will be set to this inline module's label with a converted copy text suffix35sharedModule.setInternalName(sharedLabel);3637// Publish converted module38Content.Static.publish(39sharedModule,40WebRequest.getCurrent().as(ToolRequest.class).getCurrentSite(),41WebRequest.getCurrent().as(ToolRequest.class).getCurrentUser());4243// Update the shared placement to reference the newly-published shared module44PageListModulePlacementShared sharedPlacement = ((PageListModulePlacementShared) target);45sharedPlacement.setShared(sharedModule);4647return true;48}49return false;50}5152@Override53public List<UUID> loadableTypes(Recordable recordable) {5455return ImmutableList.of(56ObjectType.getInstance(PageListModulePlacementShared.class).getId()57);58}59}
1@Recordable.DisplayName("Shared List Module")2@Recordable.Embedded3public class PageListModulePlacementShared extends Record implements4ModelWrapper,5ModulePlacement,6Interchangeable {78// --- Interchangeable support ---910@Override11public boolean loadTo(Object target) {1213if (getShared() == null) {14return false;15}1617if (target instanceof PageListModulePlacementInline) {1819PageListModule sharedModule = getShared();20State targetState = State.getInstance(target);21targetState.putAll(State.getInstance(Copyable.copy(targetState.getType(), sharedModule)).getSimpleValues());2223return true;24}25return false;26}2728@Override29public List<UUID> loadableTypes(Recordable recordable) {3031return ImmutableList.of(32ObjectType.getInstance(PageListModulePlacementInline.class).getId()33);34}35}
Some other types that you might want to implement this on (if present on your project):
- AuthorListModulePlacementInline.java
- AuthorListModulePlacementShared.java
- LogoListModulePlacementInline.java
- LogoListModulePlacementShared.java
- PageListModulePlacementInline.java
- PageListModulePlacementShared.java
- PersonListModulePlacementInline.java
- PersonListModulePlacementShared.java
- PagePromoModulePlacementInline.java
- PagePromoModulePlacementShared.java
Shelf
Uses the ContentEditDrawerItem interface to mark which classes will load a shelf within their interface.
Add ContentEditDrawerItem to the following classes:
-
Article.java -
Image.java -
Video.java -
Gallery.java -
Page.java(orAbstractPage)- Adding
ContentEditDrawerItemhere enables pages and all children of page to access the Shelf and the content within it. If your project hasPage.javaas an interface, you willextend ContentEditDrawerItemrather thanimplement.
- Adding
The Shelf is enabled by default and can be disabled per site, see Configuring The Shelf.
Enabling drag and drop from the shelf
After the shelf has been added to a specific piece of content, you need to implement Interchangeable on types that can be drag and dropped from the shelf into the content. Types that do not implement this cannot be dragged out of the shelf into a drop zone.
Article example
If you would like to be able to drag and drop an article from the shelf into another piece of content (for example, adding an article to a list on a page), then you need to implement Interchangeable on the Article type.
Important notes about the code below:
- We are explicitly supporting the ability to drag and drop an Article from the shelf into two types in the
loadTo()method:PromoandLinkRichTextElement - In
loadableTypes(),we add the current item's type ID to the list. This is REQUIRED.
1// --- Interchangeable Support ---23@Override4public boolean loadTo(Object target) {56if (target instanceof Promo) {7// Convert the current Article type to a Promo89Promo pagePromo = (Promo) target;10InternalPromoItem internalPromoItem = new InternalPromoItem();11internalPromoItem.setItem(this);12pagePromo.setItem(internalPromoItem);1314return true;1516} else if (target instanceof LinkRichTextElement) {17// Convert the current Article type to a LinkRichTextElement1819LinkRichTextElement linkRte = (LinkRichTextElement) target;20InternalLink internalLink = new InternalLink();21internalLink.setItem(this);22linkRte.setLink(internalLink);2324return true;25}26return false;27}2829@Override30public List<UUID> loadableTypes(Recordable recordable) {31return ImmutableList.of(32getState().getTypeId(), // IMPORTANT! enables dragging and dropping as itself from the shelf33ObjectType.getInstance(Promo.class).getId(),34ObjectType.getInstance(LinkRichTextElement.class).getId()35);36}
PageListModule example
Your project may also have an implementation of PageListModule, which will need a similar treatment above. This will drive the drag and drop of shared lists.
1public class PageListModule extends AbstractPageListModule implements2NoUrlsWidget,3Interchangeable,4SharedModule {56// --- Interchangeable support ---78@Override9public boolean loadTo(Object target) {1011// Support the following types for Shelf drag and drop:12// - PageListModulePlacementShared13if (target instanceof PageListModulePlacementShared) {14PageListModulePlacementShared pageListModulePlacementShared = (PageListModulePlacementShared) target;15pageListModulePlacementShared.setShared(this);16return true;17} else if (target instanceof PageListRichTextElement) {18PageListRichTextElement pageListRichTextElement = (PageListRichTextElement) target;19pageListRichTextElement.setList(this);20return true;21}22return false;23}2425@Override26public List<UUID> loadableTypes(Recordable recordable) {2728// Support the following types for Shelf drag and drop:29// - PageListModulePlacementShared30return ImmutableList.of(31getState().getTypeId(), // enable dragging and dropping as itself from the shelf32ObjectType.getInstance(PageListModulePlacementShared.class).getId(),33ObjectType.getInstance(PageListRichTextElement.class).getId()34);35}36}
Other types to consider implementation
This can be added on many different types, below are a list of types that implementation of this should be considered:
- Article.java
- Listicle.java
- Image.java
- Video.java