Feature Update Alert: Velocity Locator Tool

By Ryan Griffith — Tuesday, April 14th, 2015 at 9:00am
Feature Update Alert Velocity Locator Tool

Last March our Head of Customer Experience, Charlie Holder, put together an excellent article demonstrating two Velocity tools that were added as of Cascade 7.4 - the Locator Tool and Property Tool.

If you haven't yet read Charlie's article, or aren't familiar with either the Locator Tool or Property Tool, I highly recommend taking a moment to read over the article.

So, here we are, one year and numerous releases later. Let's look back at a couple of new features that have been added to the Velocity toolbox and use them to rework Charlie's news landing example. Specifically, these new features are the $currentPage variable, the Locator Tool's Query API, and the #import directive.

The $currentPage Variable

You read that correctly … a variable.

As of Cascade 7.10.1, the $currentPage variable gives you direct access to a Page object via the Cascade API that includes information about the calling page. This information includes, but is not limited to: metadata, structured data (ie Data Definition content), system properties, etc.

This means you are no longer required to use the Locator Tool or pair the Velocity Format with an Index Block to access information about the calling page. The result is cleaner and more maintainable code.

How to Use the Variable

Using the variable is just like any other variable;

## Output the calling page's system name

Yup, it's that simple.

To get a quick view of what methods and properties are available, we can use the Property Tool's outputProperties() method:


This should output something like the following:

Object type: com.hannonhill.cascade.api.adapters.PageAPIAdapter
 - lastPublishedBy: String
 - lastModified: Date
 - link: String
 - dataDefinitionPath: String
 - identifier: PathIdentifier
 - parentFolder: Folder
 - getStructuredDataNode(String): StructuredDataNode
 - isUserCanWrite(String): boolean
 - structuredData: StructuredDataNode[]
 - currentUserCanWrite: boolean
 - createdOn: Date
 - metadata: Metadata
 - folderOrder: int
 - parentFolderIdentifier: PathIdentifier
 - createdBy: String
 - identifer: Identifier
 - lastPublishedOn: Date
 - path: String
 - shouldBePublished: boolean
 - lastModifiedBy: String
 - currentUserCanRead: boolean
 - isUserCanRead(String): boolean
 - hideSystemName: boolean
 - siteName: String
 - shouldBeIndexed: boolean
 - siteId: String
 - xHTMLAsXMLElement: Element
 - name: String
 - xHTML: String
 - getStructuredDataNodes(String): StructuredDataNode[]

The Locator Tool's Query API

Introduced in Cascade Server 7.12.2, the Query API allows you to easily search, filter, and sort assets using a number of properties, with the returned collection containing Cascade API objects.

Much like the $currentPage variable, this means you are no longer required to pair the Velocity Format with a bulky Index Block to access information about assets. Additionally, you can choose to include assets that are not set to be indexable or publishable, something Index Blocks are unable to do.

How to Use the Tool

Before we begin, one thing to note is how you invoke the Query API. Because this is a method of the Locator Tool, it is called using the Locator Tool's "$_" syntax.

The first step is to create a new query object and set it to a variable so it can be reused:

#set ( $query = $_.query() )

All executions on the query object require that either a Metadata Set or Content Type path be specified; otherwise, an error will be shown:

#set ( $query = $query.byMetadataSet("Default") )
#set ( $query = $query.byContentType("News/Article") )

Next, you can specify which types of assets should be included in the results:

#set ( $query = $query.includePages(true) )
#set ( $query = $query.includeFiles(true) )
#set ( $query = $query.includeBlocks(false) )
#set ( $query = $query.includeFolders(true) )
#set ( $query = $query.includeSymlinks(false) )

Note: All asset type properties are true by default, but all are listed above for reference. If you are matching by Content Type, only pages will be returned.

Finally, a number of filtering and sorting options are available to you:

  • maxResults(number of results) - Limit the query to a specified number of results. The default value is 100 while the maximum value for this filter is 500. Anything greater will be trimmed down to 500.
  • sortBy(field name) - Results can be sorted by any one of the following properties:
    • Static Metadata Fields ("author", "description", "displayName", "endDate", "keywords", "reviewDate", "startDate", "summary", "teaser", "title")
    • Last modified date ("modified")
    • Creation date ("created")
    • Asset name ("name")
    • Asset path ("path")
  • sortDirection([asc|desc]) - Results can be sorted in ascending ("asc") or descending ("desc") order.
  • siteName(siteName) - Results can be limited to a particular Site using the Site's name.
  • searchAcrossAllSites() - Results can come from all Sites.
  • publishableOnly([true|false]) - Results can be filtered to only include those that are publishable.
  • indexableOnly([true|false]) - Results can be filtered to only include those that are indexable. Note that when set to true, matched results must also have an empty start date or a start date in the past and an empty end date or an end date in the future.

For those developers out there who like to keep their code minimal and concise, all query methods can be chained together:

#set ( $results = $query.byMetadataSet("Default").includePages(true).sortBy("name").execute() )

Once you have set all of your desired query object properties and filtering options, you call the execute() method to return your results:

#set ( $results = $query.execute() )
#foreach ( $asset in $results )

If you would like to get a quick view of the properties and filters that are set for the query object, you can simply output the $_.query() object itself. This will output something like the following:

[metadataFieldName = null [displayName|title|summary|teaser|keywords|description|author|startDate|endDate|reviewDate]
metadataFieldValue = null
* Assign metadataFieldName and metadataFieldValue by calling hasMetadata($name, $value)
metadataSetLink = Default
includePages = true
includeBlocks = true
includeFiles = false
includeFolders = true
includeSymlinks = true
* Assign contentTypeLink by calling byContentType($link) or metadataSetLink by calling byMetadataSet($link)
sortBy = [summary|startDate|keywords|reviewDate|endDate|modified|author|title|created|description|teaser|name|path|displayName]
sortDirection = asc [desc|asc]
maxResults = 100 [-1 for unlimited]
siteName = www.example.edu
* Call searchAcrossAllSites() to null out
indexableOnly = true
publishableOnly = false]
Call execute() to get search results

The #import Directive

Do you find yourself repeating a lot of code across your Velocity Formats? If so, the #import directive is a great solution for you.

Added in Cascade 7.6, the #import directive makes it is possible to include code from one Velocity Format in another Velocity Format. What this means for developers is you can now reuse code that would otherwise need to be copy/pasted into each Velocity Format that requires that same code.

How to Use the Directive

The #import directive takes a single argument that is a path to another Velocity Format in the system.


Going a step further, Velocity Formats can also be included from other Sites. Simply prepend the path with site://<site name>.


Putting It All Together

Now that we have a general overview of these new additions to the Velocity toolbox, let's take a look back at Charlie's news landing example to see how we can simplify it a bit.

The first improvement to note is that we can replace the required Content Type Index Block with the Query API while also sorting and limiting the results:

#set ( $query = $_.query() )
#set ( $query = $query.byMetadataSet("News/Article") )
#set ( $query = $query.byContentType("News/Article") )
#set ( $query = $query.sortBy("startDate") )
#set ( $query = $query.sortDirection("desc") )
#set ( $query = $query.maxResults(2) )
#set ( $news = $query.execute() )

The second improvement is that we no longer have to use the Locator Tool to access information about the articles, because the results are already Cascade API objects.

Our final version of the code might look something like this:

#set ( $query = $_.query() )
#set ( $news = $query.byMetadataSet("News/Article").byContentType("News/Article").sortBy("startDate").sortDirection("desc").maxResults(2).execute() )
#if ( $news.size() > 0 )
  <div class="col-md-12 column" id="latestNews">
  #foreach ( $n in $news )
    #set ( $content = $page.getStructuredDataNode("content").textValue )
    <h4><a href="${n.link}">$_EscapeTool.xml($n.metadata.title)</a></h4>
    <span class="text-muted">${n.metadata.startDate}</span>
    $_DisplayTool.truncate($_DisplayTool.stripTags($content), 250, " ...", true)

Let's take a second and assume your page region already has a Velocity Format applied to it, so you are unable to apply our news landing Format to the region. Not a problem at all, we can simply include it where we need to output the content:


You could even move the news landing code into a #macro if you wish to add all of the includes at the top of the Velocity Format and output the content later in the code.

In the end, our new Velocity Format may only be a few lines shorter; however, it is much more portable, because it can be applied to virtually any page region or included within existing Velocity Formats without requiring an additional Content Type Index Block.

Subscribe to our blog via RSS Download RSS

Hannon Hill Corporation

3423 Piedmont Road NE, Suite 520
Atlanta, GA 30305

Phone: 678.904.6900
Toll Free: 1.800.407.3540
Fax: 678.904.6901


GSA Contract Holder