OpenCms Documentation

Lists and detail pages III

 Show GitHub edit links  Hide GitHub edit links
In OpenCms since: 11.0 Valid for OpenCms: 11.0.0

OpenCms 11 introduces a list content type already shipped with the core. The example shows the main features that ease the creation of editable lists:

  • Content collection via the list type and the use of  <cms:simplesearch>
  • Formatting of list entries with display formatters via <cms:display>
  • A create option for the empty list via <cms:edit>
  • The integration of display formatter element settings into the element settings dialog of the list.
  • The "Lists" workplace app to manage the contents in the list.

The list formatter is kept simple, only showing parts of the available options (sorting and pagination). It could also facilitate facetting and a search option. Moreover, on a productive system the list content would dynamically reload on change, i.e., changing the sort order or the currently displayed page should only re-render the list itself, not the whole page. A more elaborate example for an editable list is found in the demo template, shipped with OpenCms (version 11 and later).

The example can only cover parts of the features. Have a look at the related topics if you require more detailed information.

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. You should also play with the element settings of the list formatter.

Moreover you can sort the list or use the pagination. Note that in this minimal example, the whole page reloads when you change the page or the sort order. You might reload the list via AJAX in a more advanced example.

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.

Example resources and the interesting spots

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

  • Element settings from display formatters shown as setting for the list.
  • The list type integrated in OpenCms and it's management app.
  • The tag <cms:simplesearch> that uses the list content as search configuration and that allows to adjust this configuration partly, as done for the page size here.
  • The usage of the search attribute, returned by the <cms:simplesearch> tag
  • The tag <cms:display> to display content with display formatters, even passing settings, as well as to delete, create and edit contents.
  • The tag <cms:edit> to allow content creation when the list is empty.

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">
  <div style="margin-bottom: 20px">
    <div>
      <h2>${content.value.Title}</h2>
    </div>
    <%-- Check, if the list is sufficiently configured. --%>
    <c:choose>
    <c:when test="${empty content.value.TypesToCollect}">
      <h3>Your list is not configured correctly.</h3>
      <p>Please choose at least one display formatter.</p>
    </c:when>
    <c:otherwise>
      <%-- read the page size element setting as wrapper and convert it. --%>
      <c:set var="pageSize">${cms.element.setting.pageSize.toInteger}</c:set>
      <%--
        Perform the search, the result object is stored in the variable "search"
        Note that all configuration is read from the listconfig content.
        We only overwrite part of the configuration setting the page size as it is
        provided in the list's element setting.
      --%>
      <cms:simplesearch configFile="${content.filename}" var="search"
        configString="{pagesize: ${pageSize}}"
        addContentInfo="true" />
      <div>
        <%-- Get the sort controller from the search result object --%>
        <c:set var="sortController" value="${search.controller.sorting}" />
        Sort by:
        <%-- 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]}">
              <%--
              	You might use the option as (part of a) message key for localized labels
              	instead of adding a choice here.
              --%>
              <c:choose>
                <c:when test="${sortOption.label eq 'date.asc'}">Date ascending</c:when>
                <c:when test="${sortOption.label eq 'date.desc'}">Date ascending</c:when>
                <c:when test="${sortOption.label eq 'title.asc'}">Title ascending</c:when>
                <c:when test="${sortOption.label eq 'title.desc'}">Title descending</c:when>
                <c:when test="${sortOption.label eq 'order.asc'}">Order ascending</c:when>
                <c:when test="${sortOption.label eq 'order.desc'}">Order descending</c:when>
              </c:choose>
            </option>
          </c:forEach>
        </select>
      </div>
      <%-- If results are found, display them. Otherwise, show an option to add new contents. --%>
      <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}" varStatus="status">

            <cms:display value="${result.fields['id']}"
              displayFormatters="${content.value.TypesToCollect}"
              editable="true"
              create="true"
              delete="true">
              <%--
                We pass all settings from the list to the display formatter.
                Note, that they are available as settings and parameters in the display formatter.
                Hence, we typically access them as "normal" settings in the display formatter's JSP.
              --%>
              <c:forEach var="parameter" items="${cms.element.settings}">
                 <cms:param name="${parameter.key}" value="${parameter.value}" />
              </c:forEach>

              <%-- We provide additional settings for the display formatter. --%>
              <cms:param name="index">${status.index + search.start}</cms:param>
                <cms:param name="showHr">${not status.last}</cms:param>

            </cms:display>

          </c:forEach>

          <%-- This is a simple example for a pagination using the pagination controller. --%>
          <c:set var="pagination" value="${search.controller.pagination}" />
          <!-- show pagination if it should be given and if it's really necessary -->
          <c:if test="${not empty pagination && search.numPages > 1}">
            <ul class="pagination">
              <li ${pagination.state.currentPage > 1 ? "" : "class='disabled'"}>
                <c:set var="params">${search.stateParameters.setPage['1']}</c:set>
                <a href="<cms:link>${cms.requestContext.uri}?${params}</cms:link>">
                  <span aria-hidden="true">First</span>
                </a>
              </li>
              <c:set var="previousPage">${pagination.state.currentPage > 1
                ? pagination.state.currentPage - 1 : 1}</c:set>
              <li ${pagination.state.currentPage > 1 ? "" : "class='disabled'"}>
                <c:set var="params">${search.stateParameters.setPage[previousPage]}</c:set>
                <a href="<cms:link>${cms.requestContext.uri}?${params}</cms:link>">
                  <span aria-hidden="true">Previous</span>
                </a>
              </li>
              <c:forEach var="i" begin="${search.pageNavFirst}"
                     end="${search.pageNavLast}">
                <c:set var="is">${i}</c:set>
                <li ${pagination.state.currentPage eq i ? "class='active'" : ""}>
                  <c:set var="params">${search.stateParameters.setPage[is]}</c:set>
                  <a href="<cms:link>${cms.requestContext.uri}?${params}</cms:link>">
                    ${is}
                  </a>
                </li>
              </c:forEach>
              <c:set var="pages">${search.numPages}</c:set>
              <c:set var="next">${pagination.state.currentPage < search.numPages ?
                pagination.state.currentPage + 1 : pagination.state.currentPage}</c:set>
              <li ${pagination.state.currentPage >= search.numPages ? "class='disabled'" : ""}>
                <c:set var="params">${search.stateParameters.setPage[next]}</c:set>
                <a href="<cms:link>${cms.requestContext.uri}?${params}</cms:link>">
                  <span aria-hidden="true">Next</span>
                </a>
              </li>
              <%-- We do not place a link to the last page, since this is typically not needed.
                Moreover, there might be more results than are returned at most, depending on your
                search configuration. By default at most 400 results are returned.
               --%>
            </ul>
          </c:if>
        </c:when>
        <c:otherwise>
          <%-- Show an option to create a new content via <cms:edit> --%>
          <c:if test="${cms.isEditMode}">
            <%-- We could display resources of more than one type in the list. --%>
            <c:forEach var="type" items="${content.valueList.TypesToCollect}">
              <%--
                TypesToCollect uses the schema type OpenCmsDisplayFormatter where values are stored
                 in format "{type name:display formatter id}"
              --%>
              <c:set var="createType">${fn:substringBefore(type.stringValue, ':')}</c:set>
              <cms:edit createType="${createType}" create="true">
                <div>
                  Create a new list entry of type ${createType}.
                </div>
              </cms:edit>
            </c:forEach>
          </c:if>
        </c:otherwise>
      </c:choose>
    </c:otherwise>
    </c:choose>
  </div>
</cms:formatter>

The tag <cms:simplesearch> 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 or the pagination. 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:simplesearch> (by feeding it with the list content and the configuration string for setting the page size), 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 structure id (that is read via ${result.fields['id']}) is provided via the attribute value. For rendering, it uses the formatters that are specified via the displayFormatters attribute for the specific types of content that should be rendered. Using displayFormatters really 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 also passes settings to the display formatters via the nested <cms:parameter> tags. Note that we kind of "make up" element settings by just passing a parameter, e.g., "index". It has even more options. 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 the types specified in the list content, i.e., the type documentation-demo-article in the example. The tag has more options. Have a look at the taglib documentation to get an overview.

Making a formatter a display formatter

In the list content, the display formatter to use for displaying the list elements is chosen:

The selectable formatters are collected automatically. To get your formatter to the list, the "Display formatter type" option in the formatter configuration has to be set:

If you write some value here, the formatter will appear in the content type selection. If you want to add a second content type to be displayed in the same list, only display formatters for other types that have the same display type configured are listed. Hence, using the display type you can determine which formatters can be used together in the same list.

Showing display formatter settings at the list

The settings dialog of the list displays also the setting "Show index" that is actually a setting of the display formatter for articles.

To achive this, the list formatter must be configured to show this settings in the formatter configuration by checking "Include element settings from display formatters".

If the list formatter can include settings from display formatters, we have to specify at the display formatters configuration which settings should be shown at the list and how. To do so, we have to set the "Visibility" of the element setting. In our case we use "parentShared", meaning that the setting never appears if the display formatter is used for an element directly dropped to the page, and that in the list, the option appears directly with the options of the list.

Managing the list via the app

Contents of the integrated list type can be managed via the "Lists" workplace app. Hovering over the edit point at the list, you see the icon "". Click it to open the list in the app. Here you see all collected items and can, e.g., easily blacklist single items if you do not want to collect them.

Fig. [lists_app]: The "Lists" workplace app

The "Lists" app can also be opened from the Launchpad and provides an overview on all lists in the system. It's a good place to manage the contents in the lists. You see them all as shown in the Explorer. Find out more about the "Lists" app here.

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.