Translate

Sunday, November 29, 2015

Creating Custom Dashboard

Although there is already an example in Developer's guide on how to create custom dashboard, I have added small update to make it more useful and which may be required for some clients. The update is related to one of the question asked in community:

How to create a dashboard in WCS showing only those updated assets by an user in a particular site?

Meaning, if an user logins, he/she should see only those assets which were edited by him/her in past week for that particular site. Similarly, other users should see assets updated by them not the assets which were updated by others.

Creating dashboard and understanding its elements is crucial from Developer's guide: Chapter 64 before looking into the example.

I would be updating and explaining code for the last example mentioned in the section - "Adding a widget that shows recently modified assets".

As mentioned in guide, proceed with creating following elements:
1. CustomElements/UI/Layout/CenterPane/DashBoard/RecentlyModifiedAssetsAction.jsp
2. CustomElements/UI/Layout/CenterPane/DashBoard/RecentlyModifiedAssetsJson.jsp
3. CustomElements/UI/Layout/CenterPane/DashBoard/RecentlyModifiedAssetsHtml.jsp
4. CustomElements/UI/Layout/CenterPane/DashBoardContentsConfig.jsp

All these elements are available to download from edelivery.oracle.com. After downloading, these elements are present in the following folder: WebCenterSites_11.1.1.8.0/WCS_Sites/WCS_Sites/misc/Samples/UICustomization/sample_elements/dashboard_elements. 
For the 4th element (CustomElements/UI/Layout/CenterPane/DashBoardContentsConfig.jsp), copy the code from  UI/Layout/CenterPane/DashBoardContentsConfig.jsp and add the following change  just before </componets> tag is closed:
<component id="myrecentold">
      <name>Recently Modified Assets</name>
      <url>CustomElements/UI/Layout/CenterPane/DashBoard/RecentlyModifiedAssets</url>
      <height>300px</height>
      <closable>false</closable>
      <open>true</open>
      <dragRestriction>false</dragRestriction>
      <style>recentPortlet</style>
      <column>2</column>
</component>

Just copy and paste the code and do the following changes:
1. Edit RecentlyModifiedAssetsHtml.jsp and change the following line:

String storeUrl = GenericUtil.buildControllerURL("CustomElements/UI/Layout/CenterPane/DashBoard/RecentlyModifiedAssets", GenericUtil.JSON);

2. Edit RecentlyModifiedAssetsAction.jsp and replace the code within try block with the following:
// This element uses the search service to look for the assets modified in past week for the logged in site.
    // build search criteria
    SearchCriteria searchCriteria = new SearchCriteria();   
    searchCriteria.setSiteId(GenericUtil.getLoggedInSite(ics));   
    searchCriteria.setModifiedDateOption(SearchCriteria.DateOption.PASTWEEK);
   
    //call search service to get assets modified by logged in user since last week
    Session ses = SessionFactory.getSession();
    ServicesManager servicesManager =(ServicesManager)ses.getManager(ServicesManager.class.getName());
   
AssetDataManager mgr = (AssetDataManager) ses.getManager( AssetDataManager.class.getName() );
    SearchService searchService =  servicesManager.getSearchService();
    List<ResultRow> searchResults = searchService.search(searchCriteria, -1, null);
   
    // build the asset list, its a list of map   
    List<Map>  assets = new ArrayList<Map>();   
    for (ResultRow r : searchResults)
    {   
        Map asset = new HashMap();
        //this map will have, id, name, type and asset fields.
        String id  = r.getIndexData("id").getData();
        String type = r.getIndexData("AssetType").getData();
        AssetId assetId = new AssetIdImpl(type, Long.parseLong(id));
       
        //Check who updated the asset using Asset Manager
       
AssetData data = mgr.readAttributes( assetId, Collections.singletonList("updatedby") );
        String updatedby = data.getAttributeData( "updatedby" ).getData().toString();

       
        //if asset was updated by current user, add this asset to map
       
if(updatedby.equalsIgnoreCase(GenericUtil.getLoggedInUserName(ics))){
            asset.put("id", assetId.toString());
            asset.put("asset", assetId);
            asset.put("name", r.getIndexData("name").getData());       
            asset.put("type", r.getIndexData("AssetType").getData());
            assets.add(asset);
       
}
    }
    request.setAttribute("assets", assets);


Make the changes in ReqAuthConfig.xml file and then restart your server as changes were made in xml file. After restart, you should see your dashboard working.

Now, suppose you want to add other column rather than default one's (type and name), for e.g. Updated date rather than type, then get the updateddate's value and set it in request scope by adding it to asset.add() method in RecentlyModifiedAssetsAction.jsp and update javascript method: getSelectedAsset() in RecentlyModifiedAssetsHtml.jsp to include your other column as shown below:
// get the selected asset
asset = {
     "type": store.getValue(item, 'type'),
      "id": store.getValue(item, 'id'),
      "name": store.getValue(item, 'name'),
       "updateddate": store.getValue(item, 'updateddate'),           
};

and also pass the correct controller argument in the same above element as:
<controller:argument name="configName" value="CustomElements/UI/Layout/Utils/GridConfig"/>
and create the following element to include updateddate column:
CustomElements/UI/Layout/Utils/GridConfig

Add the column - updateddate and comment the type column.

All updated elements including updateddate related changes can be downloaded from here.

Futher improvements can be done like to show assets of a particular type and subtype only.

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


UI/Functionality Customization: Creating custom button

Although there is an example present in developer's guide and the oracle tutorial here, there is not much of explanation on how one can use custom button in a live project.

Hence, I am providing a sample code for a simple use case by creating a button in Contributor UI which calls CSElement to validate if a particular asset is correctly created or not and show success/failure. Although use case discussed does not have much importance but the main purpose is to show how to hook up custom elements with WCS UI and assets.

Use Case: Page assets of specific subtype should have a particular category and if it has that category, then show success else failure.

For e.g. Product subtype page assets should have category as 'Detail Page' and Home subtype page assets as 'Info'

Procedure:
1. Create a simple Template (Element can be called from browser) and write the following code within <cs:ftcs> tag:

String type=request.getParameter("type");
String id=request.getParameter("id");
out.println("Type: " + type + " and id: " + id);
 
Current asset's type and id can be fetch from the request scope. Once you get the type and id, just add your logic and the output should be either some simple successful message or simple error message containing word - "Error:".

2. Create Custom Element under the following path - "CustomElements/[Site Name]/UI/Config/SiteConfigHtml" for a particular site or "CustomElements/UI/Config/SiteConfigHtml" for any site. I added to existing element: CustomElements/avisports/UI/Config/SiteConfigHtml in my JSK the following code:

webcenter.sites['${param.namespace}'] = function (config) { 
 // default view modes for avisports
 config.defaultView["Page"] = "web";
 config.defaultView["AVIArticle"] = "web";


  config.toolbarButtons.validatePage = {
   alt: 'Validate Page',
   title: 'Validate Page',
   src: 'js/fw/images/ui/ui/toolbarButton/smartlist.png',
   onClick: function () {
    // the document in the active tab
     var doc = SitesApp.getActiveDocument();
     // the asset object
       var asset = doc.get('asset');
       //asset id
       var id = asset.get('id');
       //asset type
    var type = asset.get('type');
 
      // make an ajax call to call an element
    dojo.xhrGet({
     //url of the element
     url: '/cs/ContentServer?pagename=avisports/ValidatePage',
     //force the browser to not cache 
     preventCache: true,
     //pass any parameters that need to be passed to the element 
     content:{'id':id,'type':type} 
    }).then(function(response){ 
     //handle response from the element here
     console.log('Response:', response);
     // the active view 
     view = SitesApp.getActiveView();
     if (response.indexOf('ERROR:') !=-1) {  
      view.error(response);
     } else {
      view.info(response);
     }
    },
    function(err){
     console.log('error on ajax call');
    });
   }
  }

  config.toolbars['form/Page/AVIHome'] = {
   'view': ['web-mode', 'edit', 'separator', 'preview', 'multi-device-preview', 'approve', 'delete','separator',
       'undocheckout', 'checkin', 'checkout', 'rollback', 'showversions','separator','validatePage','refresh'],
   'edit': config.toolbars.form.edit,
   'copy': config.toolbars.form.copy,
   'translate': config.toolbars.form.translate,
   'create': config.toolbars.form.create
  }
  
}

3. Once added,  just restart and you will see custom button like the following:

On click, you see the following screen with the success output which will stay on the screen for 2-3 seconds.

If it was error output, you will see the error output which will remain until you click on x button as shown below:

Trick is to show error via view.error([Your response]) which will stay until user clicks on x button and view.info([Your response]) which is visible just for 2-3 seconds.

Although this doesn't prevent from saving the asset but this custom button can be useful as secondary check like a post-save check if the asset was configured correctly as required or not. 

Another useful case: Suppose an user has access to only one site and on creation of asset, if an asset is automatically shared with other sites using some customization (Check out my post for this customization), he/she cannot unshare/delete this asset as user will get error that asset is shared and share function gets disabled if an user has access to only one site. Thus, a custom button can be helpful in such scenario; which can be built using same process discussed above. Basic steps would be: Get the asset type and id, share the asset to current site (current site id is available in session variable as pubid) using <asset:removesite> tag and then use <asset:void> and/or <asset:deletevoid> tag to delete the asset and show appropriate message.

There may be some other useful cases too which I shall discuss in my future posts.

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