Translate

Monday, January 11, 2016

Thumbnail search view customization

Although there is very detailed explanation on customizing search view in developer's guide; it provides example only to create thumbnail search view on basis of asset's attribute of type blob for docked thumbnail search view and undocked thumbnail search view. But what if you want to show blob attribute from asset's associated attribute? Thus, this post.

I have taken avisports site as an example. In avisports site, Article definition has attribute: relatedImage which is of type - AVIImage (subtype: ArticleImage) and thus, this assettype: AVIArticle qualifies for this customization. So overall, AVIImages's attribute: imageFile should be visible as thumbnail in docked and undocked search view on searching AVIArticle in Contributor UI.

Steps to follow:
1. Create a custom element: CustomGlobalThumbnailHtml under "CustomElements > avisports > AVIArticle > UI  > Layout > Search > View" as jsp
2. Copy the code from GlobalThumnailHtml.jsp from the element present under "UI  > Layout > Search > View" and paste in CustomGlobalThumbnailHtml file. This code is responsible for thumbnail view for AVIArticle as mentioned in ThumbnailViewConfig element under CustomElements.
3. Update CustomGlobalThumnailHtml and include following code after you get the type param, to fetch imageFile attribute and set it as url param and save it. This code will basically fetch AVIImage's imageFile attribute and set as url param.
4. Update ThumnailViewConfig element and DockedThumnailViewConfig element present under "CustomElements > avisports > AVIArticle > UI  > Layout > Search > View" to point to CustomGlobalThumbnail element.
5. Save and search any AVIArticle in your avisports, now thumbnails should be visible in thumbnail's view from imageFile attribute of asset - AVIImage.

Thus, in any project, thumbnail can be shown from associated asset's blob attribute in thumbnail search views (Docked and Undocked) with this simple customization.

To simply test it, I directly updated GlobalThumbnailHtml element which is not recommended.

AVIArticle Undocked Thumbnail View




AVIArticle Docked Thumbnail View

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

Tuesday, December 15, 2015

Using PreUpdate or PostUpdate element for flex asset

In WebCenter Sites, whenever an asset is added/read/edited/deleted, many elements are called internally before an asset's CRUD operations are performed. These elements are stored under OpenMarket/Xcelerate/AssetType/[Your custom AssetType] folder.

Among the elements, PreUpdate and PostUpdate are called many times whenever any CRUD operations are performed on an asset. Hence, if any custom update is required either before or after an asset is saved in database, you can write your logic in PreUpdate element (before saving) or in PostUpdate element (after save).

Details related to which method should be updated is clearly mentioned in Developer's Guide. As not much detail is present in guide for any implementation or usage, I am discussing one very simple use case which is very common ask by any Client.

USE CASE:
If an asset is created in one site, it should be shared automatically with other sites. This is very practical and generic requirement for e.g. client may want to show News (all English ones) from all sites in other site's English version of News page or global site's News page.

Let us consider that there are 2 sites - FirstSite and FirstSite II, and both sites have product family and corresponding other child flex families. If a product asset of type - FSII Audio is created in FirstSite, it should be automatically shared with FirstSite II and vice-versa.

At first glance, this use case seems very easy and one can think of implementing a custom asset listener by updating the assetAdded method. But there is a catch; it seems that there is one bug that one cannot fetch asset subtype value using any method in asset listener while an asset is created and thus, cannot share only that asset whose type (definition) is FSII Audio. As asset is not saved in database at the time of call of asset listener, it does not have any way to retrieve asset's subtype value (or other custom attribute values) and hence, fails to meet the purpose. In such scenario, either you have to write flex filter or update that element which will be called just before an asset is shown in contributor UI after its created. How to develop flex filter is mentioned in detail in Developer's Guide, I have provided steps for updating core element method.

Elements while are called while CRUD operations for Product_C are located under "OpenMarket/Xcelerate/AssetType/Product_C/" folder. Create/Update element calls the PreUpdate and PostUpdate elements just before saving and after saving asset respectively. Hence, you can update one of the element to perform the task of sharing the asset. As we want to update asset based on subtype, PostUpdate element is most suitable option for this task as database entry has the subtype value present for the asset before PostUpdate element is called.

STEPS:
  1. Internally, PostUpdate element for each AssetType folder calls the element: OpenMarket/Gator/FlexibleAsset/Common/PostUpdate. PostUpdate is xml file containing various checks on operations like create, delete, update, remotepost, etc. These operations are briefly defined in developer's guide.
  2. If you are using Windows and have access to Sites Explorer, then navigate to the element and edit using desired text editor.
  3. If you are using Mac or Linux, one cannot use Sites Explorer, so just create new CSElement with same name and the element code will be visible in the element tab.
  4. In our case, need to add your code under "create" condition.
  5. As writing code in XML can be difficult and cannot use any JAVA within XML, call your JSP element using <CALLELEMENT> tag under Variable.updatetype=create condition and write your logic in JSP element to share the asset.
  6. You have the following ics variables available: AssetType as asset type and id as asset id, which can be used to find the subtype using <asset:getsubtype> tag and then this current asset can be shared using <asset:share> tag. You don't need to use <asset:save> tag.
  7. Save your element and test by creating new asset. As soon as product asset of type FSIIAudio is saved, it will be shared to the sites according to your code.
CAVEATS:
  1. As this is a type of customization, one has to keep track of such elements in event of upgrades in future and update accordingly after upgrade.
  2. Always keep backup of original element before updating any core element as it may or may not behave as intended.
  3. If an user has access to only one site say FirstSite and creates FSIIAudio product asset, it will be shared to FirstSite II also, according to our implementation. But if the same user wants to unshare/delete this asset, then he/she will not be able to do so as share function will not be accessible to unshare and/or then delete the asset. In such scenario, either you have to develop  custom unshare/delete button or user has to request the admin editor, who has access to all sites, to unshare/delete the asset. For creating custom button, you can check my post.
----------------------------------------------------
SUGGESTIONS/COMMENTS ARE INVITED
----------------------------------------------------

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


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