eXo Platform 3.5

## Chapter 3. Work With Content

This chapters represents issues related to creating a new content manually via the following topics:

- Node type

- WCM template

- Document type

- Dialog Syntax

- Customize CKEditor

- Taxonomy

- Manage Template service

- Manage Navigation By Content

### Note

eXo Platform provides you with 2 options to create the content for your new extension:

- Create new content manually.

- Import an existing content into your extension.

## 3.1. Node type

To create a content for your extension, you first need to define a node type which represents the document type in the JCR. There are 2 ways to define your node type:

- Via the Content Administration portlet. For more details on how to create a note type via the Content Administration portlet, see the Manage node types in the eXo Platform 3.5 user guide.

- Via the .xml configuration files by creating a nodetypes-configuration.xml file in your extension as below.

    <nodeType hasOrderableChildNodes="false" isMixin="true" name="exo:newnodetype" primaryItemName="">

      <supertypes>

        <supertype>exo:article</supertype>

      </supertypes>

      <propertyDefinitions>

        <propertyDefinition autoCreated="true" mandatory="true" multiple="false" name="text" onParentVersion="COPY" protected="false" requiredType="String">

          <valueConstraints/>

        </propertyDefinition>

        <propertyDefinition autoCreated="false" mandatory="true" multiple="false" name="date" onParentVersion="COPY" protected="false" requiredType="Date">

          <valueConstraints/>

        </propertyDefinition>

      </propertyDefinitions>

    </nodeType>

By defining a supertype, you can reuse other node types and extend them with more properties (just like inheritance in Object Oriented Programming).

## 3.2. WCM template

After defining your node type, you need to select templates which are applied to a node type or a metadata mixin type. eXo Platform provides 2 types of WCM templates, including:

- dialogs which are HTML forms for creating node instances.

- views which are HTML fragments for displaying nodes.

From the Content Administration portlet, the Manage Template module lists all existing node types that have been associated with Dialog and/or View templates. These templates can be attached to permissions (in the usual membership:group form), so which specific templates are displayed according to user rights (which can be useful in a content validation workflow activity).

In eXo Platform, you can specify whether JavaScript is allowed to run on a field of the content template or not by using the "option" parameter.

Enable JavaScrip

1. Go to Content Administration --> Content Presentation --> Manage Templates.

2. Edit your desired template.

3. Select the Dialog tab, and then edit the content of Dialog1 in the View & Edit Template form.

4. Add option = noSanitization to the code in the Main field as the example below.

For example:

    String [] htmlArguments = ["jcrPath = / node / default.html / JCR: content / JCR: data", "options = toolbar: CompleteWCM, height: '410px ', noSanitization" htmlContent];

By default, JavaScript is disabled for any fields of some content templates to prevent the XSS attacks.

## 3.3. Document type

The Document Type checkbox is to define if the node type should be a Document Type or not. If this checkbox is selected, the Sites Explorer considers such nodes as user content and applies the following behavior:

- The View template will be used to display the DocumentType nodes.

- The document types nodes can be created by the Add Content action. The forms shown in the Add Document action are the corresponding Dialog templates of each document type.

- Non-document types are hidden unless the Show Non-document Nodes option is checked.

Templates are written using Groovy Templates and will require experiences with JCR API and HTML notions.

## 3.4. Dialog Syntax

Dialogs are Groovy Templates that generate forms by mixing static HTML fragments and Groovy calls to the components responsible for building the UI at runtime. As a result, you will get a simple but powerful syntax.

### 3.4.1. Interceptors

By placing interceptors in your template, you will be able to execute a Groovy script just before and just after saving the node. Pre-save interceptors are mostly used to validate input values and their overall meaning while the post-save interceptor can be used to do some manipulations or references for the newly created node, such as binding it with a forum discussion or wiki space.

To place interceptors, use the following fragment:

    <% uicomponent.addInterceptor("ecm-explorer/interceptor/PreNodeSaveInterceptor.groovy", "prev");%>

Interceptor Groovy scripts are managed in the 'Manage Script' section in the ECM admin portlet. They must implement the CmsScript interface. Pre-save interceptors obtain input values within the context:

    public class PreNodeSaveInterceptor implements CmsScript {

      public PreNodeSaveInterceptor() {

      }

      public void execute(Object context) {

        Map inputValues = (Map) context;

        Set keys = inputValues.keySet();

        for(String key : keys) {

          JcrInputProperty prop = (JcrInputProperty) inputValues.get(key);

          println("   --> "+prop.getJcrPath());

        }

      }

      public void setParams(String[] params) {

      }

    }

Whereas the post-save interceptor is passed the path of the saved node in the context:

    <% uicomponent.addInterceptor("ecm-explorer/interceptor/PostNodeSaveInterceptor.groovy", "post");%>

    public class PostNodeSaveInterceptor implements CmsScript {

      public PostNodeSaveInterceptor() {

      }

      public void execute(Object context) {

        String path = (String) context;

        println("Post node save interceptor, created node: "+path);

      }

      public void setParams(String[] params) {

      }

    }

### 3.4.2. Hidden fields

In the next code sample, each argument is composed of a set of keys and values. The order of arguments are not important and only the key matters. That example defines a field with the id as "hiddenField2", which will generate a hidden field. The value of this field will be automatically set to UTF-8 and no visible field will be printed on the form.

    String[] hiddenField2 = ["jcrPath=/node/jcr:content/jcr:encoding", "visible=false", "UTF-8"];

    uicomponent.addHiddenField("hiddenInput2", hiddenField2);

Once the form has been saved, the date value will be saved under the relative JCR path ./exo:image/jcr:lastModified.

#### Non-value field

You cannot either see the non-value field on the form or input value for them. Its value will be automatically created or defined when you are managing templates.

    String[] hiddenField1 = ["jcrPath=/node/jcr:content", "nodetype=nt:resource", "mixintype=dc:elementSet", "visible=false"] ;

    uicomponent.addHiddenField("hiddenInput1", hiddenField1) ;

#### Non-editable fields

It is possible to create widgets that are non-editable (and then only used to print some information).

    String[] fieldCategories = ["jcrPath=/node/exo:category", "multiValues=true", "reference=true", "editable=false"]; uicomponent.addTextField("categories", fieldCategories);

#### Create node type or mixin type

In many cases, when creating an instance where the node is out of form, you must still specify the CMS service about the node structure. Particularly, you must define if which node type is child of the newly created node or if the current node has any mixin type attributed.

By defining these arguments, the node and its children are created with the correct node type and mixin type.

See the following example:

    String[] hiddenField = ["jcrPath=/node/jcrcontent", "nodetype=nt:resource", "mixintype=exo:rss-enable", "visible=false"] ;

    uicomponent.addHiddenField("hiddenInput", hiddenField) ;

#### Hidden field with default value

In the previous sample, the value was automatically created and set according to the current date. However, it is also possible to set a default value for a field.

    String[] hiddenField = ["jcrPath=/node/jcrcontent/jcr:mimeType", "image/jpeg"] ;

    uicomponent.addHiddenField("hiddenInput", hiddenField) ;

#### Visible without null fields

It is possible to tell that a widget should be visible only if its value is not null or when the form is used to edit the node which has been existing.

    String nameArgs[] = ["jcrPath=/node", "mixintype=mix:votable", "visible=if-not-null"];

    uicomponent.addMixinField("name", nameArgs )

#### WYSIWYG widget

Widgets are natively part of the eXo Platform product to provide a simple and easy way for users to get information and notification on their application. They complete the portlet application that focuses on more transactional behaviors.

WYSIWYG stands for What You See Is What You Get. This widget is one of the most powerful tools. It renders an advanced JavaScript text editor with many functionalities, including the ability to dynamically upload images or flash assets into a JCR workspace and then to refer to them from the created HTML text.

    String[] fieldSummary = ["jcrPath=/node/exo:summary", "options=basic"] ;

    uicomponent.addWYSIWYGField("summary", fieldSummary) ;

    String[] fieldContent = ["jcrPath=/node/exo:text", "options=toolbar:CompleteWCM,'height:410px'", ""] ;

    uicomponent.addRichtextField("content", fieldContent)

The "options" argument is used to tell the component which toolbar should be used.

By default, there are five options for the toolbar: CompleteWCM, Default, BasicWCM, Basic, SuperBasicWCM.

- CompleteWCM: a full set of tools is shown.

The following buttons are shown: Source, Templates, Show Blocks, Cut, Copy, Paste Text, Undo, Redo, SpellCheck, WCM Insert Gadget, Flash, Table, Insert Special Character, WCM Insert Content Link, Bold, Italic, Underline, Strike Through, Justify Left, Justify Center, Justify Right, Justify Full, Ordered List, Unordered List, Text Color, Background Color, Remove Format, Link, WCM Insert Portal Link, Unlink, Anchor, Style, Font Format, Font Name, Font Size, Maximize.

- Default: a large set of tools is shown, no "options" argument is needed in that case.

The following buttons are shown: Source, Templates, Cut, Copy, PasteText, Undo, Redo, SpellCheck, RemoveFormat, Bold, Italic, Underline, Strike Through, Ordered List, Unordered List, Link, Unlink, Anchor, Image, Flash, Table, Special Character, Text Color, Background Color, Show Blocks, Style, Font Format, Font Name, Font Size, Maximize.

- BasicWCM: a minimal set of tools is shown.

The following buttons are shown: Source, Bold, Italic, Underline, Strike Through, OrderedList, UnorderedList, Outdent, Indent, Justify Left, Justify Center, Justify Right, JustifyFull, Blockquote, Link, Unlink, WCM Insert Portal Link, WCM Insert Content Link, Show Blocks, Style, Font Format, Font Name, FontSize, Maximize.

- Basic:

The following buttons are shown: Source, Bold, Italic, Underline, Strike Through, Ordered List, Unordered List, Outdent, Indent, Justify Left, Justify Center, Justify Right, Justify Full, Blockquote, Link, Unlink, Show Blocks, Style, Font Format, Font Name, Font Size, Maximize.

- SuperBasicWCM:

The following buttons are shown: Source, Bold, Italic, Underline, Justify Left, Justify Center, Justify Right, Justify Full, Link, Unlink, WCM Insert Portal Link, WCM Insert Gadget, WCM Insert Content Link.

There is also a simple text area widget, which has text-input area only:

    String [] descriptionArgs = ["jcrPath=/node/exo:title", "validate=empty"];

    uicomponent.addTextAreaField("description", descriptionArgs) ;

#### Create a custom RichText editor fields

In the WYSIWYG widget section, you already know about a set of default toolbars (CompleteWCM, Default, BasicWCM, Basic, SuperBasicWCM). In this section, you will learn how to create a RichText editor with custom buttons.

Just edit the configuration file and modify or add new items to the configuration file of the RichText editor is located in: apps/resource-static/src/main/Webapp/eXoConfig.js

Take a look at the eXoConfig.js file to see a definition of a custom toolbar named "MyCustomToolbar":

    FCKConfig.ToolbarSets["MyCustomToolbar"] = [

      ['Source','Templates','-','FitWindow','ShowBlocks'],

     ['Cut','Copy','PasteText','-','SpellCheck','-','Undo','Redo'],

     ['WCMInsertGadget','Flash','Table','SpecialChar', 'WCMInsertContent'],

     '/',

     ['Bold','Italic','Underline','StrikeThrough','-','JustifyLeft','JustifyCenter','JustifyRight','JustifyFull','-','OrderedList','UnorderedList','-','TextColor','BGColor','-','RemoveFormat'],

     ['Link','WCMInsertPortalLink','Unlink','Anchor'],

     '/',

     ['Style','FontFormat','FontName','FontSize']

    ] ;

Every toolbar set is composed of a series of "toolbar bands" that are grouped in the final toolbar layout. The bands items move together on new rows when resizing the editor.

Every toolbar band is defined as a separated JavaScript array of strings. Each string corresponds to an available toolbar item defined in the editor code or in a plugin.

- Put the desired button names in square bracket ("[" & "]") and separate them by commas to create a toolbar band. You can look at the above code to know all the possible toolbar item. If the toolbar item does not exist, a message will be displayed when loading the editor.

- Include a separator in the toolbar band by putting the "-" string on it.

- Separate each toolbar brands with commas.

- Use slash ("/") to tell the editor that you want to force the next bands to be rendered in a new row and not following the previous one.

### Note

The last toolbar band must have no comma after it.

#### Simple select box widget

The select box widget enables you to render a select box with static values. These values are enumerated in a comma-separated list in the "options" argument. The argument with no key (here "text/html") is selected by default.

    String[] mimetype = ["jcrPath=/node/jcrcontent/jcr:mimeType", "text/html", "options=text/html,text/plain"] ;

    uicomponent.addSelectBoxField("mimetype", mimetype) ;

As usual, the value will be stored at the relative path defined by the jcrPath directive argument.

For more examples on how to create WCM templates, refer to the WCM Templates section.

#### Advanced dynamic select box

In many cases, the previous solution with static options is not good enough and one would like to have the select box checked dynamically. That is what eXo Platform provide thanks to the introduction of a Groovy script as shown in the code fragment below.

    String[] args = ["jcrPath=/node/exodestWorkspace", "script=ecm-explorer/widget/FillSelectBoxWithWorkspaces:groovy", "scriptParams=production"];

    uicomponent.addSelectBoxField("destWorkspace", args) ;

The script itself implements the CMS Script interface and the cast is done to get the select box object as shown in the script code which fills the select box with the existing JCR workspaces.

    import java.util.List ;

    import java.util.ArrayList ;

    import org.exoplatform.services.jcr.RepositoryService;

    import org.exoplatform.services.jcr.core.ManageableRepository;

    import org.exoplatform.webui.form.UIFormSelectBox;

    import org.exoplatform.webui.core.model.SelectItemOption;

    import org.exoplatform.services.cms.scripts.CmsScript;

    public class FillSelectBoxWithWorkspaces implements CmsScript {

      private RepositoryService repositoryService_;

      public FillSelectBoxWithWorkspaces(RepositoryService repositoryService) {

        repositoryService_ = repositoryService;

      }

      public void execute(Object context) {

        UIFormSelectBox selectBox = (UIFormSelectBox) context;

        ManageableRepository jcrRepository = repositoryService_.getRepository();

        List options = new ArrayList();

        String[] workspaceNames = jcrRepository.getWorkspaceNames();

        for(name in workspaceNames) {

          options.add(new SelectItem(name, name));

        }

        selectBox.setOptions(options);

      }

      public void setParams(String[] params) {

      }

    }

### Note

It is also possible to provide a parameter to the script by using the argument "scriptParams".

#### Widget with selector

One of the most advanced functionalities of this syntax is the ability to plug your own component that shows an interface, enabling you to select the value of the field.

In the generated form, you will see an icon which is configurable thanks to the selectorIcon argument. The syntax is a bit more complex but not much.

    String[] groupArgs = ["jcrPath=/node/exogroup", "selectorClass=org:exoplatform:ecm:webui:selector:UIGroupMemberSelector"];

    uicomponent.addActionField("group", groupArgs);

You can plug your own component using the selectorClass argument. It must follow the eXo UIComponent mechanism and implements the interface ComponentSelector:

    package org.exoplatform.ecm.webui.selector;

    import org.exoplatform.webui.core.UIComponent;

    public interface ComponentSelector {

      public UIComponent getSourceComponent() ;

      public void setSourceComponent(UIComponent uicomponent, String[] initParams) ;

    }

#### Multi-valued widget

A widget can have multiple values if you add the argument "multiValues=true" to the directive.

## 3.5. Customize CKEditor

CKEditor is a text editor. This is a WYSIWYG editor which allows you to see what the published results look like while editing your text. It brings to the common web-editing features found on desktop-editing applications like Microsoft Word, and OpenOffice. To have more information about a WYSIWYG editor, see the WYSIWYG widget section.

### 3.5.1. Installation

You can install CKEditor easily by selecting an appropriate procedure (fresh installation or upgrade) and follow the steps described below.

#### Fresh Installation

This way is used to install CKEditor for the first time.

1. Download the latest version of the editor here.

2. Extract (decompress) the downloaded archive to a directory called ckeditor in the root of your website.

### Note

You can place the files in any path of your website. The ckeditor directory is the default one.

#### Upgrade

To upgrade an existing CKEditor installation, do as follows:

1. Rename your old editor folder to a backup folder, for example, "ckeditor_old".

2. Download the latest version of the editor here.

3. Extract/Decompress the downloaded archive to the original editor directory, for example, "ckeditor".

4. Copy all configuration files that you have changed from the backup folder to their corresponding positions in the new directory. These files could include (but not limited to) the following files:

- config.js

- contents.css

- plugins/templates/templates/default.js

- plugins/styles/styles/default.js

- plugins/pastefromword/filter/default.js

### 3.5.2. File and Folder Structure

#### Inside CKEditor

There are the following folders:

Folders | Description
--- | ---
samples | Contain CKEditor samples.
source | Contain CKEditor source code.
adapters | Contain CKEditor adapters. It may be removed if you do not use any adapters, like the jQuery one.
images | Contain CKEditor graphics files.
lang | Contain CKEditor language files.
plugins | Contain plugin files and is necessary for CKEditor to work.
skins | Contain CKEditor skin files along with toolbar buttons and stylesheet definitions.
themes | Contains CKEditor theme.

Files | Description
--- | ---
ckeditor.js | The heart of CKEditor application. It is the unique compressed file which contains all of codes to run CKEditor.
ckeditorbasic.js | The compressed file like ckeditor.js, but it is only a bootstrap which contains the core functionality only, so the rest of the code can be loaded at later time, avoiding delaying the initial load of the page.
ckeditorsource.js | An uncompressed version of ckeditor.js.
ckeditorbasicsource.js | An uncompressed version of ckeditorbasic.js.
config.js | Allow users to customize some configurations.
contents.css | Define the stylesheets of the CKEditor application.
ckeditor.asp | Used for ASP integration.
ckeditor.php | Used for PHP integration.
ckeditorphp4.php | Used for PHP4 integration.
ckeditorphp5.php | Used for PHP5 integration.
ckeditor.pack | Re-build the compression version of 2 files: ckeditor.js, and ckeditorbasic.js.

#### CKEditor in Context of eXo Platform

There is a .war package named eXoStaticResources which integrates the CKEditor application into eXo Platform. The source code is placed inside the apps/resources-static/src/main/webapp folder.

Its structure consists of the folders and files below:

Folders & Files | Description
--- | ---
ckeditor | Contain all source codes of CKEditor v3.3.2.
eXoPlugins | Contain the source code of 3 external plugins: {}Insert Content Link{}, {}Insert Portal Link{}, {}WCM Insert Gadget{}.
eXoConfig.js | Register 3 external plugins, and define some types of the toolbar.
eXoPlugins.js | Define some utility functions which can be used by 3 external plugins.

### 3.5.3. Configuration in CKEditor

#### Setting Configuration

CKEditor comes with a rich set of configuration options that make it possible to customize its appearance, features, and behavior. The main configuration file is named config.js. This file can be found in the root of the CKEditor installation folder (/webapps/eXoStaticResources/ckeditor/config.js). By default, this file is mostly empty. To change the CKEditor configuration, add the settings that you want to modify to the config.js file.

For example:

    CKEDITOR.editorConfig = function( config )

    {

        config.language = 'en';

        config.uiColor = '#AADC6E';

    };

Instead of using the default config.js file, you can create a copy of that file in anywhere in your website and simply point the editor instances to load it. For example, in eXo Platform, the configuration file for CKEditor is placed at /webapps/eXoStaticResources/eXoConfig.js, so the content of the config.js file will be:

    CKEDITOR.editorConfig = function( config )

    {

        config.customConfig = "../eXoConfig.js";

    };

#### Skin

You can change the CKEditor skin by adjusting a single configuration option. In eXo Platform, to change the CKEditor skin, do as follows:

1. Open the /webapps/eXoStaticResources/eXoConfig.js configuration file of CKEditor.

2. Set up a skin for CKEditor. It may be the name of the skin folder inside the editor installation path, or the name and the path separated by a comma.

        config.skin = 'v2';
        config.skin = 'myskin,/customstuff/myskin/';

By default, CKEditor has 3 skins for users to select: v2, kama, and office2003. They are placed in the /webapps/eXoStaticResources/ckeditor/skins folder.

#### Toolbar

Toolbar Definition

CKEditor is a full-featured WYSIWYG editor, but not all of its options are needed in all cases. Therefore, the toolbar customization is one of the most common and required tasks when dealing with CKEditor.

Toolbar Definition is a JavaScript array which contains the elements to be displayed in all toolbar rows available in the editor. In eXo Platform, the toolbar definition is placed in the /webapps/eXoStaticResources/eXoConfig.js file. The following code snippet contains the default CKEditor toolbar set in eXo Platform.

    config.toolbar_Default = [

         ['Source','Templates'],

         ['Cut','Copy','PasteText','-','SpellCheck'],

         ['Undo','Redo','-','RemoveFormat'],

        '/',

         ['Bold','Italic','Underline','Strike'],

         ['NumberedList','BulletedList'],

         ['Link','Unlink','Anchor'],

         ['Image','Flash','Table','SpecialChar'],

         ['TextColor','BGColor'],

         ['Maximize', 'ShowBlocks'],

         ['Style','Format','Font','FontSize']

    ] ;

Add a new toolbar in eXo Platform

- To add a new toolbar in eXo Platform, open the /webapps/eXoStaticResources/eXoConfig.js configuration file of CKEditor, then add the following code snippet to it.

    config.toolbar_MyToolbar =

    [

     ['Bold', 'Italic', '-', 'NumberedList', 'BulletedList', '-', 'Link', 'Unlink','-','About']

    ];

Show the newly added toolbar

To show the newly added toolbar, you have to add it to a field of a WCM template. For example, to show the new toolbar on the content field of HTML file, you need to modify the dialog template of HTML file as follows:

            String[] fieldSummary = ["jcrPath=/node/jcr:content/jcr:data", "", "options=toolbar:MyToolbar, noSanitization"] ;

            uicomponent.addRichtextField("contentHtml", fieldSummary) ;

By adding a new HTML file, you will see the new toolbar (MyToolbar) on the content field:

#### How to create a plugin for CKEditor

This section shows you how to create a basic CKEditor plugin.

Assuming that you will develop a timestamp plugin that inserts the current date and time into the editing area of CKEditor. The timestamp will be added after a user clicks a dedicated toolbar button. The implementation makes use of the insertHtml function which can be also easily adjusted to insert any other HTML elements into CKEditor.

1. Create a directory inside the eXoPlugins directory for CKEditor with the timestamp plugin.

2. Place the plugin.js file that contains the plugin logic inside the newly created timestamp folder. Also, you will need a toolbar icon for the plugin by adding an images folder and subsequently placing the timestamp.png file inside it.

3. Modify the plugin.js file in which you will write the behavior.

The following is the code used to create a simple plugin named timestamp:

    CKEDITOR.plugins.add( 'timestamp',

    {

     init: function( editor )

     {

      editor.addCommand( 'insertTimestamp',

       {

        exec : function( editor )

        {

         var timestamp = new Date();

         editor.insertHtml( 'The current date and time is: <em>' + timestamp.toString() + '</em>' );

        }

       });

      editor.ui.addButton( 'Timestamp',

      {

       label: 'Insert Timestamp',

       command: 'insertTimestamp',

       icon: this.path + 'images/timestamp.png'

      } );

     }

    } );

To use the created plugin, plug it to CKEditor by using the following code:

     (function() {CKEDITOR.plugins.addExternal('timestamp',CKEDITOR.eXoPath+'eXoPlugins/timestamp/','plugin.js');})();

        ....

        config.extraPlugins = 'content,insertGadget,insertPortalLink,timestamp';

        ....

        config.toolbar_MyToolbar =

        [

                       ['Bold', 'Italic', '-', 'NumberedList', 'BulletedList', '-', 'Link', 'Unlink','-','About','Timestamp']

        ];

The following is the illustration about the Timestamp plugin added to the CKEditor.

## 3.6. Taxonomy

Taxonomy is a particular classification arranged in a hierarchical structure. Taxonomy trees in eXo Platform will help you organize your content into categories.

When you create a new taxonomy tree, you will add a pre-configured exo:action (exo:scriptAction or exo:businessProcessAction) to the root node of the taxonomy tree. This action is triggered when a new document is added anywhere in the taxonomy tree. The default action moves the document to the physical storage location and replaces the document in the taxonomy tree with a symlink of the exo:taxonomyLink type pointing to it. The physical storage location is defined by a workspace name, a path and the current date and time.

Like adding document types, taxonomy trees can be managed through the Content Administration portlet, or by adding .xml configuration files.

To configure taxonomy trees by adding configuration files in the /webapp/WEB-INF/conf/acme-portal/wcm/taxonomy/ directory, create a new file called $taxonomyName-taxonomies-configuration.xml. For example, if the name of your taxonomy tree is "acme", the file should be named acme-taxonomies-configuration.xml.

You can view the file here: $PLF-HOME_/samples/acme-website/webapp/src/main/webapp/WEB-INF/conf/acme-portal/wcm/taxonomy/acme-taxonomies-configuration.xml.

As you can see, the value-params enable you to define the repository, workspace, name of the tree and its JCR path. You can then configure permissions for each group of users in the portal, and the triggered action when a new document is added to the taxonomy tree. Finally, you can describe the structure and names of the categories inside your taxonomy tree.

## 3.7. Manage Template service

The Template service enables you to create dialogs and view templates for each node type registered. Each node type may have many dialogs and view templates. The template will be used when creating or viewing nodes.

You can find the template service configuration in /webapps/ecm-wcm-core/WEB-INF/conf/wcm-core/core-services-configuration.xml.

    <component>

      <key>org.exoplatform.services.cms.templates.TemplateService</key>

      <type>org.exoplatform.services.cms.templates.impl.TemplateServiceImpl</type>

    </component>

As usual, one can register a plugin inside the service. This plugin initializes default dialogs and views template of any node type as nt:file, exo:article, exo:workflowAction, exo:sendMailAction, and more.

    <component-plugins>

      <component-plugin>

        <name>addTemplates</name>

        <set-method>addTemplates</set-method>

        <type>org.exoplatform.services.cms.templates.impl.TemplatePlugin</type>

        .........

      </component-plugin>

    </component-plugins>

With init-parameters as:

    <init-params>

      <value-param>

        <name>autoCreateInNewRepository</name>

        <value>true</value>

      </value-param>

      <value-param>

        <name>storedLocation</name>

        <value>war:/conf/ecm/artifacts/templates</value>

      </value-param>

      <value-param>

        <name>repository</name>

        <value>repository</value>

      </value-param>

      <object-param>

        <name>template.configuration</name>

        <description>configuration for the location of templates to inject in jcr</description>

        <object type="org.exoplatform.services.cms.templates.impl.TemplateConfig">

          <field name="nodeTypes">

            <collection type="java.util.ArrayList">

              <value>

                <object type="org.exoplatform.services.cms.templates.impl.TemplateConfig$NodeType">

                  <field name="nodetypeName">

                    <string>exo:article</string>

                  </field>

                  <field name="documentTemplate">

                    <boolean>true</boolean>

                  </field>

                  <field name="label">

                    <string>Article</string>

                  </field>

                  <field name="referencedView">

                    <collection type="java.util.ArrayList">

                      <value>

                        <object type="org.exoplatform.services.cms.templates.impl.TemplateConfig$Template">

                          <field name="templateFile">

                            <string>/article/views/view1.gtmpl</string>

                          </field>

                          <field name="roles">

                            <string>*</string>

                          </field>

                        </object>

                      </value>

                    </collection>

                  </field>

                  <field name="referencedDialog">

                    <collection type="java.util.ArrayList">

                      <value>

                        <object type="org.exoplatform.services.cms.templates.impl.TemplateConfig$Template">

                          <field name="templateFile">

                            <string>/article/dialogs/dialog1.gtmpl</string>

                          </field>

                          <field name="roles">

                            <string>*</string>

                          </field>

                        </object>

                      </value>

                    </collection>

                  </field>

                </object>

              </value>

            </collection>

          </field>

        </object>

      </object-param>

    </init-params>

## 3.8. 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.

### 3.8.1. 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 into 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.

### 3.8.2. 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):

### 3.8.3. Actions on 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 to open the Navigation form.

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 to open the Navigation form.

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 to open the Navigation form.

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. You will 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 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. You will 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:

### 3.8.4. Examples 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 New 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. Click Upload on the Action bar to upload some images from your local device and publish them.

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

4. Click Upload on the Action bar to upload some videos from your local device and publish it.

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

6. Create two directories, including Sale 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>

Copyright © 2009-2012. All rights reserved. eXo Platform SAS