
# Velocity Tools

> Cascade CMS's Velocity Tools are a collection of functions that can be used to retrieve and manipulate content in conjunction with Apache Velocity Template Language.

URL: https://www.hannonhill.com/cascadecms/latest/articles/developing-in-cascade/script-formats/velocity-tools.html
category: Developing in Cascade
article-type: reference
content-kind: article
release-availability: all
tags: velocity
updated: 2026-10-06

Cascade CMS's Velocity Tools are a collection of functions that can be used to retrieve and manipulate content in conjunction with [Apache Velocity Template Language](https://velocity.apache.org/engine/2.0/user-guide.html).

## Global Variables

These variables are available in every Velocity Format without any additional setup. `$contentRoot` is only set when the Format receives XML content, and the `$currentPage` variables are only set when a page is being rendered.

**Global variables available in Velocity Formats.**

| Variable | Output Type | Description |
| --- | --- | --- |
| `$contentRoot` | Element | Returns a JDOM Element containing the XML supplied for the current region. |
| `$currentPage` | PageAPIAdapter | Returns the current page as an API object. |
| `$currentPagePath` | String | Returns the path of the current page within its site, without a leading slash (for example `about/index`). |
| `$currentPageSiteName` | String | Returns the name of the current page's site, or an empty string if the page is in the Global area. |
| `$enabledCustomDirectives` | ArrayList | Returns the names of all available custom directives in the CMS environment, including the `#` (for example `#import`, `#queryexecute`, `#queryfilter`, `#querysortvalue`). |

```velocity
## The page being rendered is already an API object, so there's no need to locate it
$_EscapeTool.xml($currentPage.metadata.title)

## Check whether a custom directive is available before using it
#if ($enabledCustomDirectives.contains('#queryexecute'))
  ## safe to use #queryexecute
#end

## Loop over the XML for the current region
#foreach ($node in $_XPathTool.selectNodes($contentRoot, "//system-page"))
  $node.getChildText("title")
#end
```

## Locator Tool

The Locator Tool retrieves assets at a given path as API objects. Optionally, a site name can be specified to locate assets in a different site. Added in 7.4

**Locator Tool methods.**

| Method | Parameters | Description |
| --- | --- | --- |
| `$_.locate(path, type)` | `path` (String), `type` (EntityType) | Locates an asset of the given type at the given path, in the Site where the Format lives. `type` is an `EntityType` constant (`TYPE_LINKABLE`, `TYPE_PAGE`, `TYPE_FILE`, `TYPE_FOLDER`, `TYPE_BLOCK`, `TYPE_TEMPLATE`, `TYPE_REFERENCE`, `TYPE_SYMLINK`, `TYPE_FORMAT`), not a string. Older code gets these constants with `$_FieldTool.in("com.hannonhill.cascade.model.dom.identifier.EntityTypes")`, which is deprecated. Use the type-specific methods below instead. See [Update deprecated Velocity code](./update-deprecated-velocity-code.html). |
| `$_.locate(path, type, site)` | `path` (String), `type` (EntityType), `site` (String) | Locates an asset at the given path with the specified type in the Site with the given name. Returns null if no Site has that name. |
| `$_.locateBlock(path)` | `path` (String) | Locates a Block at the given path. |
| `$_.locateFile(path)` | `path` (String) | Locates a File at the given path. |
| `$_.locateFolder(path)` | `path` (String) | Locates a Folder at the given path. |
| `$_.locateFormat(path)` | `path` (String) | Locates a Format at the given path. |
| `$_.locateLinkable(path)` | `path` (String) | Locates a Page, File, or External Link at the given path. |
| `$_.locatePage(path)` | `path` (String) | Locates a Page at the given path. |
| `$_.locateReference(path)` | `path` (String) | Locates a Reference at the given path. |
| `$_.locateSymlink(path)` | `path` (String) | Locates an External Link at the given path. |

Each method also accepts an optional second parameter for site name (e.g. `$_.locatePage(path, site)`); for `$_.locate` it is the third parameter, after `type`. Without a site name, assets are located in the Site where the Format lives. Use `$currentPageSiteName` to locate in the Site of the page being rendered. If no asset is found at the path (or no Site has the given name) the method returns null. A leading slash on the path is optional.

```velocity
## Locate a page in the Site where the Format lives
#set ($aboutPage = $_.locatePage("/about"))

## Locate a file in a named site
#set ($logo = $_.locateFile("/images/logo.png", "Cascade University"))

## Locate in the site of the page being rendered
#set ($contact = $_.locatePage("about-us/contact", $currentPageSiteName))

## The other type-specific methods work the same way
#set ($events = $_.locateBlock("blocks/index/all-events", "Calendar"))
#set ($folder = $_.locateFolder("articles/2019", "News"))
#set ($banner = $_.locateFormat("_cms/formats/banner", "Cascade University"))

## A Page, File, or External Link
#set ($item = $_.locateLinkable("student-life/student-portal-link", "Cascade University"))

## The methods return null when nothing is found, so check before using the result
#if ($aboutPage)
  <a href="$aboutPage.link">$_EscapeTool.xml($aboutPage.metadata.title)</a>
#end
```

## Query API

The Query API allows you to construct queries using chained methods to filter and retrieve assets from the CMS. Start by creating a query object, choose a query type, set optional filters and sorting, then execute.

### Query construction

**Primary query methods.**

| Method | Parameters | Description |
| --- | --- | --- |
| `$_.query().byContentType(path)` | `path` (String) | Query pages that use the Content Type at `path`. Returns pages only. Can't be combined with `byMetadataSet()` or `byDataDefinition()`. |
| `$_.query().byMetadataSet(path)` | `path` (String) | Query assets that use the Metadata Set at `path`. Can't be combined with `byContentType()`. |
| `$_.query().byDataDefinition(path)` | `path` (String) | Query pages and structured data blocks that use the Data Definition at `path`. Can't be combined with `byContentType()`. |

Each `path` can take a site prefix, for example `"site://Calendar/Event"`. Without one, Cascade looks for the Content Type, Metadata Set, or Data Definition in the current site.

### Filters

**Query filter methods for narrowing results.**

| Method | Parameters | Description |
| --- | --- | --- |
| `.bySiteName()` | `name` (String) | Return assets from the named site instead of the current site. This sets where the returned assets live, not where the Content Type or Metadata Set lives. If you also call `searchAcrossAllSites()`, the later call wins. |
| `.byFolderPath()` | `path` (String) | Return assets in the folder at `path` and all of its subfolders. Takes one path. A `%` wildcard matches every folder that starts the same way, for example `departments/b%`. |
| `.hasAnyPaths()` | `paths` (List) | Fetch a known set of assets by their exact paths, for example `dept/sci/nye-william`. Each value must be the asset's complete path in Cascade, not a folder. Page paths don't include an output extension like `.php`. This is not a subtree filter. |
| `.hasMetadata()` | `field` (String), `value` (String) | Filter by a metadata field matching a specific value. Requires `byMetadataSet()` or `byContentType()`; without one, the query throws an error. |
| `.hasAnyMetadataValues()` | `field` (String), `values` (List) | Filter by a metadata field matching any of the specified values. Requires `byMetadataSet()` or `byContentType()`; without one, the query throws an error. |
| `.hasStructuredData()` | `path` (String), `value` (String) | Filter by a structured data field matching a specific value. `path` is the field's identifier path from the Data Definition, with groups separated by `/`, for example `contact/building`. Requires `byDataDefinition()` or `byContentType()`; without one, the query throws an error. |
| `.hasAnyStructuredDataValues()` | `path` (String), `values` (List) | Filter by a structured data field matching any of the specified values. `path` and the query type requirement work the same way as in `hasStructuredData()`. |
| `.hasStructuredDataByFieldId()` | `id` (String), `value` (String) | Filter by a structured data field's `field-id` matching a specific value. |
| `.hasAnyStructuredDataValuesByFieldId()` | `id` (String), `values` (List) | Filter by a structured data field's `field-id` matching any of the specified values. |
| `.hasTag()` | `tag` (String) | Filter results to assets with the specified tag. |
| `.hasAnyTags()` | `tags` (List) | Filter results to assets with any of the specified tags. |

> **Checkboxes and multi-selects**
>
> The structured data filters can't match multi-value fields like checkboxes and multi-selects. See [Filtering queries based on structured data values](./filtering-queries-based-on-structured-data-values.html) for a workaround.

> **Fields inside a Group Shared Field**
>
> To filter by a field inside a Group Shared Field with the `ByFieldId` methods, join two ids into one 64-character id: the `field-id` on the `<shared-field>` element in the Data Definition, then the `field-id` of the field inside the Shared Field.

> **Querying assets across multiple folders**
>
> `.hasAnyPaths()` is for fetching assets at specific, known paths, not for querying all assets within a folder or set of folders. To query assets across multiple folders, use one of these approaches:
>
> - **A `%` wildcard in `byFolderPath()`:** Works when the folders share a path prefix.
> - **Separate queries merged with `#queryexecute`:** Run a `byFolderPath()` query for each folder and accumulate results into a shared list.
> - **`#queryfilter` directive** (supported in current versions of Cascade): Apply custom Velocity logic per asset before results are returned, allowing you to check multiple parent folder conditions in one query. See [Query Tool Directives](./query-tool-directives.html).

```velocity
## Structured data: the path is the field identifiers from the Data Definition, joined with /
$_.query().byContentType("Person").hasStructuredData("contact/building", "Hannon Hall").execute()
$_.query().byContentType("Person").hasAnyStructuredDataValues("contact/building", ["Hannon Hall", "Hill Hall"]).execute()

## The same filters by field-id
$_.query().byContentType("Person").hasStructuredDataByFieldId("9fac6f9dac1e001b075cc4e8a8d4e5ec", "Hannon Hall").execute()
$_.query().byContentType("Person").hasAnyStructuredDataValuesByFieldId("9fac6f9dac1e001b075cc4e8a8d4e5ec", ["Hannon Hall", "Hill Hall"]).execute()

## A field inside a Group Shared Field: shared-field id + field id (64 characters)
$_.query().byContentType("Person").hasStructuredDataByFieldId("9fac6f9dac1e001b075cc4e8a8d4e5ec585f9112c0a8003d45455f490cff9beb", "Hannon Hall").execute()

## Metadata and tags take a List for the "any" versions
$_.query().byMetadataSet("Article").hasMetadata("department", "Biology").execute()
$_.query().byMetadataSet("Article").hasAnyMetadataValues("department", ["English", "Communications"]).execute()
$_.query().byMetadataSet("Event").hasAnyTags(["Campus Events", "Academics"]).execute()

## A folder and all of its subfolders
$_.query().byContentType("Person").byFolderPath("departments/biology").execute()
```

### Asset type filters

**Methods to include or restrict by asset type.**

| Method | Parameters | Description |
| --- | --- | --- |
| `.includeBlocks()` | `include` (Boolean) | Include Block assets in results. |
| `.blocksOnly()` | None | Return only Block assets. |
| `.includeFiles()` | `include` (Boolean) | Include File assets in results. |
| `.filesOnly()` | None | Return only File assets. |
| `.includeFolders()` | `include` (Boolean) | Include Folder assets in results. |
| `.foldersOnly()` | None | Return only Folder assets. |
| `.includePages()` | `include` (Boolean) | Include Page assets in results. |
| `.pagesOnly()` | None | Return only Page assets. |
| `.includeSymlinks()` | `include` (Boolean) | Include External Link assets in results. |
| `.symlinksOnly()` | None | Return only External Link assets. |

### Additional options

**Additional query options.**

| Method | Parameters | Description |
| --- | --- | --- |
| `.indexableOnly()` | `include` (Boolean) | Include only indexable assets in results. Defaults to `true`, so a query leaves out assets that aren't indexable unless you pass `false`. |
| `.publishableOnly()` | `include` (Boolean) | Include only publishable assets in results. Defaults to `false`. |
| `.preloadDynamicMetadata()` | None | Preload dynamic metadata fields to avoid individual database trips per asset. |
| `.preloadStructuredData()` | None | Preload structured data to avoid individual database trips per asset. |
| `.searchAcrossAllSites()` | None | Expand the query to search all sites, not just the current one. If you also call `bySiteName()`, the later call wins. |

### Sorting and limiting

**Query sorting and result limiting.**

| Method | Parameters | Description |
| --- | --- | --- |
| `.sortBy(field)` | `field` (String) | Sort results by the specified field. Valid values: `author`, `created`, `description`, `displayName`, `endDate`, `keywords`, `modified`, `name`, `path`, `reviewDate`, `startDate`, `summary`, `title`, `teaser`. Any other value throws an error. |
| `.sortDirection(dir)` | `dir` (String) | Set sort order. Valid values: `asc`, `desc`. Any other value throws an error. |
| `.maxResults(n)` | `n` (int) | Limit the number of results. Default is 100, maximum is 2,000 for `.execute()` and 100,000 for `#queryexecute`. |

### Example

```velocity
## Query all pages with the "news" content type, sorted by start date descending
#set ($newsItems = $_.query().byContentType("news").pagesOnly().sortBy("startDate").sortDirection("desc").maxResults(10).execute())
#foreach ($item in $newsItems)
  <h3>$item.metadata.title</h3>
  <p>$item.metadata.summary</p>
#end
```

### Custom directives

The Query API supports three custom directives for advanced processing. See [Query Tool Directives](./query-tool-directives.html) for detailed usage.

**Custom query directives.**

| Directive | Description |
| --- | --- |
| `#queryfilter` | Execute logic on each asset before `maxResults` is applied, allowing custom filtering. |
| `#querysortvalue` | Determine the sort value for each asset using custom logic. |
| `#queryexecute` | Execute on each asset with support for up to 100,000 results. Assets are loaded one at a time to conserve memory. |

## Date Tool

The Date Tool handles date retrieval, formatting, and comparison. See [Date Tool Essentials](./date-tool-essentials.html) for in-depth usage patterns. Added in 6.2

**Date Tool methods.**

| Method | Parameters | Return Type | Description |
| --- | --- | --- | --- |
| `$_DateTool.difference(now, then)` | `now` (Date, Calendar or long), `then` (Date, Calendar or long) | Comparison | Returns the difference from `now` to `then` (`then` minus `now`), rendered as the largest unit with a sign, e.g. `-1 week`. A long is a Unix timestamp in milliseconds. Returns null if either value can't be converted to a date. |
| `$_DateTool.format(format, date)` | `format` (String), `date` (Date, Calendar or long) | String | Formats a date. `format` is `full`, `long`, `medium`, `short`, `default`, or a `SimpleDateFormat` pattern such as `yyyy-MM-dd`. A long is a Unix timestamp in milliseconds; a String of digits is not converted (use `getDate(string)` first). `format(date)` uses the default style, and `format(dateStyle, timeStyle, date)` takes two styles. |
| `$_DateTool.getCalendar()` | — | Calendar | Returns the current date and time as a Calendar object. |
| `$_DateTool.getDate()` | None, or `timestamp` (String) | Date | Without arguments returns the current date. With a String of digits holding a Unix timestamp in milliseconds (such as the `value` of an index block timestamp node), converts it to a Date. Blank returns null; non-numeric text throws an error. A numeric (long) value is not accepted: use `toDate(timestamp)` for that. |
| `$_DateTool.getDay()` | None, or `date` (Date, Calendar or long) | Integer | Returns the day of the month for the date, or for the current date when no argument is given. |
| `$_DateTool.getMonth()` | None, or `date` (Date, Calendar or long) | Integer | Returns the month for the date, or for the current date when no argument is given (0 = January in Java). |
| `$_DateTool.getTime()` | — | long | Returns the current time as a number (Unix timestamp in milliseconds). |
| `$_DateTool.getValue(field, date)` | `field` (Integer or String), `date` (Date, Calendar or long) | Integer | Returns one Calendar field of a date. `field` is a Calendar field number (0 Era, 1 Year, 2 Month, 3 Week of Year, 4 Week of Month, 5 Day of Month, 6 Day of Year, 7 Day of Week, 8 Day of Week in Month, 9 AM/PM, 10 Hour, 11 Hour of Day, 12 Minute, 13 Second, 14 Millisecond, 15 Zone offset, 16 DST offset) or its name, such as `"YEAR"` or `"DAY_OF_MONTH"` (case-insensitive). Returns null if the field or date is invalid. `getValue(field)` reads the current date. |
| `$_DateTool.getYear()` | None, or `date` (Date, Calendar or long) | Integer | Returns the year for the date, or for the current date when no argument is given. |
| `$_DateTool.toDate(format, string)` | `format` (String), `string` (Object) | Date | Converts a formatted date string to a Date using the specified format pattern. A Date, Calendar or long is converted directly. `toDate(object)` uses the default format. Returns null if the value can't be converted. |
| `$_DateTool.toDays(timestamp)` | `timestamp` (long) | long | Converts a timestamp to days since epoch. |
| `$_DateTool.toHours(timestamp)` | `timestamp` (long) | long | Converts a timestamp to hours since epoch. |
| `$_DateTool.toMinutes(timestamp)` | `timestamp` (long) | long | Converts a timestamp to minutes since epoch. |
| `$_DateTool.toMonths(timestamp)` | `timestamp` (long) | long | Converts a timestamp to months since epoch. |
| `$_DateTool.toSeconds(timestamp)` | `timestamp` (long) | long | Converts a timestamp to seconds since epoch. |
| `$_DateTool.toWeeks(timestamp)` | `timestamp` (long) | long | Converts a timestamp to weeks since epoch. |
| `$_DateTool.toYears(timestamp)` | `timestamp` (long) | long | Converts a timestamp to years since epoch. |
| `$_DateTool.whenIs(then)` | `then` (Date, Calendar or long) | Comparison | Returns how far `then` is from the current time, rendered as text such as `3 days ago`. The Comparison also has `.days`, `.hours`, `.full`, `.abbr`, etc. |
| `$_DateTool.whenIs(now, then)` | `now` (Date, Calendar or long), `then` (Date, Calendar or long) | Comparison | Same as above, but relative to `now` instead of the current time, rendered as text such as `1 week earlier` or `2 days later`. |

```velocity
## Index block timestamps are Strings of milliseconds: getDate(String) converts them
#set ($pubDate = $_DateTool.getDate("1524080805084"))
$_DateTool.format("EEEE, MMMM dd, yyyy", $pubDate)
## Expected output (US time zones): Wednesday, April 18, 2018

## A numeric millisecond value goes through toDate, not getDate
$_DateTool.format("yyyy-MM-dd", $_DateTool.toDate(1578526435000))
## Expected output (US time zones): 2020-01-08

## Compare two timestamps
$_DateTool.difference(1578526435000, 1577836800000)
## Expected output: -1 week
$_DateTool.whenIs(1578526435000, 1577836800000)
## Expected output: 1 week earlier
```

## Display Tool

The Display Tool provides methods to control the display of references and format strings. Added in 6.10

**Display Tool methods.**

| Method | Parameters | Description |
| --- | --- | --- |
| `$_DisplayTool.alt(value, alternate)` | `value` (Object), `alternate` (Object) | Returns the value if not null, otherwise returns the alternate. `alt(value)` returns the text `null` when the value is null. |
| `$_DisplayTool.br(text)` | `text` (String) | Inserts `<br />` before each newline character (the newline is kept). |
| `$_DisplayTool.capitalize(text)` | `text` (String) | Capitalizes the first letter of the string. |
| `$_DisplayTool.cell(text, length, suffix)` | `text` (String), `length` (int, default 30), `suffix` (String, default `"..."`) | Truncates or pads (with trailing spaces) the string to exactly `length` characters. When truncated, the suffix is placed inside the length. `length` and `suffix` are both optional. |
| `$_DisplayTool.list(list, separator, lastSeparator)` | `list` (Collection or array), `separator` (String, default `", "`), `lastSeparator` (String, default `" and "`) | Joins the list items with `separator`, using `lastSeparator` before the last item. With only `separator`, it is used between every item. With neither, a list `A, B, C` becomes `A, B and C`. |
| `$_DisplayTool.message(format, args)` | `format` (String), `args` (Object...) | Formats a message string with the specified values. |
| `$_DisplayTool.plural(count, singular, plural)` | `count` (int), `singular` (String), `plural` (String, optional) | Returns the singular form when `count` is 1 (or -1), otherwise the plural form. If `plural` is omitted, basic English rules are used (`page` becomes `pages`, `city` becomes `cities`). |
| `$_DisplayTool.space(count)` | `count` (int) | Generates the specified number of spaces. |
| `$_DisplayTool.stripTags(html, allowedTags)` | `html` (String), `allowedTags` (String..., optional) | Removes HTML tags from the string. Any tag names passed as `allowedTags` (such as `"p", "code"`) are kept, opening and closing. |
| `$_DisplayTool.truncate(text, length, suffix, truncateAtWord)` | `text` (String), `length` (int, default 30), `suffix` (String, default `"..."`), `truncateAtWord` (boolean, default false) | Truncates the string to at most `length` characters, including the suffix. When `truncateAtWord` is true, it cuts at the last space that fits. All arguments after `text` are optional; `suffix` may be passed without `length`. |
| `$_DisplayTool.uncapitalize(text)` | `text` (String) | Uncapitalizes the first letter of the string. |

```velocity
## Fall back when a value is not set
$_DisplayTool.alt($title, "No title provided.")
## Expected output when $title is not set: No title provided.

## Join a list with a custom last separator
$_DisplayTool.list(["A", "B", "C"], ", ", ", or ")
## Expected output: A, B, or C
$_DisplayTool.list(["A", "B", "C"])
## Expected output: A, B and C

## Keep only some tags, and cut at a word boundary
$_DisplayTool.stripTags("<p>Here is a <code>String</code> with <strong>HTML</strong> tags.</p>", "p", "code")
## Expected output: <p>Here is a <code>String</code> with HTML tags.</p>
$_DisplayTool.truncate("Here is a truncated String.", 15, "...", true)
## Expected output: Here is a...
```

## Escape Tool

The Escape Tool provides methods for escaping content for various output contexts and accessing special characters. Added in 6.10

### Special characters

**Escape Tool special character methods.**

| Method | Alias | Character |
| --- | --- | --- |
| `$_EscapeTool.getB()` | `getBackslash()` | `\` |
| `$_EscapeTool.getD()` | `getDollar()` | `$` |
| `$_EscapeTool.getE()` | `getExclamation()` | `!` |
| `$_EscapeTool.getH()` | `getHash()` | `#` |
| `$_EscapeTool.getN()` | `getNewline()` | `\n` |
| `$_EscapeTool.getQ()` | `getQuote()` | `"` |
| `$_EscapeTool.getS()` | `getSingleQuote()` | `'` |

### Escape methods

**Escape and unescape methods.**

| Method | Parameters | Description |
| --- | --- | --- |
| `$_EscapeTool.html(text)` | `text` (String) | Escapes `&`, `<`, `>` and `"` for HTML content. Non-ASCII characters become named entities (for example `&eacute;`). Single quotes are not escaped. Returns `null` for `null`. |
| `$_EscapeTool.javascript(text)` | `text` (String) | Escapes `'`, `"`, `\` and `/` for use in JavaScript strings. Non-ASCII characters become `\uXXXX` sequences. Does not escape `<`, `>` or `&`. Returns `null` for `null`. |
| `$_EscapeTool.xml(text)` | `text` (String) | Escapes `&`, `<`, `>`, `"` and `'` for XML content (the single quote becomes the XML `apos` entity). Non-ASCII characters become numeric character references (see the `café` example below). Prefer this over `html()` in Cascade formats: `html()` emits named entities such as `&eacute;` that are not defined in XML. Returns `null` for `null`. |
| `$_EscapeTool.url(text)` | `text` (String) | URL-encodes a value as UTF-8 (`java.net.URLEncoder`): spaces become `+`, and characters such as `/`, `?` and `=` are percent-encoded. Use it on a single value, not on a whole URL. Returns `null` for `null`. |
| `$_EscapeTool.unicode(code)` | `code` (String) | Converts a hexadecimal code point, optionally prefixed with `\u`, to its Unicode character. The value is always read as hex, so `'004D'` returns `M`. |
| `$_EscapeTool.unescapeHtml(text)` | `text` (String) | Unescapes HTML entities back to their original characters. |
| `$_EscapeTool.unescapeJavascript(text)` | `text` (String) | Unescapes JavaScript escape sequences. Note the method name is spelled `unescapeJavascript`. |
| `$_EscapeTool.unescapeXml(text)` | `text` (String) | Unescapes XML entities back to their original characters. |

```velocity
## xml() vs html(): single quotes and non-ASCII characters
$_EscapeTool.html("it's")
## Expected output: it's
$_EscapeTool.xml("it's")
## Expected output: it, then the single quote escaped as the XML apos entity, then s
$_EscapeTool.xml('café')
## Expected output: caf, then the accented e as a numeric character reference (233)
$_EscapeTool.html('café')
## Expected output: caf&eacute;

$_EscapeTool.javascript('Here is a String with "quotation marks".')
## Expected output: Here is a String with \"quotation marks\".
$_EscapeTool.url('www.example.com/page?key=value')
## Expected output: www.example.com%2Fpage%3Fkey%3Dvalue

$_EscapeTool.unescapeHtml('Here is a String with an escaped ampersand (&amp;).')
## Expected output: Here is a String with an escaped ampersand (&).
$_EscapeTool.unescapeXml('&lt;foo&gt;bar&lt;/foo&gt;')
## Expected output: <foo>bar</foo>
$_EscapeTool.unicode('004D')
## Expected output: M
```

## Field Tool

The Field Tool provides access to Java public constant values via reflection. Added in 7.0

**Field Tool methods.**

| Method | Parameters | Description |
| --- | --- | --- |
| `$_FieldTool.in(object)` | `object` (Object) | Returns an object providing access to the public static constants of the object's class, such as `$_FieldTool.in($_DateTool.getCalendar()).JANUARY`. Passing a class name String (`"java.lang.Integer"`) is deprecated and doesn't work when the secure Velocity engine is enabled, so pass an object instead. Classes in `com.hannonhill` are not accessible, apart from the system version and entity type constants. |

> **Class name Strings are deprecated**
>
> Code like `$_FieldTool.in("java.util.Calendar").YEAR` is deprecated. Pass an object of the class instead, such as `$_FieldTool.in($calendar).YEAR`. To locate assets, use the Locator Tool's type-specific methods instead of `EntityTypes` constants. See [Update deprecated Velocity code](./update-deprecated-velocity-code.html).

```velocity
## Read a constant from the Calendar class by passing a Calendar
$_FieldTool.in($_DateTool.getCalendar()).JANUARY
## Expected output: 0

## Read a constant from the Integer class by passing any Integer
$_FieldTool.in(0).MAX_VALUE
## Expected output: 2147483647

## Add a day to a Calendar
#set ($calendar = $_DateTool.getCalendar())
#set ($_void = $calendar.add($_FieldTool.in($calendar).DAY_OF_YEAR, 1))
<p>Tomorrow = ${calendar.getTime()}</p>
```

## Json Tool

The Json Tool allows you to consume remote JSON resources and work with the resulting data in Velocity. Added in 2025.2

**Json Tool methods.**

| Method | Parameters | Return Type | Description |
| --- | --- | --- | --- |
| `$_JsonTool.fetch(url)` | `url` (String) | HashMap or ArrayList | Fetches JSON from the given URL and returns it as a HashMap (for objects) or ArrayList (for arrays). |
| `$_JsonTool.fetchWithApiKey(url, apiKey)` | `url` (String), `apiKey` (String) | HashMap or ArrayList | Same as `fetch`, but sends `apiKey` in the `Authorization` header. A key with no scheme (no space), such as `abc123`, is sent as `Bearer abc123`. A key that starts with a scheme, such as `Basic dXNlcjpwYXNz` or `Bearer abc123`, is sent unchanged; the scheme must be one of Basic, Bearer, Digest, HOBA, Mutual, AWS4-HMAC-SHA256, OAuth, NTLM or Negotiate. An empty key makes a plain `fetch` call with no header. |

Each Velocity format transformation can make at most 10 Json Tool requests (counting `fetch` and `fetchWithApiKey`); the 11th call fails with an error. Requests time out after 20 seconds, and responses are cached for the same period as feed blocks (5 minutes by default).

```velocity
## Fetch a JSON API and iterate over results
#set ($data = $_JsonTool.fetch("https://api.example.com/items"))
#foreach ($item in $data)
  <p>$item.get("name")</p>
#end

## Fetch a single JSON object
#set ($post = $_JsonTool.fetch("https://jsonplaceholder.typicode.com/posts/1"))
$_EscapeTool.xml($post.get("title"))

## API key without a scheme is sent as "Authorization: Bearer abc123"
#set ($response = $_JsonTool.fetchWithApiKey("https://jsonplaceholder.typicode.com/posts", "abc123"))

## API key with an explicit scheme is sent unchanged
#set ($response = $_JsonTool.fetchWithApiKey("https://api.example.com/items", "Basic dXNlcjpwYXNzd29yZA=="))
```

## List Tool

The List Tool performs common operations on lists and arrays.

**List Tool methods.**

| Method | Parameters | Description |
| --- | --- | --- |
| `$_ListTool.removeNull(list, propertyName)` | `list` (List), `propertyName` (String) | Removes, in place, every item from `list` that does not have the property `propertyName` or whose value for it is null (nested paths such as `metadata.title` are supported). Returns a new List containing the removed items, so assign the result to a variable to keep them. Returns an empty List and changes nothing if `list` is null or `propertyName` is blank. |
| `$_ListTool.reverse(list)` | `list` (List) | Returns a new List with the items in reverse order. The original list is not changed. |
| `$_ListTool.shuffle(list)` | `list` (List) | Returns a new List with the items in random order. The original list is not changed. |
| `$_ListTool.toList(array)` | `array` (Array) | Converts an array of objects to a fixed-size List (items can be read but not added or removed). |

```velocity
## reverse: returns a new list
$_ListTool.reverse(["A","B","C"])
## Expected output: [C, B, A]

## removeNull: removes items in place and returns the removed items
#set ($articles = $_.query().byContentType("Article").execute())
#set ($removed = $_ListTool.removeNull($articles, "metadata.title"))
## $articles now holds only items with a title; $removed holds the rest

## toList: convert an array to a list
#set ($parts = $_ListTool.toList("red,green,blue".split(",")))
$parts
## Expected output: [red, green, blue]
```

## Math Tool

The Math Tool provides mathematical operations and number conversions. Added in 6.10

**Math Tool methods.**

| Method | Parameters | Description |
| --- | --- | --- |
| `$_MathTool.abs(n)` | `n` (Number) | Returns the absolute value. |
| `$_MathTool.add(a, b, ...)` | `numbers` (Number...) | Returns the sum of any number of numbers. Numeric strings are accepted; returns null if any value isn't numeric. |
| `$_MathTool.ceil(n)` | `n` (Number) | Rounds up to the nearest integer. |
| `$_MathTool.div(a, b, ...)` | `numbers` (Number...) | Divides the first number by each following number, in order. Returns null if any divisor is 0. |
| `$_MathTool.floor(n)` | `n` (Number) | Rounds down to the nearest integer. |
| `$_MathTool.getRandom()` | — | Returns a random number between 0.0 and 1.0. |
| `$_MathTool.idiv(a, b)` | `a` (Number), `b` (Number) | Returns the result of integer division. |
| `$_MathTool.max(a, b, ...)` | `numbers` (Number...) | Returns the greatest of any number of numbers. |
| `$_MathTool.min(a, b, ...)` | `numbers` (Number...) | Returns the least of any number of numbers. |
| `$_MathTool.mod(a, b)` | `a` (Number), `b` (Number) | Returns the modulus (remainder). |
| `$_MathTool.mul(a, b, ...)` | `numbers` (Number...) | Returns the product of any number of numbers. |
| `$_MathTool.pow(a, b)` | `a` (Number), `b` (Number) | Returns the first number raised to the power of the second. |
| `$_MathTool.random(a, b)` | `a` (Number), `b` (Number) | Returns a random number between the two specified numbers. |
| `$_MathTool.round(n)` | `n` (Number) | Rounds to the nearest integer. |
| `$_MathTool.roundTo(places, n)` | `places` (int), `n` (Number) | Rounds to the specified number of decimal places. |
| `$_MathTool.sub(a, b, ...)` | `numbers` (Number...) | Subtracts each following number from the first, in order. |
| `$_MathTool.toDouble(n)` | `n` (Object) | Converts the value to a Double. |
| `$_MathTool.toInteger(n)` | `n` (Object) | Converts the value to an Integer. |
| `$_MathTool.toNumber(n)` | `n` (Object) | Converts the value to a Number. |

```velocity
## add takes any number of arguments
$_MathTool.add(12, 30)
## Expected output: 42
$_MathTool.add(1, 2, 3, 4)
## Expected output: 10

$_MathTool.max(7, 23, 11)
## Expected output: 23
$_MathTool.min(7, 23, 11)
## Expected output: 7

## div is true division; idiv truncates both numbers to integers first
$_MathTool.div(7, 2)
## Expected output: 3.5
$_MathTool.idiv(9.33, 3.14)
## Expected output: 3
$_MathTool.roundTo(2, 3.14159)
## Expected output: 3.14
```

## Number Tool

The Number Tool formats numeric data for display. Added in 6.10

**Number Tool methods.**

| Method | Parameters | Description |
| --- | --- | --- |
| `$_NumberTool.currency(n)` | `n` (Object) | Formats a number (or numeric string) as currency using the server's default locale (e.g. `$1,234.56`). Returns null if the value isn't numeric. |
| `$_NumberTool.format(format, n)` | `format` (String), `n` (Object) | Formats a number. `format` is `"number"` (also `"default"`), `"currency"`, `"percent"`, `"integer"`, or a `DecimalFormat` pattern such as `'#,##0.00'`. Use single quotes around patterns that contain `##`. `format(n)` uses the default number style. |
| `$_NumberTool.integer(n)` | `n` (Number) | Formats the number as an integer. |
| `$_NumberTool.isNumeric(value)` | `value` (Object) | Returns true if the value can be parsed as a number. |
| `$_NumberTool.number(n)` | `n` (Number) | Formats as a number with grouping separators. |
| `$_NumberTool.percent(n)` | `n` (Number) | Formats the number as a percentage. |
| `$_NumberTool.toNumber(format, text)` | `format` (String), `text` (Object) | Parses a formatted number string back into a Number. `format` uses the same keywords and patterns as `format`; for example `toNumber("percent", "314%")` returns 3.14. `toNumber(text)` parses with the default number format. Returns null if no conversion is possible. |
| `$_NumberTool.withPadding(n, minDigits)` | `n` (Object), `minDigits` (Object) | Pads the whole-number part with leading zeros to at least `minDigits` digits. Decimals and a leading minus sign are kept (`withPadding(3.5, 3)` returns `003.5`). Returns an empty string if either argument is null. |
| `$_NumberTool.sortable(n)` | `n` (Object), optional `direction` (String) | Generates a zero-padded string that sorts correctly as text, including for negative numbers. `direction` is `"asc"` (default) or `"desc"` (lowercase only). Throws an error if the value isn't numeric. |

```velocity
## Use single quotes around a pattern that contains ##
$_NumberTool.format("integer", 3.14159)
## Expected output: 3
$_NumberTool.format('#,##0.00', 1234.5)
## Expected output: 1,234.50

## Pad the whole-number part with zeros
$_NumberTool.withPadding(1, 3)
## Expected output: 001

## Zero-padded strings that sort as text
$_NumberTool.sortable(1)
## Expected output: 100000000000000000001
$_NumberTool.sortable(1, "desc")
## Expected output: 099999999999999999999
```

## Property Tool

The Property Tool lets you inspect and evaluate object properties at runtime. Added in 7.4

### Type checking

**Property Tool type-checking methods.**

| Method | Parameters | Description |
| --- | --- | --- |
| `$_PropertyTool.isArray(obj)` | `obj` (Object) | Returns true if the object is an array. |
| `$_PropertyTool.isIterable(obj)` | `obj` (Object) | Returns true if the object can be looped over with a `#foreach`: any Iterable (such as a List or Set), a Map, or an array. Returns false for null and strings. |
| `$_PropertyTool.isList(obj)` | `obj` (Object) | Returns true if the object is a list. |
| `$_PropertyTool.isMap(obj)` | `obj` (Object) | Returns true if the object is a map. |
| `$_PropertyTool.isSet(obj)` | `obj` (Object) | Returns true if the object is a set. |
| `$_PropertyTool.isString(obj)` | `obj` (Object) | Returns true if the object is a string. |

### Value checking

**Property Tool value-checking methods.**

| Method | Parameters | Description |
| --- | --- | --- |
| `$_PropertyTool.isEmpty(obj)` | `obj` (Object) | Returns true if the object is null or its string value is empty or whitespace-only. The check uses the object's string value, so an empty list (`[]`) is not considered empty. |
| `$_PropertyTool.isNotEmpty(obj)` | `obj` (Object) | Returns true if the object is not null and its string value is not empty or whitespace-only. |
| `$_PropertyTool.isNull(obj)` | `obj` (Object) | Returns true if the object is null. |
| `$_PropertyTool.isNotNull(obj)` | `obj` (Object) | Returns true if the object is not null. |

### Output methods

**Property Tool output methods.**

| Method | Parameters | Description |
| --- | --- | --- |
| `$_PropertyTool.outputFirstNotEmpty(values)` | `values` (Object...) | Returns the string value of the first argument that is not null, empty, or whitespace-only. Returns an empty string if every argument is empty. |
| `$_PropertyTool.outputProperties(obj)` | `obj` (Object) | Returns a string with the object's class name followed by its readable properties (every `get` or `is` method) and their return types, sorted alphabetically. Returns "The object is null" for null. Useful for debugging; wrap the output in `<pre>` to keep the line breaks. |

```velocity
$_PropertyTool.isEmpty("   ")
## Expected output: true
$_PropertyTool.isString(1)
## Expected output: false

## First non-empty value
$_PropertyTool.outputFirstNotEmpty($currentPage.metadata.title, $currentPage.getStructuredDataNode("heading").textValue, $currentPage.name)

## Discover what you can call on an API object
<pre>$_PropertyTool.outputProperties($currentPage)</pre>
```

## Regex Tool

The Regex Tool allows you to work with regular expressions in Velocity. Added in 8.16

**Regex Tool methods.**

| Method | Parameters | Return Type | Description |
| --- | --- | --- | --- |
| `$_RegexTool.compile(regex)` | `regex` (String) | Pattern | Compiles a regular expression into a `java.util.regex.Pattern`. Call `matcher(text)` on the result to get a Matcher, then use `find()` for a partial match, `matches()` to require the whole text to match, or `replaceAll(replacement)`. Throws an error if the expression is invalid. There is no flags argument; use inline flags such as `(?i)`. |

```velocity
## Compile a regex and use it for matching
#set ($pattern = $_RegexTool.compile("[0-9]+"))
#set ($matcher = $pattern.matcher("Order 12345"))
#if ($matcher.find())
  Order number: $matcher.group()
#end

## matches(): the whole string must match
#set ($pattern = $_RegexTool.compile("^[a-zA-Z0-9]*$"))
$pattern.matcher("abc123").matches()
## Expected output: true
$pattern.matcher("abc 123").matches()
## Expected output: false

## replaceAll: collapse whitespace
$_RegexTool.compile('\s+').matcher("a   b    c").replaceAll(" ")
## Expected output: a b c
```

## Serializer Tool

The Serializer Tool converts JDOM Elements to XML strings or JSON. Added in 6.2

**Serializer Tool methods.**

| Method | Parameters | Return Type | Description |
| --- | --- | --- | --- |
| `$_SerializerTool.serialize(element, removeRoot)` | `element` (Element), `removeRoot` (Boolean) | String | Converts a JDOM Element to an XML string. If `removeRoot` is `true`, the element's own start and end tags are omitted and only its contents are returned. Returns `null` if `element` is null. |
| `$_SerializerTool.toJson(element, removeRoot)` | `element` (Element), `removeRoot` (Boolean) | String | Converts a JDOM Element to a JSON string. If `removeRoot` is `true`, the element's own tags are dropped and its children become the top-level keys. Returns `null` if `element` is null. |
| `$_SerializerTool.toJson(xml, removeRoot)` | `xml` (String), `removeRoot` (Boolean) | String | Converts an XML string to a JSON string. If `removeRoot` is `true` and the XML has a single root element, that root is removed and its contents become the top-level keys. Returns `null` if `xml` is null. |
| `$_SerializerTool.toJson(map)` | `map` (Map) | String | Converts a Map with String keys to a JSON string. Key order in the output is not guaranteed. |

```velocity
## Convert the page's XML to a JSON string
#set ($json = $_SerializerTool.toJson($contentRoot, false))
<script>
  var pageData = $json;
</script>

## serialize: output the markup of a WYSIWYG without its wrapper element
## <wysiwyg><p>Here is a <strong>WYSIWYG</strong> element.</p><p>It has multiple lines.</p></wysiwyg>
#set ($wysiwyg = $_XPathTool.selectSingleNode($contentRoot, "//wysiwyg"))
$_SerializerTool.serialize($wysiwyg, true)
## Expected output:
## <p>Here is a <strong>WYSIWYG</strong> element.</p><p>It has multiple lines.</p>

## toJson with an XML string
$_SerializerTool.toJson("<a><b></b><c/></a>", false)
## Expected output: {"a":{"b":"","c":""}}
$_SerializerTool.toJson("<a><b></b><c/></a>", true)
## Expected output: {"b":"","c":""}

## toJson with a Map (key order can vary)
#set ($a = {"test": "testing", "array": [1, 2, 3]})
$_SerializerTool.toJson($a)
```

## Sort Tool

The Sort Tool sorts lists two ways: XML nodes (JDOM Elements) by criteria modeled on XSLT's `<xsl:sort>`, and any list of objects, such as query results, by a property. Added in 6.2

**Sort Tool methods.**

| Method | Parameters | Description |
| --- | --- | --- |
| `$_SortTool.addSortCriterion()` | `xpath` (String), `language` (String), `dataType` (String), `order` (String), `caseOrder` (String) | Adds a sort criterion for the next `sort(list)` of XML nodes. Call it more than once to sort by several criteria, in order. `language` is a two-letter ISO-639 code (default `en`). `dataType` is `text` (default), `number`, or `qname`. `order` is `ascending` (default) or `descending`. `caseOrder` is `lower-first` (default) or `upper-first`. Unrecognized values fall back to the defaults. |
| `$_SortTool.sort(list)` | `list` (List) | For a list of XML nodes, sorts the list in place by the criteria from `addSortCriterion()`, then clears them. Don't assign the result: it is an empty list. For any other list, returns a new list in natural order. |
| `$_SortTool.sort(list, property)` | `list` (List), `property` (String or List) | Returns a new list sorted by a property of each item, or by a list of properties in order. Add `:desc` to a property for descending order (`:asc` is the default). Write properties without the `get` prefix, with arguments in parentheses and no quotes, for example `metadata.dynamicField(course-number).value` or `structuredDataNode(main/header).textValue`. The structured data path is the same `group/field` path the Query API filters use. |

```velocity
## Sort XML nodes from an Index Block: newest first, then by title
#set ($articles = $_XPathTool.selectNodes($contentRoot, "/system-index-block/system-page"))
$_SortTool.addSortCriterion("start-date", "en", "number", "descending", "upper-first")
$_SortTool.addSortCriterion("title", "en", "text", "ascending", "upper-first")
$_SortTool.sort($articles)
#foreach ($article in $articles)
  <p>$article.getChild("title").text</p>
#end
```

```velocity
## Sort a folder's children by name
#set ($sorted = $_SortTool.sort($currentPage.parentFolder.children, "name"))

## Sort by a metadata field
#set ($sorted = $_SortTool.sort($courses, "metadata.dynamicField(course-number).value"))

## Sort query results by a structured data field, descending
#set ($articles = $_.query().byContentType("Articles").execute())
#set ($sorted = $_SortTool.sort($articles, "structuredDataNode(main/header).textValue:desc"))

## Sort repeating structured data nodes by a child field, descending
#set ($rows = $currentPage.getStructuredDataNodes("row"))
#set ($sorted = $_SortTool.sort($rows, "child(title).value:desc"))
```

## String Tool

The String Tool provides methods for finding and extracting content within strings. Added in 6.2

**String Tool methods.**

| Method | Parameters | Return Type | Description |
| --- | --- | --- | --- |
| `$_StringTool.generateUUID()` | — | String | Generates a random UUID string. |
| `$_StringTool.getStringBuilder()` | — | StringBuilder | Returns a new StringBuilder instance for building strings efficiently. |
| `$_StringTool.substringAfter(haystack, needle)` | `haystack` (String), `needle` (String) | String | Returns the portion of `haystack` after the first occurrence of `needle`. Returns an empty string if either argument is empty or whitespace-only, if `needle` is not found, or if `haystack` ends with `needle`. |
| `$_StringTool.substringBefore(haystack, needle)` | `haystack` (String), `needle` (String) | String | Returns the portion of `haystack` before the first occurrence of `needle`. Returns an empty string if either argument is empty or whitespace-only, if `needle` is not found, or if `needle` is at the very start of `haystack`. |

```velocity
$_StringTool.substringAfter("aaabbbccc", "bbb")
## Expected output: ccc
$_StringTool.substringBefore("aaabbbccc", "bbb")
## Expected output: aaa

## Not found, or needle at the edge: empty string
$_StringTool.substringBefore("aaabbbccc", "aaa")
## Expected output: (empty)
$_StringTool.substringAfter("aaabbbccc", "ccc")
## Expected output: (empty)

#set ($stringBuilder = $_StringTool.getStringBuilder())
#set ($_void = $stringBuilder.append("foo"))
#set ($_void = $stringBuilder.append("+"))
#set ($_void = $stringBuilder.append("bar"))
$stringBuilder
## Expected output: foo+bar
```

## XPath Tool

The XPath Tool queries JDOM XML trees using XPath expressions. Added in 6.2

**XPath Tool methods.**

| Method | Parameters | Return Type | Description |
| --- | --- | --- | --- |
| `$_XPathTool.selectNodes(context, xpath)` | `context` (Object), `xpath` (String) | List | Returns a list of the nodes matching the XPath expression. The expression is evaluated against `context`, typically `$contentRoot` or an element taken from it. Returns an empty list when nothing matches. |
| `$_XPathTool.selectSingleNode(context, xpath)` | `context` (Object), `xpath` (String) | Object | Returns the first node matching the XPath expression, or null when nothing matches. The expression is evaluated against `context`. |

```velocity
## Select all "item" elements from the content XML
#set ($items = $_XPathTool.selectNodes($contentRoot, "//item"))
#foreach ($item in $items)
  <p>$item.getChildText("title")</p>
#end

## Select a single element (null if nothing matches)
#set ($header = $_XPathTool.selectSingleNode($contentRoot, "/system-data-structure/header"))
#if ($header)
  $header.text
#end

## All pages in an index block
#set ($pages = $_XPathTool.selectNodes($contentRoot, "//system-page"))

## Relative paths: evaluate against a node returned by an earlier query
#foreach ($page in $_XPathTool.selectNodes($contentRoot, "//system-page"))
  #set ($title = $_XPathTool.selectSingleNode($page, "title"))
  $title.value
#end
```
