Screencast 8 – WebCenter Sites Document Library Gadget for WebCenter Content

Screencast 8 – WebCenter Sites Document Library Gadget for WebCenter Content

Within a web experience, internal or external, it may make sense to expose highly interactive components that surface backend data. One way in which WebCenter Sites supports this capability is in the form of Gadgets. In our previous post, WebCenter Sites Gadget Development Concepts Quickstart, we took at look at some of the fundamentals of Gadget development.


View of the Gadget running in a dashboard in WebCenter Sites


Confirmation dialog after a user has clicked on the bell icon for a given content item to subscribe to updates

The foundation from our prior post can now be extended to illustrate how information from other systems, namely WebCenter Content, can easily be included within the WebCenter Sites via the Gadget paradigm. The screen shot above of the Gadget that we review in this episode illustrates how we ultimately expose a simple interface on top of WebCenter Content by way of its RESTful services.

WebCenter Sites Document Library Gadget for WebCenter Content Functions
Our sample Gadget will allow the following functionality to be exposed within WebCenter Sites

  • Display Folders and Content Items from WebCenter Content on the basis of our authenticated user
  • Allow a user to Subscribe / Unsubscribe to a Content Item from within the Gadget
  • Allow an administrator to set a start node for the document library
  • Allow an administrator to specify a server location for the WebCenter Content connection

Gadget Directory Structure & Code
For our sample we used a local Apache HTTP Server instance to host the Gadget. For development purposes we disabled browser security that checks for Cross Site Scripting attacks to allow us to mix host names for portions of the environment (without doing this some AJAX calls would not work). In order to get around this, simply launch your browser (in this case Chrome) using the command line and specifying arguments to disable the checking.

Running Google Chrome in Windows disabling some cross site scripting security


Directory structure for Gadget

doclib.xml

The doclib.xml file is what is registered within WebCenter Sites. It can contain all of the logic for the Gadget, but in our case we have isolated some of the code in an external file to make it more manageable.

<?xml version="1.0" encoding="UTF-8"?>
<Module>
  <ModulePrefs 
		title="WebCenter Content Document Library"
		author="John Brunswick"
		author_email="john.brunswick@gmail.com"
		title_url="http://www.johnbrunswick.com"
		description="This Gadget provides a document library view of a folder and its contents residing in WebCenter Content"
    >
    <Require feature="opensocial-0.8"/>
    <Require feature="dynamic-height"/>
    <Require feature="views"/>
    <Require feature="setprefs"/>
  </ModulePrefs>
  <UserPref 
		name="libstyle"
		display_name="Library Style" 
		datatype="enum" 
		default_value="horizontal">
    <EnumValue value="vertical" display_value="Vertical" />
    <EnumValue value="horizontal" display_value="Horizontal" />
  </UserPref>
  <UserPref 
    name="rootfolder"
    default_value="909964822906001807"
    datatype="hidden"/>
  <UserPref 
    name="cgiPath"
    default_value="http://owcvm03/cs/idcplg"
    datatype="hidden"/>
  <UserPref 
		name="showdescription"
		display_name="Show Description" 
		datatype="enum" 
		default_value="descyes">
    <EnumValue value="descyes" display_value="Yes" />
    <EnumValue value="descno" display_value="No" />
  </UserPref>
  <Content 
  		type="html">
<![CDATA[
<link rel="stylesheet" type="text/css" href="http://localhost/gadget/style/style.css"/>
<script src="http://localhost/gadget/js/jquery.min.js"></script>

<!--Thanks to Bex Huff (http://bexhuff.com/) for his JQuery library to make calls into WebCenter Content simple! -->
<script src="http://localhost/gadget/js/jquery.oracle-ucm-1.0.js"></script>

<!-- Logic for operating the doc lib -->
<script src="http://localhost/gadget/js/wc.doclib.js"></script>

<script>
  // Get prefs object for Gadget
	var prefs = new gadgets.Prefs();

  // Set variable for use throughout Gadget with base root info - placeholder only works directly in this XML file, hence the variable assignment
  var docLibRoot = '__UP_rootfolder__';

  function pageload() {
      // Set the WebCenter Content location
      $.ucm.cgiPath = '__UP_cgiPath__';

      // Load the content for a given folder
      browseFolder(docLibRoot);

      // Set the style based on user preferences
      $('#searchResults').addClass(prefs.getString("libstyle"));
  }

  // Initialize this gadget
  gadgets.util.registerOnLoadHandler(pageload);
</script>

<div id="docLib">

  <ul id="breadCrumbs"></ul>

  <div id="folderDescription"></div>

  <div id="notification"></div>

  <ul id="backLink"></ul>
  <ul id="searchResults"></ul>

</div>
<div style="clear:both"></div>
]]>
  </Content>
</Module>

wc.doclib.js

For our Gadget we have chosen to place most of the processing logic within the wc.doclib.js file to make it more manageable. The browseFolder function is the cornerstone of the Gadget and handles all of the content rendering within the Gadget by making RESTful calls into to WebCenter Content to retrieve folder and content item listings.

function setItemSubscription(dIDValue, tokenValue, dDocTitleValue, collectionIDValue)
{
  $.ucm.setSubscription(dIDValue, tokenValue, dDocTitleValue, function (ucmResponse) {
      $('#notification').html('<img src="http://localhost/gadget/img/information-italic.png"/> You have subscribed to '+dDocTitleValue);
      $('#notification').fadeIn('slow').slideDown('slow').delay(4000).fadeOut('slow');
      browseFolder(collectionIDValue);
     });
     return false;
}

function removeItemSubscription(dIDValue, tokenValue, dDocTitleValue, dDocNameValue, collectionIDValue)
{
  $.ucm.removeSubscription(dIDValue, tokenValue, dDocTitleValue, dDocNameValue, function (ucmResponse) {
      $('#notification').html('<img src="http://localhost/gadget/img/information-italic.png"/> You have unsubscribed to '+dDocTitleValue);
      $('#notification').fadeIn('slow').delay(4000).fadeOut('slow');
      browseFolder(collectionIDValue);
     });
     return false;
}

function browseFolder(collectionID) {

     // Clear the current document display, folder description and back link
     $('#searchResults').html('');
     $('#folderDescription').html('');
     $('#backLink').html('');

     // Get subscription items before getting the item list for a given folder
     $.ucm.getSubscriptions(function (ucmResponse) {

        // Grab subscriptions for the current user and place into array
        var subscribeResults = ucmResponse.ResultSets.SUBSCRIPTION_LIST.rows;
        var subscribeArray = [];

        for (var rowIndex in subscribeResults) {
          subscribeArray.push(subscribeResults[rowIndex][3]);
        }

     $.ucm.collectionDisplay(collectionID, function (ucmResponse) {
          var sessionToken = ucmResponse.LocalData.idcToken;

          // Render the results on page
          var resultHtml = "";
          var searchResults = ucmResponse.ResultSets.COLLECTIONS.rows;
          
          for (var rowIndex in searchResults) {
                // Build HTML for the folders
                resultHtml += '<li><a class="folder" href="#" onclick="return browseFolder('' + searchResults[rowIndex][0] + '')">' + searchResults[rowIndex][3] + '</a></li>';
          }

          // Get the docs based on the collectionID
          var docResults = ucmResponse.ResultSets.CONTENTS.rows;

          for (var rowIndex in docResults) {

            // Check to see if the row has a description
            if (docResults[rowIndex][11] != "")
            {
              itemDescription = '<div class="itemdesc">' + docResults[rowIndex][11] + '</div>';
            }
            else
            {
              itemDescription = '';
            }

            // Check if the item is already subscribed to - rowIndex must be same as what is placed into subscription array
            var activeSubscription = $.inArray(docResults[rowIndex][53], subscribeArray);

            // Start the build of the content item list HTML
            resultHtml += "<li>";

            // Item is not subscribed to
            if(activeSubscription == -1)
            {
                resultHtml += '<a href="#" class="subscribeicon" onclick="return setItemSubscription(''+docResults[rowIndex][0]+'', ''+sessionToken+'', ''+docResults[rowIndex][54]+'', ''+collectionID+'')"><img src="http://localhost/gadget/img/bell_off.png"/></a>';
            }
            // Item is currently subscribed
            else
            {
                resultHtml += '<a href="#" class="subscribeicon" onclick="return removeItemSubscription(''+docResults[rowIndex][0]+'', ''+sessionToken+'', ''+docResults[rowIndex][54]+'', ''+docResults[rowIndex][53]+'', ''+collectionID+'')"><img src="http://localhost/gadget/img/bell_on.png"/>';
            }

            // Add the link to the content item HTML
            resultHtml += '</a> <a class="' + docResults[rowIndex][77] + '" href="' + $.ucm.cgiPath + '?IdcService=GET_FILE&dID=' + docResults[rowIndex][0] + '&allowInterrupt=1">' + docResults[rowIndex][54] + '</a>' +  itemDescription + '</li>';
          }

          // Get the title for the folder that is currently being browsed          
          breadCrumbs = '<li><img src="http://localhost/gadget/img/blue-folder-horizontal-open.png"/>&nbsp;' + ucmResponse.LocalData.collectionName + '</li>';

          // Check to make sure that we are not at the root node and only show Previous Folder link if not
          if (ucmResponse.LocalData.dParentCollectionID == docLibRoot)
          {
            backLink = "<li><a href=\"#\" class=\"backlink\" onclick=\"return browseFolder('" + ucmResponse.LocalData.dParentCollectionID + "')\"><img src=\"http://localhost/gadget/img/arrow_left_16.png\" style=\"margin-right: 4px\"/> Previous Folder</a></li>";
          }
          else
          {
            backLink = '';
          }

          $('#folderDescription').html(ucmResponse.LocalData.xComments);

          $('#backLink').html(backLink);
          $("#breadCrumbs").html(breadCrumbs);
          $("#searchResults").html(resultHtml);

          // Resize the gadget as needed
          gadgets.window.adjustHeight();
       });
       return false;

     });
     return false;
}

jquery.oracle-ucm.1.0.js (modified)

The following lines have been added to the Oracle UCM JQuery plugin to add additional functionality required for our document library.

...

		// Get folders for a given ID
		collectionDisplay : function(collectionID, callback) {
			var collectionData = {
				IdcService: "COLLECTION_DISPLAY",
				hasCollectionID : "1",
				dCollectionID : collectionID,
				IsJson : "1"
			}
			$.ucm.executeService(collectionData, callback);
		},

		// Get subscriptions for the logged in user
		getSubscriptions : function(callback) {
			var collectionData = {
				IdcService: "SUBSCRIPTION_LIST",
				IsJson : "1"
			}
			$.ucm.executeService(collectionData, callback);
		},

		// Subscribe to a content item
		setSubscription : function(dIDValue, tokenValue, dDocTitleValue, callback) {
			var collectionData = {
				IdcService: "SUBSCRIBE",
				dID: dIDValue,
				idcToken : tokenValue,
				dDocTitle: dDocTitleValue,
				dSubscriptionEmail : '',
				dSubscriptionType: 'Basic',
				unsubscribeService: 'UNSUBSCRIBE',
				subscribeService: 'SUBSCRIBE'
			}
			$.ucm.executeService(collectionData, callback);
		},

		// Unsubscribe to a content item
		removeSubscription : function(dIDValue, tokenValue, dDocTitleValue, dDocNameValue, callback) {
			var collectionData = {
				IdcService: "UNSUBSCRIBE",
				dID: dIDValue,
				idcToken : tokenValue,
				dDocTitle: dDocTitleValue,
				dSubscriptionID : dDocNameValue,
				dSubscriptionEmail : '',
				dSubscriptionType: 'Basic',
				unsubscribeService: 'UNSUBSCRIBE',
				subscribeService: 'SUBSCRIBE'
			}
			$.ucm.executeService(collectionData, callback);
		},

...

style.css

The following plain old CSS styles the contents within our Gadget.

a:visited, a.folder:visited
{ 
	color: #0000FF;
}

#docLib
{
	font-size: .8em;
}

ul
{
     list-style-type: none;
	margin-left: -40px;
}

ul a 
{
	text-decoration: none;
}

ul#breadCrumbs li
{
	float: left;
	margin-bottom: 20px;
	font-weight: bold;
	font-size: 1.2em;
}

ul#breadCrumbs li img
{
	vertical-align: middle;
	margin-top: -3px;
}

ul#searchResults
{
	clear: both;
	margin-top: 30px;
}

ul#searchResults li
{
	margin-bottom: 5px;
	margin-left: 20px;
	border-top: 1px dotted #ddd;
	padding-top: 5px;
}

ul#searchResults li:first-child
{
	border-top: none;
}

div.itemdesc
{
	padding-left: 20px;
	margin-right: 30px;
}

a.backlink
{
	padding-left: 20px;
	margin-top: 12px;
	margin-bottom: 12px;
	float: left;
}

a.backlink img
{
	vertical-align: middle;
	margin-top: -3px;
}

a.folder
{
	background: url(http://localhost/gadget/img/blue-folder-horizontal.png) no-repeat;
	padding-left: 20px;
}

a.pdf
{
	background: url(http://localhost/gadget/img/doctypes/blue-document-pdf.png) no-repeat;
	padding-left: 20px;
}

a.doc
{
	background: url(http://localhost/gadget/img/doctypes/blue-document-word.png) no-repeat;
	padding-left: 20px;
}

a.txt
{
	background: url(http://localhost/gadget/img/doctypes/blue-document.png) no-repeat;
	padding-left: 20px;
}

a.ppt
{
	background: url(http://localhost/gadget/img/doctypes/blue-document-powerpoint.png) no-repeat;
	padding-left: 20px;
}

a.mp3
{
	background: url(http://localhost/gadget/img/doctypes/blue-document-music.png) no-repeat;
	padding-left: 20px;
}

a.mp4, a.mov, a.mpg, a.mpeg, a.avi, a.divx, a.wmv, a.flv
{
	background: url(http://localhost/gadget/img/doctypes/blue-document-film.png) no-repeat;
	padding-left: 20px;
}

a.jpg, a.gif, a.png, a.jpeg
{
	background: url(http://localhost/gadget/img/doctypes/blue-document-image.png) no-repeat;
	padding-left: 20px;
}

a.html, a.htm, a.css, a.js
{
	background: url(http://localhost/gadget/img/doctypes/document-globe.png) no-repeat;
	padding-left: 20px;
}

a.zip, a.rar, a.7z
{
	background: url(http://localhost/gadget/img/doctypes/document-zipper.png) no-repeat;
	padding-left: 20px;
}

a.subscribeicon
{
	float: right;
}

#folderDescription
{
	clear: both;
}

div#notification img
{
	vertical-align: middle;
	margin-top: -3px;
}

#notification
{
	display: none;
    padding: 10px;
    text-align: center;
    margin-bottom: -15px;
    margin-top: 10px;
    border-radius: 5px;
    box-shadow: 
        0 1px 1px rgba(0,0,0,0.1), 
        inset 0 1px 0 rgba(255,255,255,0.6);  
    cursor: default;
	border: 1px solid #69C0CA;
	color: #3D8D98;
	background-color: #8ACED6;
	background-image: -webkit-gradient(linear,50% 0%,50% 100%,color-stop(0%,#99E2EB),color-stop(100%,#79C6CD));
	background-image: -webkit-linear-gradient(#99E2EB,#79C6CD);
	background-image: -moz-linear-gradient(#99E2EB,#79C6CD);
	background-image: -o-linear-gradient(#99E2EB,#79C6CD);
	background-image: linear-gradient(#99E2EB,#79C6CD);
	-moz-text-shadow: 0px 1px rgba(255,255,255,0.2);
	text-shadow: 0px 1px rgba(255, 255, 255, 0.2);
}

Special Thanks to Third Party Components

  • Icons – The excellent set of icons used within the Gadget have been designed by Yusuke Kamiyamane
  • JQuery UCM Plugin – A great piece of work supplied by Bex Huff

Gadget Code Download
Download a copy of the WebCenter Sites Document Library Gadget for WebCenter Content

0 comments

Trackbacks

  1. [...] E2.0 Workbench Podcast 8 – WebCenter Sites Document Library Gadget for WebCenter Content [...]