11 Working With Scripts and Forms

This section covers the following topics:

11.1 About Scripts and Forms

To enhance the contributor's experience beyond the out-of-the-box behavior in an element, you can use a custom form or script that you apply to an element.

You do this to allow contributors to add objects that are not supported in a WYSIWYG element, as an example, or as a mechanism that the designer uses to enforce content requirements or restrictions for the contributor.

Site Studio includes several sample forms and scripts in the content server (described in the topics above) that you can begin using right away. To help you organize, edit, and manage these files, you may want to add each one to your site as a site asset.

This section is about these topics:

11.1.1 About Scripts

Scripts are often used in a web site to make the web site more effective in displaying information or in interacting with the user. You can add scripts, edit scripts, copy scripts, and add or remove scripts from your site.

11.1.2 About Custom Configuration Scripts

Custom Configuration Scripts are used to modify the interface for editing within an element in Contributor. Each configuration script can be used to add or replace commands, toolbar buttons, and context menu items. Any part of the editor can be customized to fulfill a specific requirement.

For more information, see Section 11.1.2, "About Custom Configuration Scripts."

11.1.3 About Custom Element Forms

A custom element form is designed to work with a custom element definition, which you add to a region template and region definition like any other element. The custom element form is used to create a custom interface that contributors use to add and control various types of content to a web site. Site Studio comes with several predefined custom element forms (in [CS-Dir]\custom\SiteStudio\support). Sample forms are also checked into the Oracle Content Server during installation.

11.1.4 About Validation Scripts

You can enforce site rules such as what content is added using your own validation scripts. Validation scripts can only be written in JavaScript.

Validation scripts are associated with element definitions, and are used to validate the edits that contributors make. Some things that validation scripts could be used for include size requirements on images, limiting text to 140 characters, and so forth.

11.2 Managing Assets

This section covers the following topics:

11.2.1 Creating a New Script

To create a script, perform these tasks:

  1. In the menu on the Site Assets pane, select Scripts.

  2. Click the Create New icon (Figure 11-1), select New, and then Javascript File.

    The Assign Info Form opens for you to check the item into the content server.

    Note:

    VBScript cannot be used to write or run custom validation scripts.

    Figure 11-1 Create New Icon

    Create New icon
  3. Enter appropriate values for the Assign Info Form.

    For guidelines on naming Site Studio assets, see Section 4.2, "Naming Site Assets."

  4. When complete, click Assign Info.

  1. The script is created.

When a new script is added, there is no code in it. It is blank.

Note:

When you create a new script by selecting Validation Scripts (rather than Scripts) using the Assets pane, the customization is pre-populated with code compatible with an FCK element.

11.2.2 Copying a Script

You can select a script from the list to copy, select a script from the content server to copy, or select a script from your local instance to copy.

Copying a script from the list

  1. In the menu on the Site Assets pane, select Scripts.

  2. Select the script you want to copy from the list.

  3. Click the Create New icon (Figure 11-2), select Copy, and then Selected:

    The Assign Info Form opens for you to check the item into the content server.

    Figure 11-2 Create New Icon

    Create New icon
  4. Enter appropriate values for the Assign Info Form.

  5. When complete, click Assign Info.

  6. The script is copied.

Copying a script from the server

  1. In the menu on the Site Assets pane, select Scripts.

  2. Click the Create New icon (Figure 11-2), select Copy, and then from Server.

    A search results page opens.

  3. Click the corresponding Select button of the one you want to copy.

    The Assign Info Form opens for you to check the item into the content server.

  4. Enter appropriate values for the Assign Info Form.

  5. When complete, click Assign Info.

  6. The script is copied.

Copying a script from your local instance

  1. In the menu on the Site Assets pane, select Scripts.

  2. Click the Create New icon (Figure 11-2), select Copy, and then from Local.

    A navigation window opens.

  3. On your local instance, navigate to the script you want to copy.

  4. Select the script and click Open.

    The Assign Info Form opens for you to check the item into the content server.

  5. Enter appropriate values for the Assign Info Form.

  6. When complete, click Assign Info.

  1. The script is copied.

11.2.3 Editing a Script

To edit a script, perform these tasks:

  1. In the menu on the Site Assets pane, select Scripts.

  2. From the list, select the script to be edited and click the Edit icon (Figure 11-3).

    The selected script is opened and can be edited in source view.

    Figure 11-3 Edit Icon

    Edit icon

11.2.4 Viewing the Content Information of a Script

To view content information for a script, perform these tasks:

  1. In the menu on the Site Assets pane, select Scripts.

  2. Select a script from the list.

  3. Click the Doc Info icon (Figure 11-4).

    The content information page opens.

    Figure 11-4 Doc Info icon

    Document Info icon

11.2.5 Adding a Script to a Site

To add a script to a site, perform these tasks:

  1. In the menu on the Site Assets pane, select Scripts.

  2. Click the Add to Site icon (Figure 11-5).

    A search results page opens.

    Figure 11-5 Add to Site Icon

    Add to Site icon
  3. Select the scripts to add, click Site Studio, and then Select Marked Documents.

  4. Depending on your configuration, you may receive a caution that you are about to add existing asset(s) to your site. Click OK.

    The scripts are now associated with the web site, and you can now open it to edit.

11.2.6 Removing a Script from a Site

To remove a script from a site, perform these tasks:

  1. In the menu on the Site Assets pane, select Scripts.

  2. From the list, select the script to be removed and click the Remove From Site icon (Figure 11-6).

    When you remove a script, you are simply removing it from the site, not deleting from the content server.

    Figure 11-6 Remove from Site Icon

    Remove from Site icon

11.3 Custom Element Forms

A custom element is a user-defined element. In addition to the other elements (WYSIWYG, text only, image only, static list, dynamic list), the custom element provides one way to extend Site Studio to suit individual business needs. Custom elements are created when the other elements are not as suitable.

Custom elements are full HTML files that reside within an IFRAME in the Contributor form. A custom element must use an API and implement several callbacks to function correctly as a Site Studio element.

Important:

If you upgrade a Site Studio site from a version before 10gR3 (10.1.3.3.3), you must manually update any custom elements in the site. For more information, see Section 11.3.2, "Backward Compatibility and Upgrading."

A custom element form is designed to work with a custom element, which you add to a contribution region like any other element. The custom element form is unique in that it can be used to create a custom interface that contributors use to add various types of content to a web site.

You might, for example, create a custom form that queries the content server for a certain file type, enables users to select one of those files, and returns the user to the Contributor. Or, you might create a form that provides a text box that contributors use to copy and paste source code or formatted text to a web page.

The form can be a generic browser-based form that collects arbitrary information from the user and returns a value to the host (in this case, Contributor).

This section includes the following topics:

11.3.1 Implementing a Custom Element

In order for a custom element to function properly in a Contributor form, a custom element must use an API and implement a hand-full of callbacks. The following notes highlight the ElementAPI and its methods for creating custom elements.

Note:

The path (URL) to a custom element must be in the same domain as the Contributor form. This is so that the Contributor form and the custom element can communicate without violating cross-domain scripting rules.

ElementAPI

The ElementAPI object is a JavaScript object explicitly loaded into the custom element page that facilitates communication between the Contributor form and the custom element. The ElementAPI provides methods for custom elements to communicate to the Contributor from, and a callback mechanism for the Contributor form to pass notifications to the custom element.

Loading the ElementAPI JavaScript Object

Before the ElementAPI and its supporting libraries can be used, the ElementAPI must first be loaded into the custom element page. After the ElementAPI is loaded, the custom element should continue with page initialization and notify the Contributor form that the custom element is loaded and ready to go.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
  <title>Simple Custom Element</title>

  <style type="text/css">
  * { padding: 0; margin: 0; }
  body { font-size: 10pt; font-family: Tahoma; }
  #container { height: 150px; overflow: auto; width: 100%; text-align: center; align: center;}
  #content { width: 99%; }
  </style>

  <script type="text/javascript">
  var Custom = {};

  Custom.originalData = null;

  Custom.Initialize = function()

  {
    // Set callback methods for the Contributor Form to send notifications to this Element.
    ElementAPI.SetCallback('GetElementContent', function(callback)    {
      callback(Custom.GetData());
    });
    ElementAPI.SetCallback('ActivateElement', function(callback){ $ID('content').focus(); callback(); });
    ElementAPI.SetCallback('CanCloseElement', function(callback){ callback({canClose: true}); });
    ElementAPI.SetCallback('Show', function(callback){ callback(); });
    ElementAPI.SetCallback('Hide', function(callback){ callback(); });
    ElementAPI.SetCallback('IsDirty', function()
   {
      return { isDirty: (Custom.originalData !== Custom.GetData()) };
    });
    // Set the height of the Custom Element form's host.
    var height = ( ElementAPI.GetElementCount() == 1 ) ? '100%' : '150px';
    var flags = ElementAPI.GetElementConfigProperty('flags') || {};
    var config = ElementAPI.GetElementConfiguration() || {};
    if( flags.canSetHeight && config.height && ( config.height.length > 0 ) )
    {
      height = config.height;
    }
     height = ElementAPI.SetHostHeight(height);

  // Get the default content stored within the data file.
    $ID('content').value = Custom.GetDefaultData();

    // Add base styles
    WCM.DHTML.AddStyleSheet({path: WCM.path + './base/wcm.base.css', context: window});

    // Add a resize handler for when the window is resized.
    var ResizeHandler = function()
    {
    WCM.DHTML.SetStyle('container', 'height', WCM.DHTML.GetViewportHeight() + 'px');
    WCM.DHTML.SetStyle('container', 'width', WCM.DHTML.GetViewportWidth() + 'px');   };
    ResizeHandler();

    WCM.DHTML.AddWindowResizeEvent(window, ResizeHandler);

   // Immediately store the original data as a baseline for the IsDirty comparison.
   Custom.originalData = Custom.GetData();

    // Let the Contributor Form know this Custom Element is ready. (required)    ElementAPI.Ready();
  };

  Custom.GetDefaultData = function()
  {
    return ElementAPI.GetDefaultData()
                .replace(/<pre>/g, '')
                .replace(/<\/pre>/g, '')
                .replace(/&lt;/g, "<")
                .replace(/&gt;/g, ">") || 'Some default content.';
   };

  Custom.GetData = function()
  {
    return '<pre>' + $ID('content').value.replace(/</g, "&lt;").replace(/>/g, "&gt;") + '</pre>';
  };

  try {
    // Tell the Contributor Form to load the Custom Element dependent JavaScript libraries. (required)
    // PARAMETERS
    // 1st parameter: The context (window) in which the form loads the Custom Element dependent JavaScript libraries into.
    // 2nd parameter: A user defined function pointer for the Contributor Form to call when initialization is complete.
    window.top.WCM.InitializeCustomElement(window, Custom.Initialize);
    catch(e) { }
  </script>

  </head>
<body>
  <div id="container">
  <div id="title">Simple Custom Element</div>
  <div><textarea id="content" rows="5"></textarea></div>
  </div>
</body>
</html>

Note:

The code example loads the ElementAPI and notifies the Contributor form that it is loaded and ready. It does not, however, collect or save data.

Communication from Contributor Form to Custom Element

The Contributor form communicates with a custom element by executing functions implemented by the custom element. As part of the initialization process, a custom element must register these functions by passing their function pointers to the Contributor form.

The table below lists the functions that can be registered with the Contributor form. None of these functions must be implemented by the custom element; however, a few of them are required if the intention is to collect and save data from a Contributor user. Furthermore, all of these functions (except the IsDirty() function), when executed, pass a callback function pointer to execute when the task is complete. This enables asynchronous communication if a custom element must perform an asynchronous task during execution.

Function Signature Description
CanCloseElement(callback); The Contributor form executes this method when the Contributor user performs an update. The implementation of the function should calculate whether the custom element can be safely closed. For instance, if the data does not pass validation, then the custom element should indicate that it cannot be closed.
GetElementContent(callback); The Contributor form executes this method when the Contributor user performs an update. The implementation of the function should pass back string content to be saved.
Hide(callback); The Contributor form executes this method whenever the form performs a DHTML task that overlays an HTML element over the custom element. For instance, this method is executed when the Metadata tab is activated and the Contributor elements are obscured.

This method was introduced specifically for the Ephox-based elements, because Java applets always have top z-index. All other elements (HTML-based elements) can ignore this method.

Show(callback); The Contributor form executes this method whenever the form performs a DHTML task that removes an overlay that makes custom element reappear.

This method was introduced specifically for the Ephox-based elements because Java applets always have top z-index. All other elements (HTML-based elements) can ignore this method.

IsDirty(); The Contributor form executes this method whenever the form popup is being closed without updating. The custom element should calculate whether unsaved changes exist so that the Contributor user can be notified if there are unsaved changes.

The following is a JavaScript code snippet of how a custom element can register functions with the Contributor form:

function CanCloseElement(callback)
{
    // No data validation in this sample - just pass back a true value.
    callback({canClose: true});

    // Here is an example of passing a false value
    // callback({canClose: false, reason: 'Failed validation. Use only lowercase
    // letters.'};
}

function GetElementContent(callback)
{
    // Pass back some sample content for demo purposes.
    callback('This is my Custom Element Content.');
}

function Show(callback)
{
    // Just handle this notification by executing the callback.
    callback(); 
}

function Hide(callback)
{
    // Just handle this notification by executing the callback.
    callback(); 
}

function IsDirty()
{
    // This Custom Element is never dirty - so pass a false value.
    return {isDirty: false}; 
}

// Set callback methods for the Contributor Form to send notifications to this
// Element.
ElementAPI.SetCallback('CanCloseElement', CanCloseElement);
ElementAPI.SetCallback('GetElementContent', GetElementContent);
ElementAPI.SetCallback('Show', Show);
ElementAPI.SetCallback('Hide', Hide);
ElementAPI.SetCallback('IsDirty', IsDirty);

Communication From Custom Element to Contributor Form

A custom element initiates communication with the Contributor form by using the ElementAPI JavaScript object. The following is a list of available ElementAPI methods.

Function Signature Description
ElementAPI.GetDefaultData(); Retrieves the default content stored in the data file.
ElementAPI.GetSearchResults(options); Opens the Oracle Content Server's Get Search Results page.
ElementAPI.GetQueryText(options); Opens the Get Query Text UI.
ElementAPI.CaptureQuery(options); Opens the Oracle Content Server's Capture Query page.
ElementAPI.GetHyperlink(options); Opens the Hyperlink Wizard UI.
ElementAPI.FocusForm(options); Focuses the parent window thereby blurring the Element window.
ElementAPI.SetHostHeight(height); Sets the height of the Element's containing IFRAME.
ElementAPI.SetRequiredIndicator(isRequired); Toggles the Required graphic indicator in the Contributor Form UI.
ElementAPI.GetSite(options); Opens the Choose Website picker UI.
ElementAPI.GetSection(options); Opens the Choose Website Section picker UI.
ElementAPI.GetColor(options); Opens the Color picker UI.
ElementAPI.GetFont(options); Opens the Get Font picker UI.

ElementAPI Dependent Scripts

When the ElementAPI is loaded into the custom element page, so are the ElementAPI dependent scripts. These scripts contain most of the JavaScript WCM library and is also available for custom element authors to use. The following script files are loaded into a custom element:

  • wcm.js

  • ./base/wcm.dhtml.js

  • ./base/wcm.get.js

  • ./base/wcm.http.js

  • ./base/wcm.popup.js

  • ./sitestudio/wcm.contentserver.popup.js

  • ./form/elements/wcm.elementapi.js

  • ./sitestudio/elements/wcm.sitestudio.elementapi.js

  • ./sitestudio/wcm.idc.js

  • ./form/elements/element/wcm.element.js

  • ./form/elements/custom/wcm.custom.js

Example of a Source-Mode Type of Custom Element

The following simple custom element example saves content entered into a TEXTAREA by Contributor users, and opens it directly in the layout.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
  <title>Simple Custom Element</title>

  <style type="text/css">
  * { padding: 0; margin: 0; }
  body { font-size: 10pt; font-family: Tahoma; }
  #container
  {
    height: 150px;
    overflow: auto;
    width: 100%;
    text-align: left;
    align: left;
    padding: 7px;
   }
 .controls
 {
   border: 1px solid #B9B9B4;
   border-top-color: #DDDDD8;
   border-bottom-color: #797975;
   vertical-align: middle;
   font-family: tahoma,verdana,arial,helvetica,sans-serif;
   font-size: 100%; /* 11px @ Normal */
   font-color: #000;
  }
  </style>

  <script type="text/javascript">
  var Custom = {};

  Custom.originalData = null;

  Custom.Initialize = function()
  {
    ElementAPI.SetCallback('GetElementContent', function(callback)
    {
      callback(Custom.GetData());
    });
    ElementAPI.SetCallback('ActivateElement', function(callback){ $ID('content').focus(); callback(); });
    ElementAPI.SetCallback('CanCloseElement', function(callback){ callback({canClose: true}); });
    ElementAPI.SetCallback('Show', function(callback){ callback(); });
    ElementAPI.SetCallback('Hide', function(callback){ callback(); });
    ElementAPI.SetCallback('IsDirty', function()
    {
      return { isDirty: (Custom.originalData !== Custom.GetData()) };
    });

    var height = ( ElementAPI.GetElementCount() == 1 ) ? '100%' : '150px';
    var flags = ElementAPI.GetElementConfigProperty('flags') || {};
    var config = ElementAPI.GetElementConfiguration() || {};
    if( flags.canSetHeight && config.height && ( config.height.length > 0 ) )
    {
      height = config.height;
    }
    height = ElementAPI.SetHostHeight(height);

    $ID('input-value').value = ElementAPI.GetDefaultData();

    WCM.DHTML.AddStyleSheet({path: WCM.path + './base/wcm.base.css', context: window});

    var ResizeHandler = function()

    {
      var viewPortWidth = WCM.DHTML.GetViewportWidth();
      var viewPortHeight = WCM.DHTML.GetViewportHeight();
      WCM.DHTML.SetStyle('container', 'height', viewPortHeight + 'px');
      WCM.DHTML.SetStyle('container', 'width', viewPortWidth + 'px');
      WCM.DHTML.SetStyle('input-value', 'height', (viewPortHeight - 15) + 'px');
      WCM.DHTML.SetStyle('input-value', 'width', (viewPortWidth - (WCM.IS_IE ? 15 : 15)) + 'px');
    };
    ResizeHandler();

    WCM.DHTML.AddWindowResizeEvent(window, ResizeHandler);
    Custom.originalData = Custom.GetData();
    ElementAPI.Ready();
   };

  Custom.GetData = function()
  {
    return $ID('input-value').value;
  };

  try {
    window.top.WCM.InitializeCustomElement(window, Custom.Initialize);
  } catch(e) { }
  </script>

  </head>
<body>
  <div id="container" nowrap>
  <textarea id="input-value" class="controls" type="text" title="URL"></textarea>
  </div>
</body>
</html>

11.3.2 Backward Compatibility and Upgrading

All custom element forms compatible with Site Studio releases before Site Studio 10gR3 (10.1.3.3.3) are not compatible with Site Studio 10gR4 (10.1.4) and later releases, and they must be manually upgraded (re-authored). The primary reason for not maintaining backward compatibility is due to Site Studio's prior dependency upon Internet Explorer's proprietary 'window.external' functionality. This is because Contributor used an ActiveX control. The window.external functionality blocked at the point of code execution and is not easily duplicated in a cross-browser/platform DHTML solution. The upside to breaking backward compatibility is that the new custom elements are much more flexible and better integrated into the Contributor application architecture (in addition to being a cross-browser/platform solution).

Detecting Legacy Custom Element Forms

A legacy custom element form (that is, pre-10gR3, 10.1.3.3.3), if loaded into the new Contributor application, is detected (by default) and an error message opens in its place within the Contributor form. The Contributor application does this by first downloading the custom element form, parsing the source code, and ascertaining whether the custom element form is compatible with the new Contributor application.

The functionality and overhead to detect legacy custom element forms is unnecessary on production installations and is turned off by default. If you have legacy custom element forms to upgrade, you need to turn the flag on. To turn on legacy custom element form detection, add the following line to the Oracle Content Server's config.cfg file and restart the server:

SSValidateCustomElements=true

11.3.3 Sample Custom Elements

Site Studio 11gR1 comes with eleven sample custom forms or elements. These sample forms are example implementations of custom elements that work within the Contributor form framework.

These samples are checked into the content server with the xWebsiteObjectType metadata value of 'Custom Element Form' upon installation of the Site Studio component.

ss_boolean_checkbox_form.htm

This sample form is an example of a simple custom element with a checkbox.

ss_boolean_radio_form.htm

This sample form is an example of a simple custom element with a radio button.

ss_docname_form.htm

This sample form is an example of a custom element that enables a content server search to select a Content ID.

ss_option_form.htm

This sample form is an example of a custom element that enables you to build an option list. This example is a very simple example. It more complex instances, the list could be populated from many other sources, such as content server metadata.

ss_query_form.htm

This sample form is an example of a custom element that enables you to capture a content server query.

ss_url_form.htm

This sample form is an example of a custom element that enables a content server search to return a weblayout URL.

ss_textarea_form.htm

This sample form is an example of a simple custom element with minimal amount of code. This particular example implements a text-only custom element utilizing a TEXTAREA HTML element, which is a very lightweight method of providing a text-only editor.

ss_integer_form.htm

This sample form is an example of a custom element for capturing an integer value. This is useful for capturing a parameter to use elsewhere.

ss_simple_form.htm

This sample form is an example of a simple custom element with minimal amount of code. This particular example implements a text-only custom element utilizing a TEXTAREA HTML element, which is a very lightweight method of providing a text-only editor.

ss_flash_form.htm

This sample form is an example of a custom element that enables Contributor users to select a FLASH object from the content server.

ss_sample_form.htm

This sample form is an example of a custom element that uses all available ElementAPI methods. A complete list of these methods is available in Section 11.3.1, "Implementing a Custom Element."

11.4 Custom Configuration Scripts

Custom configuration scripts are javascript files that are used specifically to modify the contributor's editing area for each individual element. A configuration script can be associated with different elements, but each element must individually call the script.

The customizations affect the FCKeditor and Ephox-based elements, which are WYSIWYG, text only, and image only. Custom configuration scripts can be used to add or replace commands, toolbar buttons, and context menu items. Any part of the editor can be customized to fulfill a specific requirement.

Site Studio ships with several sample custom configuration scripts. These samples are checked into the content server with the xWebsiteObjectType metadata value of "Custom Configuration Script" when the Site Studio component is installed on the content server.

The following scripts are the included samples:

FCKeditor-Compatible Sample Scripts

  • ss_fck_simple_command.js: This sample script is an example of a simple FCKeditor command. This particular example implements an FCKeditor command, toolbar button, and menu item.

  • ss_fck_image_command.js: This sample script is an example of an FCKeditor command that overrides the existing "ssImage" and "Image" commands. In this example, the commands are redefined and the existing Image toolbar button and context menu items are re-used.

  • ss_fck_load_multiple_commands.js: This sample script shows how to load multiple Custom Configuration Scripts.

  • ss_fck_template_command.js: This sample script enables users to insert pre-defined HTML.

For more information on the Custom Configuration Scripts in the Site Assets pane, see Chapter 11, "Working With Scripts and Forms."

11.5 Default Validation Options

Depending on the type of element you're working with, you see different default validation options in the Element Validation dialog (see below):

Element type Validation options
WYSIWYG min: Enforces a minimum amount of text (in characters).

max: Enforces a maximum amount of text (in characters).

Disallowed characters: Prevents contributors from using the characters that you specify here.

Allow line breaks <br>: When checked, contributors can create a line break (soft return), which is usually done by pressing SHIFT + ENTER on the keyboard.

Allow paragraphs <p>: When checked, contributors can create a new paragraph (hard return), which is usually done by pressing ENTER on the keyboard.

Allow images <img>: When checked, contributors can add images.

Plain Text min: Enforces a minimum amount of text (in characters).

max: Enforces a maximum amount of text (in characters).

Disallowed characters: Prevents contributors from using the characters that you specify here.

Allow multiple lines: When checked, contributors can add new lines of text (by pressing ENTER on their keyboard).

Image Width:
  • min: Enforces a minimum width (in pixels).

  • max: Enforces a maximum width (in pixels).

Height:

  • min: Enforces a minimum height (in pixels).

  • max: Enforces a maximum height (in pixels).

Static List Number Rows
  • min: Enforces a minimum number of rows.

  • max: Enforces a maximum number of rows.