Search the documentation
 Show GitHub edit links  Hide GitHub edit links
In OpenCms since: 10.0 Documented since: 10.0 Latest revision for: 10.0 Valid for OpenCms: 10.5.2

OpenCms 10 introduces a new way to build lists. The example shows the three new features that ease the creation of editable lists:

  • Content collection via <cms:search>
  • Formatting of list entries with "normal" formatters via <cms:display>
  • A create option for the empty list via <cms:edit>

The list formatter is kept simple. Typically, it would facilitate pagination, dynamic content reloading and a like. Moreover, for a list in a productive site, changing the sort order should only re-render the list itself, not the whole page. A more elaborate example for an editable list is found in the Apollo demo, shipped with OpenCms 10 (see blog entries and events).

Using <cms:search> for the collecting contents provides the full power of the Solr search engine. Thus, it can be featured with facets, full text search, sorting etc. In the example, we only implement a sort option.

The result

Click on the headings of the list entries to see their detail views. If you are offline, you might also change the list content or even play with it's formatter (shown below). You can also edit, create or delete list entries.

In the detail view a special formatter is used. It displays the title, the text and the image, if present. Hence, it may happen that the content says "Do something!" - but you simply can't because of the different formatter that is used. Or the article might describe somethings that's not true for its detail view formatter.
 

Articles

Sort by date last modified:

Create a new list entry of type documentation-demo-article.

 

Example resources and the interesting spots

The example focuses on several OpenCms features used in combination. These are:

  • The DisplayTypeSelectWidget for chosing formatters for the list content
  • The tag <cms:search> to select content
  • The tag <cms:display> to edit content and create new contents
  • The tag <cms:edit> to allow content creation when the list is empty

Display formatters and the DisplayTypeSelectWidget

Since OpenCms 10, formatters can be marked as "Use as display formatter" in their configuration. All formatters with that option enabled and active in the current subsitemap are collected by the DisplayTypeSelectWidget. In the example, we use the widget to select the formatter for the elements in the list. We have only two display formatters present, both for the type documentation-demo-article.

Here is the schema of the list type:

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
	elementFormDefault="qualified">

	<xsd:include schemaLocation="opencms://opencms-xmlcontent.xsd" />

	<xsd:element name="DocumentationDemoLists" type="OpenCmsDocumentationDemoLists" />

	<xsd:complexType name="OpenCmsDocumentationDemoLists">
		<xsd:sequence>
			<xsd:element name="DocumentationDemoList" type="OpenCmsDocumentationDemoList"	minOccurs="0" maxOccurs="unbounded" />
		</xsd:sequence>
	</xsd:complexType>

	<xsd:complexType name="OpenCmsDocumentationDemoList">
		<xsd:sequence>
			<xsd:element name="Headline" type="OpenCmsString" />
			<xsd:element name="TypesToCollect" type="OpenCmsDisplayFormatter" />
		</xsd:sequence>
		<xsd:attribute name="language" type="OpenCmsLocale" use="required" />
	</xsd:complexType>

	<xsd:annotation>
		<xsd:appinfo>
			<mappings>
				<mapping element="Headline" mapto="property:Title" />
			</mappings>
			<searchsettings containerPageOnly="true">
				<searchsetting element="TypesToCollect" searchcontent="false" />
			</searchsettings>
		</xsd:appinfo>
	</xsd:annotation>

</xsd:schema>

The interesting part is the use of the type OpenCmsDisplayFormatter for the element TypesToCollect. Via the type, the DisplayTypeSelectWidget is configured for the element. The widget makes up a select box for the item formatters as shown below.

Rendering the list

Below, you see the formatter for the list. In the code, you find some comments that point to the interesting spots.

<%@page buffer="none" session="false" trimDirectiveWhitespaces="true"%>
<%@ taglib prefix="cms" uri="http://www.opencms.org/taglib/cms"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>

<cms:formatter var="content">
	<c:choose>
		<c:when test="${content.value.TypesToCollect.isEmptyOrWhitespaceOnly}">
			<div>Please edit the list and choose the resources you want to collect.</div>
		</c:when>
		<c:otherwise>
			<%-- Get the type of the resources to display --%>
			<c:set var="resType">${fn:substringBefore(content.value.TypesToCollect, ":")}</c:set>
			<%-- Get the path under which the resources should be searched (including subpathes) --%>
			<c:set var="path">${cms.requestContext.siteRoot}/</c:set>
			<%-- Create the configuration for <cms:search> --%>
			<c:set var="searchconfig">
				{
					"ignorequery" : true,
					"extrasolrparams" : "fq=parent-folders:\"${path}\"&fq=type:${resType}a",
					"sortoptions" : [
						{ "label" : "Ascending", "paramvalue" : "asc", "solrvalue" : "lastmodified asc" },
						{ "label" : "Descending", "paramvalue" : "desc", "solrvalue" : "lastmodified desc" }
					  ],
					"pagesize" : 3
				}	
			</c:set>
			<div style="margin-bottom: 20px">
				<div>
					<h2>${content.value.Headline}</h2>
				</div>
				<%-- Perform the search, the result object is stored in the variable "search" --%>
				<cms:search configString="${searchconfig}" var="search"
					addContentInfo="true" />
				<div>
					<%-- Get the sort controller from the search result object --%>
					<c:set var="sortController" value="${search.controller.sorting}" />
					<c:if
						test="${not empty sortController and not empty sortController.config.sortOptions}">
						Sort by date last modified:
						<%-- Render the sort options making heavy use of the sort controller --%>
						<select onchange="window.location.href='<cms:link>${cms.requestContext.uri}</cms:link>?'
											+ this.value">
							<c:forEach var="sortOption"
								items="${sortController.config.sortOptions}">
								<option ${sortController.state.checkSelected[sortOption] ? 
											' selected="selected"' : ""} 
										value="${search.stateParameters.setSortOption[sortOption.paramValue]}">
									${sortOption.label}
								</option>
							</c:forEach>						
						</select>
					</c:if>
				</div>
				<hr />
				<c:choose>
					<c:when test="${search.numFound > 0}">
						<%-- Iterate through the search results and render them via <cms:display> --%>
						<c:forEach var="result" items="${search.searchResults}">
							<cms:display value='${result.xmlContent.filename}'
								displayFormatters="${content.value.TypesToCollect}"
								editable="true" create="true" delete="true" />
								<hr />
						</c:forEach>
					</c:when>
					<c:otherwise>
						<%-- Show an option to create a new content via <cms:edit> --%>
						<cms:edit createType="${resType}" create="true">
							<div>
								Create a new list entry of type ${resType}.						
							</div>
						</cms:edit>
						<hr />
					</c:otherwise>
				</c:choose>
			</div>
		</c:otherwise>
	</c:choose>
</cms:formatter>

Looking at the code, we see that the <cms:search> tag is quite mighty. The configuration is done via a JSON String. It tells that:

  • we do not want to enter a special search query ("ignorequery")
  • we search only for contents of one type and only in folders under a specific root folder ("extrasolrparams")
  • we want two sort options ("sortoptions")
  • we set the page size (number of returned results) to 3 ("pagesize").

There are many more configuration options. For details, consult the JavaDoc for the classes under org.opencms.jsp.search.config and the class org.opencms.jsp.search.config.parser.CmsJSONSearchConfigurationParser.

The tag <cms:search> returns a deeply structured result object of type org.opencms.jsp.search.result.CmsSearchResultWrapper that allows to

  • access the search result
  • access the search configuration
  • access the state of search options (e.g., the currently selected sort option)
  • construct the correct request parameters to manipulate the search state (e.g., set another search option)

To get an impression of the result object's structure, take a look at the code snippets for rendering the select box with the sort options. Also, have a look at how we iterate through the search results (that are provided as objects of type org.opencms.jsp.search.result.CmsSearchResourceBean). For a detailed overview on the result object, take a look at the JavaDoc for CmsSearchResultWrapper.

Besides handing over a configuration to <cms:search>, we specify addContentInfo. If set to true, the content that is collected in the list is treated as part of the page on publish. Hence, when you publish the page and an element in the list - even if on a page of the list not visible at the moment - is published as related resource of the page well.

The second interesting tag in the example is <cms:display>. The tag simply renders the resource, whose (site-relative) path is provided via the attribute value. For rendering, it uses the formatter that is specified via the displayFormatters attribute for the specific type of content that should be rendered. Using displayFormatters also several formatters for different types can be provided. Setting editable, delete and create to true enables the edit, delete and the create options at the rendered element. The tag has more options, e.g., it can pass element settings to formatters. Have a look at the taglib documentation to get an overview.

The last interesting spot in the example is the use of <cms:edit>. The tag allows to display an edit point at any HTML snippet. Here we use it to generate an add option for resources of type ${resType}, i.e., documentation-demo-article in the example. The tag has more options. Have a look at the taglib documentation to get an overview.

You can improve this page

Please contribute your suggestions or comments regarding this topic on our wiki. For support questions, please use the OpenCms mailing list or go for professional support.