eXo Platform 3.5

## Chapter 9. New Features

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:

- Actual content navigation

- Add content to the navigation

- Actions on the Navigation By Content

- Example of creating data for Navigation By Content

  - Create a Product page

  - Develop your own Product content

## Manage Navigation By Content

Navigation By Content is a feature which allows users to browse content of each page easily.

With this feature, users experiencing eXo Platform 3.5 can navigate from a page to another or browse site content inside one page directly from a contextual menu.

## Actual content navigation

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, all contents in the /Sites Management/acme/events/All node will be used.

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 Autofit Two Columns container.

4. Add two Content List portlets to each column.

5. Add content.

i. Configure the left portlet as follows:

In which:

- Folder path = /Sites Management/acme/events/All: The path to the folder that contains the content.

- Header = Browse by: The title of all contents that are listed in the content list viewer.

- Template = CategoryTree.gtmpl: The template used for displaying the content list.

- Contextual Folder = Disabled: The Contextual Content property is set to "Disable", the Advanced pane is closed by default and a single content will be opened by an URL containing the content path.

- Show in page =Events: A single content in CLV will be shown in the Events page.

- With = folder-id: The parameter containing the content path.

ii. Configure the right portlet as follows:

In which:

- Folder path = /Sites Management/acme/events/All: The path to the folder that contains the content.

- Template = OneColumnCLVTemplate.gtmpl: The template used for displaying the content list.

- Contextual Folder = Enabled: The Contextual Content property is set to "Enable". This portlet is configured with the provided parameter (content-id by default).

- Show in page = Details: A single content in CLV will be shown in the "Details" page.

- With = content-id: The parameter containing the content path.

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.

## Add content to the navigation

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 Sites Explorer page and navigate to /Sites Management/acme/events/All.

2. Click the Content Navigation button, the Navigation form will appear. If you do not see this button on the Action bar, add this button via the Content Administration page.

3. Fill values into the Content Navigation form:

In which:

- 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 homepage, you will see changes from the Events drop-down menu.

In which:

- Visible: The /Sites Management/acme/events/All node is navigable and its child nodes are rendered in the contextual menu.

- Target parent navigation: The /Sites Management/acme/events/All node is attached to the site menu item called Events.

- Clickable: The /Sites Management/acme/events/All node is not clickable but all of 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, you will 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 displayed in a separate page (details):

## Actions on the Navigation By Content

Restrict the visibility of some contents

1. Go to the Sites Explorer page and navigate to the /Sites Management/acme/events/All/Fire node.

2. Click the Content Navigation button, the Navigation form will appear.

3. Uncheck the Visible field and save.

4. Go back to the ACME homepage, you will see that the Fire sub-menu is not displayed in the contextual menu anymore.

Sort elements of the contextual menu

1. Go to the Sites Explorer page and navigate to the /Sites Management/acme/events/All node.

2. Select the /Sites Management/acme/events/All/Earth node.

3. Click the Content Navigation button, the Navigation form will appear.

4. Set the Display order field to "1" and save.

5. Select the /Sites Management/acme/events/All/Water node.

6. Click the Content Navigation button.

7. Set the Display order field to "2" and save.

8. Select the /Sites Management/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 ACME homepage, you will see that the display order from the contextual menu is Earth, Water, Air. Note that the Fire sub-menu is not displayed because it is set to "Invisible" in the previous example.

Restore a node to the contextual menu and attach it to another page

1. Go to the Sites Explorer page and navigate to the /Sites Management/acme/events/All/Fire node.

2. Click the Content Navigation button, the Navigation form will appear.

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.

Add your newly created contents to the contextual menu

1. Go to the Sites Explorer page and navigate to the /Sites Management/acme/events/All/Fire node.

2. Click the Manage Actions button and add the exo:populateToMenu action.

3. Create a document under the /Sites Management/acme/events/All/Fire node 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:

Select the X-Ray content and see the newly implemented content using new visual effects and presentation.

- "Benefits" and "Features" tabs:

- Coverflow section:

- Related documents:

## Example of creating data for Navigation By Content

### Create a Product page

Create content about the product

1. Go to the Sites Explorer page and navigate to somePath/someDirectory.

2. Click the Add Content button on the Action bar. There will be a several content type appears.

3. Select the Product content type.

4. Fill the Product dialog form.

5. Save changes.

Add media files to enrich the Product page

1. Go to somePath/someDirectory/sampleProduct/medias/images.

2. Upload some images and publish them.

3. Go to somePath/someDirectory/sampleProduct/medias/videos.

4. Upload a video and publish it.

5. Go to somePath/someDirectory/sampleProduct/documents.

6. Create two directories, including Sales Materials and Technical Documentations.

7. Upload a PDF document and publish it under each sub-folder.

8. Add your sampleProduct to some categories or add it to the Content List portlet.

Your newly created product is now 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:

### Develop your own Product content

The sample Product page is composed of the following fields and folders:

- Product content type fields: Name, Title, Illustration Image, Summary, Benefits, and Features.

(The Product content type is the template specified for the Product page.)

- Other content folders: documents, medias/images, medias/videos.

(These folders contain documents and media files to enrich the Product page.)

Create fields in the Product content type

- Name

Other content 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>

Develop the Product's view form

The 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>

In which:

- Name: The name of the product.

- Title: The title of the product.

- Illustration Image: The image that is used as an illustration for the product.

- Summary: The summary about the product that goes with the illustration.

- Benefits: The benefits of the product.

- Features: The features of the product.

Benefits and Features fields are rendered in two tabs using 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 used to display the product's images (in the coverflow view) from the images folder.

    <div class="jQProBoxC">
    <!-- Begin jCarouselLite part -->
    <button class="jQprev">&nbsp;</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">&nbsp;</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 to a document or video.

2. Use some customized CSS classes to display a link for this node.

Labels and/or messages are displayed in the dialog and the view form are localized.

The use of this instruction is described as below:

    <td class="FieldLabel"><%=_ctx.appRes("Product.dialog.label.summary")%></td>
    (...)
    <h1><%=_ctx.appRes("Product.view.label.seeItInAction")%></h1>

This can be 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>