Magnolia 5.7 reached extended end of life on May 31, 2022. Support for this branch is limited, see End-of-life policy. Please note that to cover the extra maintenance effort, this EEoL period is a paid extension in the life of the branch. Customers who opt for the extended maintenance will need a new license key to run future versions of Magnolia 5.7. If you have any questions or to subscribe to the extended maintenance, please get in touch with your local contact at Magnolia.
The flickr-simple-browser
module is an example of how to build a custom content app. The module installs an app that allows you to browse photos on Flickr. We use a String
as ItemId, which is easy to implement but has the disadvantage that we cannot tell the difference between photos and albums. This example also shows how to use an external Web service as the data source. The list view and the thumbnail view use the same Container coupled to a . See also Content app with an Object ItemId and different containers for an example of a hierarchical container.
Limitation: using a String as an ItemId
The example app can only display photos from Flickr, not albums. We have this limitation because the container uses a simple String
as ItemId. The string we use is the photo_id
from the Flickr API.
Using a string provided by the data source is often sufficient. A string simplifies the code but it can also have downsides. In this case we can only display photos from Flickr, no albums. Look at these two IDs which originate from Flickr: 72157648362070329
and 15355595988
. One is a photo ID, the other one is an album ID also known as a photo set. It is impossible to tell which is which just by looking at the string. We could use the Flickr API to check whether it's a photo or photoset by calling the appropriate methods and checking the return value. But such a test is not very elegant.
For the purposes of this example we accept the limitation and use a string. However, in the second tutorial Content app with an Object ItemId and different containers we use a POJO for the ItemId. There we can display photos and albums.
Overview
In this example we implement the following custom classes:
Item
ContentConnector
Container
ImageProvider
Presenter
We also add two views to the workbench:
- Thumbnail view
- List view
Setting up the module and app
We assume you already know how to set up a new module. We also assume that you also know the Parts of a content app.
name: flickr-simple-browser appClass: info.magnolia.ui.contentapp.ContentApp subApps: browser: subAppClass: info.magnolia.ui.contentapp.browser.BrowserSubApp class: info.magnolia.ui.contentapp.browser.BrowserSubAppDescriptor # contentConnector: # workbench: # imageProvider:
Node name | Value |
---|---|
flickr-simple-browser | |
apps | |
flickr-simple-browser | |
subApps | |
browser | |
contentConnector | |
workbench | |
imageProvider | |
class | info.magnolia.ui.contentapp.browser.BrowserSubAppDescriptor |
subAppClass | info.magnolia.ui.contentapp.browser.BrowserSubApp |
appClass | info.magnolia.ui.contentapp.ContentApp |
Properties:
flickr-simple-browser
| required App |
| required |
| required Subapp |
| required |
| required |
| required Must be |
For the details of contentConnector
, workbench
and imageProvider
definition, see below.
Creating items
Interface
An Item
has properties and every property is identified by its property ID. Define the properties in an interface. In this module we define the interface .
public interface SimpleFlickrItem extends BasicFlickrItem { }
The interface extends in the
flickr-integration
module which is the basic item interface that other submodules extend. SimpleFlickrItem
has no other properties.
public interface BasicFlickrItem extends Item { public static String PROPERTY_TITLE = "title"; public static String PROPERTY_DESCRIPTION = "description"; public static String PROPERTY_PHOTOID = "photoId"; public class IDs { private static Map<String, Class> properties = new HashMap<String, Class>(); static { properties.put(PROPERTY_TITLE, String.class); properties.put(PROPERTY_DESCRIPTION, String.class); properties.put(PROPERTY_PHOTOID, String.class); } public static Map<String, Class> getIDs() { return properties; } } }
Our interface has the following property IDs:
photoId
: The ID of the photo given by Flickr.title
: The title given to the photo. If the photo has no title we use the photo ID as a title.description
: The description of the photo. Often photos have no descriptions on Flickr.
The interface contains also a static class which provides a map of all the properties and their types. We use the properties map later to configure the container.
Map Map<String, Class> properties = SimpleFlickrItem.IDs.getIDs();
Implementation
The implementation class allows us to handle a set of identified properties. extends PropertysetItem, one of the implementations provided by Vaadin.
public class SimpleFlickrPropertysetItem extends PropertysetItem implements SimpleFlickrItem { public SimpleFlickrPropertysetItem(Photo photo){ String photoId = photo.getId(); String title = StringUtils.isNotBlank(photo.getTitle()) ? photo.getTitle() : photoId; String description = StringUtils.isNotBlank(photo.getDescription()) ? photo.getDescription() : ""; addItemProperty(PROPERTY_PHOTOID, new ObjectProperty(photoId)); addItemProperty(PROPERTY_TITLE, new ObjectProperty(title)); addItemProperty(PROPERTY_DESCRIPTION, new ObjectProperty(description)); } }
The parameter Photo
is class from Flickr4Java, the Java API that wraps the REST-based Flickr API.
Creating and configuring a content connector
We use a non-hierarchical (flat) Container
for both the list and the thumbnail view. This allows us to use the same container for both views and makes it possible to couple the container and the ContentConnector
.
Interface
Define an interface so that the content connector can use the container:
public interface SimpleFlickrBrowserContentConnector extends ContentConnector { Container getContainer(); }
Implementation
Then implement the interface:
Methods:
getItemUrlFragment(Object itemId)
(see line 16) andgetItemIdByUrlFragment(String urlFragment)
(see line 20) methods can return their input parameters since our ItemId is a string and the URL fragment and the ItemId are identical in this simple example.getDefaultItemId()
method does not need to be implemented. It can return null. But since FlickrService provides a default photo we implement the method. In some cases it is helpful to work with a default item.canHandle()
should reject invalid ItemIds. The app could fire an event with an ID/
and the event would be handled by the ContentConnector but there is no Flickr photo with the ID/
.- The
interface is injected in the constructor. FlickrService is instantiated by
which is registered in the
flickr-integration
module.
Configuration
Every subapp must configure its own content connector. The class extends
and sets the implementation class
SimpleFlickrBrowserContentConnectorImpl
.
browser: contentConnector: class: info.magnolia.flickr.simplebrowser.app.contentconnector.ConfiguredSimpleFlickrBrowserContentConnectorDefinition
Node name | Value |
---|---|
browser | |
contentConnector | |
class | info.magnolia.flickr.simplebrowser.app.contentconnector.ConfiguredSimpleFlickrBrowserContentConnectorDefinition |
creates and provides only one instance of
SimpleFlickrBrowserContentConnector
in our subapp. The content connector can be injected into any class used within the subapp. You can cast it to your own type if required.
Creating a flat container
We use the same container for the tree view and the thumbnail view. In this case it would be sufficient to implement just the base interface Container. However, we also implement so we can use the refresh mechanism in the Magnolia workbench. We also need the
Container.Indexed
subinterface for lazy loading.
Notes:
- The container extends AbstractContainer which provides basic methods for event handling.
- The code snippet above does not show any methods that throw UnsupportedOperationException or any logging procedures.
- Inject FlickrService via constructor.
- The container must be configured. It has to know about the properties of its item. See in #configure (line 111-116) how the properties are set.
- By implementing
Container.Indexed
it would be possible to lazy load the Items and ItemIds. To keep the example simple we haven't done that.
Creating a thumbnail view
Thumbnail view is good for displaying visual content such as photos and diagrams. However, you should not use it alone. In this example we pair a thumbnail view with a list view. Both are non-hierarchical.
Presenter
In the presenter class, extend and initialize the container:
Definition
Set the implementation class in the definition class:
Configuration
A thumbnail view has no columns so its configuration is simple: just add the definition class.
browser: workbench: contentViews: - name: thumbnail class: info.magnolia.flickr.simplebrowser.app.workbench.SimpleFlickrThumbnailPresenterDefinition
Node name | Value |
---|---|
browser | |
workbench | |
contentViews | |
thumbnail | |
class | info.magnolia.flickr.simplebrowser.app.workbench.SimpleFlickrThumbnailPresenterDefinition |
Creating and configuring an image provider
Image provider is a component that renders images used in apps. It generates the portrait image at the bottom of the action bar and the thumbnails for the thumbnail view.
Implementation
For this simple example it is sufficient to implement just the getThumbnailResource() method from the ImageProvider interface. The ItemId string is the Flickr photo ID. To get the URL of a Flickr photo we must call the Flickr API with the photo ID for each item. This consumes time and resources.
To improve the performance, we could use a POJO as an ItemId. We do so in the second tutorial Content app with an Object ItemId and different containers .
Configuration
An image provider must be configured per subapp.
browser: imageProvider: class: info.magnolia.ui.imageprovider.definition.ConfiguredImageProviderDefinition imageProviderClass: info.magnolia.flickr.simplebrowser.app.imageprovider.SimpleFlickrBrowserPreviewImageProvider
Node name | Value |
---|---|
browser | |
imageProvider | |
class | info.magnolia.ui.imageprovider.definition.ConfiguredImageProviderDefinition |
imageProviderClass | info.magnolia.flickr.simplebrowser.app.imageprovider.SimpleFlickrBrowserPreviewImageProvider |
Creating and configuring a list view
It's a Magnolia best practice to always pair a thumbnail view with another view. Here we add a list view since both are non-hierarchical.
Definition
Implementation
In the presenter, inject the ContentConnector
, cast it its specific type, and then use it to return the Container
.
Configuration
browser: workbench: contentViews: - name: list class: info.magnolia.flickr.simplebrowser.app.workbench.SimpleFlickrListPresenterDefinition columns: - name: title class: info.magnolia.flickr.app.workbench.FlickrBrowserItemColumnDefinition formatterClass: info.magnolia.flickr.app.workbench.FlickrBrowserItemColumnFormatter propertyName: title
Node name | Value |
---|---|
browser | |
workbench | |
contentViews | |
list | |
columns | |
title | |
class | info.magnolia.flickr.app.workbench.FlickrBrowserItemColumnDefinition |
formatterClass | info.magnolia.flickr.app.workbench.FlickrBrowserItemColumnFormatter |
propertyName | title |
class | info.magnolia.flickr.simplebrowser.app.workbench.SimpleFlickrListPresenterDefinition |
Properties:
list | required |
| required Column definitions for tree, list and search views. You don't need to define columns for the thumbnail view. |
| required You can choose the name of the column. H owever, it is common practice to use same name as in |
| required The column definition class reads the column configuration and displays the column accordingly. The class must implement ColumnDefinition . |
| required Defines how the column's value is displayed in the UI. This is useful for making the raw data more readable or making it adhere to a formatting convention.The formatter class must extend AbstractColumnFormatter |
| required The name of the property as configured in the container. See
SimpleFlickrFlatContainer
|
| required Presenter definition class. |