Abstract
Create a demonstrative explanatory app that illustrates the content app - pulse (workflow actions) - mobile author possibilities offered by M5.
Basic use case
Author for a restaurant site, Bob, needs to add new restaurants to the website, he will use his company's M5 app to directly take the picture, add some basic information and submit the new content for approbation to a publisher which for sake of simplicity will be superuser.
- Bob sees a nice restaurant he wants to promote
- Bob logs in to M5 author
- Bob opens his dedicated M5 app on his tablet
- Bob takes a picture of his restaurant, enters some basic content and sends it for approbation using pulse actions
- Alice and Eve are reviewing the information.
Proposed Implementation
MANDATORY
Start from the content app example here My first content app and modify it to make it fit the above use case.
Products will be restaurants
They will have a picture, a title, an abstract and an automatic geo-location(if this is possible), and content will be tagged.
Bob can add, modify and delete only his restaurants.
The actions will be configured to allow the mobile user Bob to submit the publication to the publishers.
The publishers will then review the addition or modification of the restaurant.
OPTIONAL
The published content will be pushed into an external index ( solr )
The last 10 restaurants will be displayed in a single page, the rest will be available for search ( geolocalised, keywords, faceted page)
Actual Implementation
Project setup
Check out the project from git here.
Slightly modified Content app
Import the following configuration as you can see, we slightly modified certain fields of a "standard" content app.
Adding categorization
Categorization was added in the same way as for 4.5:
Basically you add a tab and extend /modules/categorization/dialogs/generic/tabCategorization.
Linking the image to use stuff from the assets
Instead of using the image uploader we will link the image field of the restaurant to a Node in the assets. Uploading assets will be done using the asset app. It is foreseen to be able to upload as well assets directly through the content app (probably 5.1).
This is done by adding the following:
Or link to the asset will be stored under the image node, to retrieve it in our page, we can use the DAMTemplating API, we can use it as follows:
//restoNode is the node in our restaurant workspace that holds the restaurant we want to submit. ContentMap cm = new ContentMap(restoNode); Asset asset = damTemplatingFunctions.getAssetForId((String)cm.get("image"));
Adding an activation action
We duplicate the activation action we can find in the Pages app and add it under the actions, add the corresponding activate action in the actionbar.
As you can see we only allowed superuser to activate content, this is because we would like to add a sort of activation authorization command or a basic approval workflow.
Adding the public instance
We add a public server as described in the documentation, for easiness of use we will have author and public on teh same instance.
Creating the blog page for displaying the activated content
We now create a new template and blog page by extending stkHome, we will as well add a new FTL and new component to the project.
The modelClass will list all activated restaurants.
The new FTL will just display the activated items.
public class FetchRestos extends AbstractSTKTemplateModel<TemplateDefinition> { private static Logger log = Logger.getLogger(FetchRestos.class); private static final String ACTIVE_RESTOS = "select * from [mgnl:content] where [mgnl:activationStatus] = CAST('true' AS BOOLEAN)"; private List<Resto> restos = null; private DamTemplatingFunctions damTemplatingFunctions; @Inject public FetchRestos(Node content, TemplateDefinition definition, RenderingModel<?> parent, STKTemplatingFunctions stkFunctions, TemplatingFunctions templatingFunctions,DamTemplatingFunctions damFunctions) { super(content, definition, parent, stkFunctions, templatingFunctions); damTemplatingFunctions=damFunctions; restos = new ArrayList<Resto>(); } public String execute(){ try { Session session = MgnlContext.getJCRSession("restaurants"); Query query = session.getWorkspace().getQueryManager().createQuery(ACTIVE_RESTOS,Query.JCR_SQL2); NodeIterator ni = query.execute().getNodes(); while(ni.hasNext()){ ContentMap cm = new ContentMap(ni.nextNode()); Asset asset = damTemplatingFunctions.getAssetForId((String)cm.get("image")); Map assetMap = damTemplatingFunctions.getAssetMap(asset); String type = (String)assetMap.get("type"); restos.add(new Resto((String)cm.getJCRNode().getName(),damTemplatingFunctions.getAssetRendition(asset, "thumbnail").getLink(), (String) cm.get("description"),type)); } } catch (RepositoryException e) { log.error(e.getMessage()); } catch (DamException e) { log.error(e.getMessage()); } return "ok"; } public List<Resto> getRestos() { return restos; } public void setRestos(List<Resto> restos) { this.restos = restos; } }
Creating normal users and the correct roles
We create a new role for teh mobile authors who submit the restos, add a new user and give him the correct roles and basic groups for only accessing the addrsto app.
Giving correct access rights to the app
We give the correct rights to the app.
Adding a "Request for activation action"
We now add some custom logic to the app, creating a custom action as defined below, the action will be fired from the addresto subapp, so we decide to inject the following.
We want to send a message, so we need the SubAppContext
We want to show a notification so we need the Shell.
Adding a messageView
We need to register a messageView so that the pulse can find it and open it. So we add a messageview to the addresto app as follows.
Adding a review action to the messageView.
We now add some custom logic to the messageView by defining a review action who will open the content the mobile author wants us to review. To do so we inject the Message, the LocationController and the Shell since we want to add some notifications.
Adding a reject action to the contentApp View.