This chapter presents new features added to eXo Platform 3.5. At present, in this chapter, you will have opportunity to learn about one feature called Navigation By Content with the following specific topics:
Navigation By Content is a feature which allows users to browse content of each page easily. With this feature, users experiencing eXo Platform can navigate from a page to another or browse site content inside one page directly from a contextual menu.
One of the powerful features of Enterprise Content Management System (ECMS) that comes out with eXo Platform 3.5 is the ability to navigate in site contents using taxonomies. This functionality can easily be added in a page with the help of two Content List Viewer (CLV) portlets. The pre-configured example can be found in the News page of the sample ACME website. In this example, the /site contents/live/acme/events/All node will be used.
To add "Actual content navigation" to a page:
1. Log in to the sample ACME website.
2. Add a new page, for example "Events".
3. Parameterize this page with the two-column container.
4. Add two content list portlets.
i. First portlet:
In which:
Folder path = /site contents/live/acme/events/All
Header = Browse by:
Template = CatgoryTree.gtmpl
Contextual Folder = Disabled
Show in page = Events
With = folder-id
ii. Second portlet:
In which:
Folder path = /site contents/live/acme/events/All
Template = OneColumnCLVTemplate.gtmpl
Contextual Folder = Enabled
Show in page = Details
With = content-id
As a result, the created "Events" page will look like:
You can now navigate from the left portlet to see contents displayed in the right portlet.
The new Navigation By Content feature will traduce this example in a contextual menu.
You need to attach your root folder/node to some page nodes from the homepage (the drop-down menu holds your new contextual menu):
1. Go to the Content Explorer page and navigate to /site contents/live/acme/events/All.
2. Click the Content Navigation button.
3. Fill values into the navigation form, including:
Visible = true. This node will be navigable.
Target parent navigation = Events. The contextual menu will be attached to the Events drop-down menu.
Clickable = false. This node will not be clickable.
Page for list = catalog. This page is a system page that contains a content list viewer portlet and will be used to display the list of child nodes.
Page for detail = detail. This page is a system page that contains a single content viewer portlet and will be used to display details of child nodes.
4. Save changes, then go back to the Acme/Overview homepage.
You will see changes from the Events drop-down menu.
In which:
Visible: The /site contents/live/acme/events/All node is navigable and its child nodes are rendered in the contextual menu.
Target parent navigation: The /site contents/live/acme/events/All node is attached to the site menu item called Events.
Clickable: The /site contents/live/acme/events/All node is not clickable but all its child nodes are clickable.
Page for list: The list of child nodes (if a child node is directory/folder) will be rendered in the following page.
Click the Earth menu item from the contextual menu and see that contents of the Earth directory are rendered in a separate page (catalog):
Page for detail: The details of child nodes (if a child node is a sample content) will be rendered in this page:
Select the Power 1 - Fire menu item from the contextual menu to see the Fire content which is displayed in a separate page (details):
To restrict the visibility of some contents:
1. Go to the Content Explorer page and navigate under /site contents/live/acme/events/All/Fire.
2. Click the Content Navigation button.
3. Uncheck the field visible and save.
4. Go back to the homepage and see that the Fire sub-menu is not displayed in the contextual menu.
To sort elements of the contextual menu:
1. Go to the Content Explorer page and navigate under /site contents/live/acme/events/All.
2. Select the /site contents/live/acme/events/All/Earth node.
3. Click the Content Navigation button.
4. Set the Display order field to 1 and save.
5. Select the /site contents/live/acme/events/All/Water node.
6. Click the Content Navigation button.
7. Set the Display order field to 2 and save.
8. Select the /site contents/live/acme/events/All/Air node.
9. Click the Content Navigation button.
10. Set the Display order field to 3 and save.
11. Go back to the homepage and see that the display order from the contextual menu is sorted to Earth, Water, Air. Note that the Fire sub-menu is not displayed because it is set to "invisible" in the previous example.
To restore a node to the contextual menu (if you already removed it) and attach it to another page:
1. Go to the Content Explorer page and navigate under /site contents/live/acme/events/All/Fire.
2. Click the Content Navigation button.
3. Fill values into the navigation form fields, including:
Visible = true
Target parent navigation = News
Clickable = false
Page for list = catalog
Page for detail = detail
4. Save changes and go back to the Acme/Overview homepage and see that the Fire node is attached to the News drop-down menu from the site menu:
However, if you want to add your newly created content directly to the contextual menu, you need to add the populateToMenu action first.
To add your newly created contents to the contextual menu:
1. Go to the Content Explorer page and navigate under /site contents/live/acme/events/All/Fire.
2. Click the Manage Actions button and add the exo:populateToMenu action.
3. Create a document under /site contents/live/acme/events/All/Fire (for example, uploading a file) and publish it.
4. Go back to the homepage and see that your newly created document is added to the contextual menu.
The sample ACME website comes with a configured navigation by content menu:
You can click the Vision sub-menu and see contents of vision directory rendered in the catalog page:
For example, select the X-Ray content and see the newly implemented content using new visual effects.
"Benefits" and "Features" tabs:
Coverflow section:
Related documents:
To create a new product:
1. Go to the Content Explorer page and navigate under somePath/someDirectory.
2. Click the Add Document button.
3. Select Product.
4. Fill the product dialog form.
Name
Title
Illustration Image
Summary
Benefits
Features
5. Save changes.
To improve your newly created product, for example "sampleProduct":
1. Go under somePath/someDirectory/sampleProduct/medias/images.
2. Upload some images and publish them.
3. Go under somePath/someDirectory/sampleProduct/medias/videos.
4. Upload a video and publish it.
5. Go under somePath/someDirectory/sampleProduct/documents.
6. Create two directories, including Sales materials and Technical documentation.
7. Upload a PDF document and publish it under each sub-folder.
8. Add sampleProduct to some categories or add it to Content List Portlet.
Your newly created product is ready to be displayed in some pages.
9. Publish your newly created product.
Note that you can select this content from a CLV:
As a result, the content will be displayed in a detailed page as follows:
The product content is composed of fields and folders:
Fields: Name, Title, Illustration Image, Summary, Benefits, and Features.
Folders: documents, medias/images, medias/videos.
Folders are created within the product content when the name field is created.
This can be achieved (from the .gtmpl product dialog) as follows:
<tr>
<td class="FieldLabel"><%=_ctx.appRes("Product.dialog.label.name")%></td>
<td class="FieldComponent">
<%
String[] productFieldName = ["jcrPath=/node", "mixintype=mix:votable,mix:commentable","editable=if-null","validate=name,empty"] ;
uicomponent.addTextField("name", productFieldName) ;
String[] documentsFolder = ["jcrPath=/node/documents", "nodetype=nt:folder","mixintype=exo:documentFolder", "defaultValues=documents"] ;
String[] mediasFolder = ["jcrPath=/node/medias", "nodetype=exo:multimediaFolder", "defaultValues=medias"] ;
String[] imagesFolder = ["jcrPath=/node/medias/images", "nodetype=nt:folder", "defaultValues=images"] ;
String[] videoFolder = ["jcrPath=/node/medias/videos", "nodetype=nt:folder", "defaultValues=videos"] ;
uicomponent.addHiddenField("documentsFolder", documentsFolder);
uicomponent.addHiddenField("mediasFolder", mediasFolder);
uicomponent.addHiddenField("imagesFolder", imagesFolder);
uicomponent.addHiddenField("videoFolder", videoFolder);
%>
</td>
</tr>
Other fields are created almost in the same way:
Title:
<tr>
<td class="FieldLabel"><%=_ctx.appRes("Product.dialog.label.title")%></td>
<td class="FieldComponent">
<%
String[] productFieldTitle = ["jcrPath=/node/exo:title", "validate=empty", "editable=if-null"];
uicomponent.addTextField("title", productFieldTitle) ;
%>
</td>
</tr>
Illustration Image:
<%
private void setUploadFields(name) {
String[] illustrationHiddenField1 = ["jcrPath=/node/medias/images/illustration", "nodetype=nt:file", "mixintype=mix:referenceable", "defaultValues=illustration"];
String[] illustrationHiddenField2 = ["jcrPath=/node/medias/images/illustration/jcr:content", "nodetype=nt:resource", "mixintype=dc:elementSet", "visible=false"];
String[] illustrationHiddenField3 = ["jcrPath=/node/medias/images/illustration/jcr:content/jcr:encoding", "visible=false", "UTF-8"];
String[] illustrationHiddenField4 = ["jcrPath=/node/medias/images/illustration/jcr:content/jcr:lastModified", "visible=false"];
String[] illustrationHiddenField5 = ["jcrPath=/node/medias/images/illustration/jcr:content/dc:date", "visible=false"];
uicomponent.addHiddenField("illustrationHiddenField1", illustrationHiddenField1);
uicomponent.addHiddenField("illustrationHiddenField2", illustrationHiddenField2);
uicomponent.addHiddenField("illustrationHiddenField3", illustrationHiddenField3);
uicomponent.addCalendarField("illustrationHiddenField4", illustrationHiddenField4);
uicomponent.addCalendarField("illustrationHiddenField5", illustrationHiddenField5);
String[] fieldImage = ["jcrPath=/node/medias/images/illustration/jcr:content/jcr:data"] ;
uicomponent.addUploadField(name, fieldImage) ;
}
%>
<tr>
<td class="FieldLabel"><%=_ctx.appRes("Product.dialog.label.illustrationImage")%></td>
<td class="FieldComponent">
<%
String illustration = "illustration";
if(ProductNode != null && ProductNode.hasNode("medias/images/illustration") && (uicomponent.findComponentById(illustration) == null)) {
def imageNode = ProductNode.getNode("medias/images/illustration") ;
def resourceNode = imageNode.getNode("jcr:content");
if(resourceNode.getProperty("jcr:data").getStream().available() > 0) {
def imgSrc = uicomponent.getImage(imageNode, "jcr:content");
def actionLink = uicomponent.event("RemoveData", "/medias/images/illustration/jcr:content");
%>
<div>
<image src="$imgSrc" width="100px" height="80px"/>
<a onclick="$actionLink">
<img src="/eXoResources/skin/DefaultSkin/background/Blank.gif" class="ActionIcon Remove16x16Icon"/>
</a>
</div>
<%
} else {
setUploadFields(illustration);
}
} else {
setUploadFields(illustration);
}
%>
</td>
</tr>
Summary:
<tr>
<td class="FieldLabel"><%=_ctx.appRes("Product.dialog.label.summary")%></td>
<td class="FieldComponent">
<%
String[] fieldSummary = ["jcrPath=/node/exo:summary", "options=Basic", ""] ;
uicomponent.addRichtextField("summary", fieldSummary) ;
%>
</td>
</tr>
Benefits:
<tr>
<td class="FieldLabel"><%=_ctx.appRes("Product.dialog.label.benefits")%></td>
<td class="FieldComponent">
<div class="UIFCKEditor">
<%
String[] productFieldBenefits = ["jcrPath=/node/exo:productBenefits", "options=toolbar:CompleteWCM", ""] ;
uicomponent.addRichtextField("productBenefits", productFieldBenefits) ;
%>
</div>
</td>
</tr>
Features:
<tr>
<td class="FieldLabel"><%=_ctx.appRes("Product.dialog.label.features")%></td>
<td class="FieldComponent">
<div class="UIFCKEditor">
<%
String[] productFieldFeatures = ["jcrPath=/node/exo:productFeatures", "options=toolbar:CompleteWCM", ""] ;
uicomponent.addRichtextField("productFeatures", productFieldFeatures) ;
%>
</div>
</td>
</tr>
Now let's look at the product's view form.
Illustration image, title and summary are grouped together:
<!-- Hot news -->
<div class="BigNews ClearFix">
<!-- Begin illustrative image -->
<%
RESTImagesRendererService imagesRenderer = uicomponent.getApplicationComponent(RESTImagesRendererService.class);
def imageURI = imagesRenderer.generateImageURI(currentNode.getNode("medias/images/illustration"),null);
if (imageURI != null){
%>
<a class="Image"><img width="93" src="$imageURI" alt=""></a>
<%
}
%>
<div class="Content">
<!-- Begin title -->
<%
if(currentNode.hasProperty("exo:title")) {
def title = currentNode.getProperty("exo:title").getString();
%>
<a href="#" class="Title">$title</a>
<div class="Index1">$title</div>
<%
}
%>
<!-- End title -->
<!-- Begin summary -->
<%
if(currentNode.hasProperty("exo:summary")) {
def summary = currentNode.getProperty("exo:summary").getString();
%>
<div class="Summary">$summary</div>
<%
}
%>
<!-- End summary -->
</div>
</div>
Benefits and Features fields are rendered in two tabs. It uses the jQuery library (already integrated into eXo Platform 3.5).
<div id="sectionsTabs" class="ui-tabs">
<ul class="ui-tabs-nav ClearFix">
<li class="ui-state-default">
<!-- Begin Benefits head section -->
<a class="ArrowCtrl" href="#tab-benefits"><%=_ctx.appRes("Product.view.label.benefits")%></a>
<!-- End Benefits head section -->
</li>
<li class="ui-tabs-selected">
<!-- Begin Features head section -->
<a class="ArrowCtrl" href="#tab-features"><%=_ctx.appRes("Product.view.label.features")%></a>
<!-- End Features head section -->
</li>
</ul>
<div id="tab-benefits">
<%
if(currentNode.hasProperty("exo:productBenefits")) {
def benefits = currentNode.getProperty("exo:productBenefits").getString();
print benefits;
}
%>
</div>
<div id="tab-features">
<%
if(currentNode.hasProperty("exo:productFeatures")) {
def features = currentNode.getProperty("exo:productFeatures").getString();
print features;
}
%>
</div>
</div>
<script type="text/javascript">
jQuery.noConflict();
jQuery(document).ready(function() {
jQuery("#sectionsTabs").tabs();
});
</script>
The jQuery-based feature is display of the product's images (coverflow) from the images folder.
<div class="jQProBoxC">
<!-- Begin jCarouselLite part -->
<button class="jQprev"> </button>
<div class="jCarouselLite">
<ul>
<%
FOR IMAGE IN PRODUCT'S IMAGE FOLDER
String imgSrc = "";
/*
GET THE IMAGE PATH
imgSrc = GET THE IMAGE PATH;
*/
%>
<li><img src="$imgSrc" width="204" height="200"/></li>
<%
%>
</ul>
</div>
<button class="jQnext"> </button>
<!-- End jCarouselLite part -->
</div>
<script type="text/javascript">
jQuery.noConflict();
jQuery(document).ready(function(){
//jQuery.noConflict();
jQuery(".jCarouselLite").jCarouselLite({
btnNext: ".jQprev",
btnPrev: ".jQnext",
//auto: 500,
//speed: 500
});
});
</script>
Documents and videos are simply displayed within the view form as follows:
1. Get the node path (document or video).
2. Use some customized CSS classes to display a link for this node.
Labels and/or messages displayed in the dialog and the view form are localized.
Note the use of this instruction as below:
<td class="FieldLabel"><%=_ctx.appRes("Product.dialog.label.summary")%></td>
[...]
<h1><%=_ctx.appRes("Product.view.label.seeItInAction")%></h1>
This is achieved by adding locale files. For example:
<Product>
<view>
<label>
<benefits>Benefits</benefits>
<features>Features</features>
<seeItInAction>See it in action</seeItInAction>
<resources>Resources</resources>
<videos>Videos</videos>
</label>
</view>
</Product>
Make sure that locale files are added to the resource bundle configuration. If locale files (dialogs and views) are under the classes/locale/wcm directory, use the following code:
<value>locale.wcm.dialogs</value>
<value>locale.wcm.views</value>