Add FreeMarker macro support in spring-webflux
Closes gh-23002
This commit is contained in:
		
							parent
							
								
									9b084bb3ea
								
							
						
					
					
						commit
						b58f34d802
					
				| 
						 | 
					@ -46,8 +46,6 @@ import org.springframework.util.Assert;
 | 
				
			||||||
 * instance via the "configuration" property. This allows to share a FreeMarker
 | 
					 * instance via the "configuration" property. This allows to share a FreeMarker
 | 
				
			||||||
 * Configuration for web and email usage for example.
 | 
					 * Configuration for web and email usage for example.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * <p>TODO: macros
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * <p>This configurer registers a template loader for this package, allowing to
 | 
					 * <p>This configurer registers a template loader for this package, allowing to
 | 
				
			||||||
 * reference the "spring.ftl" macro library contained in this package:
 | 
					 * reference the "spring.ftl" macro library contained in this package:
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,355 @@
 | 
				
			||||||
 | 
					<#ftl output_format="HTML" strip_whitespace=true>
 | 
				
			||||||
 | 
					<#--
 | 
				
			||||||
 | 
					 * spring.ftl
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This file consists of a collection of FreeMarker macros aimed at easing
 | 
				
			||||||
 | 
					 * some of the common requirements of web applications - in particular
 | 
				
			||||||
 | 
					 * handling of forms.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Spring's FreeMarker support will automatically make this file and therefore
 | 
				
			||||||
 | 
					 * all macros within it available to any application using Spring's
 | 
				
			||||||
 | 
					 * FreeMarkerConfigurer.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * To take advantage of these macros, the "exposeSpringMacroHelpers" property
 | 
				
			||||||
 | 
					 * of the FreeMarker class needs to be set to "true". This will expose a
 | 
				
			||||||
 | 
					 * RequestContext under the name "springMacroRequestContext", as needed by
 | 
				
			||||||
 | 
					 * the macros in this library.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Darren Davison
 | 
				
			||||||
 | 
					 * @author Juergen Hoeller
 | 
				
			||||||
 | 
					 * @author Issam El-atif
 | 
				
			||||||
 | 
					 * @since 5.2
 | 
				
			||||||
 | 
					 -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<#--
 | 
				
			||||||
 | 
					 * message
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Macro to translate a message code into a message.
 | 
				
			||||||
 | 
					 -->
 | 
				
			||||||
 | 
					<#macro message code>${springMacroRequestContext.getMessage(code)?no_esc}</#macro>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<#--
 | 
				
			||||||
 | 
					 * messageText
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Macro to translate a message code into a message,
 | 
				
			||||||
 | 
					 * using the given default text if no message found.
 | 
				
			||||||
 | 
					 -->
 | 
				
			||||||
 | 
					<#macro messageText code, text>${springMacroRequestContext.getMessage(code, text)?no_esc}</#macro>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<#--
 | 
				
			||||||
 | 
					 * messageArgs
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Macro to translate a message code with arguments into a message.
 | 
				
			||||||
 | 
					 -->
 | 
				
			||||||
 | 
					<#macro messageArgs code, args>${springMacroRequestContext.getMessage(code, args)?no_esc}</#macro>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<#--
 | 
				
			||||||
 | 
					 * messageArgsText
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Macro to translate a message code with arguments into a message,
 | 
				
			||||||
 | 
					 * using the given default text if no message found.
 | 
				
			||||||
 | 
					 -->
 | 
				
			||||||
 | 
					<#macro messageArgsText code, args, text>${springMacroRequestContext.getMessage(code, args, text)?no_esc}</#macro>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<#--
 | 
				
			||||||
 | 
					 * url
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Takes a relative URL and makes it absolute from the server root by
 | 
				
			||||||
 | 
					 * adding the context root for the web application.
 | 
				
			||||||
 | 
					 -->
 | 
				
			||||||
 | 
					<#macro url relativeUrl extra...><#if extra?? && extra?size!=0>${springMacroRequestContext.getContextUrl(relativeUrl,extra)?no_esc}<#else>${springMacroRequestContext.getContextUrl(relativeUrl)?no_esc}</#if></#macro>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<#--
 | 
				
			||||||
 | 
					 * bind
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Exposes a BindStatus object for the given bind path, which can be
 | 
				
			||||||
 | 
					 * a bean (e.g. "person") to get global errors, or a bean property
 | 
				
			||||||
 | 
					 * (e.g. "person.name") to get field errors. Can be called multiple times
 | 
				
			||||||
 | 
					 * within a form to bind to multiple command objects and/or field names.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This macro will participate in the default HTML escape setting for the given
 | 
				
			||||||
 | 
					 * RequestContext. This can be customized by calling "setDefaultHtmlEscape"
 | 
				
			||||||
 | 
					 * on the "springMacroRequestContext" context variable, or via the
 | 
				
			||||||
 | 
					 * "defaultHtmlEscape" context-param in web.xml (same as for the JSP bind tag).
 | 
				
			||||||
 | 
					 * Also regards a "htmlEscape" variable in the namespace of this library.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Producing no output, the following context variable will be available
 | 
				
			||||||
 | 
					 * each time this macro is referenced (assuming you import this library in
 | 
				
			||||||
 | 
					 * your templates with the namespace 'spring'):
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *   spring.status : a BindStatus instance holding the command object name,
 | 
				
			||||||
 | 
					 *   expression, value, and error messages and codes for the path supplied
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param path the path (string value) of the value required to bind to.
 | 
				
			||||||
 | 
					 *     Spring defaults to a command name of "command" but this can be
 | 
				
			||||||
 | 
					 *     overridden by user configuration.
 | 
				
			||||||
 | 
					 -->
 | 
				
			||||||
 | 
					<#macro bind path>
 | 
				
			||||||
 | 
					    <#if htmlEscape?exists>
 | 
				
			||||||
 | 
					        <#assign status = springMacroRequestContext.getBindStatus(path, htmlEscape)>
 | 
				
			||||||
 | 
					    <#else>
 | 
				
			||||||
 | 
					        <#assign status = springMacroRequestContext.getBindStatus(path)>
 | 
				
			||||||
 | 
					    </#if>
 | 
				
			||||||
 | 
					    <#-- assign a temporary value, forcing a string representation for any
 | 
				
			||||||
 | 
					    kind of variable. This temp value is only used in this macro lib -->
 | 
				
			||||||
 | 
					    <#if status.value?exists && status.value?is_boolean>
 | 
				
			||||||
 | 
					        <#assign stringStatusValue=status.value?string>
 | 
				
			||||||
 | 
					    <#else>
 | 
				
			||||||
 | 
					        <#assign stringStatusValue=status.value?default("")>
 | 
				
			||||||
 | 
					    </#if>
 | 
				
			||||||
 | 
					</#macro>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<#--
 | 
				
			||||||
 | 
					 * bindEscaped
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Similar to spring:bind, but takes an explicit HTML escape flag rather
 | 
				
			||||||
 | 
					 * than relying on the default HTML escape setting.
 | 
				
			||||||
 | 
					 -->
 | 
				
			||||||
 | 
					<#macro bindEscaped path, htmlEscape>
 | 
				
			||||||
 | 
					    <#assign status = springMacroRequestContext.getBindStatus(path, htmlEscape)>
 | 
				
			||||||
 | 
					    <#-- assign a temporary value, forcing a string representation for any
 | 
				
			||||||
 | 
					    kind of variable. This temp value is only used in this macro lib -->
 | 
				
			||||||
 | 
					    <#if status.value?exists && status.value?is_boolean>
 | 
				
			||||||
 | 
					        <#assign stringStatusValue=status.value?string>
 | 
				
			||||||
 | 
					    <#else>
 | 
				
			||||||
 | 
					        <#assign stringStatusValue=status.value?default("")>
 | 
				
			||||||
 | 
					    </#if>
 | 
				
			||||||
 | 
					</#macro>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<#--
 | 
				
			||||||
 | 
					 * formInput
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Display a form input field of type 'text' and bind it to an attribute
 | 
				
			||||||
 | 
					 * of a command or bean.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param path the name of the field to bind to
 | 
				
			||||||
 | 
					 * @param attributes any additional attributes for the element
 | 
				
			||||||
 | 
					 *    (such as class or CSS styles or size)
 | 
				
			||||||
 | 
					 -->
 | 
				
			||||||
 | 
					<#macro formInput path attributes="" fieldType="text">
 | 
				
			||||||
 | 
					    <@bind path/>
 | 
				
			||||||
 | 
					    <input type="${fieldType}" id="${status.expression?replace('[','')?replace(']','')}" name="${status.expression}" value="<#if fieldType!="password">${stringStatusValue}</#if>" ${attributes?no_esc}<@closeTag/>
 | 
				
			||||||
 | 
					</#macro>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<#--
 | 
				
			||||||
 | 
					 * formPasswordInput
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Display a form input field of type 'password' and bind it to an attribute
 | 
				
			||||||
 | 
					 * of a command or bean. No value will ever be displayed. This functionality
 | 
				
			||||||
 | 
					 * can also be obtained by calling the formInput macro with a 'type' parameter
 | 
				
			||||||
 | 
					 * of 'password'.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param path the name of the field to bind to
 | 
				
			||||||
 | 
					 * @param attributes any additional attributes for the element
 | 
				
			||||||
 | 
					 *    (such as class or CSS styles or size)
 | 
				
			||||||
 | 
					 -->
 | 
				
			||||||
 | 
					<#macro formPasswordInput path attributes="">
 | 
				
			||||||
 | 
					    <@formInput path, attributes, "password"/>
 | 
				
			||||||
 | 
					</#macro>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<#--
 | 
				
			||||||
 | 
					 * formHiddenInput
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Generate a form input field of type 'hidden' and bind it to an attribute
 | 
				
			||||||
 | 
					 * of a command or bean. This functionality can also be obtained by calling
 | 
				
			||||||
 | 
					 * the formInput macro with a 'type' parameter of 'hidden'.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param path the name of the field to bind to
 | 
				
			||||||
 | 
					 * @param attributes any additional attributes for the element
 | 
				
			||||||
 | 
					 *    (such as class or CSS styles or size)
 | 
				
			||||||
 | 
					 -->
 | 
				
			||||||
 | 
					<#macro formHiddenInput path attributes="">
 | 
				
			||||||
 | 
					    <@formInput path, attributes, "hidden"/>
 | 
				
			||||||
 | 
					</#macro>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<#--
 | 
				
			||||||
 | 
					 * formTextarea
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Display a text area and bind it to an attribute of a command or bean.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param path the name of the field to bind to
 | 
				
			||||||
 | 
					 * @param attributes any additional attributes for the element
 | 
				
			||||||
 | 
					 *    (such as class or CSS styles or size)
 | 
				
			||||||
 | 
					 -->
 | 
				
			||||||
 | 
					<#macro formTextarea path attributes="">
 | 
				
			||||||
 | 
					    <@bind path/>
 | 
				
			||||||
 | 
					    <textarea id="${status.expression?replace('[','')?replace(']','')}" name="${status.expression}" ${attributes?no_esc}>
 | 
				
			||||||
 | 
					${stringStatusValue}</textarea>
 | 
				
			||||||
 | 
					</#macro>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<#--
 | 
				
			||||||
 | 
					 * formSingleSelect
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Show a selectbox (dropdown) input element allowing a single value to be chosen
 | 
				
			||||||
 | 
					 * from a list of options.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param path the name of the field to bind to
 | 
				
			||||||
 | 
					 * @param options a map (value=label) of all the available options
 | 
				
			||||||
 | 
					 * @param attributes any additional attributes for the element
 | 
				
			||||||
 | 
					 *    (such as class or CSS styles or size)
 | 
				
			||||||
 | 
					-->
 | 
				
			||||||
 | 
					<#macro formSingleSelect path options attributes="">
 | 
				
			||||||
 | 
					    <@bind path/>
 | 
				
			||||||
 | 
					    <select id="${status.expression?replace('[','')?replace(']','')}" name="${status.expression}" ${attributes?no_esc}>
 | 
				
			||||||
 | 
					        <#if options?is_hash>
 | 
				
			||||||
 | 
					            <#list options?keys as value>
 | 
				
			||||||
 | 
					            <option value="${value}"<@checkSelected value/>>${options[value]}</option>
 | 
				
			||||||
 | 
					            </#list>
 | 
				
			||||||
 | 
					        <#else> 
 | 
				
			||||||
 | 
					            <#list options as value>
 | 
				
			||||||
 | 
					            <option value="${value}"<@checkSelected value/>>${value}</option>
 | 
				
			||||||
 | 
					            </#list>
 | 
				
			||||||
 | 
					        </#if>
 | 
				
			||||||
 | 
					    </select>
 | 
				
			||||||
 | 
					</#macro>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<#--
 | 
				
			||||||
 | 
					 * formMultiSelect
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Show a listbox of options allowing the user to make 0 or more choices from
 | 
				
			||||||
 | 
					 * the list of options.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param path the name of the field to bind to
 | 
				
			||||||
 | 
					 * @param options a map (value=label) of all the available options
 | 
				
			||||||
 | 
					 * @param attributes any additional attributes for the element
 | 
				
			||||||
 | 
					 *    (such as class or CSS styles or size)
 | 
				
			||||||
 | 
					-->
 | 
				
			||||||
 | 
					<#macro formMultiSelect path options attributes="">
 | 
				
			||||||
 | 
					    <@bind path/>
 | 
				
			||||||
 | 
					    <select multiple="multiple" id="${status.expression?replace('[','')?replace(']','')}" name="${status.expression}" ${attributes?no_esc}>
 | 
				
			||||||
 | 
					        <#list options?keys as value>
 | 
				
			||||||
 | 
					        <#assign isSelected = contains(status.actualValue?default([""]), value)>
 | 
				
			||||||
 | 
					        <option value="${value}"<#if isSelected> selected="selected"</#if>>${options[value]}</option>
 | 
				
			||||||
 | 
					        </#list>
 | 
				
			||||||
 | 
					    </select>
 | 
				
			||||||
 | 
					</#macro>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<#--
 | 
				
			||||||
 | 
					 * formRadioButtons
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Show radio buttons.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param path the name of the field to bind to
 | 
				
			||||||
 | 
					 * @param options a map (value=label) of all the available options
 | 
				
			||||||
 | 
					 * @param separator the HTML tag or other character list that should be used to
 | 
				
			||||||
 | 
					 *    separate each option (typically ' ' or '<br>')
 | 
				
			||||||
 | 
					 * @param attributes any additional attributes for the element
 | 
				
			||||||
 | 
					 *    (such as class or CSS styles or size)
 | 
				
			||||||
 | 
					-->
 | 
				
			||||||
 | 
					<#macro formRadioButtons path options separator attributes="">
 | 
				
			||||||
 | 
					    <@bind path/>
 | 
				
			||||||
 | 
					    <#list options?keys as value>
 | 
				
			||||||
 | 
					    <#assign id="${status.expression?replace('[','')?replace(']','')}${value_index}">
 | 
				
			||||||
 | 
					    <input type="radio" id="${id}" name="${status.expression}" value="${value}"<#if stringStatusValue == value> checked="checked"</#if> ${attributes?no_esc}<@closeTag/>
 | 
				
			||||||
 | 
					    <label for="${id}">${options[value]}</label>${separator?no_esc}
 | 
				
			||||||
 | 
					    </#list>
 | 
				
			||||||
 | 
					</#macro>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<#--
 | 
				
			||||||
 | 
					 * formCheckboxes
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Show checkboxes.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param path the name of the field to bind to
 | 
				
			||||||
 | 
					 * @param options a map (value=label) of all the available options
 | 
				
			||||||
 | 
					 * @param separator the HTML tag or other character list that should be used to
 | 
				
			||||||
 | 
					 *    separate each option (typically ' ' or '<br>')
 | 
				
			||||||
 | 
					 * @param attributes any additional attributes for the element
 | 
				
			||||||
 | 
					 *    (such as class or CSS styles or size)
 | 
				
			||||||
 | 
					-->
 | 
				
			||||||
 | 
					<#macro formCheckboxes path options separator attributes="">
 | 
				
			||||||
 | 
					    <@bind path/>
 | 
				
			||||||
 | 
					    <#list options?keys as value>
 | 
				
			||||||
 | 
					    <#assign id="${status.expression?replace('[','')?replace(']','')}${value_index}">
 | 
				
			||||||
 | 
					    <#assign isSelected = contains(status.actualValue?default([""]), value)>
 | 
				
			||||||
 | 
					    <input type="checkbox" id="${id}" name="${status.expression}" value="${value}"<#if isSelected> checked="checked"</#if> ${attributes?no_esc}<@closeTag/>
 | 
				
			||||||
 | 
					    <label for="${id}">${options[value]}</label>${separator?no_esc}
 | 
				
			||||||
 | 
					    </#list>
 | 
				
			||||||
 | 
					    <input type="hidden" name="_${status.expression}" value="on"/>
 | 
				
			||||||
 | 
					</#macro>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<#--
 | 
				
			||||||
 | 
					 * formCheckbox
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Show a single checkbox.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param path the name of the field to bind to
 | 
				
			||||||
 | 
					 * @param attributes any additional attributes for the element
 | 
				
			||||||
 | 
					 *    (such as class or CSS styles or size)
 | 
				
			||||||
 | 
					-->
 | 
				
			||||||
 | 
					<#macro formCheckbox path attributes="">
 | 
				
			||||||
 | 
						<@bind path />
 | 
				
			||||||
 | 
					    <#assign id="${status.expression?replace('[','')?replace(']','')}">
 | 
				
			||||||
 | 
					    <#assign isSelected = status.value?? && status.value?string=="true">
 | 
				
			||||||
 | 
						<input type="hidden" name="_${status.expression}" value="on"/>
 | 
				
			||||||
 | 
						<input type="checkbox" id="${id}" name="${status.expression}"<#if isSelected> checked="checked"</#if> ${attributes?no_esc}/>
 | 
				
			||||||
 | 
					</#macro>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<#--
 | 
				
			||||||
 | 
					 * showErrors
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Show validation errors for the currently bound field, with
 | 
				
			||||||
 | 
					 * optional style attributes.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param separator the HTML tag or other character list that should be used to
 | 
				
			||||||
 | 
					 *    separate each option (typically ' ' or '<br>')
 | 
				
			||||||
 | 
					 * @param classOrStyle either the name of a CSS class element (which is defined in
 | 
				
			||||||
 | 
					 *    the template or an external CSS file) or an inline style. If the value passed
 | 
				
			||||||
 | 
					 *    in here contains a colon (:) then a 'style=' attribute will be used,
 | 
				
			||||||
 | 
					 *    otherwise a 'class=' attribute will be used.
 | 
				
			||||||
 | 
					-->
 | 
				
			||||||
 | 
					<#macro showErrors separator classOrStyle="">
 | 
				
			||||||
 | 
					    <#list status.errorMessages as error>
 | 
				
			||||||
 | 
					    <#if classOrStyle == "">
 | 
				
			||||||
 | 
					        <b>${error}</b>
 | 
				
			||||||
 | 
					    <#else>
 | 
				
			||||||
 | 
					        <#if classOrStyle?index_of(":") == -1><#assign attr="class"><#else><#assign attr="style"></#if>
 | 
				
			||||||
 | 
					        <span ${attr}="${classOrStyle}">${error}</span>
 | 
				
			||||||
 | 
					    </#if>
 | 
				
			||||||
 | 
					    <#if error_has_next>${separator?no_esc}</#if>
 | 
				
			||||||
 | 
					    </#list>
 | 
				
			||||||
 | 
					</#macro>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<#--
 | 
				
			||||||
 | 
					 * checkSelected
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Check a value in a list to see if it is the currently selected value.
 | 
				
			||||||
 | 
					 * If so, add the 'selected="selected"' text to the output.
 | 
				
			||||||
 | 
					 * Handles values of numeric and string types.
 | 
				
			||||||
 | 
					 * This function is used internally but can be accessed by user code if required.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param value the current value in a list iteration
 | 
				
			||||||
 | 
					-->
 | 
				
			||||||
 | 
					<#macro checkSelected value>
 | 
				
			||||||
 | 
					    <#if stringStatusValue?is_number && stringStatusValue == value?number>selected="selected"</#if>
 | 
				
			||||||
 | 
					    <#if stringStatusValue?is_string && stringStatusValue == value>selected="selected"</#if>
 | 
				
			||||||
 | 
					</#macro>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<#--
 | 
				
			||||||
 | 
					 * contains
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Macro to return true if the list contains the scalar, false if not.
 | 
				
			||||||
 | 
					 * Surprisingly not a FreeMarker builtin.
 | 
				
			||||||
 | 
					 * This function is used internally but can be accessed by user code if required.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param list the list to search for the item
 | 
				
			||||||
 | 
					 * @param item the item to search for in the list
 | 
				
			||||||
 | 
					 * @return true if item is found in the list, false otherwise
 | 
				
			||||||
 | 
					-->
 | 
				
			||||||
 | 
					<#function contains list item>
 | 
				
			||||||
 | 
					    <#list list as nextInList>
 | 
				
			||||||
 | 
					    <#if nextInList == item><#return true></#if>
 | 
				
			||||||
 | 
					    </#list>
 | 
				
			||||||
 | 
					    <#return false>
 | 
				
			||||||
 | 
					</#function>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<#--
 | 
				
			||||||
 | 
					 * closeTag
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Simple macro to close an HTML tag that has no body with '>' or '/>',
 | 
				
			||||||
 | 
					 * depending on the value of a 'xhtmlCompliant' variable in the namespace
 | 
				
			||||||
 | 
					 * of this library.
 | 
				
			||||||
 | 
					-->
 | 
				
			||||||
 | 
					<#macro closeTag>
 | 
				
			||||||
 | 
					    <#if xhtmlCompliant?exists && xhtmlCompliant>/><#else>></#if>
 | 
				
			||||||
 | 
					</#macro>
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,128 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2019 the original author or authors.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					 * you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					 * You may obtain a copy of the License at
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *      https://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					 * See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					 * limitations under the License.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package org.springframework.web.reactive.result.view;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.springframework.context.support.GenericApplicationContext;
 | 
				
			||||||
 | 
					import org.springframework.ui.ModelMap;
 | 
				
			||||||
 | 
					import org.springframework.web.server.ServerWebExchange;
 | 
				
			||||||
 | 
					import org.springframework.web.util.UriTemplate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Dummy request context used for VTL and FTL macro tests.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Darren Davison
 | 
				
			||||||
 | 
					 * @author Juergen Hoeller
 | 
				
			||||||
 | 
					 * @author Issam El-atif
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @see org.springframework.web.reactive.result.view.RequestContext
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class DummyMacroRequestContext {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private final ServerWebExchange exchange;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private final ModelMap model;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private final GenericApplicationContext context;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private Map<String, String> messageMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private String contextPath;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public DummyMacroRequestContext(ServerWebExchange exchange, ModelMap model, GenericApplicationContext context) {
 | 
				
			||||||
 | 
							this.exchange = exchange;
 | 
				
			||||||
 | 
							this.model = model;
 | 
				
			||||||
 | 
							this.context = context;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public void setMessageMap(Map<String, String> messageMap) {
 | 
				
			||||||
 | 
							this.messageMap = messageMap;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @see org.springframework.web.reactive.result.view.RequestContext#getMessage(String)
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public String getMessage(String code) {
 | 
				
			||||||
 | 
							return this.messageMap.get(code);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @see org.springframework.web.reactive.result.view.RequestContext#getMessage(String, String)
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public String getMessage(String code, String defaultMsg) {
 | 
				
			||||||
 | 
							String msg = this.messageMap.get(code);
 | 
				
			||||||
 | 
							return (msg != null ? msg : defaultMsg);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @see org.springframework.web.reactive.result.view.RequestContext#getMessage(String, List)
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public String getMessage(String code, List<?> args) {
 | 
				
			||||||
 | 
							return this.messageMap.get(code) + args;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @see org.springframework.web.reactive.result.view.RequestContext#getMessage(String, List, String)
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public String getMessage(String code, List<?> args, String defaultMsg) {
 | 
				
			||||||
 | 
							String msg = this.messageMap.get(code);
 | 
				
			||||||
 | 
							return (msg != null ? msg + args : defaultMsg);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public void setContextPath(String contextPath) {
 | 
				
			||||||
 | 
							this.contextPath = contextPath;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @see org.springframework.web.reactive.result.view.RequestContext#getContextPath()
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public String getContextPath() {
 | 
				
			||||||
 | 
							return this.contextPath;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @see org.springframework.web.reactive.result.view.RequestContext#getContextUrl(String)
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public String getContextUrl(String relativeUrl) {
 | 
				
			||||||
 | 
							return getContextPath() + relativeUrl;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @see org.springframework.web.reactive.result.view.RequestContext#getContextUrl(String, Map)
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public String getContextUrl(String relativeUrl, Map<String,String> params) {
 | 
				
			||||||
 | 
							UriTemplate template = new UriTemplate(relativeUrl);
 | 
				
			||||||
 | 
							return getContextPath() + template.expand(params).toASCIIString();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @see org.springframework.web.reactive.result.view.RequestContext#getBindStatus(String)
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public BindStatus getBindStatus(String path) throws IllegalStateException {
 | 
				
			||||||
 | 
							return new BindStatus(new RequestContext(this.exchange, this.model, this.context), path, false);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @see org.springframework.web.reactive.result.view.RequestContext#getBindStatus(String, boolean)
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public BindStatus getBindStatus(String path, boolean htmlEscape) throws IllegalStateException {
 | 
				
			||||||
 | 
							return new BindStatus(new RequestContext(this.exchange, this.model, this.context), path, true);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,120 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2019 the original author or authors.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					 * you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					 * You may obtain a copy of the License at
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *      https://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					 * See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					 * limitations under the License.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package org.springframework.web.reactive.result.view.freemarker;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.util.HashMap;
 | 
				
			||||||
 | 
					import java.util.Properties;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import freemarker.cache.ClassTemplateLoader;
 | 
				
			||||||
 | 
					import freemarker.cache.MultiTemplateLoader;
 | 
				
			||||||
 | 
					import freemarker.template.Configuration;
 | 
				
			||||||
 | 
					import freemarker.template.Template;
 | 
				
			||||||
 | 
					import freemarker.template.TemplateException;
 | 
				
			||||||
 | 
					import org.junit.Test;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.springframework.beans.factory.support.DefaultListableBeanFactory;
 | 
				
			||||||
 | 
					import org.springframework.beans.factory.support.RootBeanDefinition;
 | 
				
			||||||
 | 
					import org.springframework.core.io.ByteArrayResource;
 | 
				
			||||||
 | 
					import org.springframework.core.io.DefaultResourceLoader;
 | 
				
			||||||
 | 
					import org.springframework.core.io.FileSystemResource;
 | 
				
			||||||
 | 
					import org.springframework.core.io.Resource;
 | 
				
			||||||
 | 
					import org.springframework.core.io.ResourceLoader;
 | 
				
			||||||
 | 
					import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
 | 
				
			||||||
 | 
					import org.springframework.ui.freemarker.SpringTemplateLoader;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static org.assertj.core.api.Assertions.assertThat;
 | 
				
			||||||
 | 
					import static org.assertj.core.api.Assertions.assertThatIOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @author Juergen Hoeller
 | 
				
			||||||
 | 
					 * @author Issam El-atif
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class FreeMarkerConfigurerTests {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void freeMarkerConfigurerDefaultEncoding() throws IOException, TemplateException {
 | 
				
			||||||
 | 
							FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
 | 
				
			||||||
 | 
							configurer.afterPropertiesSet();
 | 
				
			||||||
 | 
							Configuration cfg = configurer.getConfiguration();
 | 
				
			||||||
 | 
							assertThat(cfg.getDefaultEncoding()).isEqualTo("UTF-8");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void freeMarkerConfigurerWithConfigLocation() {
 | 
				
			||||||
 | 
							FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
 | 
				
			||||||
 | 
							configurer.setConfigLocation(new FileSystemResource("myprops.properties"));
 | 
				
			||||||
 | 
							Properties props = new Properties();
 | 
				
			||||||
 | 
							props.setProperty("myprop", "/mydir");
 | 
				
			||||||
 | 
							configurer.setFreemarkerSettings(props);
 | 
				
			||||||
 | 
							assertThatIOException().isThrownBy(
 | 
				
			||||||
 | 
									configurer::afterPropertiesSet);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void freeMarkerConfigurerWithResourceLoaderPath() throws Exception {
 | 
				
			||||||
 | 
							FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
 | 
				
			||||||
 | 
							configurer.setTemplateLoaderPath("file:/mydir");
 | 
				
			||||||
 | 
							configurer.afterPropertiesSet();
 | 
				
			||||||
 | 
							Configuration cfg = configurer.getConfiguration();
 | 
				
			||||||
 | 
							assertThat(cfg.getTemplateLoader()).isInstanceOf(MultiTemplateLoader.class);
 | 
				
			||||||
 | 
							MultiTemplateLoader multiTemplateLoader = (MultiTemplateLoader)cfg.getTemplateLoader();
 | 
				
			||||||
 | 
							assertThat(multiTemplateLoader.getTemplateLoader(0)).isInstanceOf(SpringTemplateLoader.class);
 | 
				
			||||||
 | 
							assertThat(multiTemplateLoader.getTemplateLoader(1)).isInstanceOf(ClassTemplateLoader.class);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						@SuppressWarnings("rawtypes")
 | 
				
			||||||
 | 
						public void freeMarkerConfigurerWithNonFileResourceLoaderPath() throws Exception {
 | 
				
			||||||
 | 
							FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
 | 
				
			||||||
 | 
							configurer.setTemplateLoaderPath("file:/mydir");
 | 
				
			||||||
 | 
							Properties settings = new Properties();
 | 
				
			||||||
 | 
							settings.setProperty("localized_lookup", "false");
 | 
				
			||||||
 | 
							configurer.setFreemarkerSettings(settings);
 | 
				
			||||||
 | 
							configurer.setResourceLoader(new ResourceLoader() {
 | 
				
			||||||
 | 
								@Override
 | 
				
			||||||
 | 
								public Resource getResource(String location) {
 | 
				
			||||||
 | 
									if (!("file:/mydir".equals(location) || "file:/mydir/test".equals(location))) {
 | 
				
			||||||
 | 
										throw new IllegalArgumentException(location);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									return new ByteArrayResource("test".getBytes(), "test");
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								@Override
 | 
				
			||||||
 | 
								public ClassLoader getClassLoader() {
 | 
				
			||||||
 | 
									return getClass().getClassLoader();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
							configurer.afterPropertiesSet();
 | 
				
			||||||
 | 
							assertThat(configurer.getConfiguration()).isInstanceOf(Configuration.class);
 | 
				
			||||||
 | 
							Configuration fc = configurer.getConfiguration();
 | 
				
			||||||
 | 
							Template ft = fc.getTemplate("test");
 | 
				
			||||||
 | 
							assertThat(FreeMarkerTemplateUtils.processTemplateIntoString(ft, new HashMap())).isEqualTo("test");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test  // SPR-12448
 | 
				
			||||||
 | 
						public void freeMarkerConfigurationAsBean() {
 | 
				
			||||||
 | 
							DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
 | 
				
			||||||
 | 
							RootBeanDefinition loaderDef = new RootBeanDefinition(SpringTemplateLoader.class);
 | 
				
			||||||
 | 
							loaderDef.getConstructorArgumentValues().addGenericArgumentValue(new DefaultResourceLoader());
 | 
				
			||||||
 | 
							loaderDef.getConstructorArgumentValues().addGenericArgumentValue("/freemarker");
 | 
				
			||||||
 | 
							RootBeanDefinition configDef = new RootBeanDefinition(Configuration.class);
 | 
				
			||||||
 | 
							configDef.getPropertyValues().add("templateLoader", loaderDef);
 | 
				
			||||||
 | 
							beanFactory.registerBeanDefinition("freeMarkerConfig", configDef);
 | 
				
			||||||
 | 
							beanFactory.getBean(Configuration.class);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,238 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2019 the original author or authors.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					 * you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					 * You may obtain a copy of the License at
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *      https://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					 * See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					 * limitations under the License.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package org.springframework.web.reactive.result.view.freemarker;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.FileWriter;
 | 
				
			||||||
 | 
					import java.io.InputStreamReader;
 | 
				
			||||||
 | 
					import java.util.HashMap;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import freemarker.template.Configuration;
 | 
				
			||||||
 | 
					import org.junit.Before;
 | 
				
			||||||
 | 
					import org.junit.Test;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.springframework.context.support.GenericApplicationContext;
 | 
				
			||||||
 | 
					import org.springframework.core.io.ClassPathResource;
 | 
				
			||||||
 | 
					import org.springframework.core.io.FileSystemResource;
 | 
				
			||||||
 | 
					import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
 | 
				
			||||||
 | 
					import org.springframework.mock.web.test.server.MockServerWebExchange;
 | 
				
			||||||
 | 
					import org.springframework.tests.sample.beans.TestBean;
 | 
				
			||||||
 | 
					import org.springframework.ui.ExtendedModelMap;
 | 
				
			||||||
 | 
					import org.springframework.ui.ModelMap;
 | 
				
			||||||
 | 
					import org.springframework.util.FileCopyUtils;
 | 
				
			||||||
 | 
					import org.springframework.util.StringUtils;
 | 
				
			||||||
 | 
					import org.springframework.web.reactive.result.view.DummyMacroRequestContext;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static org.assertj.core.api.Assertions.assertThat;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @author Darren Davison
 | 
				
			||||||
 | 
					 * @author Juergen Hoeller
 | 
				
			||||||
 | 
					 * @author Issam El-atif
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class FreeMarkerMacroTests {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private MockServerWebExchange exchange;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private Configuration freeMarkerConfig;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Before
 | 
				
			||||||
 | 
						public void setUp() throws Exception {
 | 
				
			||||||
 | 
							this.exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/path"));
 | 
				
			||||||
 | 
							FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
 | 
				
			||||||
 | 
							configurer.setTemplateLoaderPaths("classpath:/", "file://" + System.getProperty("java.io.tmpdir"));
 | 
				
			||||||
 | 
							this.freeMarkerConfig = configurer.createConfiguration();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void testName() throws Exception {
 | 
				
			||||||
 | 
							assertThat(getMacroOutput("NAME")).isEqualTo("Darren");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void testAge() throws Exception {
 | 
				
			||||||
 | 
							assertThat(getMacroOutput("AGE")).isEqualTo("99");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void testMessage() throws Exception {
 | 
				
			||||||
 | 
							assertThat(getMacroOutput("MESSAGE")).isEqualTo("Howdy Mundo");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void testDefaultMessage() throws Exception {
 | 
				
			||||||
 | 
							assertThat(getMacroOutput("DEFAULTMESSAGE")).isEqualTo("hi planet");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void testMessageArgs() throws Exception {
 | 
				
			||||||
 | 
							assertThat(getMacroOutput("MESSAGEARGS")).isEqualTo("Howdy[World]");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void testMessageArgsWithDefaultMessage() throws Exception {
 | 
				
			||||||
 | 
							assertThat(getMacroOutput("MESSAGEARGSWITHDEFAULTMESSAGE")).isEqualTo("Hi");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void testUrl() throws Exception {
 | 
				
			||||||
 | 
							assertThat(getMacroOutput("URL")).isEqualTo("/springtest/aftercontext.html");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void testUrlParams() throws Exception {
 | 
				
			||||||
 | 
							assertThat(getMacroOutput("URLPARAMS")).isEqualTo("/springtest/aftercontext/bar?spam=bucket");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void testForm1() throws Exception {
 | 
				
			||||||
 | 
							assertThat(getMacroOutput("FORM1")).isEqualTo("<input type=\"text\" id=\"name\" name=\"name\" value=\"Darren\"     >");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void testForm2() throws Exception {
 | 
				
			||||||
 | 
							assertThat(getMacroOutput("FORM2")).isEqualTo("<input type=\"text\" id=\"name\" name=\"name\" value=\"Darren\" class=\"myCssClass\"    >");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void testForm3() throws Exception {
 | 
				
			||||||
 | 
							assertThat(getMacroOutput("FORM3")).isEqualTo("<textarea id=\"name\" name=\"name\" >\nDarren</textarea>");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void testForm4() throws Exception {
 | 
				
			||||||
 | 
							assertThat(getMacroOutput("FORM4")).isEqualTo("<textarea id=\"name\" name=\"name\" rows=10 cols=30>\nDarren</textarea>");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void testForm9() throws Exception {
 | 
				
			||||||
 | 
							assertThat(getMacroOutput("FORM9")).isEqualTo("<input type=\"password\" id=\"name\" name=\"name\" value=\"\"     >");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void testForm10() throws Exception {
 | 
				
			||||||
 | 
							assertThat(getMacroOutput("FORM10")).isEqualTo("<input type=\"hidden\" id=\"name\" name=\"name\" value=\"Darren\"     >");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void testForm11() throws Exception {
 | 
				
			||||||
 | 
							assertThat(getMacroOutput("FORM11")).isEqualTo("<input type=\"text\" id=\"name\" name=\"name\" value=\"Darren\"     >");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void testForm12() throws Exception {
 | 
				
			||||||
 | 
							assertThat(getMacroOutput("FORM12")).isEqualTo("<input type=\"hidden\" id=\"name\" name=\"name\" value=\"Darren\"     >");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void testForm13() throws Exception {
 | 
				
			||||||
 | 
							assertThat(getMacroOutput("FORM13")).isEqualTo("<input type=\"password\" id=\"name\" name=\"name\" value=\"\"     >");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void testForm15() throws Exception {
 | 
				
			||||||
 | 
							String output = getMacroOutput("FORM15");
 | 
				
			||||||
 | 
							assertThat(output.startsWith("<input type=\"hidden\" name=\"_name\" value=\"on\"/>")).as("Wrong output: " + output).isTrue();
 | 
				
			||||||
 | 
							assertThat(output.contains("<input type=\"checkbox\" id=\"name\" name=\"name\" />")).as("Wrong output: " + output).isTrue();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void testForm16() throws Exception {
 | 
				
			||||||
 | 
							String output = getMacroOutput("FORM16");
 | 
				
			||||||
 | 
							assertThat(output.startsWith(
 | 
				
			||||||
 | 
									"<input type=\"hidden\" name=\"_jedi\" value=\"on\"/>")).as("Wrong output: " + output).isTrue();
 | 
				
			||||||
 | 
							assertThat(output.contains(
 | 
				
			||||||
 | 
									"<input type=\"checkbox\" id=\"jedi\" name=\"jedi\" checked=\"checked\" />")).as("Wrong output: " + output).isTrue();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void testForm17() throws Exception {
 | 
				
			||||||
 | 
							assertThat(getMacroOutput("FORM17")).isEqualTo("<input type=\"text\" id=\"spouses0.name\" name=\"spouses[0].name\" value=\"Fred\"     >");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void testForm18() throws Exception {
 | 
				
			||||||
 | 
							String output = getMacroOutput("FORM18");
 | 
				
			||||||
 | 
							assertThat(output.startsWith(
 | 
				
			||||||
 | 
									"<input type=\"hidden\" name=\"_spouses[0].jedi\" value=\"on\"/>")).as("Wrong output: " + output).isTrue();
 | 
				
			||||||
 | 
							assertThat(output.contains(
 | 
				
			||||||
 | 
									"<input type=\"checkbox\" id=\"spouses0.jedi\" name=\"spouses[0].jedi\" checked=\"checked\" />")).as("Wrong output: " + output).isTrue();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private String getMacroOutput(String name) throws Exception {
 | 
				
			||||||
 | 
							String macro = fetchMacro(name);
 | 
				
			||||||
 | 
							assertThat(macro).isNotNull();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							FileSystemResource resource = new FileSystemResource(System.getProperty("java.io.tmpdir") + "/tmp.ftl");
 | 
				
			||||||
 | 
							FileCopyUtils.copy("<#import \"spring.ftl\" as spring />\n" + macro, new FileWriter(resource.getPath()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Map<String, String> msgMap = new HashMap<>();
 | 
				
			||||||
 | 
							msgMap.put("hello", "Howdy");
 | 
				
			||||||
 | 
							msgMap.put("world", "Mundo");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							TestBean darren = new TestBean("Darren", 99);
 | 
				
			||||||
 | 
							TestBean fred = new TestBean("Fred");
 | 
				
			||||||
 | 
							fred.setJedi(true);
 | 
				
			||||||
 | 
							darren.setSpouse(fred);
 | 
				
			||||||
 | 
							darren.setJedi(true);
 | 
				
			||||||
 | 
							darren.setStringArray(new String[] {"John", "Fred"});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Map<String, String> names = new HashMap<>();
 | 
				
			||||||
 | 
							names.put("Darren", "Darren Davison");
 | 
				
			||||||
 | 
							names.put("John", "John Doe");
 | 
				
			||||||
 | 
							names.put("Fred", "Fred Bloggs");
 | 
				
			||||||
 | 
							names.put("Rob&Harrop", "Rob Harrop");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ModelMap model = new ExtendedModelMap();
 | 
				
			||||||
 | 
							DummyMacroRequestContext rc = new DummyMacroRequestContext(this.exchange, model, new GenericApplicationContext());
 | 
				
			||||||
 | 
							rc.setMessageMap(msgMap);
 | 
				
			||||||
 | 
							rc.setContextPath("/springtest");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							model.put("command", darren);
 | 
				
			||||||
 | 
							model.put("springMacroRequestContext", rc);
 | 
				
			||||||
 | 
							model.put("msgArgs", new Object[] { "World" });
 | 
				
			||||||
 | 
							model.put("nameOptionMap", names);
 | 
				
			||||||
 | 
							model.put("options", names.values());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							FreeMarkerView view = new FreeMarkerView();
 | 
				
			||||||
 | 
							view.setBeanName("myView");
 | 
				
			||||||
 | 
							view.setUrl("tmp.ftl");
 | 
				
			||||||
 | 
							view.setConfiguration(freeMarkerConfig);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							view.render(model, null, this.exchange).subscribe();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// tokenize output and ignore whitespace
 | 
				
			||||||
 | 
							String output = this.exchange.getResponse().getBodyAsString().block();
 | 
				
			||||||
 | 
							output = output.replace("\r\n", "\n");
 | 
				
			||||||
 | 
							return output.trim();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private String fetchMacro(String name) throws Exception {
 | 
				
			||||||
 | 
							ClassPathResource resource = new ClassPathResource("test-macro.ftl", getClass());
 | 
				
			||||||
 | 
							assertThat(resource.exists()).isTrue();
 | 
				
			||||||
 | 
							String all = FileCopyUtils.copyToString(new InputStreamReader(resource.getInputStream()));
 | 
				
			||||||
 | 
							all = all.replace("\r\n", "\n");
 | 
				
			||||||
 | 
							String[] macros = StringUtils.delimitedListToStringArray(all, "\n\n");
 | 
				
			||||||
 | 
							for (String macro : macros) {
 | 
				
			||||||
 | 
								if (macro.startsWith(name)) {
 | 
				
			||||||
 | 
									return macro.substring(macro.indexOf("\n")).trim();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return null;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,82 @@
 | 
				
			||||||
 | 
					<#--
 | 
				
			||||||
 | 
					test template for FreeMarker macro test class
 | 
				
			||||||
 | 
					-->
 | 
				
			||||||
 | 
					<#import "spring.ftl" as spring />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					NAME
 | 
				
			||||||
 | 
					${command.name}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AGE
 | 
				
			||||||
 | 
					${command.age}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MESSAGE
 | 
				
			||||||
 | 
					<@spring.message "hello"/> <@spring.message "world"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEFAULTMESSAGE
 | 
				
			||||||
 | 
					<@spring.messageText "no.such.code", "hi"/> <@spring.messageText "no.such.code", "planet"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MESSAGEARGS
 | 
				
			||||||
 | 
					<@spring.messageArgs "hello", msgArgs/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MESSAGEARGSWITHDEFAULTMESSAGE
 | 
				
			||||||
 | 
					<@spring.messageArgsText "no.such.code", msgArgs, "Hi"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					URL
 | 
				
			||||||
 | 
					<@spring.url "/aftercontext.html"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					URLPARAMS
 | 
				
			||||||
 | 
					<@spring.url relativeUrl="/aftercontext/{foo}?spam={spam}" foo="bar" spam="bucket"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FORM1
 | 
				
			||||||
 | 
					<@spring.formInput "command.name", ""/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FORM2
 | 
				
			||||||
 | 
					<@spring.formInput "command.name", 'class="myCssClass"'/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FORM3
 | 
				
			||||||
 | 
					<@spring.formTextarea "command.name", ""/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FORM4
 | 
				
			||||||
 | 
					<@spring.formTextarea "command.name", "rows=10 cols=30"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FORM5
 | 
				
			||||||
 | 
					<@spring.formSingleSelect "command.name", nameOptionMap, ""/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FORM6
 | 
				
			||||||
 | 
					<@spring.formMultiSelect "command.spouses", nameOptionMap, ""/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FORM7
 | 
				
			||||||
 | 
					<@spring.formRadioButtons "command.name", nameOptionMap, " ", ""/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FORM8
 | 
				
			||||||
 | 
					<@spring.formCheckboxes "command.stringArray", nameOptionMap, " ", ""/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FORM9
 | 
				
			||||||
 | 
					<@spring.formPasswordInput "command.name", ""/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FORM10
 | 
				
			||||||
 | 
					<@spring.formHiddenInput "command.name", ""/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FORM11
 | 
				
			||||||
 | 
					<@spring.formInput "command.name", "", "text"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FORM12
 | 
				
			||||||
 | 
					<@spring.formInput "command.name", "", "hidden"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FORM13
 | 
				
			||||||
 | 
					<@spring.formInput "command.name", "", "password"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FORM14
 | 
				
			||||||
 | 
					<@spring.formSingleSelect "command.name", options, ""/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FORM15
 | 
				
			||||||
 | 
					<@spring.formCheckbox "command.name"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FORM16
 | 
				
			||||||
 | 
					<@spring.formCheckbox "command.jedi"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FORM17
 | 
				
			||||||
 | 
					<@spring.formInput "command.spouses[0].name", ""/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FORM18
 | 
				
			||||||
 | 
					<@spring.formCheckbox "command.spouses[0].jedi" />
 | 
				
			||||||
		Loading…
	
		Reference in New Issue