{"id":4469,"date":"2013-02-13T08:55:36","date_gmt":"2013-02-13T16:55:36","guid":{"rendered":"http:\/\/localhost\/exoblog\/?p=4469"},"modified":"2013-02-13T08:55:36","modified_gmt":"2013-02-13T16:55:36","slug":"portlet-development-in-exo-platform-with-jsf-and-richfaces-part-23","status":"publish","type":"post","link":"https:\/\/www.exoplatform.com\/blog\/portlet-development-in-exo-platform-with-jsf-and-richfaces-part-23\/","title":{"rendered":"Portlet Development in eXo Platform with JSF and RichFaces (Part 2\/3)"},"content":{"rendered":"<p>eXo Platform comes with powerful content management features and a large set of portlets to use these features. However, you may want to use these content management capabilities in your own portlets.<\/p>\n<p>This second tutorial will teach you how to use the eXo Content Management API and portlets to create a sample store application. The first part, about JSF\/RichFaces integration, is available <a href=\"https:\/\/www.exoplatform.com\/blog\/2013\/02\/12\/portlet-development-in-exo-platform-with-jsf-and-richfaces-part-13\/\/\">here<\/a>.<\/p>\n<p>Our sample application will display a list of products with related pictures. Each product will be composed of a name, a description, a price, a thumbnail, and some pictures. The thumbnail and the related pictures will be retrieved from the eXo Content Management system.<\/p>\n<p>The page will display the product\u2019s thumbnail and the product\u2019s characteristics. The related pictures will be available as links. A Content Detail portlet will be used to display each related picture.<\/p>\n<p><!--more--><\/p>\n<p>The source code for this tutorial is available <a href=\"https:\/\/github.com\/thomasdelhomenie\/exo-jsfportletbridge-ecms-portlet\" target=\"_blank\" rel=\"noopener\">here<\/a>.<\/p>\n<h2>Adding contents<\/h2>\n<p>In order for us to retrieve the contents easily, they should be organized correctly. Within the root folder (let\u2019s say <span class=\"navCode\">repository:collaboration:\/sites content\/live\/acme\/documents\/products<\/span>), a folder will be created for each product, named with the product\u2019s ID. All product-related contents will be stored in the corresponding folder. The thumbnail will also be stored in the product\u2019s folder, but with the special name \u2018thumbnail\u2019 in order to distinguish it from the other pictures.<\/p>\n<p>So, the content tree will look like this:<\/p>\n<p><a href=\"https:\/\/www.exoplatform.com\/blog\/wp-content\/uploads\/2013\/02\/arbo.png\"><img decoding=\"async\" class=\"aligncenter size-full\" title=\"arbo\" src=\"https:\/\/www.exoplatform.com\/blog\/wp-content\/uploads\/2013\/02\/arbo.png\" alt=\"arbo\" width=\"178\" \/><\/a><\/p>\n<p>Instead of creating all the contents by hand, you can download <a href=\"https:\/\/raw.github.com\/thomasdelhomenie\/exo-jsfportletbridge-ecms-portlet\/master\/contents-sysview.xml\" target=\"_blank\" rel=\"noopener\">this JCR export<\/a>. To use it:<\/p>\n<ul>\n<li>Go to the Content Explorer;<\/li>\n<li>Select the Sites Management drive;<\/li>\n<li>Go to acme &gt; documents;<\/li>\n<li>Click on the System tab;<\/li>\n<li>Click on the Import Node button;<\/li>\n<li>Select the export file for the Upload File field;<\/li>\n<li>Click on Import.<\/li>\n<\/ul>\n<p><strong>Important:<\/strong> To ensure that all contents are in published state, you may need to republish them.<\/p>\n<h2>Adding eXo dependencies<\/h2>\n<p>The first step is to add the eXo dependencies that will be used in our portlet. Edit your pom.xml file and add these lines:<\/p>\n<pre class=\"wrap:true lang:default decode:true \">&lt;dependency&gt;\n    &lt;groupId&gt;org.exoplatform.ecms&lt;\/groupId&gt;\n    &lt;artifactId&gt;exo-ecms-core-publication&lt;\/artifactId&gt;\n    &lt;version&gt;2.3.4&lt;\/version&gt;\n    &lt;scope&gt;provided&lt;\/scope&gt;\n&lt;\/dependency&gt;\n&lt;dependency&gt;\n    &lt;groupId&gt;org.exoplatform.ecms&lt;\/groupId&gt;\n    &lt;artifactId&gt;exo-ecms-core-services&lt;\/artifactId&gt;\n    &lt;version&gt;2.3.4&lt;\/version&gt;\n    &lt;scope&gt;provided&lt;\/scope&gt;\n&lt;\/dependency&gt;\n&lt;dependency&gt;\n    &lt;groupId&gt;org.exoplatform.ecms&lt;\/groupId&gt;\n    &lt;artifactId&gt;exo-ecms-core-webui&lt;\/artifactId&gt;\n    &lt;version&gt;2.3.4&lt;\/version&gt;\n    &lt;scope&gt;provided&lt;\/scope&gt;\n&lt;\/dependency&gt;<\/pre>\n<h2>Using the eXo Content Management API<\/h2>\n<p>In order to manage data displayed on our page, we need a managed bean:<\/p>\n<p><a href=\"https:\/\/www.exoplatform.com\/blog\/wp-content\/uploads\/2013\/02\/code2.jpg\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-4532\" title=\"code2\" src=\"https:\/\/www.exoplatform.com\/blog\/wp-content\/uploads\/2013\/02\/code2.jpg\" alt=\"code2\" width=\"600\" \/><\/a><\/p>\n<p>(See the code <a href=\"https:\/\/github.com\/thomasdelhomenie\/exo-jsfportletbridge-ecms-portlet\/blob\/master\/src\/main\/java\/org\/exoplatform\/ComicsStoreBean.java#L18\" target=\"_blank\" rel=\"noopener\">here<\/a>)<\/p>\n<p>This bean instantiates two services: one for the products (ProductService) and one for the contents (ContentService), which is initialized with the root folder in the content management system that houses the contents used in the store (retrieved from a portlet preference).<\/p>\n<p>The bean exposes only one kind of data: the products, via the getProducts method. This method calls the ProductService object to retrieve all the products. The ProductService class simply returns sample products:<\/p>\n<pre class=\"wrap:true lang:default decode:true \">public class ProductServiceImpl implements ProductService {\n    @Override\n    public List getProducts() {\n        List products = new ArrayList();\n        products.add(new Product(1, \"Ironman\", \"Ironman\", 12));\n        products.add(new Product(2, \"Wolverine\", \"Wolverine\", 15.5));\n        products.add(new Product(3, \"Spiderman\", \"Spiderman\", 13));\n        products.add(new Product(4, \"Thor\", \"Thor\", 10));\n        products.add(new Product(5, \"Hulk\", \"Hulk\", 11));\n        products.add(new Product(6, \"Captain America\", \"Captain America\", 15));\n        products.add(new Product(7, \"Human Torch\", \"Human Torch\", 11));\n        products.add(new Product(8, \"Magneto\", \"Magneto\", 17));\n        products.add(new Product(9, \"Dardevil\", \"Dardevil\", 16.5));\n        return products;\n    }\n}<\/pre>\n<p>Then the managed bean calls the ContentService object to retrieve the contents relating to the products. This is the most interesting part as it deals with eXo Platform\u2019s content management features.<\/p>\n<p>eXo Platform provides <a href=\"http:\/\/docs.exoplatform.com\/PLF35\/topic\/org.exoplatform.doc.35\/DEV.eXoPlatformAPIs.html\" target=\"_blank\" rel=\"noopener\" class=\"broken_link\">an API to interact with all content management capabilities<\/a> (contents, taxonomy, links, publication, SEO, \u2026). In our sample application we will use the <a href=\"http:\/\/docs.exoplatform.com\/PLF35\/index.jsp?topic=%2Forg.exoplatform.doc.35%2FCONTref.JavaServices.WCMComposer.html\" target=\"_blank\" rel=\"noopener\" class=\"broken_link\">WCMComposer API<\/a>, which allows us to work with contents.<\/p>\n<p>eXo Platform\u2019s Content Management feature provides a utility method to easily instantiate a service:<\/p>\n<pre class=\"wrap:true lang:default decode:true \">WCMComposer wcmComposer = WCMCoreUtils.getService(WCMComposer.class);<\/pre>\n<p>This service is used in the two methods of ContentService: <span class=\"navCode\">getProductThumbnailPath<\/span> and <span class=\"navCode\">getProductPictures<\/span>.<\/p>\n<p>The <span class=\"navCode\">getProductThumbnailPath<\/span> method returns the path of the thumbnail of the product if it exists:<\/p>\n<pre class=\"wrap:true lang:default decode:true \">@Override\npublic String getProductThumbnailPath(int productId) {\n    String thumbnailPath = null;\n\n    \/\/ get wcmcomposer service\n    WCMComposer wcmComposer = WCMCoreUtils.getService(WCMComposer.class);\n\n    HashMap filters = new HashMap();\n    \/\/filters.put(WCMComposer.FILTER_LANGUAGE, Util.getPortalRequestContext().getLocale().getLanguage());\n    filters.put(WCMComposer.FILTER_MODE, Utils.getCurrentMode());\n    \/\/ take the last published version\n    filters.put(WCMComposer.FILTER_VERSION, null);\n\n    try {\n        Node productThumbnailNode = wcmComposer.getContent(workspace, path + productId + \"\/thumbnail\", filters,\n        WCMCoreUtils.getUserSessionProvider());\n        if (productThumbnailNode != null) {\n            thumbnailPath = path + productId + \"\/thumbnail\";\n        }\n    } catch (Exception e) {\n        e.printStackTrace();\n        thumbnailPath = null;\n    }\n\n    return thumbnailPath;\n}<\/pre>\n<p>The WCMComposer API\u2019s <span class=\"navCode\">getContent<\/span> method is used here. It allows us to retrieve content based on its path. Some filters can be added to select the right content base (for instance, based on its language, its version or its publication state).<\/p>\n<p>The <span class=\"navCode\">getProductPictures<\/span> method returns all the contents related to a product:<\/p>\n<pre class=\"wrap:true lang:default decode:true \">@Override\npublic List getProductPictures(int productId) {\n    List pictures = null;\n\n    \/\/ get wcmcomposer service\n    WCMComposer wcmComposer = WCMCoreUtils.getService(WCMComposer.class);\n\n    HashMap filters = new HashMap();\n    \/\/ content of the currently selected language\n    \/\/filters.put(WCMComposer.FILTER_LANGUAGE, Util.getPortalRequestContext().getLocale().getLanguage());\n    \/\/ live or edit mode (will respectively get draft or published content)\n    filters.put(WCMComposer.FILTER_MODE, Utils.getCurrentMode());\n    filters.put(WCMComposer.FILTER_VERSION, WCMComposer.BASE_VERSION);\n    \/\/ order clauses\n    filters.put(WCMComposer.FILTER_ORDER_BY, \"exo:dateModified\");\n    filters.put(WCMComposer.FILTER_ORDER_TYPE, \"ASC\");\n\n    try {\n        \/\/ service call to retrieve the contents\n        List picturesNodes = wcmComposer.getContents(workspace, path + productId, filters,\n        WCMCoreUtils.getUserSessionProvider());\n        pictures = new ArrayList();\n        for (Node pictureNode : picturesNodes) {\n            \/\/ exclude thumbnail\n            if (!pictureNode.getProperty(\"exo:name\").getString().equals(\"thumbnail\")) {\n\n                \/\/ In live mode, the last published version is a frozenNode, so\n                \/\/ we need to get the node referenced by this frozen to get the real path\n                String picturePath = null;\n                if (pictureNode.isNodeType(\"nt:frozenNode\")) {\n                    String uuid = pictureNode.getProperty(\"jcr:frozenUuid\").getString();\n                    Node originalNode = pictureNode.getSession().getNodeByUUID(uuid);\n                    picturePath = originalNode.getPath();\n                } else {\n                    picturePath = pictureNode.getPath();\n                }\n\n                Picture picture = new Picture(pictureNode.getProperty(\"exo:name\").getString(), picturePath, pictureNode.getProperty(\"exo:title\").getString());\n                pictures.add(picture);\n            }\n        }\n    } catch (Exception e) {\n        e.printStackTrace();\n        pictures = null;\n    }\n\n    return pictures;\n}<\/pre>\n<p>We now use the <span class=\"navCode\">getContents<\/span> method, which is equivalent to <span class=\"navCode\">getContent<\/span> but for multiple contents.<\/p>\n<p>One really interesting point is that, by using this API, you can benefit from all the eXo Platform content management features. For example, by calling the <span class=\"navCode\">getContents<\/span> method with the filter <span class=\"navCode\">WCMComposer.FILTER_VERSION<\/span> set to <span class=\"navCode\">WCMComposer.BASE_VERSION<\/span> and the filter <span class=\"navCode\">WCMComposer.FILTER_MODE<\/span> set to the current mode (live or edit), only the link of the published pictures will be visible in live mode, whereas the link of the pictures in draft state will be visible in edit mode.<\/p>\n<p>As you can see, the node\u2019s type is checked to distinguish the frozenNode case:<\/p>\n<pre class=\"wrap:true lang:default decode:true \">if (pictureNode.isNodeType(\"nt:frozenNode\"))<\/pre>\n<p>In fact, a frozenNode is a published version of an original node. A frozenNode references the original node (in our case, the picture node) through its <span class=\"navCode\">jcr:frozenUuid<\/span> property. So we need to get this property to retrieve the original node and get its path.<\/p>\n<p>The last part is to edit our JSF pages to display all the products. For this, a RichFaces data grid (<span class=\"navCode\">rich:dataGrid<\/span>) is used:<\/p>\n<pre class=\"wrap:true lang:default decode:true \">&lt;rich:dataGrid value=\"#{comicsStoreBean.products}\" var=\"product\" columns=\"3\" elements=\"6\" width=\"600px\" border=\"0\"&gt;\n    &lt;rich:panel bodyClass=\"pbody\"&gt;\n        &lt;f:facet name=\"header\"&gt;\n            &lt;h:outputText value=\"#{product.name}\"&gt;&lt;\/h:outputText&gt;\n        &lt;\/f:facet&gt;\n        &lt;h:panelGrid id=\"product\" columns=\"2\" columnClasses=\"productThumbnailColumn, productDetailColumn\"&gt;\n                &lt;h:panelGroup&gt;\n                        &lt;h:panelGroup rendered=\"#{not empty product.thumbnailPath}\"&gt;\n                                &lt;img src=\"\/rest\/jcr\/repository\/collaboration#{product.thumbnailPath}\" width=\"80px\"&gt;&lt;\/img&gt;\n                        &lt;\/h:panelGroup&gt;\n                &lt;\/h:panelGroup&gt;\n                &lt;h:panelGrid columns=\"2\"&gt;\n                     &lt;h:outputText value=\"Description:\" styleClass=\"label\"&gt;&lt;\/h:outputText&gt;\n                     &lt;h:outputText value=\"#{product.description}\"\/&gt;\n                     &lt;h:outputText value=\"Price:\" styleClass=\"label\"&gt;&lt;\/h:outputText&gt;\n                     &lt;h:outputText value=\"#{product.price} \u20ac\"\/&gt;\n                     &lt;h:outputText value=\"Photos:\" styleClass=\"label\"&gt;&lt;\/h:outputText&gt;\n                     &lt;ui:repeat value=\"#{product.pictures}\" var=\"picture\" varStatus=\"status\"&gt;\n                        &lt;h:outputLink onclick=\"window.location = window.location.pathname + '?content-id=\/repository\/collaboration#{picture.path}'; return false;\" value=\"#\"&gt;\n                                &lt;h:outputText value=\"#{picture.title}\"&gt;&lt;\/h:outputText&gt;\n                        &lt;\/h:outputLink&gt;\n                        #{status.last ? '' : ' | '}\n                     &lt;\/ui:repeat&gt;\n                &lt;\/h:panelGrid&gt;\n        &lt;\/h:panelGrid&gt;\n        &lt;div class=\"buyButtonDiv\"&gt;\n                &lt;h:commandButton value=\"Buy\" styleClass=\"buyButton\"&gt;&lt;\/h:commandButton&gt;\n        &lt;\/div&gt;\n    &lt;\/rich:panel&gt;\n    &lt;f:facet name=\"footer\"&gt;\n        &lt;rich:dataScroller\/&gt;\n    &lt;\/f:facet&gt;\n&lt;\/rich:dataGrid&gt;<\/pre>\n<p>The <span class=\"navCode\">ui:repeat<\/span> property lists all links to the product\u2019s pictures. The link simply goes to the same page (because a Content Detail portlet will be added to this page) and passes the content-id parameter with the path of the content to display. The Content Detail will just look at this parameter and display the targeted content.<\/p>\n<h2>Adding Content Detail in the page<\/h2>\n<p>In order to display the products&#8217; pictures we need to add a Content Detail portlet to the page:<\/p>\n<ul>\n<li>Edit the page (<span class=\"navCode\">Edit &gt; Page &gt; Layout<\/span> in the top bar);<\/li>\n<li>In the portlet catalog, open the Contents category;<\/li>\n<li>Drag and drop a Content Detail portlet onto the page, under the first portlet;<\/li>\n<li>Edit the portlet;<\/li>\n<li>Select the default content for the Content Path field (products\/Comics);<\/li>\n<li>Scroll down the preferences screen and click on the Advanced link;<\/li>\n<li>Choose Enabled for the Contextual Content field;<\/li>\n<li>Leave the other field as default (by content-id);<\/li>\n<li>Click on Save;<\/li>\n<li>Click on Close;<\/li>\n<li>Click on the Save icon (upper right) to save the modifications made to the page.<\/li>\n<\/ul>\n<p>Our store is ready!<\/p>\n<p><a href=\"https:\/\/www.exoplatform.com\/blog\/wp-content\/uploads\/2013\/02\/final.png\"><img decoding=\"async\" class=\"aligncenter size-full\" title=\"final\" src=\"https:\/\/www.exoplatform.com\/blog\/wp-content\/uploads\/2013\/02\/final.png\" alt=\"final\" width=\"600\" \/><\/a><\/p>\n<p>Let\u2019s play with it!<\/p>\n<p>Click on a picture link (for instance, the Photo 1 of Wolverine):<\/p>\n<p><a href=\"https:\/\/www.exoplatform.com\/blog\/wp-content\/uploads\/2013\/02\/final-wolverine1.png\"><img decoding=\"async\" class=\"aligncenter size-full\" title=\"final-wolverine1\" src=\"https:\/\/www.exoplatform.com\/blog\/wp-content\/uploads\/2013\/02\/final-wolverine1.png\" alt=\"final-wolverine1\" width=\"600\" \/><\/a><\/p>\n<p>The picture is displayed below.<\/p>\n<p>Let\u2019s now add a new picture. To do so:<\/p>\n<ul>\n<li>Go to the Content Explorer;<\/li>\n<li>Go to the Iron Man folder (<span class=\"navCode\">acme &gt; documents &gt; products &gt; 1<\/span>);<\/li>\n<li>Click on the Upload Files button;<\/li>\n<li>Select your file;<\/li>\n<li>Click on Save.<\/li>\n<\/ul>\n<p><a href=\"https:\/\/www.exoplatform.com\/blog\/wp-content\/uploads\/2013\/02\/final-addpicture.png\"><img decoding=\"async\" class=\"aligncenter size-full\" title=\"final-addpicture\" src=\"https:\/\/www.exoplatform.com\/blog\/wp-content\/uploads\/2013\/02\/final-addpicture.png\" alt=\"final-addpicture\" width=\"600\" \/><\/a><\/p>\n<p>The picture is now uploaded. Leave it in draft state and go back to the Comics store page. As you can see, your new picture does not appear in the Iron Man\u2019s pictures yet.<\/p>\n<p>Now switch to edit mode by clicking on <span class=\"navCode\">Edit &gt; Content<\/span>.<\/p>\n<p><a href=\"https:\/\/www.exoplatform.com\/blog\/wp-content\/uploads\/2013\/02\/final-editmode.png\"><img decoding=\"async\" class=\"aligncenter size-full\" title=\"final-editmode\" src=\"https:\/\/www.exoplatform.com\/blog\/wp-content\/uploads\/2013\/02\/final-editmode.png\" alt=\"final-editmode\" width=\"600\" \/><\/a><\/p>\n<p>The picture link now appears and you can click it to display the new picture. It\u2019s up to you now to publish it to make it available in live mode.<\/p>\n<p>This tutorial has shown you how to use the eXo Content Management API to benefit from all the eXo Content Management features inside your own portlets.<\/p>\n<p>Don\u2019t hesitate to take a look at the documentation to discover all the available APIs. Enjoy!<\/p>\n<p><span style=\"font-size: 10px;\">(<a href=\"http:\/\/thomasdelhomenie.wordpress.com\/2012\/05\/14\/jsf-2-richfaces-portlet-in-exo-part-23-content-management-integration\/\" target=\"_blank\" rel=\"noopener\">original article from Thomas blog<\/a>)<\/span><\/p>\n","protected":false},"excerpt":{"rendered":"eXo Platform comes with powerful content management features and a large set of portlets to use these features. However, you may want to use these content management capabilities in your own portlets. This second tutorial will teach you how to use the eXo Content Management API and portlets to create a sample store application. The [&hellip;]","protected":false},"author":7,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[699],"tags":[],"lang":"en","translations":{"en":4469},"pll_sync_post":[],"_links":{"self":[{"href":"https:\/\/www.exoplatform.com\/blog\/wp-json\/wp\/v2\/posts\/4469"}],"collection":[{"href":"https:\/\/www.exoplatform.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.exoplatform.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.exoplatform.com\/blog\/wp-json\/wp\/v2\/users\/7"}],"replies":[{"embeddable":true,"href":"https:\/\/www.exoplatform.com\/blog\/wp-json\/wp\/v2\/comments?post=4469"}],"version-history":[{"count":0,"href":"https:\/\/www.exoplatform.com\/blog\/wp-json\/wp\/v2\/posts\/4469\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.exoplatform.com\/blog\/wp-json\/wp\/v2\/media?parent=4469"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.exoplatform.com\/blog\/wp-json\/wp\/v2\/categories?post=4469"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.exoplatform.com\/blog\/wp-json\/wp\/v2\/tags?post=4469"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}