Basic Supermodel pagination
This is a simple implementation of pagination, with previous/next links and numbered pages. The howMany value of the <sm:paginate /> tag determines how many items per page will be ouput, and the windowSize variable determines how many pages either side of the current one will be displayed in the page link list. Where there are more pages than those available, and ellipsis will be output.
The code below contains minimal CSS classes, so it will be up to you to style the output correctly.
<sm:paginate field="items" howMany="10" sort="date desc" var="_paginator" page="${empty param.page ? 0 : param.page - 1}" />
<sm:forEach paginator="_paginator" var="_item">
[ output conten items here ]
</sm:forEach>
<sm:ifNot name="windowSize"><sm:set var="windowSize" value="3"/></sm:ifNot>
<sm:choose>
<sm:when test="${_paginator.pageCount < (1 + ( 2 * windowSize ))}">
<sm:set var="_firstPage" value="0"/>
<sm:set var="_lastPage" value="${_paginator.pageCount - 1}"/>
</sm:when>
<sm:otherwise>
<sm:set var="_firstPage" value="${_paginator.pageIndex - windowSize < 0 ? 0 : _paginator.pageIndex - windowSize}"/>
<sm:set var="_lastPage" value="${_paginator.pageIndex + windowSize < _paginator.pageCount ? _paginator.pageIndex + windowSize : _paginator.pageCount - 1}"/>
</sm:otherwise>
</sm:choose>
<sm:if test="${_paginator.pageCount > 1}">
<nav class="pagination">
<a href="?page=${_paginator.pageIndex + 0}"<sm:if test="${_paginator.pageIndex eq 0}"> disabled</sm:if>>
Previous
</a>
<div class="pages">
<sm:if test="${_firstPage > 0}">
<a href="?page=1">1</a>
<sm:if test="${_firstPage > 1}">...</sm:if>
</sm:if>
<sm:forEach start="${_firstPage}" end="${_lastPage}" varStatus="_status">
<sm:choose>
<sm:when test="${_status.index == _paginator.pageIndex}">
<span>${_status.index + 1 }</span>
</sm:when>
<sm:otherwise>
<a href="?page=${_status.index + 1}">${_status.index + 1 }</a>
</sm:otherwise>
</sm:choose>
</sm:forEach>
<sm:if test="${_lastPage < _paginator.pageCount - 2}">...</sm:if>
<sm:if test="${_lastPage < _paginator.pageCount - 1}">
<a href="?page=${_paginator.pageCount}">${_paginator.pageCount}</a>
</sm:if>
</div>
<a href="?page=${_paginator.pageIndex + 2}"<sm:if test="${!_paginator.next}"> disabled</sm:if>>
Next
</a>
</nav>
</sm:if>
Allow SVG as an image upload
By default Supermodel does not allow you to upload an SVG into an image field type, but you can allow this by adding configuration for that field to specify all the allowed types:
allowedTypes=image/gif
allowedTypes=image/jpeg
allowedTypes=image/png
allowedTypes=image/svg+xml
allowedTypes=image/webp
Note that you can't only add the missing SVG type, you must include all the types that you want available.
Pass attributes to a Supermodel include
When uaing <sm:include> to output a collection of data, it is possible to pass attributes that can be used by the included templates:
<sm:include field="blocks" page="/object/blocks/default">
<sm:attribute name="theme" value="dark" />
<sm:attribute name="parent" value="${object}" />
</sm:include>
Add custom attributes to Supermodel inputs
To add custom attributes to an <sm:input /> field, you can use the attributesText property:
<input:text className="field" id="fieldname" name="fieldname" attributesText="data-id='1234' data-another-value='blah'" />
Deploying an old Supermodel site from an M-series Mac
For some of our older Supermodel sites you won't be able to run the deploy script successfully because the sites require and older version of Java. To fix this issue you need to tell the deploy to use Java 8 instead of whatever more current version you are running on your machine:
export JAVA_HOME=$(/usr/libexec/java_home -v 1.8)
Basic WYSIWYG field config
For adding basic configuration options so that only simple text is alowed in a field, add the following into the configuration for the field in Supermodel and adjust to suit.
tinyMCEConfig.height=150
tinyMCEConfig.toolbar=\"undo redo | bold italic | pastetext | link unlink | removeformat code\"
tinyMCEConfig.statusbar=false
tinyMCEConfig.valid_elements=\"p[class]\,em/i\,strong/b\,a[href|target|title]\,br\,span[class]\"
See the previous instructions if you need more detailed configuration.
Create re-usable code blocks within a JSP template
Sometimes you may have a block of code that you would like to output in multiple places in the page, and for whatever reason don’t want to use includes or tags. To do this you can use the <sm:block /> feature. To start you set the block as a variable:
<sm:setBlock var="_blockname">
...
</sm:setBlock>
Then later in the page you can output it:
<sm:block name="_blockname" />
Set a content type for admin previews
Often when editing content in Supermodel you will be editing an object that lives inside a larger page, and so doesn’t have its own page template for viewing. This means when you use the ‘Preview’ button in Supermodel there is nothing to show and you generally get an error page, which can be confusing for users. The solution is to explicitly set content types that can be previewed. When this configuration is added, any time you try to preview an object it will check to see if it is one of those content types, and if not then it will look up the chain at all parents until it finds the first one that does match, and use that to display as the preview template.
To set this configuration, add the following line to your supermodel.properties file:
supermodel.displayable.class=im:Displayable
Where im:Displayable here would match the model type for templates you want to match. This can be a comma-separated list.
Generate sitemap.xml
To generate an automated sitemap file, uncomment the following code in the actions-config.xml file and edit to suit your field structure:
<action class="SitemapAction">
<mapping url-pattern="/main/sitemap.xml"/>
<mapping url-pattern="/sitemap.xml"/>
<init-param>
<param-name>root</param-name>
<param-value>im_root</param-value>
</init-param>
<init-param>
<param-name>fieldCodes</param-name>
<param-value>navigation,footerNavigation,pages,products,customers,links,posts</param-value>
</init-param>
<init-param>
<param-name>classCodes</param-name>
<param-value>AbstractNavigable,FooterLinkGroup</param-value>
</init-param>
<init-param>
<param-name>hiddenClassCodes</param-name>
<param-value>Site,FooterLinkGroup</param-value>
</init-param>
</action>
Output slug for an object
To output the Supermodel slug for any slugged object, you can use the following syntax:
${object:slug(object)}
Where the object in parenthesis is the object you wish to obtain the slug for.
If you find it’s not working, check that the following line exists in globals.jspf:
<%@ taglib prefix="object" uri="http://www.supermodel.co.nz/tld/object" %>
Output srcset values from an image
To generate a comma-delimited list of images generated for the srcset of an image, loop through them with a forEach statement:
<sm:image field="image"
maxWidths="1980, 990, 500"
var="_img"
/>
<sm:forEach name="_img" var="_i" varStatus="_s"><sm:url name="_i"/><sm:ifNot name="_s" field="last"/>,</sm:ifNot></sm:forEach>
This would generate a list of images in the format /img/size-1980.jpg, /img/size-990.jpg, /img/size-500jpg, which is useful if you need to add them as data attributes for reference in javscript.
Static images in the sm:image tag
This is how you can reference a static image asset inside the <sm:image /> tag, which can be useful when defining fallback images etc.
<sm:image value="/static/img/home-hero-placeholder.png" staticAsset="true" ... />
Create a list object in Supermodel
Useful when you want to create a subset of values, for instance when needing to filter output and then do something with it.
<list:empty name="listName"/>
<sm:forEach field="items" var="_item">
<list:add list="${listName}" value="${_item}"/>
</sm:forEach>
Force IE8/9’s best rendering mode
Add the following to the start of your page, after the usual global Supermodel includes.
<%response.setHeader("X-UA-Compatible", "IE=Edge"); %>
Test whether you are in a dev Supermodel environment
First add this import to the top of your page:
<%@page import="com.cactuslab.supermodel.Application"%>
Then you can test against the Application.isDevMode() value.
<% if (Application.isDevMode()) { ... } %>
Redirect to first page in section
<%@include file="/WEB-INF/templates/frag/global.jspf" %>
<sm:url var="_url" name="object" field="pages" index="0" addContextPath="false"><sm:attribute name="section" value="${object}" /></sm:url>
<c:redirect url="${_url}" />
Where section in this instance is the variable set up by the routing rules.
Using a test to restrict forEach results
<sm:forEach field="things" var="_thing" test="!empty _thing.values.fieldname">
...
</sm:forEach>
Note that the test does not require ${ } wrappers like you would usually use in test attributes.
Supermodel WYSIWYG config options
Configuration options for WYSIWYG fields in Supermodel. To add config for a specific field you will need to create a file (.jsp) inside /WEB-INF/supermodel-local/tinymce/config/.
File names need to match the field name, but lowercased, and should live inside a subfolder with the same name as the parent object. e.g. if you want to add configuration for a 'content' field inside a block.text object, then the structure would look like:
config/
├─ block.text/
├─ content.jsp
Inside the file you can add your configuration:
<%@include file="/WEB-INF/templates/frag/global.jspf" %>
tinyMCEConfig.content_css = "<sm:static path="/static/css/admin/page-content.css" />";
tinyMCEConfig.height = "250";
tinyMCEConfig.theme_advanced_blockformats = "p,br";
tinyMCEConfig.theme_advanced_buttons1 = "styleselect,|,bold,italic,|,link,unlink";
tinyMCEConfig.valid_elements = "p[class],em/i,strong/b,a[href|target],br";
tinyMCEConfig.theme_advanced_statusbar_location = "none";
Ideally you will want to set the tinyMCEConfig.valid_elements to only allow elements that you have provided styling for.
The theme_advanced_buttons1 values determind which buttons show up on the WYSIWYG toolbar, and the most common values are:
bold, italic, underline, strikethrough, justifyleft, justifycenter, justifyright, justifyfull, bullist, numlist, outdent, indent, cut, copy, paste, undo, redo, link, unlink, image, cleanup, code, hr, removeformat, formatselect, styleselect, sub, sup, blockquote,
You can also turn off all buttons by passing in an empty “” string. Note that this will hide the buttons but not the bar, so to remove that you’ll also need to add some javascript into the bottom of the config file:
if ( $('#supermodel-custom-admin-styles').length == 0 ) {
var adminStyles = '';
adminStyles += '#field_fieldCode_tbl tr.mceFirst { display:none; } #field_fieldCode_ifr, table#field_fieldCode_tbl.mceLayout { height: 54px !important; }';
$('body').append('<style id="supermodel-custom-admin-styles">'+adminStyles+'</style>');
}
Where “fieldCode” needs to be replaced by the relevant ID in the markup for the WYSIWYG wrapper. Adjust the height values to suit based on the font size etc.
Check the TinyMCE docs for information about the valid_elements options.
Create a tag file in Supermodel
Format the tag content like this, and include any relevant attributes:
<%@tag %>
<%@include file="/WEB-INF/templates/frag/global.jspf" %>
<%@attribute name="object" required="true" rtexprvalue="true" type="java.lang.Object" %>
<%@attribute name="something" required="false" rtexprvalue="true" type="java.lang.String" %>
<%@attribute name="test" required="false" rtexprvalue="true" type="java.lang.Boolean" %>
[ ... tag template code here, e.g. <sm:out name="something" /> ]
Format a 'double' field, show a single decimal if one exists
Useful for working with currency in Supermodel, or when specific decimal places are required.
<fmt:formatNumber value="${_object.values.fieldName}" pattern="#.#" />
Use sm:include for blocks of content
E.g. Store templates for each content type in /object/block/ and then add into main page like this:
<sm:include field="blocks" page="/object/block/default" />
This will iterate through all the objects in your collection and output the content using the matching template found in the folder specified by the page attribute.
You can optionally pass attributes which will be used by the includded file, which means includes may be used instead of tags in some cases:
<sm:include field="blocks" page="/object/block/default">
<sm:attribute name="mycontent" value="..." />
</sm:include>
Get length of object in Supermodel
${fn:length(_object)}
Reverse output order of sm:forEach
<sm:forEach field="blah" sort="reverse">
...
</sm:forEach>
Increment index inside sm:forEach
<sm:forEach name="whatever" varStatus="_status">
New index is: <sm:out value="${_status.index+1}" />
</sm:forEach>
Get parent object in Supermodel
<sm:parent var="_parent" />
<sm:out name="_parent" field="code" />
Get file attributes in Supermodel
<sm:asset var="_file" name="download" field="file"/>
<sm:if name="_file">
<sm:set var="_filename" name="download" field="name"/>
<sm:ifNot name="download" field="name">
<sm:set var="_filename" value="${_file.file.name}"/>
</sm:ifNot>
<a href="<sm:out name="_file" field="url"/>"><sm:out name="_filename" /> (${_file.fileSizeString} ${_file.suffix})</a>
</sm:if>
Format colours from Supermodel
#${_colourField.hexString}
rgb(${_colourField.red}, ${_colourField.green}, ${_colourField.blue})
Strip http://www. from URLs
<sm:out field="url" format="unurl"><sm:transform pattern="www." replaceFirst="" /></sm:out>
Output list of pages within section
<ul>
<sm:ancestor var="_section" classCodes="AbstractSection"/>
<sm:forEach name="_section" field="pages" var="_page">
<li<sm:if test="${object eq _page}"> class="selected"</sm:if>><a href="<sm:url name="_page"/>"><sm:out name="_page"/></a></li>
</sm:forEach>
</ul>
Note that if you are using namespaced parent slugging then the <sm:ancestor /> part of this is likely unnecessary, as the routing will also set up a variable for the parent which you can reference (e.g. name="section").
Format a date in Supermodel
<fmt:formatDate value="${object.values.date}" pattern="d MMMM yyyy"/>
Here is a list of possible patterns that can be used.
Add namespaced routing for pages in sections
In routes.xml:
<route>
<object name="object" class-code="AbstractPage" slug-parent="section" slug-space="pages"/>
<object name="section" class-code="AbstractSection" child="object" />
<route pattern="/{section}/{object}/" path="/object/default"/>
</route>
In configuration for page object in Supermodel model:
slug.space=pages
slug.parentClass=AbstractSection
slug.parentField=pages
Add a ‘nowrap’ span around last word and following element
This is useful for times where there may be a count value at the end of a title, and you don’t ever want the count to be split from the last word (where it can wrap down to the next line all by itself).
<h2>
<set var="_encoded"><out name="_item"></out></set><out name="_encoded" format="none"><sm:transform pattern="^(.*\s)?([^\s]+)$" replaceFirst="$1<span class='nowrap'>$2" /></out><small class="count">${fn:length(_item.value)}</small>
</h2>
The accompanying CSS style needs to be added for the .nowrap class.