dmBridge 1.x User's Manual

About this manual

The dmBridge User's Manual is a "living document" that is constantly under revision. Comments are allowed on most pages. Missing information or information that needs clarification should be brought to our attention by posting a comment or contacting us directly.

To download the entire text of the manual in one long page, click "Printer-friendly version" below.

Getting help

If you have a question about anything in this manual - or anything related to dmBridge - feel free to post a comment on the relevant manual page or in the forums.


Note: Throughout this manual, folder paths are written with forward slashes (/), Unix-style. Windows users should interpret them as backslashes (\).

System Requirements

dmBridge is a client-server system, with the templating engine running on the client, and the core (HTTP API) running on the server. The core must run on the CONTENTdm® server, but the client can run on any server.

Self-hosted users will need to make sure they have all of the following on both the client and server. (Hosted users already have all of these and can skip to the next section.)

Additionally, the server requires the following:


This section applies only to first-time installation. If you are upgrading, see the Upgrading chapter instead.


dmBridge is a client-server system. The client is (or can be) the template engine, which drives one or more of your custom template sets. The server is the dmBridge core (HTTP API) component, which drives the template engine and the PHP API. The client can run on any server, including the CONTENTdm® server, but the server must run on the CONTENTdm® server. It's your decision on which server you want to the template engine to reside, but to keep the instructions simple, we'll put everything on the CONTENTdm® server for now. Moving it later will be as simple as moving the files and setting the new URL in the Control Panel. Extracting a downloaded dmBridge distribution will result in a single folder called something like dmbridge-x.x.x. Within it there are four folders:

The dmBridge core
Configuration data required by dmBridge (and possibly social data as well)
The template engine
The Control Panel (essentially a graphical front-end to dmBridge's main configuration file, data/config/config.xml)

Each folder is a separate component that requires a few simple installation steps, documented in the following subsections. But before we proceed, we want to move our downloaded files onto the web server:

  1. Rename the dmbridge-x.x.x folder to dm
  2. Move the dm folder into your CONTENTdm® web server's document root. (This is the same place where your current cdm4 and dmscripts folders reside.

Next, we are ready to set up our individual components.


Installation of the dmBridge core (HTTP API) involves two main steps: installation of the core itself, and installation of the configuration data.

Please read these steps very carefully.

  1. Move the dm/data folder somewhere where it is not web-accessible. This may be anywhere outside your web server's document root; or you may be able to lock down a directory with an Apache/IIS configuration directive. Failure to complete this step means that your database password and/or social data will be exposed to the public.
  2. Make sure the permissions on the dm/data folder are set so that the web server process has read/write access to all of the files and folders inside it.
  3. CONTENTdm® 4.x only; if running 5.x, skip to the next step. In Content4/docs/dmscripts/DMSystem.php, find the following line near the very top:

    $slash = getPath();

    And change it to (don't worry, this shouldn't adversely affect anything):

    // Modified for dmBridge (your name, date)
    $slash = $_SERVER['DOCUMENT_ROOT'] . '/';
    // $slash = getPath(); // commented out for dmBridge
  4. CONTENTdm® 5.x only; if running 4.x, skip to the next step. In Content5/docs/dmscripts/DMSystem.php, find the following around line 1811:

    print("<!-- $url //-->\n");

    And comment it out by appending two slashes to it:

    // print("<!-- $url //-->\n");

  5. Open api/config.php.
  6. Change CDM_ROOT to the absolute path to your CONTENTdm® PHP API folder (called dmscripts).
  7. Change DATA_ROOT to the absolute path to your data folder from step 1. (Locating it with a relative path ["../"] will not work.)

    If you are unsure of the absolute path, create an empty PHP script in the same folder, type the following code into it, and load it in a web browser:

    <?php die(dirname(__FILE__)); ?>

  8. This will show you the full path to your dmBridge core folder. Adjust it appropriately to point to your data folder instead. Make sure to change any backslashes to forward slashes.

  9. If necessary, change the other settings in the file. If not sure, skip to the next step.
  10. Navigate to the following URLs and verify that they output XML without any <dmException> elements showing up (you may have to view the page source to see it):
    • http://my_cdm_server/dm/api/?r=collections.xml
    • http://my_cdm_server/dm/api/?r=objects/uw.xml
    • http://my_cdm_server/dm/api/?r=objects/uw/5.xml

    (Change "/uw" in the URLs above to a different collection alias ("CISOROOT") if the Sample Collection is not live on your CONTENTdm® server.)

If everything appears OK, the installation was successful. This was the hardest component to install; the others will be easier.

Template engine

The template engine is a one-step installation process.

  1. Open dm/objects/config.xml. If necessary, change the <param name="api.url"> element to the URL of the dmBridge core that you set up in the previous section. (By default, http://localhost/dm/api/.)

The template engine is now installed, but it does not work yet. We need to configure some stuff in the Control Panel first, which we will get to soon.

Control Panel

Installing the Control Panel takes two steps:

  1. Open dm/ctrl/config.php and set CDM_ROOT and DATA_ROOT appropriately.
  2. Open dm/ctrl/config.php and set URL_OF_LOGIN_EXE to the full URL of your login.exe file. Normally this will just mean changing "my_cdm_server" to your web server's hostname.

Finally, try to log in by navigating to http://my_cdm_server/dm/ctrl/ and entering your CONTENTdm® username and password. (This is the same username and password you use to log into the admin module located at /cgi-bin/admin/start.exe on your server.)

Once you are in, you need to complete some initial setup:

  1. In the Basic Setup section, set all URLs appropriately.
  2. In the Collections -> Template Set Mapping section, click Change.
  3. If you do not have the Sample Collection (the UW Buildings collection with alias of "/uw") installed:
    1. Go to Template Sets -> View All
    2. Click Edit next to the Basic template set
    3. Choose a new default collection for it.

Finally, navigate to http://my_cdm_server/dm/objects/. You should see your default collection appear in the basic template set. At this point, you are done with the essential installation steps, but there are some other things you'll want to look at before deploying dmBridge in a production environment, which the remainder of this section will discuss.

Securing the Control Panel

By default, anyone on the Internet can get to the Control Panel's login page. If you only plan on using the Control Panel internally, you may want to block it off from the public Internet by restricting access to the dm/ctrl folder from outside your LAN (or your desktop PC, or whatever). The procedure for doing this varies depending on your web server.


Host-based access control is available with Apache's mod_authz_host. Create an .htaccess file in your dm/ctrl folder something like the following:

<Files "*">
Order deny,allow
Deny from All
Allow from
Allow from 100.x.x.

You can put this in your httpd.conf as well, but remember to change Files "*" to the location of your Control Panel folder.


Please post a comment if you are familiar with this.

Data store

If you wish to use the commenting, social tagging, or rating features, you will need to configure a data store on the server. dmBridge currently supports three different data stores:

SQLite 3 (default)
A simple file-based database library that is fast, reliable, and easy to configure. Recommended if you don't have access to a MySQL or PostgreSQL server. Requires the PHP PDO extension. Will not work over an NFS or CIFS share.
Requires the PHP PDO extension.
Requires the PHP PDO extension.

Configuring MySQL/PostgreSQL

The MySQL/PostgreSQL data store options are intended for institutions that already have a MySQL/PostgreSQL server set up and available for use, and have someone on staff who knows the basics of administering them. If this is not the case, you should use SQLite instead. Installation instructions for the MySQL/PostgreSQL databases themselves will not be provided here; we will assume they are already up and running at your site.

Configuring with PHP

MySQL and PostgreSQL require the PHP PDO extension for use with dmBridge. To verify that it is installed, run phpinfo():


And search for a "PDO" section. If you don't also see a "pdo_mysql" or "pdo_pgsql" section (depending on your database), you will need to install the appropriate PDO driver. Depending on your OS distribution, it may be available as a package, or you may have to install it from PECL:

# pecl install pdo_mysql
# pecl install pdo_pgsql

Please post a comment if you need assistance.

Creating the database

dmBridge does not have a tool for creating the database; you will have to create it yourself. You should not have to create the tables; see the next section.

Enabling in the Control Panel

The final step is to enter the database connection settings. Log in to the Control Panel, navigate to the Data Store section, select your database, and enter the connection settings.

If the database does not already have the required dmBridge tables, the Control Panel will attempt to create them. If this should happen to fail for some reason, you can manually run (and if necessary, tweak) one of the sample SQL scripts provided in the dm/ctrl/core/data/schema folder.

Configuring SQLite

Downloading and installing SQLite

Note: The OCLC hosted environment already has SQLite installed, so hosted users can skip this section.


From the SQLite download page, download the "" file and unzip it into your windows\system32 folder. Restart IIS.


SQLite may already be installed. Check your package manager or try find / -name 'libsqlite*' and see if anything turns up. If not, download the "" file from the SQLite download page, install it, and restart Apache. Please post a comment if you need assistance with this.


Please post a comment if you have any information about SQLite on Solaris.

Configuring with PHP

SQLite requires the PHP PDO extension. To verify that it is installed, run phpinfo():


And search for a "PDO" section. If you don't also see a "pdo_sqlite" section, you will need to install the PDO driver for SQLite:

# pecl install pdo_sqlite

Please post a comment if you need assistance.

Enabling in the Control Panel

The final step (which is the same across all platforms) is to enable the SQLite in the Control Panel. Log in to the Control Panel, navigate to the Data Store section, select SQLite 3 and enter the path to the database file.

Clean URLs

The HTTP API and Templating Engine both support URL rewriting, which is optional, but recommended. Check it out:

A cool URI befitting of the Mobots

Using the HTTP API as an example, some normal URLs might look like this:


With URL rewrite support enabled, these would become:


The latter are cleaner and more "taxonomically correct," if you will. They go further toward hiding your implementation details, which is a good thing. They become uniform resource identifiers rather than just lowly locators.

URL rewriting is easy to get working on both Apache and IIS. On Apache, it requires the mod_rewrite module to be enabled in httpd.conf. On IIS, there are a few different options, but one that we have minor experience with is ISAPI Rewrite. The free Lite version does not support .htaccess files, but it's easy enough to add global rules instead. Here is a sample set:

RewriteCond %{REQUEST_FILENAME} !-f [OR]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^/dm/objects(.*)$ /dm/objects/index.php?q=$1 [L,QSA]

This gets saved in C:\Program Files\Helicon\ISAPI_Rewrite3\httpd.conf. Naturally, you want to change /dm/objects to the path to your template engine. You can use the same rules in your Apache httpd.conf as well if you can't or don't want to use .htaccess files. (Keep reading.)

The dmBridge template engine includes a .htaccess file which the web server should read (if it is configured to do so; in Apache, see the AllowOverride directive in httpd.conf). This is all that is needed; just enable rewriting in dm/objects/config.xml and it will be enabled in all of your template sets. Do the same thing in dm/api/config.php as well and it will be enabled in the HTTP API, too.

If you are worried about the performance impact of enabling .htaccess files, they will typically add less than a few thousandths of a second to each page request, depending on the performance of the server and the depth of the folder in the filesystem.

Reference URL redirection

Reference URLs are persistent locators for CONTENTdm® objects that look like this:,87

Notice how the page that loads has a different URL than the reference URL. Accessing a reference URL redirects you to the CONTENTdm® template corresponding with that object. dmBridge, however, is going to use a different template, with a different URL. You will, therefore, want to selectively redirect your reference URLs depending on which templating system the object appears in. The way to do this is by installing the provided replacement reference URL routing script.

The default reference URL routing script lives in /u/index.php (relative to your CONTENTdm® server's document root). The replacement script lives in the dm/api/u/ folder. Follow the directions below to hook it up:

  1. Make a backup of /u/index.php
  2. Copy dm/api/u/index.php to /u/index.php
  3. Open /u/index.php and edit DMAPI_ROOT, if necessary
  4. Set up your redirects in the "Reference URLs" section of the Control Panel


The upgrade process is similar to the installation process, with the exception of a few steps that do not need to be carried out again.


Before doing anything else:


  1. Make a backup of your entire dmBridge installation (located, by default, in the /dm folder relative to your web server's document root).
  2. Also make a backup of your supplementary dmBridge data (located, by default, in /dm/data, but hopefully no longer located there because you moved it someplace safer).
  3. Finally, if you are storing social data in an external database (such as MySQL), make a backup of the database.


Next, begin the upgrade process:


Move your old dmBridge folder (by default, "/dm") out of the way.

Unzip the distribution.


  1. Move the resulting dmbridge-x.x.x folder to the location of your previous "/dm" folder.
  2. Go through the Installation section, skipping any steps that only needed to be completed once.


Template Engine


The dmBridge templating engine attempts to strike a balance between ease of use, advanced customization potential, and upgrade-safety (meaning that underlying implementation changes in either dmBridge or CONTENTdm® won't break your page templates). The macro-scale HTML layout is left entirely up to the designer, while some smaller bits - rendered by the Draw methods - return HTML in a fixed structure that can be styled, but not customized. This is the price to pay for the ease of being able to use "one-liners" to call up sophisticated functionality. The default functionality of any of the Draw methods can be overridden by extensions, but this is generally a job for a programmer.

In practice, even without custom extensions, the template development process is a whole lot easier and more flexible than that for the CONTENTdm® templates. (This was actually the #1 design priority for dmBridge.) However, it is also totally different, so there is a necessary-but-modest learning curve.

Template-to-collection mapping

A single set of dmBridge templates can be used with a single collection, multiple collections, or all collections. Conventionally, a "single collection" set of templates would be customized for that collection, and an "all-collections" set of templates would be styled to harmonize with the digital collections department or organization. That's just an idea, and you are free to do whatever you want. It is easy to reassign which collections appear in which set of templates at any time via the Control Panel.

Coexistence with the CONTENTdm® default templates

dmBridge templates can coexist peacefully with CONTENTdm® default templates. If you are just starting out with dmBridge, it wouldn't be a bad idea to dip your toes in the water with just one template set at first. You should never delete your default templates, as they are required by the CONTENTdm® administration module.


The dmBridge template engine is separate from the core system and can reside on any web server, as long as it can communicate with your CONTENTdm® server. No extra steps are necessary to run it on another server; just move the dm/objects folder as well as any template folders you may have to another server, and they should just work.

Template customization

The template set included with the dmBridge distribution is deliberately spartan. It is intended to be a starting point for your own creations, not a finished set of templates itself.

The dmBridge templates can be designed like any other HTML page using the text or WYSIWYG editor of your choice. The only extra consideration is styling the dynamically-generated content produced by the Draw methods. You need to see the HTML code they produce in order to style it. One way to do this is by viewing the source of the generated page in your web browser. A better way is using a DOM inspection tool:

  • The Firebug extension for Firefox
  • The Developer Tools built into IE
  • The Web Inspector built into Safari
  • All of these will enable you to zoom in on a specific element in the page in order to find CSS classes/IDs that your CSS rules can "grab onto."

    The following is a walkthrough of the template customization process.

    1. In dm/objects/templates, make a copy of the basic folder. Give it a descriptive alphanumeric name in all-lowercase (you can use underscores for spaces). We'll call it newtpls.
    2. Register the new template set in the Control Panel, and assign one or more collections to it.
    3. Navigate to your dm/objects folder in your web browser, appending the collection alias you wish to view to the URL like so:


      Your collection should come up, displaying results view. Click on a result and object view should load, and so on.

    4. Enter the newtpls/templates folder. Notice subfolders like object, error, favorite, etc. Take a look inside any one of them and notice one or more files with the .html.php extension. These are dmBridge template files. Open one up and notice that it's an HTML file, with some <?php ?> tags in it. Each one of these files corresponds to one of the main views in the dmBridge templates: object view, results view, favorites view, etc. It should be fairly obvious just by looking at them which one is which; check the <title> tag if you're not sure.

    At this point, you know what you need to do, which is to go nuts editing these page templates. If you have inspected any of the template files, you may be wondering about the code within the <?php ?> tags. These are calls to methods in the PHP API, which will be addressed in the coming sections.

URI model

The Template Engine supports cool URIs, which are stable and based on a thoughtful schema. By default, they look like this:

With "clean URLs" enabled, they look like this:

Finally, to make them fully semantic, we could move the objects folder one level up:

To enable clean URLs, the template engine uses URL rewriting, which it supports using Apache mod_rewrite (Apache) and ISAPI Rewrite (IIS). This eliminates the "?r=" from the URI. Both the template engine and the core (HTTP API) support URL rewriting. See Clean URLs for more information about configuring this.


The template engine is based on the concept of views. In practice, every digital collection uses several common views which, in dmBridge, each have their own page template. The following sections discuss the views in more detail.

Favorites view

Favorites view is a subset of results view that displays a user's favorites. Because it inherits from results view, it has the same display options (e.g. list, grid, tile). It adds checkboxes and a "Remove From Favorites" button.

The dmBridge favorites are stored in the same cookie as the default CONTENTdm® favorites, so changes made to the favorites in dmBridge will show up in the default CONTENTdm® templates, and vice versa.

Favorites view only displays those objects to which the user has access from the current template set. If two template sets (A and B) exist, and neither has access to any of the collections accessible by the other, neither will render any favorites that the other will. But the favorites are still present in the user's cookie at all times.

Object view

Object view is used in single-object context. Typically, it is accessed by clicking on a link from an object in results view. Object view supports both compound and non-compound objects. When using the HTML templates in this view, keep in mind that either view_simple.html.php or view_compound.html.php may be loaded depending on whether or not the object is compound.

Object viewers

Defining viewer associations

Every object viewer in dmBridge is its own class, in its own file on disk. A number of object viewers are provided (in the dm/objects/helpers/viewers folder) that are capable of handling some common file types. Which viewer is used for which file type depends on the associations defined in the Object View section of the Control Panel.

Due to the way the CONTENTdm® PHP API returns information about files, object viewers in dmBridge are associated with filename extensions. In the Control Panel, you can associate a particular object viewer class with a particular filename extension on a global, per-template-set, or per-collection basis. Be aware that some file formats may have more than one extension (e.g. .tif vs. .tiff), so you'll need to define the association once for each.

Writing custom viewer modules

Every object viewer class must implement the idmViewerDelegate interface. This requires writing just one method - getHTMLTag() - which should return an HTML tag. Take a look at dm/objects/helpers/viewers/dmGenericImageViewer.class.php for an example.

Adding your own custom object viewer is as simple as writing your own class in the pattern of the existing ones and defining the associations in the Control Panel.

You have two choices as to where to save your custom viewer:

  1. If you want it to be available to more than one template set, you can save it in the dm/objects/extensions/viewers folder (not the dmclient/helpers/viewers folder).
  2. If it is template-set-specific, you can also save it in the template set's extensions folder.

Object-results view

Object-results view is used to display compound object search results. Results are displayed in a two-column HTML table, one object per row, with the page title in the left column and the full text of the page in the right column. Only pages that match the search query are displayed; matching terms are enclosed in an HTML <span> element with class dmHighlightedTerm. This view is only available for searchable compound objects.

Results view

Results view is used in contexts where a number of objects are displayed alongside each other. It can be populated by any group of objects, such as search results, browse results, or a user's favorites. (Favorites are handled by Favorites view, a subset of results view.)

There are two results view templates inside any template set folder: object/results.html.php and object/results_faceted.html.php. When dmBridge has facets to display, it will load the latter.

To change the number of results per page, supply the rpp parameter in the URI query string; for example, ?rpp=10." This works in all output formats. The default is currently hard-coded at 20 and cannot be changed.

Results view provides several possible views within itself, each of which are just different ways of displaying objects. An easy way of rendering links to each of these views is using the ResultsDraw::viewLinks() method. The user can always get any view by supplying the view parameter in the URI query string, e.g. ?view=tile. Of course, they first have to know that they are able to do that, and there is no reason you need to tell them; but it's not currently possible to disable those views entirely.

Grid view

Grid view displays results in an HTML table, with one result per row. Columns are customizable and configurable in the Control Panel on a per-collection (not per-template-set) basis. Grid view is the default view within results view.

List view

List view is like an inverted grid view, with metadata fields displayed vertically per-object instead of horizontally. List view can be accessed within results view by supplying the ?view=list parameter in the URI.

Tile view

Tile view, like grid view, displays results in an HTML table. However, in tile view, each object has its own table cell. The number of columns is configurable in the Control Panel on a per-collection (not per-template set) basis. Tile view can be accessed within results view by supplying the ?view=tile parameter in the URI.

Search view

Search view is used to display the advanced search form(s) (not search results, which are handled by Results view). This view is not strictly necessary for all digital collections - you could write your own search interface, or use Custom Queries and Results (CQR) - but is provided anyway due to the complexity of the forms.

As you may already know, CONTENTdm® provides four different search forms on its Advanced Search page, allowing the user to search "across all fields;" "selected fields;" "by proximity;" or "by date." dmBridge merges the first two of these forms together, offering a total of three different search forms, which can be added to the advanced search template (search/index.html.php) using the fieldSearch(), proximitySearch(), and dateSearch() methods of the SearchDraw class.


The dmBridge PHP API is a high-level, object-oriented API for CONTENTdm® that goes far beyond the CONTENTdm® PHP API. It expedites development by making CONTENTdm® objects easy to work with:

= new dmCollection('/uw');
$obj = new dmObject($col, 5);


if (
$obj->isCompound()) {
"Page 1 is titled: " . $obj->getChild(0)->getTitle();
"It's in this collection: "
. $obj->getChild(0)->getCollection()->getName();

The API is fully documented and quite stable. The documentation is not hosted here; it is included with the templating engine module in your dm/objects/doc folder.

The PHP API is provided by the template engine and is available automatically within the page templates. (The Draw class and its subclasses are actually part of it, although they only work inside the templates.) It can also be included by other PHP scripts on the same server with code like the following:


If you have been poking around, you may have noticed that similar classes exist in the dmapi and dmctrl folders. These are not part of the PHP API and should not be used.

How to read the API documentation

In the left frame, expand the "Data Structures" menu. Click on dmCollection. This is one of the many classes that the dmBridge API provides. Near the top, there are two sections: "public member functions" and "static public member functions." (A deep explanation of what these mean will not be provided here; please flip to the "object orientation" chapter of your favorite PHP book.)

dmBridge is all about giving objects intelligent behavior. If you want to know a collection's name, you ask it, by calling its getName() method:

// Create a dmCollection object
$col = dmCollection::instantiate('/uw');

// Get its name
echo $col->getName();

What if you want a list of all collections to which you currently have access? You wouldn't ask a single collection, because it doesn't know; it only knows about itself. But the dmCollection class itself knows:


It returns the list as an array of dmCollection objects. You can loop through that array, ask each collection its name, ask it how many objects it contains, ask it what metadata fields it uses, ask each of those fields whether they are full-text-enabled, and so on.

In terms of trying to accomplish your goals with the dmBridge PHP API, think in terms of what CONTENTdm® entities are involved in the task. Objects? Collections, Metadata fields? Comments? Each one of these has its own class in the API, with lots of methods already written for interfacing with them. And like any other class, they can be extended by your own classes.

The API documentation provides a list of all methods that are publicly available in all classes. Most you will never have any reason to work with. But classes like dmObject,
dmCollection, dmComment, etc. may offer some interesting functionality if you need it.

The class you will probably work with the most in the templates is Draw and its subclasses. For a list of methods available in these classes, consult the API documentation. Also, check out the sample templates for working examples of these methods in action.

Using the PHP API in the templates

Digital collections websites perform a lot of common tasks: they display objects, render a paginated results list, etc. The Draw class exists to make a lot of this common functionality as close to automatic as possible, to spare you from having to get involved with the inner workings of dmBridge yourself. For example, by leveraging ResultsDraw::pageLinks(), you can do what would ordinarily take 100 lines of code with just one line. And if we ever update the way pageLinks() works, all of your templates that call it will use the upgraded functionality automatically. (We will try, of course, not to make major changes to the output HTML structure that might break your CSS rules.)

As the class diagram in the PHP API documentation depicts, Draw has several subclasses, each one of which is meant to correspond to a particular view. The child classes are essentially all supersets of Draw; they share all of its functionality, and add more of their own.

All superclass methods can be accessed via any subclass; so, for example, Draw::loginLink() and ResultsDraw::loginLink() are both valid because ResultsDraw is a superset of Draw (where loginLink() lives). But Draw::results() is invalid because Draw knows nothing about ResultsDraw, where results() lives. The idea is that superclasses should contain any functionality that would be used by more than one subclass. So, outside of the templates, where there is no conceptual "view" per se, Draw would be used exclusively, whereas object view could utilize both Draw and ObjectDraw, results view could utilize both Draw and ResultsDraw, etc.

Each view is associated with certain entities. For example, object view is handled by ObjectController::view(). When that method runs, it stores some important information (like the object being viewed) in a temporary location accessible by the templates: in this case, the dmObject class' getCurrent() method. To retrieve the current object from within object view, you would do:

$obj = dmObject::getCurrent();

The naming of these methods should be fairly straightforward. getCurrent() gets the current object in object view; getAllCurrent() returns all current objects in results view; etc. There is no centralized list of these methods, but most of them have the word "current" in them and are associated with the class representing what they are - for example, objects by dmObject, favorites by dmFavorite, queries by dmQuery, etc. Consult the API documentation for a full list of classes and their available methods.

Content representations

The dmBridge Template Engine supports several content representations (output representations) in addition to HTML. This list is different from that supported by the Core (HTTP API).

Name URI extension MIME type Object view Results view Object-results view Favorites view Search view
Atom 1.0 .atom application/atom+xml Yes Yes
HTML text/html Yes Yes Yes Yes Yes Yes
RDF/XML .rdf application/rdf+xml Yes
RSS 2.0 .rss application/rss+xml Yes Yes

HTML is the default representation. To specify an alternate representation, append the URI extension to the end of the URI. Feed metadata can be changed in the Feeds section of the Control Panel.

Mobile template sets

This section has not been written yet.

Record caching

Upon each load of a page template, the template engine initiates a number of requests against the dmBridge HTTP API - sometimes 40 or more, depending on the template. Every time the API receives one of these requests, the PHP engine has to compile and run the API application. Generally, this takes less than a tenth of a second per request; but even then, all 40 of the requests could still take four seconds or more, during which the web server is bogged down and the patron is waiting impatiently.

Under heavy traffic, these effects are multiplied. If two patrons were to hit a template at the same time, for example, they would each have to wait 8 seconds. This is obviously not acceptable.

To work around this problem, the template engine employs an API record cache. When it is enabled, the template engine saves most of the records it has received to disk. Upon each subsequent patron request, it checks to see if it has already downloaded the record, and if so, loads it from disk instead of requesting it from the API. Server load as well as response times are dramatically reduced.

The main drawback of any sort of cache is that changes to data that has been cached are not immediately reflected in the cached version of the data. This means that if a metadata record gets updated in CONTENTdm®, the changes will not appear in the dmBridge templates until the cache has been refreshed. Certain records, such as comment and tag lists that need to stay fresh, are never cached; but there is no way to completely circumvent this problem without disabling caching.

The record cache can be configured in the dm/objects/config.xml file, using the several parameters that begin with api.cache. Feel free to delete any of the .tmp files in the cache folder to force an update of particular (or all) records.

After a cached record exceeds duration days old, it gets overwritten with a fresh copy from the server. If your collections and metadata change often and those changes need to be public immediately, it may be best to reduce the duration parameter. If not, by all means, increase it, as the higher it is, the more performance benefit you will receive from caching.

Template extensions

If the functionality that the Draw classes provide does not meet your needs, and you have basic skills in PHP, you can add your own functionality by writing an extension.

There are actually several ways you could add custom code to dmBridge to either add or override existing functionality.

  • The wrong way: You could take a look at the source code of the method you're using and re-implement your own version right there in the page template. This would be quick, but it would clutter up your template.
  • A variation on the wrong way: Break that custom code out into a separate file and include() it. This would work, but would be nonstandard.
  • The way you should never even think about: Instead of reimplementing the code in the template, you could rewrite the method itself directly into the dmBridge. This was necessary in CONTENTdm® template customization process, but is highly discouraged in dmBridge - you should never touch the dmBridge files. There is a better way.
  • The right way: You could override the method. That's what extensions do. Extensions are the only supported way to add or override functionality in dmBridge.

Some of the advantages of using extensions are:

  • They are isolated from the rest of the dmBridge code
  • They are isolated from the rest of your template code
  • They are consolidated and organized, making customizations easy to find
  • They make upgrading easier
  • They respect the standard dmBridge data interfaces
  • They prevent unexpected side effects from propagating to unexpected places
  • They are systematically testable
  • They can be easily shared with other dmBridge users

dmBridge is an object-oriented system composed of classes which are themselves composed (in part) of methods, each one of which performs a specific task. For example, Draw::loadTime() displays the amount of time the page took to load, in seconds, rounded to 4 digits.

What if you wanted it to be rounded to 8 because time is money? Without extensions, there would be no clean way to do it. With extensions, you could add a file called CustomDraw.class.php to your templates' extensions folder, copy the loadTime() method into it from the original place, and tweak it to round to 8 digits instead of 4. Alternately, if you put this custom file in the dmBridge core's extensions folder, it would be available to all template sets.

PHP is not able to load two classes with the same name simultaneously (without namespaces, anyway, which dmBridge does not use). Whenever dmBridge looks for a class, its class loader will look in the template sets' extensions folders first. If it finds a matching class, it will load that class and then stop. The loaded extension will override the built-in class with the same name. The class loader will look for classes in the following sequence:

  1. Inside template sets' extensions folder
  2. Inside dmBridge core's extensions folder
  3. In the dm/objects/extensions folder

So, when an extension with the same name as a built-in class exists in an extensions folder, the built-in class will not be loaded. This is bad; don't give an extension class a name that is already being used by dmBridge.

But what if the extension only contains one overridden method from the built-in class? What happens to the rest of the methods in the overridden class? Do they just get ignored? What if they're important?

The answer is the the extension class needs to extend (inherit from) its overridden class, so that all of the rest of the functionality remains in place.

If you don't have much exposure to object-oriented programming, don't worry, because the hard part is all behind you now and the examples are straight ahead.

Here's what an extension looks like:

class CustomObjectDraw extends ObjectDraw {

    * Here, we are reimplementing the method we want to
    * override: in this case, the ObjectDraw class' metadata()
    * method. And then we return the result:
public static function metadata() {
// Do some stuff...
$result = '<p>Test</p>';

// Finished, now return it
return $result;

    * Here, we write our own totally new and unique method
    * which does not already exist in ObjectDraw.
public static function doSomethingCool() {
$text = '<p>I just did something cool...</p>';


In the first method ("public static function"), we are overriding the metadata() method in the ObjectDraw class. In the second, we are writing a new method called doSomethingCool(). Either of these will be accessible from our page templates like so:

echo CustomObjectDraw::metadata()

echo CustomObjectDraw::doSomethingCool()

Note that CustomObjectDraw::metadata() does not affect ObjectDraw::metadata() in any way. No code in any collection templates that references the latter will be affected. So, you can use the CustomObjectDraw version in some places, and the built-in version in others.

Extension files must be saved in one of two places:

  1. Your templates folder's extensions subfolder
  2. Your dm/objects/extensionsfolder

They need to be named the same as their class name; the example above would be named CustomObjectDraw.class.php. The class name doesn't matter; you could name it PeanutButter if you wanted. CustomObjectDraw just helps to make it clear what it is.

Real-world extension example

The above code is not very useful in practice. Here is an example of an extension written at the University of Nevada, Las Vegas for the Southern Nevada: The Boomtown Years collection. We have a bunch of classroom activities stored in an XML file, with references to certain objects within each activity node. We needed to be able to provide dynamic links, from object view, to activities that referenced the current object. Here's how we did it in an extension:

abstract class CustomObjectDraw extends ObjectDraw {

   private static

    * @return Boolean
public static function arelinksToRelatedActivities() {
      if (!
self::$html) {
self::$html = self::linksToRelatedActivities();
      return (bool)

    * @return String of HTML anchor tags
public static function linksToRelatedActivities() {
      if (
self::$html) {

// DOMDocument is part of PHP's XML DOM extension.
$dxml = new DOMDocument('1.0', 'utf-8');
$path = dirname(__FILE__) . '/activities/activities.xml';
// Load our activites XML file...

// Find the XML nodes we need using an XPath query...
$dxp = new DOMXPath($dxml);
$xpath = sprintf(
'//activity[relatedArtifacts/artifact/@uri = "%s"]',

// Insert the node values into HTML hyperlinks...
$links = array();
      foreach (
$dxp-&gt;query($xpath) as $node) {
$links[] = sprintf(
'<li><a href="/boomtown/activities/view.php?id=%d">%s</a></li>',
// Return the hyperlinks
self::$html = implode("\n", $links);


From the object view template, we want to find out if there are any links to related activities, and if so, draw them. We put this in object/view_simple.html.php and object/view_compound.html.php:

<h4>Related activities:</h4>
<?php if (CustomObjectDraw::areLinksToRelatedActivities()): ?>
      <?php echo CustomObjectDraw::linksToRelatedActivities() ?>
<?php else: ?>
<?php endif ?>

Now when we upgrade either CONTENTdm® or dmBridge, our customization is safe. It's also neat, organized, and easy to understand from the templates.

The code above would look quite daunting to a non-programmer. For better or worse, extensions are for programmers. Non-programmers will have to rely on the Draw methods.


The dmBridge HTTP API is a multi-format (XML/JSON), versioned API that embraces REST principles. The URI space is based on a general digital collections domain model. It is mostly read-only, except for social data.

The XML and JSON APIs return the same data, respond to the same request URIs, and send the same HTTP response codes. The only difference is the data encoding.

The HTTP API attempts to returns a content representation based on the preference ("Q value") supplied by the client in the HTTP Accept header. A representation can be forced by appending the appropriate file extension to the URI (.xml, .json, or .jsonp). If no extension is supplied, XML will be provided.

Full method-level documentation is not hosted on this website. If your dmBridge installation is live, you are hosting it yourself. Navigate to http://my_cdm_server/dmapi/ to view it.


Normally, the HTTP API is fully exposed to the public Internet. This gives anyone on the internet the ability to use data from your digital collections in their own applications, mashups, etc. This might seem cause for alarm, but in reality, everyone on the Internet has already had this ability, either by harvesting your OAI-PMH content, or by screen-scraping your templates. Little, if any, information that was previously private is made public by the dmBridge HTTP API; it has just been made more convenient to access programmatically.

Present in the output of every HTTP API method is a copyright statement asserting your institution's copyright on the information being downloaded. This allows for widespread use of your content while protecting your legal rights. The copyright statement can be changed in the Feeds section of the Control Panel.

Still, some users might wish to restrict their HTTP API, for whatever reason. There is no built-in provision for access controls built into the HTTP API component itself. We recommend that you use the access control features built into your web server (Apache/IIS) to restrict access to your dmapi folder based on IP address. If you are using the templating engine on the same server as the HTTP API, it would be safe to block all hosts except for localhost.

Control Panel

The Control Panel is a central administration point for dmBridge. For the most part, it is a web-based front-end to the dm/data/config/config.xml file. It also enables point-and-click moderation of social features.

From a usability standpoint, our goal is to make the Control Panel easy and intuitive, meaning that tasks can be accomplished without needing to consult a lot of wordy documentation such as you are now reading. Inline help is provided in certain areas that we have assumed might be potentially confusing or not fully self-explanatory. Where necessary - as suggested by you - we will add extra help here. As always, feedback is welcome.

The Control Panel is not regularly tested in Internet Explorer and was known to have issues in it, last time we checked. Please keep in mind that the developers' time is limited and that spending time working around bugs in IE cannot really rank as a priority considering all the other work to be done.

Social Features

dmBridge supports social features such as comments, ratings, and tags.

How the social features work

By clicking "Submit," the visitor posts form data to the template engine, which processes it and attempts to save it onto the CONTENTdm® server via the HTTP API. The template engine then refreshes the view with a status ("flash") message depending on whether or not the posting was successful.

The HTML forms for entering the data are rendered onto the object view web template by methods in the ObjectDraw class (commentsSection(), ratingsSection(), or taggingSection()).

How social data is stored

CONTENTdm® does not have a facility for storing social data. dmBridge, therefore, stores it in its own data store. See Installing the Data Store for more information.


Users can post comments for any object, but comments that are posted to a compound object page will be ascribed to the compound object itself - not the compound object page - and will appear on all pages of the compound object. The reason for this is that some compound objects have many pages and ascribing a comment to just a single one might cause it to get "buried." This behavior is currently not configurable.

In order for comments to work, a data store must be available. Once this condition is met, a commenting section can be included within an object view template (object/single.html.php and object/compound.html.php) using the ObjectDraw::commentsSection() method. This method will handle all aspects of generating the comment form and list of comments. You are free to style them however you wish, however.

dmBridge will post status messages to the flash in response to certain types of user input to this form - for example, when validation has failed or when the comment has been successfully posted. Therefore, when including comments in the template, it is a good idea to also include a call to Draw::formattedFlash() in a conspicuous place in the template as well, in order to provide feedback to the user as to what has just happened in response to their input.


The ratings feature allows users to rate objects on a configurable numeric scale.

The ratings feature can be included within an object view template (object/single.html.php and object/compound.html.php) using the ObjectDraw::ratingsSection() method. This method will handle all aspects of generating the rating form.

Ratings can be disabled on a per-template set basis by simply removing any calls to ObjectDraw::ratingsSection() from the templates. It is not currently possible to disable ratings on a per-collection basis.


Social tagging allows users to ascribe words or brief phrases ("tags") to objects to facilitate finding and gathering based on a flat (non-hierarchical) taxonomy.

The social tagging section can be included within an object view template
(object/single.html.php and object/compound.html.php) using the ObjectDraw::taggingSection() method. This method will handle all aspects of generating the tag input form. dmBridge will post status messages to the flash in response to certain types of user input to this form - for example, when validation has failed or when the tag has been successfully posted. Therefore, when including social tagging in the template, it is a good idea to also include a call to Draw::formattedFlash() in a conspicuous place in the template as well, in order to provide feedback to the user as to what has just happened in response to their input.

Social tagging can be disabled on a per-template set basis by simply removing any calls to ObjectDraw::taggingSection() from the templates. It is not currently possible to disable tagging on a per-collection basis.

Other Features

Custom Queries and Results (CQR)

With minor changes, dmBridge is compatible with CQR generated by the CONTENTdm® CQR generator. The CQR generator returns code pointing to /cdm4/browse.php; this needs to be changed to /dm/objects/ (or wherever your templates are located) to work with dmBridge.

A custom query consists of the URL of a PHP script (/cdm4/results.php) followed by a series of "key-value pairs." Below is a custom query with key-value pairs split apart onto their own lines for readability:


There are quite a few key-value pairs here. dmBridge needs only the ones that begin with CISOOP, CISOFIELD, CISOBOX, and CISOROOT (which mean matching mode, field, search string, and collection alias, respectively). As far as dmBridge is concerned, it's safe to delete the rest, or keep them - it doesn't matter. The above CONTENTdm® CQR converted to dmBridge format would look like:


As you can see, we have dropped anything not starting with CISOOP, CISOFIELD, CISOBOX, or CISOROOT. We have also dropped CISOOP 2 through 4, CISOFIELD 2 through 4, and CISOBOX 2 through 4, because they were empty. (If the CISOOP of a particular number is empty, it and the corresponding CISOFIELD and CISOBOX can be dropped.)

  • CISOBOX is a search string
  • CISOOP is a field matching mode (all, any, none, exact)
  • CISOFIELD is a field nickname
  • CISOROOT is a collection alias

Highlighted Object

The highlighted object can be returned using the dmBridge PHP and HTTP APIs.

To access it using the PHP API, use the dmObject::getHighlighted() method, which returns an object of type dmObject. Example:

= dmObject::getHighlighted();
if (
$obj->isHighlighted()) {
"Yup, I'm highlighted";

To access the highlighted object using the HTTP API, navigate to http://my_cdm_server/dm/api/?r=objects/highlighted.

Random Object

The random object can be returned using the PHP and HTTP APIs.

To access it using the PHP API, use the dmObject::getRandom() method, which returns an object of type dmObject:

= dmObject::getRandom();

To access the random object using the HTTP API, navigate to http://my_cdm_server/dm/api/?r=objects/random. JSON is available by appending .json to the URI. HTTP clients that request JSON as a preferred content type in their HTTP "Accept" header should receive it automatically.

Search Suggestions

dmBridge is able to provide search suggestions from CONTENTdm® field indices based on user input from the HTTP query string. What this means is that you can do something like the following:


And receive a list of suggestions for your term - in this case, "building." Whatever JavaScript solution you use for suggestions should query this URI. dmBridge tries to be client-code-agnostic, and to that end, it leaves the JavaScript up to you.

Search suggestions are a documented feature of the HTTP API. More information about them is available by navigating to your HTTP API documentation, at http://my_cdm_server/dm/api/.

Term Clouds

dmBridge supports "term clouds," which is a broader way of saying that it supports clouds based on social tags (tag clouds), and clouds based on field vocabulary (vocabulary clouds).

A cloud is a visual cluster of words or phrases based on some kind of vocabulary. This vocabulary may be the contents of one or more local metadata fields, or a user-submitted vocabulary ("folksonomy"). dmBridge supports both types. Normally, terms in a cloud have some kind of visual distinction from each other (color and/or size) based on the frequency that they appear in the information corpus. On a web page, these distinctions are controlled by CSS properties like color, font-size, font-weight, etc.

dmBridge is able to generate term clouds based on Dublin Core field vocabulary (see Draw::vocabularyAsCloud()). Social tag clouds are output by Draw::tagsAsCloud(). Both classes output tags as HTML anchor (&lt;a&gt;) elements with no enclosing element. These elements receive a CSS class attribute of dmTagX, where X is an integer from 1 to 10. With social tags, all tags are output with the same class of dmTag.


The dmBridge template engine supports the Sitemaps standard, which is designed to help search engines (including Google, Yahoo!, and Bing) index your content.

By default, your sitemap index is located at /dm/objects/?r=sitemap. To inform search engines about its location, modify the "Sitemap" element in robots.txt, located at the root of your template engine folder (by default, /dm/objects). Note that the format of the robots.txt file is picky and you might want to use a validation tool on it afterwards.


dmBridge is much more resource-intensive than the default CONTENTdm® templates. The page load process works like this:

  1. User hits the templating engine
  2. Templating engine fires off multiple (often dozens) of requests to the HTTP API
  3. For each request, the core (HTTP API provider) is compiled and executed
  4. The templating engine completes the request and sends the page to the user

Step 3 is by far the most resource-intensive. When it comes down to it, PHP is not the best language in which to implement a RESTful HTTP API. Unfortunately, it's what we've got. Beyond rewriting the HTTP API component in C++, there are some steps you can take to hopefully make the performance bearable.

Run the HTTP API on a server with a fast CPU.

Compilation of the HTTP API is mostly CPU-bound. In addition, a few fast CPUs are preferable to a lot of slow CPUs. Keep in mind that the server running the HTTP API is also running the rest of CONTENTdm®, including the CPU-gobbling getimage.exe image generator, which means it needs all the CPU it can get.

Run the template engine on a different server than the HTTP API.

This may make a slight difference. Moving the template engine onto another server means one less thing that the already-overtaxed HTTP API server to has to deal with. If possible, the HTTP API server should be the faster one.

Disable .htaccess support on the API server.

The performance hit caused by .htaccess files is generally imperceptibly slight, but is magnified by the great number of requests made to the HTTP API. If you are depending on the rewrite rules in your .htaccess files to enable clean URLs, copy them into the web server's configuration instead.

Install an opcode cache.

We have not yet tested this and would not expect it to do much for the HTTP API due to its use of a class autoloader. Please post a comment if you have experience with this.

If you are using CONTENTdm® on Windows with IIS, switch your server to Linux.

Apache with mod_php is faster than running PHP in CGI or even FastCGI mode under IIS. Running PHP as an ISAPI module on IIS is deprecated, and ISAPI has been removed as of PHP 5.3.


Template engine

I get a message that says, "Unable to grant access to this object. You may not be allowed to view it, or it may not exist."

This is usually a template set permissions problem. It can occur in object view or results view, whether searching or browsing.

To solve it, log into the Control Panel and make sure the current template set (specified in Collections -> Template Set Mapping) has access to all of the collections being searched. Go to Template Sets -> View All and click "Edit" next to the relevant template set. In the "Allowed Collections" section, make sure that if "All" is not checked, all of the collections being searched are.