Part XV - What is RHCore?

Over the last year I've been blogging about various topics in OpenText Content Server development. In most of these blog posts I referred to a module called RHCore, which is the topic of this next post. So what exactly is RHCore?

RHCore is an Content Server application framework. It's written in OScript, installs like any other module, and provides a new and fresh way to develop applications with OpenText Content Server.

The module works by simplifying many of the complex development patterns commonly used in Content Server development. It lets the developer write beautiful and elegant OScript, which results in a module that can be developed in less time with fewer bugs.

# What motivated RHCore?

I've been developing with Content Server for over 14 years and often found myself writing the same code over and over again. It was this repetitiveness and realisation after working with other frameworks that much of this repetition could be abstracted away into something that's reusable and easier to work with. It was during a break in my contract work that I prototyped RHNode (an abstraction of DTree nodes) and was delighted with the results. The rest of the framework snowballed from there.

I'm using the module as a basis for a few applications that are now running on production systems. The results have spoken for themselves: The applications were built in less time, are more feature rich, and are easier to maintain than anything I have ever worked on before.

# What does RHCore do?

The last 14 blog posts have highlighted some of the key features of RHCore. In the remainder of this blog post I will summarise some of these features, throw in a bit more, and try to show how it fits together.

Let's start with the fundamental concept.

# An object-oriented programming approach

Much of RHCore is based on an object-oriented programming (OOP) approach. In simple terms, an "object" is a data structure that contains data and functions (or "methods") to operate on itself. The beauty of this design is that most of what you need is self contained and doesn't require referencing external functions in different places. RHCore introduces a number of classes (the "blueprint" for an object instance) to simplify common patterns and interaction with many Content Server data structures.

One such class is RHNode, which is used to interact with DTree nodes (it is analogous to the newly introduced CSNode, but more generic). An RHNode object contains the node data (e.g., DataID, User, Extendeddata, etc.), but also methods to fetch related information (DAPINODE, LLNode, user, parent, categories, etc.) and perform common tasks (e.g., copy, move, rename, etc.). Let's look at an example.

Say you have the DataID of a node and want to get the URL to open it. With the standard Content Server API this looks something like the following (skipping error handling for brevity):

DAPINODE node = DAPI.GetNodeByID(prgCtx.DapiSess(), DAPI.BY_DATAID, DataID)
Record nodeRec = $WebNode.WebNodeUtils.NodeToWebNode(node)
Object webNode = $WebNode.WebNodes.GetItem(nodeRec.SUBTYPE)
Object cmd = webNode.cmd('open')
String url = cmd.url(request, nodeRec)

That's a lot of code and requires calling functions in various locations to get to the required result. With RHCore we can do the same by creating an RHNode instance and calling the url() method:

Frame node = $RHCore.RHNode.New(prgCtx, DataID)
String url = node.url(request, 'open')

All the internal workings to generate the URL (getting the DAPINODE, WebNode representation, WebNode object, WebNodeCMD, etc.) is abstracted away. It's a simple example, but demonstrates how an OOP approach can simplify some otherwise complex code. This pattern of simplification is used throughout RHCore.

Have a look at Part I of this blog series for more information on the OOP approach.

# Table Schemas and Data Persistence

Creating a database table in Content Server is a tedious and manual process. The tables must be manually defined (accommodating variations among database flavours) and query, create, read, update, & delete operations require direct database calls. Writing inline SQL isn't a good idea (it leads to repetition of code and little reusability), and building an API for these operations is another manual and repetitive process.

RHCore simplifies this with its RHModel framework. The framework allows a model to be defined, which automatically generates the table schema and an API for interacting with the data (e.g., setters, getters, and methods for querying independent of the database type). This allows a schema and API to be generated in mere minutes in what used to take hours by conventional means. Best of all, no SQL needs to be written and the generated API provides a natural place for business logic that is tied to the model (e.g., send an e-mail whenever the "status" field is changed to "Completed").

Have a look at Part II of this blog series for more information on RHModel.

# Templates with RHTemplate

RHCore provides a new template language called RHTemplate that can be used as an alternative to WebLingo (plus many other uses). The template syntax is simple and provides tools to quickly build pages with the most common layouts. In particular, it contains functionality to:

  • page, filter, and sort your data (read more in Part V);
  • render your page in a style consistent with Content Server (without having to write extra CSS; read more in Part XI);
  • render interactive widgets (e.g., user picker, date picker, sort headers, paging controls, tabs, etc.) that "just work" without having to import or write any extra JavaScript or CSS (read about this in Part XII); and
  • traverse and resolve complex relationships (e.g., to get the display name of the owner on the parent node: {{ node.parent.user.displayname }}).

RHCore provides extensions to the standard request handlers (including WebNodeAction) to support RHTemplate, which makes the creation of requests with RHTemplate a simple task. As a bonus the extensions also abstract away guiComponents and pageManager so you don't have to deal with those either.

Have a look at Part III of this blog series for more information on RHTemplate.

# Form Lifecycle with RHForm

Writing a form in Content Server to present, capture, and persist a value is a tedious and repetitive process. It involves:

  • fetching the initial values and passing them to the WebLingo;
  • hardcoding the form in the WebLingo (including the initial values, layout, UI widgets, etc.);
  • writing a request handler to consume, validate, and persist the submitted values;
  • spending lots of time debugging and maintaining it; and
  • presenting a jarring error page if anything goes wrong (as in the following screenshot).

Little of this is reusable and doing something small like adding a new field requires modifying code in multiple locations.

RHCore introduces RHForm, which consolidates the definition, rendering, and validation of the form into a single object. This is quite useful since it makes it possible to programmatically control the behaviour of the form at runtime without having to write any logic in the WebLingo or RHTemplate.

RHForm also supports a validation pattern that displays errors inline, which the user can correct and resubmit:

This is far more user friendly than the Content Server error page that requires the use of the back button to recover.

Have a look at Part IV of this blog series for more information on RHForm.

# Generic Admin Configuration Pages

Many modules have a configuration page linked from the admin.index page. Configuration pages are often a set of form fields that allow preferences to be configured and persisted in the KINI table or opentext.ini file. Most configuration pages are built by hardcoding everything (although the lesser-known LLConfig OSpace is available to help here) and creating these pages often means a lot of copy and pasting.

RHCore introduces a framework for defining, rendering, and persisting a configuration page without having to write any HTML and a minimal amount of OScript. What used to take hours to write, debug, and maintain can now be done in minutes. The following is a screenshot of a sample admin page created with RHCore:

Have a look at Part VII of this blog series for more information on Generic Admin Configuration Pages.

# Override System

I always attempt to minimise the intrusiveness of my code by minimising the number of overrides of core functionality. However, overrides are sometimes unavoidable and it's unfortunate how often developers will override entire scripts or WebLingo files just to make a small change. The problem with this approach is that it's not forward compatible: If OpenText changes the original script or WebLingo in a patch or subsequent release then these changes will no longer be reflected in the override.

RHCore provides tools to minimise the impact of overrides by allowing the original script or Weblingo to be called from the override. This doesn't satisfy every override scenario, but when it does (and it often does) it provides forwards compatibility in the event the original script or WebLingo gets changed.

RHCore also provides a way to monkey patch a script at run time. Sometimes a script is available that does exactly what you want except for a hardcoded value or assumption in the middle of it. A monkey patch allows you to take that script at run time, modify it with a search and replace, and recompile it for execution. This isn't always failsafe, but until now has worked well and has prevented me from having to inherit hundreds of lines of code from somebody else.

# Other Utilities

RHCore does a lot more, and the following is a short overview to highlight some of the other features:

  • Categories and Attributes are notoriously difficult to develop with. RHCore provides an extended API to make this easier (i.e., no more traversing the fData structure; read about it in Part VI).
  • An add-on HTTPClient library is available (based on Apache HTTPClient), which provides a robust interface for making HTTP requests from Content Server.
  • Sending HTML e-mails to users or groups has been simplified to one line of code (read about it in Part XIII).
  • Enumerated types are available for some syntactic sugar (read about it in Part VIII).
  • Documents can be Base64 encoded in one line of code.
  • A messaging framework is available for one-time notifications (this will get its own blog post someday).
  • A framework is available to create custom function menus.
  • Custom Views can be augmented to contain dynamic content and logic (via RHTemplate).
  • Documentation for your project can be generated directly from the OScript source code (in the same spirit as javadoc).
  • A markdown processor is available to convert markdown into HTML.
  • Help pages for your module can be generated from markdown (no more copy and pasting the help page template).
  • Scripts and workflow event callbacks can be edited directly in the Web UI (read about it in Part XIV).
  • and much more...

# Wrapping Up

RHCore simplifies many areas in Content Server development. What I also find nice is that modules built on RHCore are easier to migrate to newer versions of Content Server since most of the upgrade logic is rooted in RHCore.

I say it again: RHCore simplifies development and makes it possible to build richer applications with fewer bugs in less time.

If you're interested in an evaluation, demonstration, or more information about RHCore then please get in touch! I also welcome comments below.