Translate

Tuesday, July 7, 2015

Creating a custom tree tab


Tree tabs within WebCenter Sites (WCS) 11.1.1.8.0 can provide an idea on how a particular site is architected e.g. Design Elements, Content Parent Hierarchy and Custom Tree tab (if any). Furthermore, helps contributors and editors in creating and analyzing content easily. WCS provides in-built (default) tree tabs which are shared with each site but are visible provided that sufficient roles and ACLs are given to an user within WCS. This blog describes on how to create a custom tree tab.

Before jumping into this exercise, it is better to read about Tree Tabs from developer's guide.

Custom Tree Tabs can be build for both assets and non-assets. Developer’s guide includes code snippets to build custom tree tab node for a single asset and non-asset (adhoc). This section describes about how to build custom tree tab for non-assets; a simple tree tab with single node. This tree tab will be created from Admin tab which will point to our custom element which builds the tree tab. On double-clicking the node, another CSElement loads up which will simply process and show the content of CSElement. Hence, need to implement a custom view to show the contents and custom action for it.


Whole process can be broken into following steps:
1. Create one CSElement which will build the tree tab. I have considered creating tree tab
using XML as its easy to copy from other tree tabs elements; which ends up like this




2. Create another CSElement which will show the content on double-clicking on the node. In this example, I am using already available CSElement - OpenMarket/Demos/index which is present in JSK 11.1.1.8.0 installation. This CSElement needs to be passed while creating custom view as described in next point. 

3. Create custom element to implement custom view and custom action for the node. This requires little bit knowledge on how customization works in WCS which is described in Developer’s guide in full detail. Hence, proceed with creating one element under CustomElements for AviSports using Sites Explorer which should end up like the following: CustomElements/avisports/UI/Config/SiteConfigHtml which provides custom UI configuration settings. If this element is already present, then just add your code within it or else create this element and then add your code. First of all generate the URL using satellite:link in SiteConfigHtml element as shown (don’t forget to include the satellite tld):

<satellite:link pagename="OpenMarket/Demos/index" outstring="demoURL">             <satellite:argument name="contributorUI" value="true"/>
</satellite:link>

This demoURL variable will be passed while creating your custom view as show below:

config.views['sampleSitesView'] = { viewClass: 'fw.ui.view.SimpleView', viewParams: {
url: '<%= ics.GetVar("demoURL") %>' }
};

Create your custom action as shown below:

config.myActions = { sampleSitesAction: function () {
var views = SitesApp.getViews();
var view;
dojo.forEach(views, function (v) {
if (v.viewType === 'sampleSitesView') {

view = v; }
});
// view already opened - just focus it
if (view) {
view.focus();
}else{
// create view
view = fw.ui.ViewFactory.buildView('sampleSitesView'); view.set('title', 'Sample Sites');
view.show();
} } };

// Finally add action to tree
config.treeActions.SampleSitesAction = config.myActions.sampleSitesAction;

4. Finally, register your tree tab from Admin UI with AviSports site. Check developer’s guide on how to register custom tree tab.

Download sample files from here

To download summary of Tree Tabs, click here - TreeTabs

----------------------------------------------------
SUGGESTIONS/COMMENTS ARE INVITED
----------------------------------------------------

Sunday, June 28, 2015

Creating custom Event Listeners

Oracle WebCenter Sites (erstwhile FatWire) provides few API's to perform general auditing tasks or housekeeping processes like generating reports/weekly assessment on asset operations and publishing process which may or may not required by an organization but certainly is demanding feature for many customers.

Albeit there is no such simple button/process to do so within Oracle WebCenter Sites UI and hence, developers have to provide custom solutions to meet their customer requirements. And to do so, this custom business logic has to be implemented in form java classes and deploy to <webapps>/WEB-INF/lib folder.

Before browsing through this blog, it better to understand this topic: Event Listener from developer's guide. The process of registering listener is already mentioned in the guide, hence, I won't describe here but rather a sample snippet of code code on how to implement listener.

Note: All the below methods and classes are available with latest Oracle WebCenter Sites patch which may or may not be present in older version of FatWire/Oracle WCS.

There are 2 type of event listener which are of more importance:

Asset Event Listener: which fires on following asset operations: Add/Update/Delete/UndoCheckout/Approved/UnApproved

 public final class MyAssetEventListener extends AbstractAssetEventListener  
   {  
     public void assetAdded(AssetId id)  
     {  
       log.info("Asset Type: " + id.getType() + " and id:" + id.getId() + " added");  
     }  
     public void assetUpdated(AssetId id)  
     {  
      log.info("Asset Type: " + id.getType() + " and id:" + id.getId() + " updated");  
     }  
     public void assetDeleted(AssetId id)  
     {  
      log.info("Asset Type: " + id.getType() + " and id:" + id.getId() + " deleted");  
     }  
     public void assetApproved(AssetId id)  
     {  
      log.info("Asset Type: " + id.getType() + " and id:" + id.getId() + " approved");  
     }  
     public void assetUnapproved(AssetId id)  
     {  
      log.info("Asset Type: " + id.getType() + " and id:" + id.getId() + " unapproved");  
     }  
     public void assetUnapproved(AssetId id, Map<String, Object> properties)  
     {  
      log.info(" User " + properties.get("user") + " has unapproved an asset:" + id + " for the target:" + properties.get("targetId") + ".");  
     }  
   }  

Publish Event Listener: which fires on publishing tasks

 public class MyPublishEventListener implements PublishingEventListener {  
      public void onEvent(PublishingEvent event) {  
           PublishingTasks task = event.getTaskName();  
           /*  
            * task can be any one of the following: GATHERER, PACKAGER, TRANSPORTER, UNPACKER, CACHEUPDATER, NOTEPUBLISH, SESSION  
            */  
           PublishingStatusEnum status = event.getStatus();  
           /*  
            * status can be one of the following: STARTED, DONE, FAILED, CANCELLED, SUBTASK_FINISHED  
            */  
           List<AssetId> assetids = event.getAssetIds();  
           /*  
            * AssetId contains info about type and id in following format: ASSETTYPE:ID  
            */  
           String pubSessionId = event.getPubSessionId();  
           /*  
            * Publication Session  
            */  
           /*  
            * Publishing target Id and target Name  
            */  
           String targetId = event.getTargetId();  
           String targetName = event.getTargetname();  
           /*  
            * Username  
            */  
           String userName = event.getUsername();   
           if (PublishingTasks.NOTEPUBLISH.equals(task)) {  
                if (PublishingStatusEnum.STARTED.equals(status)) {  
                     for (AssetId id : assetids) {  
                          // do something  
                     }  
                }  
                if (PublishingStatusEnum.DONE.equals(status)) {  
                     for (AssetId id : assetids) {  
                          // do something  
                     }  
                }  
                if (PublishingStatusEnum.FAILED.equals(status)) {  
                     for (AssetId id : assetids) {  
                          // do something  
                     }  
                }  
           }  
      }  
 }  
Required jars: basic.jar, assetapi.jar, cs-core.jar, cs.jar, xcelerate.jar and log jars if logging.

Performance: There are caveats associated using custom listeners as it can affect authoring experience if frequent amount of asset activity is carried out. But again it depends on the method which you have implemented. Even if you are using single method of particular listener class, it then boils down to what further actions are taken. For e.g. Just printing a single message in log will not affect much but if process includes asset retrieval, streaming output in file system or database and notifying certain other processes, etc. can really degrade performance.

----------------------------------------------------
SUGGESTIONS/COMMENTS ARE INVITED
----------------------------------------------------


Sunday, June 21, 2015

Creating a simple custom WEM application

WEM - Web Experience management is a feature in webcenter sites/fatwire where in separate module can be implemented/deployed to integrate external content/repository via REST API or by deploying applications. All possible use of this feature are listed and discussed in detail here (Developer's Guide).

One feature is available to test and deploy: Articles Sample application (which can be downloaded  from Oracle edelivery site). Although following the steps given in guide should be enough, there are few errors and less info available for known issues. I am listing those so that you can make the changes and make it work:

1. SSO authentication failure (multi cast port not updated). Open the file: cas-cache.xml present at /WEB-INF/classes/cas-cache.xml and search for the term -"multicastGroupPort=@casCacheMultiCastGroupPort@" and change it to "multicastGroupPort=<SOME PORT NUMBER>". Re-deploy articles-1.0.war and run install.app

2. If you are not able to run install.app, then after deploying article-1.0.war file, register your app manually as mentioned here. There are few issues even after registering manually:

  • Not able to find app icon: To resolve this either you keep the icon within /cs/ folder of your application server directory and update your FW_Application or else update your FW_Application icon url to "../articles-1.0/images/articles.png"
  • Other issue is after each login on JSK, you have to run install.app and also there are multiple http redirects made leading to errors. To resolve this, rather than deploying the article-1.0.war file, unzip the folder under <APP_SERVER>/webapps/ folder and update your FW_Application accordingly. More details present here in oracle community. 
Although sample is present, it is little complex to understand it as it requires good knowledge on how controller works and creating app & assets programatically. But there is another simpler way to create and test WEM app. As a FatWire/Oracle Webcenter Sites consultant, often challenges are met with developing custom solutions which are useful for content contributors and administrators. For eg: Mass edit/save/update/delete/sharing assets, managing roles and ACLs, searching assets on attributes, etc. Such tools/solutions are often created as CSElement+SiteEntry where SiteEntry is called via browser to perform the task. If a project has so many custom requirements, I think it is better to have them separately in another WEM app which would be available to only permitted users. By doing so, custom admin tasks are separated from normal users and is secure as the solution is not exposed. I won't be telling you how to create these custom tools but rather about how to create a simple custom WEM app as followed:

Step 1: Create one typeless-template which should have the following code within the default code:
<div id="myapp" style="float:left;height:100%;width:100%" class="wemholder"></div>

This would be your layout element which would be included while creating FW_Application asset. Note: This id - myapp is important as it would be added to field: parent node while creating FW_View asset and should be unique.

Step 2: Create another typeless-template which will include your code to render the content. This content can come from anywhere like REST API, 3rd party api, etc. In our example, I will just include and proceed with Sample site URL present in JSK which shows the number of sites present and two sample sites urls to access sites: http://localhost:8080/cs/ContentServer?pagename=OpenMarket/Demos/index



This url would be added in the element while creating FW_View asset. 

Step 3: Download an icon (.gif or .png) from internet for your app and place it under some folder in <webapps>/cs/ folder

Step 4: Go to AdminSite, create new asset of Type: FW_View, fill in all the details like name, parent node (value would the id present in layout template i.e. myapp), view type: iframe and source url: sample site url. It should look like the following:


Step 5: After creating FW_View asset, add it to active list/bookmark it or it should be available in HISTORY tab if you did not navigate to other site or logged out. Create new FW_Application asset, include your FW_View asset and fill in all the details which should end like the following:


Step 6: Navigate to WEM Admin in AdminSite and register your app for avisports. Click on Apps from top menu and hover over you MyApp app, you will get 2 options: Edit and Manage App. Select Manage App, select site/s & role and then save it. I selected avisports and assigned all roles. Select site - avisports, an icon should be now visible for you to click on to open your WEM app. Overall you should see like below:

Similarly, any webcenter sites content or small functionality can be shown.

----------------------------------------------------
SUGGESTIONS/COMMENTS ARE INVITED
----------------------------------------------------

Saturday, March 28, 2015

UI/Functionality: Developing Gadgets

This blog is about rendering Gadgets: Static content, Dynamic content and CS-based content.

Before jumping into creating Gadgets, it is better to go through architecture of Gadget server and the topic: About Developing Gadgets.

Applies to only 11.1.1.8.0 and above. All the following examples were tested using JSK 11.1.1.8.0 (installed all sites + community-gadget) on Windows 7 64-bit machine.

Gadget created with Static Content (JSON):

1. Create one following text file which contains json (json.txt) as shown and save it as location where it can be fetched by ContentServer (I saved it under <webapps>/cs/custom/ folder) :

{"Name" : "Rowan", "Breed" : "Labrador Retriever", "Hobbies" : ["fetching", "swimming", "tugging", "eating"]}

2. Create one simple XML (gadget descriptor) file which parses JSON and save it as FetchJson.xml in the same above folder location

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
  <ModulePrefs title="Fetch JSON Example"/>
  <Content type="html">
  <![CDATA[
    <div id="content_div"></div>
    <script type="text/javascript">

    function makeJSONRequest() {    
      var params = {};
      params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.JSON;
      // This URL returns a JSON-encoded string that represents a JavaScript object
      var url = "http://localhost:9080/cs/custom/gadgets/json.txt";
      gadgets.io.makeRequest(url, response, params);
    };

    function response(obj) { 
      var jsondata = obj.data;
      var html = "";
      // Process returned JS object as an associative array
      for (var key in jsondata) {
        var value = jsondata[key];
        html += key + ": ";
        // If 'value' is an array, render its contents as a bulleted list
        if (value instanceof Array)
        {
          html += "<br /><ul>";
          for (var i = 0; i < value.length ; i++)
          {
            html += "<li>"+ jsondata.Hobbies[i] + "</li>";
          }
          html+= "</ul>";
        }  
        // If 'value' isn't an array, just write it out as a string
        else {        
          html += value + "<br />";
        }      
      }               
      document.getElementById('content_div').innerHTML = html;
     };
     gadgets.util.registerOnLoadHandler(makeJSONRequest);
     </script>
  ]]>
  </Content>
</Module>

3. Login to WebCenter Sites -  Go to "Gadgets" UI - Select Catalog - Register Gadget
4. Paste the following URL: http://localhost:9080/cs/custom/gadgets/FetchJson.xml (Your location hostname, portname and location may vary according to your installation). Provide all the other fields and SAVE. Deploy your gadget or dashboard. Your gadget on Dashboard should look like this:

Gadget created dynamically: If you have a gadget descriptor XML url, then you can directly "Register Gadget" and  preview. You can try with this "Hello World" example (just copy the URL and paste) which ends up like this:

Usage: If you have certain dynamic URLs, like feeds, dictionary, weather, news, map, calendar, clock. etc. , then they can be deployed directly into Gadgets Server and can be viewed on site as required.

CS-Based Gadget created with content from WebCenter Sites:
Although detailed explanation is provided in Developer's Guide and User's Guide , it seems difficult to understand and test the examples provided. So after understanding the main concept, I created one of the example - List Gadget. (All other examples can be downloaded from oracle edelivery)Before we jump into example, one should understand the template flow. For List Gadget, template flow is as shown:
Description of Figure 92-1 follows

Procedure (download all files from here):
1. Create one basic asset type using AssetMaker Utility - FW_CSGadget (field: assetdescriptor) and add an association to it: DataAsset (type: recommendation) and create one asset: ListGadget from Contributor UI. Associate Recommendation (Content_C) to the DataAsset association.
2. Create Main template - GenerateGadgetXML (Type:FW_CSGadget; Template can be called externally or in browser) which is responsible for generating the Gadget Descriptor XML file.
3. This main template calls - ListGadget template (G_List: Type-FW_CSGadget and Template can be called externally or in browser) which basically loads the ListGadget asset and the associated recommendation (this recommendation contains list of Content_C assets) which calls recommendation template (AdvCols/G_JSON) and which in turn calls Content_C(Content_C/G_JSON)
4. Few files are deployed to <webapps>/cs/FirstSiteII/gadget location for <locale> attribute of XML.
5. If you have followed the above procedure by copying all the code from the shared folder and deploying gadget folder to the above mentioned location, then there should be no issue in creating ListGagdet. Go to "Gadget UI" - Catalog - "Register Gadget" and enter the URL, fill other fields and save. All done!! (Note: cid in the URL should be your ListGadget basic asset id)
6. Now you either deploy it as a single Gadget or update your Dashboard and then deploy full Dashboard itself. This is how ListGadget looks in Dashboard:



----------------------------------------------------
SUGGESTIONS/COMMENTS ARE INVITED
----------------------------------------------------

Wednesday, March 18, 2015

UI Customization: Changing icons in FatWire/Oracle WebCenter Sites

This is just a simple UI customization which may or may not be required but just listing it down (mostly applies to Oracle WebCenter Sites 11g)

1. Changing login icon: Few Client may want to show their own logo while logging to Sites instance

Procedure:
  • Just add all the image files under this folder - <webapps>/cs/js/fw/images/logos/
  • Update login.css file placed under this folder - <webapps>/cas/fatwire/css/ to include your image name
2.  Changing Asset Type icon (Only applies to Oracle WebCenter Sites 11g): It is possible to show different icons on basis of asset types and definitions. For e.g. If you have media flex family and have 2-3 different asset type under it like video, image, audio, etc. Your client may ask to show icons which loads up everywhere like tree tabs in Admin UI, content tree in Contributor UI, etc. wherever this asset types shows up.

 Procedure:
  • Download all the icons (recommended: 16x16 png)
  • Place all the files under this folder with correct asset name:<App_Server>/webapps/cs/Xcelerate/OMTree/TreeImages/AssetTypes/<ASSET_TYPE>.png
  • If your asset name is - "Content_A", your icon name should be Content_A.png placed under the folder mentioned above.
  • For showing icon on definition basis(i.e. subtype): <App_Server>/webapps/cs/Xcelerate/OMTree/TreeImages/AssetTypes/<ASSET_TYPE>/<ASSET_TYPE>-<SUBTYPE>.png
----------------------------------------------------
SUGGESTIONS/COMMENTS ARE INVITED
----------------------------------------------------

Sunday, March 15, 2015

UI/Functionality Customization: Proxy Assets (Google Map)

After the introduction of Proxy Assets in Oracle WebCenter Sites 11.1.1.8.0, it has become easy to fetch data from external repository. As content is not stored in Sites (rather its present in the service provider database) there is no longer need for storing data or importing bulk content in Sites and later bothering with content storing, publishing and handling tell-tale errors (given that there is API for fetching data from third party repository which generates data on the fly).

Hence, the use of Proxy Assets opens up plethora of opportunities for implementation like:
  • Use of Google Translator API (only paid version is available) to replace use of Dimensions (May be possible, not tried though)
  • Use of Google Map API (free and paid, both available) to include Google Map on your site. Other maps like MapQuest, etc. can also be included if such API is available.
  • Including location based weather information on your site (various free and paid version are available)
  • Including NewsLetter (automatically get updated news on daily basis) from various News provider and target audience via Segments and Recommendation (Implemented and works perfectly)
  • Including third-party search which rankes your web pages first (Note: Google no longer supports custom search API. Other search providers like Yahoo and Bing are open to use with either both free and/or paid version API)
  • Google Youtube API to include youtube videos.

Proxy Asset follows the architecture:
Proxy Asset Architecture

Requirements for using Proxy Assets (Applies only to Oracle WebCenter Sites 11.1.1.8.0 and above):
  1. You need to have service URL (API provided by third-party service) whose output on querying results in either JSON or XML(mainly these 2 because already there are jar present in webapps/cs/WEB-INF/lib folder to process them). Other outputs can also be handled, but will require deploying jar for them.
  2.  You may or may not need a key to access these API (API can be public or from some organization; again depends on service provider)
Implemenation:
There is already an example present in JSK 11.1.1.8.0 for Youtube Proxy Assets and an excerpt in guide for static content. So I just thought to create my own example and ended up creating Google Map Proxy Asset.

For testing Youtube proxy assets, navigate to Contributor UI (avisports), select Youtube and search any video. The search result views are customized for Youtube proxy assets: List View and Thumbnail View. You can check the code under <Sites_Installation>/Shared/elements/CustomElements/Youtube folders where folder structure looks like this:


I will just summarize what each of them does:
  1. Youtube/UI/Data/Search/SearchAction.jsp: This element is responsible for searching against third-party API on basis of keyword (keyword can be anything and depends on API, check the service provider API documentation) and gets JSON output and then registers the proxy assets (saves one unique key for later use)
  2. Youtube/UI/Data/Search/SearchJson.jsp: Just stores the proxy asset
  3. Youtube/Layout/CenterPane/Form/ProxyHtml.jsp: This element shows an invidual youtube proxy asset. It fetches the registered proxy asset, uses the stored unique key (externalid) and then uses the API to retrieve the data on the fly. This element is called when you click on any search results from either View (List or Thumbnail).
  4. Rest of them are search view customization: Read about them here -> Customizing the Contributor Interface
Creating Proxy Assets for GoogleMap (ReverseGeoCoding) (AssetType name - "GoogleMap") :
  1. Get a key (available free but with some limitations or the paid one). Details present here.
  2. Just get any icon for GoogleMap (16x16 png file) and place it under <App_Server>/webapps/cs/Xcelerate/OMTree/TreeImages/AssetTypes/GoogleMap.png 



  3. Create one Proxy Asset Type from Admin Tab. In the Admin tab, expand Proxy Asset Manager and Double-click Add New.   
  4. Open Sites Explorer and create folder structure under CustomElements (same as it was done for Youtube proxy asset but name would be assettype i.e. GoogleMap) which should end up like this:
  5.  Create "SearchAction.jsp", "SearchJson.jsp" and "ProxyHtml.jsp" file under respective folder from SitesExplorer and save. Navigate to <Sites_Installation>/Shared/elements/CustomElements/ to see if there is a "GoogleMap" named folder created and the structure will be as shown above.
  6. Copy the element code from SearchAction.jsp, SearchJson.jsp and ProxyHtml.jsp and paste it in respective elements.
  7. Open ProxyHtml.jsp in some file editor and paste your Google Map key on line number 23. Just replace <USE YOUR OWN KEY> with GoogleMap key
  8. Create new "search" start menu for GoogleMap proxy asset (Google Map location) and enable GoogleMap proxy asset for your site. I enabled it for AviSports.

    Select "Google Map Location" and search against any location. You may get one or more results. Click on any one of the result to view it, which should look this:


    Note: I am storing latitude, longitude and address in externalid so that when map loads up, I can use latitude and longitude for finding location on map (reverse geocoding) and address for dropping marker on the map.

    Furthermore, you can change search views, add custom tree tabs, add the proxy asset via insite editing to your assets, etc. All details are present in developer's guide and the templates for Youtube proxy asset. 


    ----------------------------------------------------
    SUGGESTIONS/COMMENTS ARE INVITED
    ----------------------------------------------------