Archive

Archive for the ‘OracleUCM’ Category

Executing a service from a ServiceHandler

Executing a service from a custom ServiceHandler is very easy.

ServiceHandlers always extend the ServiceHandler class. So within a custom ServiceHandler Java class, you have access to the Service object using the variable m_service. From this Service object, you can get a ServiceRequestImplementor object: m_service.getRequestImplementor(). With the ServiceRequestImplementor object, you can execute services with a few different methods (executeSubServiceCode, executeSafeServiceInNewContext, executeServiceEx, executeServiceDirect, executeServiceTopLevelSimple). I typically tend to use the executeServiceTopLevelSimple method.

The executeServiceTopLevelSimple method signature looks like the following: public void executeServiceTopLevelSimple(DataBinder binder, String serviceName, UserData userData) {}.

Below is a sample method I use to wrap the executeServiceTopLevelSimple method:

/**
* This method executes a service.
* @throws ServiceException
*/
private void executeService(final String DataBinder serviceBinder, final String serviceName) throws ServiceException {
     traceVerbose("Start executeService");
     try {
          trace("Calling service " + serviceName + ": " + serviceBinder.getLocalData().toString());           // Execute service
          m_service.getRequestImplementor().executeServiceTopLevelSimple(serviceBinder, serviceName, m_service.getUserData());
          trace("Finished calling service");
     } catch (final DataException e) {
          trace("Something went wrong executing service " + serviceName);
          e.printStackTrace(System.out);
          throw new ServiceException("Something went wrong executing service " + serviceName, e);
     } finally {
          traceVerbose("End executeService");
     }
}

Categories: OracleUCM

UCM Service Handlers and Java Filters

April 25, 2012 4 comments

Quite often we need to customize our Content Servers using some specific Java code. Usually we accomplish this by creating a custom component and utilizing Service Handlers or Java Filters (or both). Unfortunately, sometimes it might be hard to decide when you should use one over the other. So, let’s dig deeper into both and understand how they work.

Service Handlers

Service Handlers are a mechanism that allows us to add new services or modify existing services by using  Java.

Multiple Service Handlers could contain the same Java method, but the Content Server would only execute the one that is contained within the Service Handler that has the lowest Search Order. The Search Order is defined during the creation of the configuration table. The configuration table is a table that exposes your custom code to the Content Server.

Service Handlers could be chained/put-in-place to be executed:

  • Instead of new service actions:
    • Create a Service Handler with a lower Search Order.
  • Before existing service actions:
    • Create a Service Handler with a lower Search Order,
    • Make a call to the same method after your custom java code:

… custom java code …
m_service.doCodeEx(“<sameMethodName>”, this);

  • After existing service actions:
    • Create a Service Handler with a lower Search Order,
    • Make a call to the same method before your custom java code:

m_service.doCodeEx(“<sameMethodName>”, this);
… custom java code …

Java Filters

Java Filters is an event subscription mechanism through which developers can subscribe to the various events that occur within the Content Server and execute custom Java code.

During the operation of Content Server, there are various events that constantly being fired up. The developer can subscribe to these events and alter the behavior of Content Server. When the desired event would occur the control would be passed to the subscribers of the event and the custom java code could be executed. The Content Server then expects a result of the custom code execution.  The results are pretty straight forward:

  • CONTINUE – informs Content Server that everything went accordingly;
  • FINISHED – informs Content Server to stop running any other filters with the similar name;
  • ABORT – informs Content Server to halt execution of the current process.

When to Use Service Handlers versus Java Filters

Java Filters is the least-intrusive and easiest way to customize the behavior of Content Server with Java, whereas Service Handlers require more thorough understanding of the actions that need to be customized.

Java Filters is the preferred and most commonly used way of customization, but it is limited to the existing set of filters within the Content Server. So, if there is no defined filter to which you can subscribe, then you can create your own custom component that uses Service Handlers to customize the behavior.

WebCenter Patch Set 5 (11.1.1.6)

February 24, 2012 2 comments

Patch Set 5 Released!

Oracle recently released Patch Set 5 for Fusion Middleware.  This includes my primary area of interest WebCenter Content.  This technically started rolling out to Oracle eDelivery and then OTN sometime on February 22nd, 2012.  I was a little late to the blogging game so instead of “announcing” the release let’s do a quick link roundup about this release from some of my favorite community members!  While this list isn’t comprehensive, it does come from a lot of people that have taught me a lot down the years so add them to your blog roll if you haven’t already.

Link Round-Up

  1. One of my friends, John Sim might have been the first to point out the release availability.  He also has a great rundown of the products included in the release
  2. Ryan has some handy links and also points out that the documentation has been updated
  3. Bex runs down the patch sets you may need to navigate if you plan to upgrade your now out-of-date UCM instance using his recently setup shortcuts for Oracle Patches
  4. The Official WebCenter Content Blog lists a host of new features around User Engagement, Content Enabling Applications and Infrastructure
  5. Webmonkey really elaborates on the new Folders Component (This one is sometimes referred to as Framework Folders while the older one was called Folders_g)
  6. And finally, on a separate note, but still related to PS5 note, Shay has a really nifty video showing some of the iPad related ADF stuff that’s new in JDeveloper/ADF for PS5
  7. Yannick Ongena has a good review with information on the new RIDC connection in JDeveloper
  8. The folks at AMIS also have a good link roundup

Ok, so these guys are a hard act to follow.  What can I say/do that they didn’t cover?  Only thing I can think about are some screenshots!  Once we got all the final bits downloaded (caution: that part takes a while if you want it all) we got a VM put together pronto and here’s some of the things we thought were interesting.

JDeveloper

First up, JDeveloper PS5 with Site Studio for External Applications (SSXA).  Notice in the shot below there is a dedicated RIDC connection you can now add to projects.  This could be handy for all kinds of things!

Also in JDevleoper PS5 (and I think it acts this way in the 11gR2 releases as well) you can now set the default built-in WebLogic Domain password to something you know and will remember when you first start the integrated server.  It used to be that the default password for the integrated server was weblogic1 and this was a major issue for those first setting out to use the product.  New users would be very frustrated trying to find out why they couldn’t login as weblogic user to test that first site they tried to build.  Love this!

Branding

Having booted up the integrated WebLogic server we see our contribution mode banner is now updated with the new product branding (WebCenter Content).  This is also updated in the content server user interface as well.

Out-of-the-box Assets

During the setup of our sandbox we obviously enabled Site Studio and SSXA.  It appears as though those components now ship with all the sample Custom Element Forms right from the start.  No more loading up the Site Studio Samples site from OTN just to grab these Custom Element Forms!  This made my day.

Folders and Desktop Integration

Of all the updates the Framework Folders and Desktop Integration Suite (DIS) changes are my favorite so far.  As mentioned before, Webmonkey runs down a lot of the features for us, but let’s see what it looks like.  One thing I want to point out though is that DIS works with Framework Folders and that Framework Folders overcomes a lot of the performance problems people used to experience with “Folders” in the past (previously called Contribution Folders).  This bears repeating because it opens a lot of doors and numerous customers have been waiting on these two features to converge.

Once you have DIS installed you’ll see an icon on the desktop (similar to how DIS has always worked) and you’ll double click that to open Windows Explorer which now has a “WebCenter Content Servers” node.  Clicking that node will show you the list of servers you have configured for your desktop (initially it will be blank, right click the blank white area and select Add Server).

If you have Rules and Profiles (custom check-in screens, etc.) enabled you’ll be able to use them in a drag and drop fashion right from your desktop by opening the “New Check-In” node.

Categories: OracleUCM, OracleWCM, WebLogic

Java Filter – Retrieving a File

January 13, 2012 Comments off

When within a Java Filter, one may need to get the file for a particular revision of a content item. There is a class intradoc.server.DirectoryLocator that is very helpful. From this class, we can obtain the FileStoreProvider.

FileStoreProvider fileStoreProvider = DirectoryLocator.m_fileStore;

DirectoryLocator has several static methods that can get paths within the filestore to places such as weblayout directory, the vault directory, the temporary directory, the provider directory, etc. It can provide you a String path to almost any directory you want.

IdcFileDescriptor is a class that can provide the pathing to files. From a FileStoreProvider, we can create an IdcFileDescriptor.

IdcFileDescriptor idcFileDescriptor = fileStoreProvider.createDescriptor(binder, null, context);

The first parameter is a DataBinder. This is where the paremeters that are needed to build the path to the file are set. The second parameter is not needed and can be set to null. The third parameter is the ExecutionContext.

The required parameters that need to be in the DataBinder are “RenditionId”, “dDocName”, “dDocType”, “dRevLabel”, “dSecurityGroup”, “dDocAccount”, “dID”, and “dExtension”. RenditionId should be set to “primaryFile” for the primary file and “web” for the alternateFile.

Fortunately, a lot of the time most of these required parameters are already in your the original DataBinder. If you are missing some parameters you should be able to make a service call to DOC_INFO to get a ResultSet which contains all of the necessary parameters. You can then dump these parameters into a new DataBinder and feed it to the createDescriptor method call.

The file descriptor that comes back will give you something like this:

idcFileDescriptor: intradoc.filestore.BasicIdcFileDescriptor{dWebExtension=xml, dID=80, RenditionId.path=primaryFile, dStatus=RELEASED, StorageClass=vault, RenditionId=primaryFile, dSecurityGroup=Public, dDocType=Web, dOriginalName=jh000035.xml, dExtension=xml, uniqueId=c:/oracle/ucm/server/vault/web/80.xml, fileNamePrefix=null, dReleaseState=Y, dDocAccount=, path=c:/oracle/ucm/server/vault/web/80.xml, dDocName=JH000035, dRevLabel=27, dRenditionId=primaryFile}

To get the path to the file, do the following:

fileStoreProvider.forceToFilesystemPath(idcFileDescriptor, null, context);
String filePath = fileStoreProvider.getFilesystemPath(idcFileDescriptor, context);

You can of course turn this file path into a Java object by using:

File myFile = new File(filePath);
Categories: OracleUCM

UCM Content Rule Side Effects

January 11, 2012 Comments off

Content Profiles and Content Rules provide a mechanism for customizing a variety of pages within UCM.  Generally just referred to as Profiles, you can conditionally configure/customize the check-in, update, search and information pages as relates to the context of the content and user intent.

One of the often used by seldom explained parts of Profiles is something called Side Effects.  You will find the Side Effects tab on the Add/Edit Activation Conditions screen and it primarily allows you to accomplish two things:

  1. Add name/value pairs as IdocScript that will then get pushed to local data if the activation condition is true.
  2. Add custom IdocScript to a rule that is only evaluated if the activation condition is true and this can include logic like if and else statements or loops.  Basically all IdocScript is possible here (though all of it may or may not be useful).  As such, once a rule is activated, you can include logic, use includes from a component, etc.

What specifically might one use Side Effects for?  For a simple example let’s pretend we have a profile for some scanned content.  This content is checked in as an image based PDF (no OCR, no searchable text).  Your profile should not display the full text search box in this case.  There is no full text to be searched.  You can accomplish this with a Side Effect in your profile:

<$SearchEngineName="DATABASE.METADATA"$>

Let’s take another example.  Perhaps on the check-in page you want the alternate file field to be removed.  You could go into config.cfg and add this setting, but it would be universal.  More likely you want this to happen in a specific, conditional context.  Try adding this as a Side Effect to your rule:

<$suppressAlternateFile="1"$>

Want more examples of Profiles, Rules and Side Effects? See below:

  1. Using Rules & Profiles To Drive Custom Pages
  2. Suppressing Alternate File
  3. Thumbnail search in Content Server
  4. Thumbnail search in Content Server #2
  5. Hiding the Primary File field in UCM
  6. Metadata field tool tips

AlphabetizeMenus – 11g

September 29, 2011 1 comment

We recently updated our AlphabetizeMenus component to work with 11g. Do not worry, it is still backwards compatible with 10g. You can grab the newest version build_5_20110913 here. Sometimes a picture is worth a thousand words, so I will demonstrate with some before and after screenshots. After the screenshots will be an in-depth code review of what all is being done to accomplish menu sorting on both 10g and 11g.

Trays – Before (unsorted)

Trays – After (sorted)

Top Menus – Before (unsorted)

Top Menus – After (sorted)

The first thing we do in this component is hook into the dynamichtml include for custom_finish_layout_init. We want to first call all other includes that might be using this hook by calling super.custom_finish_layout_init. Almost any time you want to add something to a dynamichtml include, the first thing you should do is call super on that include.

<@dynamichtml custom_finish_layout_init@>
  <$include super.custom_finish_layout_init$>

Next, we want to determine what version of UCM this component is installed on. There is an Idoc Script variable, ProductVersion, in 11g  that will start with 11 if the UCM instance is 11g. If it is, we call a custom include for 11g. Otherwise, we call a custom include for 10g.

<$if ProductVersion like "11*"$>
  <$include alphabetize_menus_11g$>
<$else$>
  <$include alphabetize_menus_10g$>
<$endif$>

And here is all that code put together.

<@dynamichtml custom_finish_layout_init@>
  <$include super.custom_finish_layout_init$>
  <$if isTrue(#env.EnableMenuSorting)$>
    <$if ProductVersion like "11*"$>
      <$include alphabetize_menus_11g$>
    <$else$>
      <$include alphabetize_menus_10g$>
    <$endif$>
  <$endif$>
<@end@>

10g Menus

We first create a new array. Then we get the children of the node that was passed in.

var sortData = new Array();
var children = navBuilder.getNodeById(menuID).childNodes;

Next, we want to loop through the children. If we find children of children (I suppose you would call these grandchildren), then we recursively call the sortMenu function. After we have finished dealing with any children, we build a JSON style array object containing the node object (what object type that may be) and the text label.

for (var i = 0; i < children.length; i++) {
  if (children[i].childNodes.length > 0) {
    this.sortMenu(children[i].getAttribute("id"));
  }

  sortData[sortData.length] = {
    id: children[i].getAttribute("id"),
    label: children[i].getAttribute("label")
  };
}

Next, we need to sort the array of items on the current level and we do this by defining our own JavaScript sort method.

sortData.sort (
  function (a, b) {
    if (a.label < b.label)
      return -1;
    if (a.label > b.label)
      return 1;
    return 0; [[% a == b %]]
  }
);

After our data (for the current level) is sorted, we loop over the sorted array and add/remove the items to get them in the correctly alphabetized order.

for (var i = 0; i < sortData.length; i++) {
  navBuilder.moveItemInto(menuID, sortData[i].id, false);
  if (its.safari)	{
    navBuilder.deleteItem(sortData[i].id);
  }
}

The last step is to call our menuSortingMachine.sortMenu function. Most people choose to pass in “NAVTREE” which will sort all the menus (except the menubar level items such as My Content Server, Adminsitration, Browse Content, etc.). However, you can pass in a specific “ADMINISTRATION” and that will sort only that menu and its children.

menuSortingMachine.sortMenu("NAVTREE");

And finally all the 10g  Trays and Top Menus layout sorting code together.

var menuSortingMachine = {
  sortMenu:function(menuID) {

    var sortData = new Array();

    var children = navBuilder.getNodeById(menuID).childNodes;

    for (var i = 0; i < children.length; i++) {
      if (children[i].childNodes.length > 0) {
	this.sortMenu(children[i].getAttribute("id"));
      }

      sortData[sortData.length] = {
	id: children[i].getAttribute("id"),
	label: children[i].getAttribute("label")
      };
    }

    sortData.sort (
      function (a, b) {
	if (a.label < b.label)
	  return -1;
	if (a.label > b.label)
	  return 1;
	return 0; [[% a == b %]]
      }
    );

    for (var i = 0; i < sortData.length; i++) {
      navBuilder.moveItemInto(menuID, sortData[i].id, false);
      if (its.safari)	{
	navBuilder.deleteItem(sortData[i].id);
      }
    }
  }
};
[[% menuSortingMachine.sortMenu("ADMINISTRATION"); %]]
menuSortingMachine.sortMenu("NAVTREE");

11g Menus

The navBuilder object that we used in 10g should be considered deprecated in 11g. Instead, we have new objects we can use manipulate menus such as: YAHOO.widget.MenuManagerYAHOO.widget.MenuYAHOO.widget.MenuItemYAHOO.widget.TreeViewYAHOO.widget.Node

oMenuBarA is a YAHOO.widget.MenuBar for menuA. This is the menu that contains items such(such as “Search” and “New Check In”).
oMenuBarB is a YAHOO.widget.MenuBar for menuB.
oTreeViewA is a YAHOO.idc.widget.TrayTreeView for the side tray (in Trays layout). Many of the methods from YAHOO.widget.TreeView can be used for the oTreeViewA object.

For more information on the new menu objects in 11g, seeKyle Hatlestad’s post Modifying Navigation Menus in UCM 11g.

11g Top Menus

The first thing we want to do is get the submenus or what we called children in 10g.

var subMenus = menu.getSubmenus();

Next. we want to loop through the submenus and recursively call the sortMenu function.

for (var i = 0; i < subMenus.length; i++) {
  menuSortingMachine.sortMenu(subMenus[i], new Boolean(true));
}

Next, we always add a check to make sure we want to sort this level and check to make sure the menu parent exists.

if (sortLevel == true || typeof menu.parent != "undefined") {}

Next, we get an array of the current level’s children menuItems and we also create a new array to hold our sorted data.

var items = menu.getItems();
var sortData = new Array();

Next, we want to loop through the menuItems and build a JSON style array object containing the MenuItem object and the text label.

for (var j = 0; j < items.length; j++) {
  sortData[j] = {
    element: items[j].element,
    label: items[j].cfg.getProperty("text")
  };
}

Like in 10g, we need to sort the array of items on the current level and we do this by defining our own JavaScript sort method.

sortData.sort (
  function (a, b) {
    if (a.label < b.label)
      return -1;
    if (a.label > b.label)
      return 1;
    return 0; [[% a == b %]]
  }
);

Similar to 10g, after our data (for the current level) is sorted, we loop over the sorted array and add the items to get them in the correctly alphabetized order.

for (var k = 0; k < sortData.length; k++) {
  menu.element.appendChild(sortData[k].element);
}

The last step is to call the menuSortingMachine.sortMenu function. We first check to make sure the variable (oMenuBarA or oMenuBarB has a value) and if so, we can use that menu to start sorting from. If we want the menubar level items for oMenuBarA or oMenuBarB to get sorted alphabetically, we can pass in true boolean value.

if (typeof oMenuBarA != "undefined" && oMenuBarA != undefined) {
  menuSortingMachine.sortMenu(oMenuBarA, new Boolean(false));
}
if (typeof oMenuBarB != "undefined" && oMenuBarB != undefined) {
  menuSortingMachine.sortMenu(oMenuBarB, new Boolean(false));
  [[% menuSortingMachine.sortMenu(YAHOO.widget.MenuManager.getMenu("MENU_B_ADMINISTRATION"), new Boolean(true)) %]];
}

And finally all the Top Menus layout sorting code together.

var menuSortingMachine = {
  sortMenu:function(menu, sortLevel) {

    var subMenus = menu.getSubmenus();

    for (var i = 0; i < subMenus.length; i++) {
      menuSortingMachine.sortMenu(subMenus[i], new Boolean(true));
    }

    if (sortLevel == true || typeof menu.parent != "undefined") {
      var items = menu.getItems();

      var sortData = new Array();

      for (var j = 0; j < items.length; j++) {
        sortData[j] = {
          element: items[j].element,
          label: items[j].cfg.getProperty("text")
        };
      }

      sortData.sort (
        function (a, b) {
          if (a.label < b.label) {
            return -1;
          }
          else if (a.label > b.label) {
            return 1;
          }
          return 0; [[% a == b %]]
        }
      );

      for (var k = 0; k < sortData.length; k++) {
        menu.element.appendChild(sortData[k].element);
      }
    }
  }
};
if (typeof oMenuBarA != "undefined" && oMenuBarA != undefined) {
  menuSortingMachine.sortMenu(oMenuBarA, new Boolean(false));
}
if (typeof oMenuBarB != "undefined" && oMenuBarB != undefined) {
  menuSortingMachine.sortMenu(oMenuBarB, new Boolean(false));
  [[% menuSortingMachine.sortMenu(YAHOO.widget.MenuManager.getMenu("MENU_B_ADMINISTRATION"), new Boolean(true)) %]];
}

11g Trays Menus

For Trays layout, we can do very similar code to that for 10g, except we have some different objects and method calls. We first create a new array. Then we get the children of the node that was passed in.

var sortData = new Array();
var children = node.children;

Similar to 10g, we want to loop through the children and recursively call the sortMenu function. Then we build a JSON style array object containing the YAHOO.widget.Node object and the text label.

for (var i = 0; i < children.length; i++) {
  if (children[i].children.length > 0) {
    this.sortMenu(children[i]);
  }

  sortData[sortData.length] = {
    node: children[i],
    label: children[i].label
  };
}

Like in 10g, we need to sort the array of items on the current level and we do this by defining our own JavaScript sort method.

sortData.sort (
  function (a, b) {
    if (a.label < b.label)
      return -1;
    if (a.label > b.label)
      return 1;
    return 0; [[% a == b %]]
  }
);

Similar to 10g, after our data (for the current level) is sorted, we loop over the sorted array and add/remove the items to get them in the correctly alphabetized order.

for (var i = 0; i < sortData.length; i++) {
  oTreeViewA.popNode(sortData[i].node);
  node.appendChild(sortData[i].node);
}

The last step is to call the menuSortingMachine.sortMenu function. We first check to make sure the variable (oTreeViewA has a value) and then we can use the getRoot() method on the oTreeViewA variable to get the root node to start sorting from.

if (typeof oTreeViewA != "undefined" && oTreeViewA != undefined) {
  menuSortingMachineTrays.sortMenu(oTreeViewA.getRoot());
}

And finally all the Trays layout sorting code together.

var menuSortingMachineTrays = {
  sortMenu:function(node) {

    var sortData = new Array();

    var children = node.children;

    for (var i = 0; i < children.length; i++) {
      if (children[i].children.length > 0) {
	this.sortMenu(children[i]);
      }

      sortData[sortData.length] = {
	node: children[i],
	label: children[i].label
      };
    }

    sortData.sort (
      function (a, b) {
	if (a.label < b.label)
	  return -1;
	if (a.label > b.label)
	  return 1;
	return 0; [[% a == b %]]
	}
      );

    for (var i = 0; i < sortData.length; i++) {
      oTreeViewA.popNode(sortData[i].node);
      node.appendChild(sortData[i].node);
    }
  }
};
if (typeof oTreeViewA != "undefined" && oTreeViewA != undefined) {
  menuSortingMachineTrays.sortMenu(oTreeViewA.getRoot());
}

Note: Any code you see between [[% %]] is an Idoc Script comment.

Categories: OracleUCM

11g Publish Static Files Using Custom Component

September 28, 2011 Comments off

Recently on the forums it was asked how to publish static files to the Weblayout folder using a custom component in 11g. We covered this earlier for 10g. Publishing files is slightly different for 11g. Follow the steps below to publish static files in 11g.

1. In the custom component directory, create a folder “publish”. Files that are placed within this folder will become relative to the weblayout folder.

2. In Component Wizard -> Build -> Build Settings, add a “Component Extra” entry type and link to the above path (customComponentName/publish/).

3. In Component Wizard, under the Resource Definition tab, click the “Add” button to add a new resource.

4. Check the box for “Resource – Static Table (HTML Format)”. Set the “File name” to something such as “resources/customComponent_published_static_files.htm” so that you can easily identify what this resource definition is by looking at the file name. Set the “Load Order” to “1000″. Click “Next”.

5. Set the “Table Name” to something such as “CustomComponent_PublishedStaticFiles”. Make sure the “Merge To” box is checked and set it to “PublishedStaticFiles”. Click “Finish”.

6. Edit the file that was just created with your favorite text editor. You want the second <tr> to contain something like the following:

<td>publish/resources</td>
<td>resources</td>
<td>resources:sitestudio</td>
<td>1000</td>
<td><$doPublish = 1$></td>
<td>1</td>
  1. The first <td> represents the source folder to publish
  2. The second represents the path, relative to weblayout, to place the files
  3. The third represents the “class” that controls the publishing and processing of the content
  4. The fourth is the loadOrder and should be set to 1000 (which is the highest available)
  5. The fifth is doPublish.  This is a string of iDocScript that can be used to determine whether or not this set of files should be published.  You can do fancy things like construct time of day, week or month to conduct publishing.
  6. The sixth (canDeleteDir) should be set to 1.

That is it! Build your component and try it out.

Note: If you have problems with the files publishing (for example they should publish on start-up) you can manually invoke this action by invoking Administration -> Admin Actions – > Weblayout Publishing -> Publish static, string and dynamic files.  This will likely take a few minutes to complete.

Categories: OracleUCM

Customizing Workflow Forms

September 13, 2011 Comments off

Ever wanted to create a custom form with added metadata inside a workflow review? This post will show you how.  This topic has come up many times on the Oracle forums. Customizing the workflow form is not really that difficult. Below are the steps you will need to know in order to customize the workflow form.

1. Create a custom component called MyWorkflowComponent

2. Add a template which overrides WORKFLOW_REVIEW_FRAMES

3. Edit this template. Insert the following in the HTML head after this line <$include std_html_head_declarations$>


<$if dWfName like "MyWorkflow" and dWfStepName like "Data.Input"$>

   <$include my_workflow_custom_javascript$>

<$endif$>

This code checks the workflow name and step to see if they match what you want. This way you can customize the workflow form for different workflows.

Search for this line within the WORKFLOW_REVIEW_FRAMES template: <$include workflow_doc_action_forms$> There will be a </table> </td> right before this. Insert the following code before this this </table>:


<$if dWfName like "MyWorkflow" and dWfStepName like "Data.Input"$>

   <$include my_workflow_custom_forms$>

<$endif$>

Again we check to make sure this is the Workflow we want to customize. If so, we insert a custom include that we will be creating.

4. Within your component, create a Dynamic HTML resource file.

5. Edit this resource file.

Create a dynamic include for anything that should go within the HTML head (this example includes jQuery and jQueryUI.

<@dynamichtml my_workflow_custom_javascript@>
<script src="<$HttpRelativeWebRoot$>resources/CSXWorkflowEnhancements/jquery-1.4.4.min.js"></script>
<script src="<$HttpRelativeWebRoot$>resources/CSXWorkflowEnhancements/jquery-ui-1.8.5.custom.min.js"></script>
<script type="text/javascript">
$(function(){
   $("a[href^='javascript:document.Approve_<$dID$>']").click(function() {
      $("input[name='xEmployeeID']").val($("input[id='xEmployeeID']").val());
 [[% Submit my form %]]

 $("#frm_medical").submit();

 return false;    });

});

<@end@> 

Notice the use of HttpRelativeWebRoot. When you use this, you will need to put these files within your weblayout directory. Use this blog post as a reference.

Now we will create the actual form that shows up below or Approve / Reject links.

<@dynamichtml my_workflow_custom_forms@>

<tr>

 <td style="padding-top:10px">

 <form id="frm_employee_info" method="POST" action="javascript:document.Approve_<$dID$>.submit();">

 <p><label for="xEmployeeID">Employee ID</label><input type="text" id="xEmployeeID" /></p>

 </form>

 </td>

</tr>

<@end@>

Notice the form POST action. This is where we hook into the Approve link. When someone clicks the Approve link, our form (frm_employee_info) will get submitted first. This is where we can do validation and if something is not correct we can return FALSE and stay on the page. For this blog post, we are simply going to copy the values from this form and put them in the hidden field (which we will create next) that actually gets submitted to Content Server. If our form submits fine, then we will submit the Content Server form.

Create a dynamic include that overrides workflow_doc_approve_special_parameters. Within this include, we need to place HTML input elements that will get populated from or forms above.

<@dynamichtml workflow_doc_approve_special_parameters@>

<$include super.workflow_doc_approve_special_parameters$>

 <input type="hidden" name="xEmployeeID" value="" />

<@end@>

It may look like a lot of code, but it is not too much work. Have any tips or tricks to making your own custom forms? Send them to tips@corecontentonly.com.

Categories: OracleUCM

One-Click Login into the Oracle Content Server 11g

July 25, 2011 Comments off

Are you tired of constantly needing to type in your Login information into the Oracle Content Server (11g) each time you try to open it in a new browser window?

No More - Oracle UCM 11g Generic Login Screen

We have a solution for you!

The main problem on the login page is that those two html input tags for the User Name and Password fields have the attribute autocomplete set to off.  This forces any browser to behave accordingly and not allow any auto-fill like functionality for these two fields. This gets annoying, especially if you do a lot of development and have to restart your content server every so often.

You can resolve this problem by creating a bookmark to the following address:

http://hostname:port/cs/login/j_security_check?j_character_encoding=UTF-8&j_username=your_username&j_password=your_password

Here is an example URL:

http://devucm11g:16200/cs/login/j_security_check?j_character_encoding=UTF-8&j_username=weblogic&j_password=welcome1

Where:

  • hostname = devucm11g
  • port = 16200
  • your_username = weblogic
  • your_password = welcome1

Now, each time you click such bookmark you would automatically be logged in and forwarded to your landing page on the Content Server.

Hint: you can choose to create a bookmark or set this as your homepage.

Note: it goes without saying that you should never do anything like this for any production environment.

Java Constants

June 29, 2011 Comments off

It is easy to misspell variable names in Java. Using Java static final Strings (perhaps better known as constants) are a great way to help avoid this problem. Here and here are two articles on the subject. Below is a sample class for some very commonly used metadata field names in UCM.

public class RCSConstants
{
public static final String dDocTitle = “dDocTitle”;
public static final String dDocName = “dDocName”;
public static final String dDocName_encoded = “dDocName_encoded”;
public static final String dFormat = “dFormat”;
public static final String dID = “dID”;
public static final String dDocType =”dDocType”;
public static final String dRevLabel = “dRevLabel”;
public static final String dOriginalName = “dOriginalName”;
public static final String dDocAuthor = “dDocAuthor”;
public static final String dSecurityGroup = “dSecurityGroup”;
}

These constants (strings) can now be used in other classes as follows:

RCSConstants.dDocTitle

If you are using an IDE (such as JDeveloper or Eclipse) it will only let you choose values that are spelled correctly (provided you spelled them correctly in your constants class).

Categories: OracleUCM
Follow

Get every new post delivered to your Inbox.