This is an automated email from the ASF dual-hosted git repository.
mridulpathak pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git
The following commit(s) were added to refs/heads/trunk by this push:
new 4ccdb3e917 Improved: The macro renderers cannot compile a string as a
template anymore (OFBIZ-13682)
4ccdb3e917 is described below
commit 4ccdb3e917b64245a527b1c01d8f9de613c6c714
Author: Mridul Pathak <[email protected]>
AuthorDate: Fri Oct 16 18:42:30 2026 +0530
Improved: The macro renderers cannot compile a string as a template anymore
(OFBIZ-13682)
All the macro calls of the widget renderers are now made with data, so the
code that compiled text as a template is not used anymore and is removed, which
makes sure that a value can never be parsed as template code again. FtlWriter
only compiles the constant text that calls a macro, and a test checks that no
other class builds macro text or compiles a template, and that FtlWriter
compiles that one constant only.
- FtlWriter.processFtlString, MacroFormRenderer.executeMacro,
MacroFormRenderer.encode with its escaping for a FreeMarker string literal,
MacroCommonRenderer.escapeFtlString and the unused
MacroFormRenderer.appendContentUrl are removed, as well as a macro call as text
that RenderableFtlFormElementsBuilder built and never used.
- RenderableFtlString is renamed RenderableFtlVerbatim, because FtlWriter
writes it as it is and does not process it as a template.
---
.../ofbiz/widget/renderer/macro/FtlWriter.java | 35 ++------
.../widget/renderer/macro/MacroCommonRenderer.java | 11 ---
.../widget/renderer/macro/MacroFormRenderer.java | 38 ---------
.../macro/RenderableFtlFormElementsBuilder.java | 16 ++--
...leFtlString.java => RenderableFtlVerbatim.java} | 30 +++----
.../macro/renderable/RenderableFtlVisitor.java | 2 +-
.../ofbiz/widget/renderer/macro/FtlWriterTest.java | 6 +-
.../macro/MacroRenderersTemplateTextGuardTest.java | 98 ++++++++++++++++++++++
.../RenderableFtlFormElementsBuilderTest.java | 8 +-
9 files changed, 135 insertions(+), 109 deletions(-)
diff --git
a/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/FtlWriter.java
b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/FtlWriter.java
index 8261efbd8b..0a960ae699 100644
---
a/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/FtlWriter.java
+++
b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/FtlWriter.java
@@ -30,19 +30,18 @@ import org.apache.ofbiz.widget.renderer.VisualTheme;
import org.apache.ofbiz.widget.renderer.macro.renderable.RenderableFtl;
import
org.apache.ofbiz.widget.renderer.macro.renderable.RenderableFtlMacroCall;
import org.apache.ofbiz.widget.renderer.macro.renderable.RenderableFtlNoop;
-import org.apache.ofbiz.widget.renderer.macro.renderable.RenderableFtlString;
+import org.apache.ofbiz.widget.renderer.macro.renderable.RenderableFtlVerbatim;
import org.apache.ofbiz.widget.renderer.macro.renderable.RenderableFtlVisitor;
import java.io.IOException;
-import java.io.Reader;
import java.io.StringReader;
import java.util.Locale;
import java.util.Map;
-import java.util.UUID;
import java.util.WeakHashMap;
/**
- * Processes FTL templates and writes result to Appendables.
+ * Calls the macros of a macro library and writes the result to Appendables.
The only template text it compiles is the
+ * constant text that calls a macro, so no value of the data is ever parsed as
template code.
*/
public final class FtlWriter {
private static final String MODULE = FtlWriter.class.getName();
@@ -90,26 +89,6 @@ public final class FtlWriter {
renderableFtl.accept(writerVisitor, locale);
}
- /**
- * Process the given FTL string as a template and write the result to the
Appendable.
- *
- * @param writer The Appendable to write the result of the template
processing to.
- * @param ftlString The FTL string to process as a template.
- */
- public void processFtlString(Appendable writer, Locale locale, String
ftlString) {
- try {
- final Environment environment = getEnvironment(writer, locale);
- setThemeVariables(environment);
- Reader templateReader = new StringReader(ftlString);
- Template template = new Template(UUID.randomUUID().toString(),
templateReader,
- FreeMarkerWorker.getDefaultOfbizConfig());
- templateReader.close();
- environment.include(template);
- } catch (TemplateException | IOException e) {
- Debug.logError(e, "Error rendering ftl, ftlString: " + ftlString,
MODULE);
- }
- }
-
/**
* Call a macro of the macro library and write the result to the
Appendable. The name of the macro and the
* parameters are passed to FreeMarker as data, never as template text.
@@ -173,7 +152,7 @@ public final class FtlWriter {
/**
* Visitor for RenderableFtl objects that will call the macros of the
macro library, or write the finished output
- * of a RenderableFtlString as it is, to the configured Appendable.
+ * of a RenderableFtlVerbatim as it is, to the configured Appendable.
*/
private final class RenderableFtlFtlWriterVisitor implements
RenderableFtlVisitor {
@@ -198,12 +177,12 @@ public final class FtlWriter {
}
@Override
- public void visit(RenderableFtlString renderableFtlString, Locale
locale) {
+ public void visit(RenderableFtlVerbatim renderableFtlVerbatim, Locale
locale) {
// the string holds finished output that can contain data, so it
is written as it is and never compiled
try {
- writer.append(renderableFtlString.getFtlString());
+ writer.append(renderableFtlVerbatim.getOutput());
} catch (IOException e) {
- Debug.logError(e, "Error writing the output of a
RenderableFtlString", MODULE);
+ Debug.logError(e, "Error writing the output of a
RenderableFtlVerbatim", MODULE);
}
}
}
diff --git
a/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroCommonRenderer.java
b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroCommonRenderer.java
index 060fcb1d6a..8be5547151 100644
---
a/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroCommonRenderer.java
+++
b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroCommonRenderer.java
@@ -38,17 +38,6 @@ import org.apache.ofbiz.widget.model.ModelForm;
public class MacroCommonRenderer {
- /**
- * Escape a value so that it is rendered literally when it is placed
inside a double-quoted FreeMarker
- * string literal of a macro call: the backslash and the double quote are
escaped, and so is the opening
- * brace, so that neither <code>${...}</code> nor <code>#{...}</code> is
interpolated.
- * @param value the value to escape, may be null
- * @return the escaped value, or an empty string if the value is null
- */
- public static String escapeFtlString(String value) {
- return value == null ? "" : value.replace("\\", "\\\\").replace("\"",
"\\\"").replace("{", "\\{");
- }
-
/**
* Create an ajaxXxxx JavaScript CSV string from a list of UpdateArea
objects. See
* <code>OfbizUtil.js</code>.
diff --git
a/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroFormRenderer.java
b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroFormRenderer.java
index 94ee7be6e6..b79ab21049 100644
---
a/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroFormRenderer.java
+++
b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroFormRenderer.java
@@ -38,7 +38,6 @@ import org.apache.ofbiz.base.util.UtilValidate;
import org.apache.ofbiz.base.util.string.FlexibleStringExpander;
import org.apache.ofbiz.entity.Delegator;
import org.apache.ofbiz.webapp.control.RequestHandler;
-import org.apache.ofbiz.webapp.taglib.ContentUrlTag;
import org.apache.ofbiz.widget.WidgetWorker;
import org.apache.ofbiz.widget.content.StaticContentUrlProvider;
import org.apache.ofbiz.widget.model.CommonWidgetModels;
@@ -82,7 +81,6 @@ import
org.apache.ofbiz.widget.renderer.macro.renderable.RenderableFtlMacroCall;
public final class MacroFormRenderer implements FormStringRenderer {
private static final String MODULE = MacroFormRenderer.class.getName();
- private final UtilCodec.SimpleEncoder internalEncoder;
private final RequestHandler rh;
private final HttpServletRequest request;
private final HttpServletResponse response;
@@ -106,7 +104,6 @@ public final class MacroFormRenderer implements
FormStringRenderer {
this.visualTheme = ThemeFactory.resolveVisualTheme(request);
this.rh = RequestHandler.from(request);
this.javaScriptEnabled = UtilHttp.isJavaScriptEnabled(request);
- internalEncoder = UtilCodec.getEncoder("string");
this.ftlWriter = ftlWriter != null ? ftlWriter : new
FtlWriter(macroLibraryPath, this.visualTheme);
final StaticContentUrlProvider staticContentUrlProvider = new
StaticContentUrlProvider(request);
this.renderableFtlFormElementsBuilder =
renderableFtlFormElementsBuilder != null
@@ -130,34 +127,6 @@ public final class MacroFormRenderer implements
FormStringRenderer {
ftlWriter.processFtl(writer, locale, renderableFtl);
}
- private void executeMacro(Appendable writer, String macro) {
- ftlWriter.processFtlString(writer, null, macro);
- }
-
- /**
- * Make locale available before executing macro
- * @param writer
- * @param locale
- * @param macro
- */
- @SuppressWarnings("unused")
- private void executeMacro(Appendable writer, Locale locale, String macro) {
- ftlWriter.processFtlString(writer, locale, macro);
- }
-
- private String encode(String value, ModelFormField modelFormField,
Map<String, Object> context) {
- if (UtilValidate.isEmpty(value)) {
- return value;
- }
- UtilCodec.SimpleEncoder encoder = (UtilCodec.SimpleEncoder)
context.get("simpleEncoder");
- if (modelFormField.getEncodeOutput() && encoder != null) {
- value = encoder.encode(value);
- } else {
- value = internalEncoder.encode(value);
- }
- return value;
- }
-
private String encodeForMacro(String value, ModelFormField modelFormField,
Map<String, Object> context) {
if (UtilValidate.isEmpty(value)) {
return value;
@@ -803,13 +772,6 @@ public final class MacroFormRenderer implements
FormStringRenderer {
writeFtlElement(writer, renderableFtl);
}
- public void appendContentUrl(Appendable writer, String location) throws
IOException {
- StringBuilder buffer = new StringBuilder();
- ContentUrlTag.appendContentPrefix(this.request, buffer);
- writer.append(buffer.toString());
- writer.append(location);
- }
-
@Override
public void renderContainerFindField(Appendable writer, Map<String,
Object> context, ContainerField containerField) throws IOException {
final RenderableFtlMacroCall containerMc =
renderableFtlFormElementsBuilder.containerMacroCall(context, containerField);
diff --git
a/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/RenderableFtlFormElementsBuilder.java
b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/RenderableFtlFormElementsBuilder.java
index b83a0766e0..cebabbe491 100644
---
a/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/RenderableFtlFormElementsBuilder.java
+++
b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/RenderableFtlFormElementsBuilder.java
@@ -75,8 +75,8 @@ import
org.apache.ofbiz.widget.renderer.macro.renderable.RenderableFtlMacroCall;
import
org.apache.ofbiz.widget.renderer.macro.renderable.RenderableFtlMacroCall.RenderableFtlMacroCallBuilder;
import org.apache.ofbiz.widget.renderer.macro.renderable.RenderableFtlNoop;
import org.apache.ofbiz.widget.renderer.macro.renderable.RenderableFtlSequence;
-import org.apache.ofbiz.widget.renderer.macro.renderable.RenderableFtlString;
-import
org.apache.ofbiz.widget.renderer.macro.renderable.RenderableFtlString.RenderableFtlStringBuilder;
+import org.apache.ofbiz.widget.renderer.macro.renderable.RenderableFtlVerbatim;
+import
org.apache.ofbiz.widget.renderer.macro.renderable.RenderableFtlVerbatim.RenderableFtlVerbatimBuilder;
import org.jsoup.nodes.Element;
/**
@@ -170,8 +170,6 @@ public final class RenderableFtlFormElementsBuilder {
.stringParameter("class", modelFormField.getWidgetStyle())
.stringParameter("alert", modelFormField.shouldBeRed(context)
? "true" : "false");
- StringWriter sr = new StringWriter();
- sr.append("<@renderDisplayField ");
if (ajaxEnabled) {
String url = inPlaceEditor.getUrl(context);
StringBuffer extraParameterBuffer = new StringBuffer();
@@ -827,8 +825,8 @@ public final class RenderableFtlFormElementsBuilder {
parameterMap.put(viewIndexField, Integer.toString(viewIndex));
parameterMap.put(viewSizeField, Integer.toString(viewSize));
- final RenderableFtlStringBuilder renderableFtlStringBuilder =
RenderableFtlString.builder();
- final StringBuilder htmlStringBuilder =
renderableFtlStringBuilder.getStringBuilder();
+ final RenderableFtlVerbatimBuilder renderableFtlVerbatimBuilder =
RenderableFtlVerbatim.builder();
+ final StringBuilder htmlStringBuilder =
renderableFtlVerbatimBuilder.getStringBuilder();
if ("multi".equals(modelForm.getType())) {
final Element anchorElement =
WidgetWorker.makeHiddenFormLinkAnchorElement(linkStyle,
@@ -839,12 +837,12 @@ public final class RenderableFtlFormElementsBuilder {
// this is a bit trickier, since we can't do a nested form
we'll have to put the link to submit the
// form in place, but put the actual form def elsewhere, ie
after the big form is closed
- final RenderableFtlString postFormRenderableFtlString =
RenderableFtlString.withStringBuilder(sb -> {
+ final RenderableFtlVerbatim postFormRenderableFtlVerbatim =
RenderableFtlVerbatim.withStringBuilder(sb -> {
final Element hiddenFormElement =
WidgetWorker.makeHiddenFormLinkFormElement(target, targetType,
targetWindow, parameterMap, modelFormField,
request, response, context);
sb.append(hiddenFormElement.outerHtml());
});
- appendToPostFormRenderableFtl(postFormRenderableFtlString,
context);
+ appendToPostFormRenderableFtl(postFormRenderableFtlVerbatim,
context);
} else {
final Element hiddenFormElement =
WidgetWorker.makeHiddenFormLinkFormElement(target, targetType,
@@ -857,7 +855,7 @@ public final class RenderableFtlFormElementsBuilder {
}
}
- return renderableFtlStringBuilder.build();
+ return renderableFtlVerbatimBuilder.build();
} else {
if ("layered-modal".equals(realLinkType)) {
diff --git
a/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/renderable/RenderableFtlString.java
b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/renderable/RenderableFtlVerbatim.java
similarity index 69%
rename from
framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/renderable/RenderableFtlString.java
rename to
framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/renderable/RenderableFtlVerbatim.java
index eb251ed9f5..0b290a7144 100644
---
a/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/renderable/RenderableFtlString.java
+++
b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/renderable/RenderableFtlVerbatim.java
@@ -25,31 +25,31 @@ import java.util.function.Consumer;
* Finished output, such as the HTML of a link, that is written as it is. It
is not a template: nothing in it is
* interpreted, so it can hold values of the data.
*/
-public final class RenderableFtlString implements RenderableFtl {
- private final String ftlString;
+public final class RenderableFtlVerbatim implements RenderableFtl {
+ private final String output;
- public RenderableFtlString(final String ftlString) {
- this.ftlString = ftlString;
+ public RenderableFtlVerbatim(final String output) {
+ this.output = output;
}
- public String getFtlString() {
- return ftlString;
+ public String getOutput() {
+ return output;
}
- public static RenderableFtlStringBuilder builder() {
- return new RenderableFtlStringBuilder();
+ public static RenderableFtlVerbatimBuilder builder() {
+ return new RenderableFtlVerbatimBuilder();
}
- public static RenderableFtlString withStringBuilder(final
Consumer<StringBuilder> callback) {
- final RenderableFtlStringBuilder builder = builder();
+ public static RenderableFtlVerbatim withStringBuilder(final
Consumer<StringBuilder> callback) {
+ final RenderableFtlVerbatimBuilder builder = builder();
callback.accept(builder.getStringBuilder());
return builder.build();
}
@Override
public String toString() {
- return "RenderableFtlString{"
- + "ftlString='" + ftlString + '\''
+ return "RenderableFtlVerbatim{"
+ + "output='" + output + '\''
+ '}';
}
@@ -58,15 +58,15 @@ public final class RenderableFtlString implements
RenderableFtl {
visitor.visit(this, locale);
}
- public static final class RenderableFtlStringBuilder {
+ public static final class RenderableFtlVerbatimBuilder {
private final StringBuilder stringBuilder = new StringBuilder();
public StringBuilder getStringBuilder() {
return stringBuilder;
}
- public RenderableFtlString build() {
- return new RenderableFtlString(stringBuilder.toString());
+ public RenderableFtlVerbatim build() {
+ return new RenderableFtlVerbatim(stringBuilder.toString());
}
}
}
diff --git
a/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/renderable/RenderableFtlVisitor.java
b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/renderable/RenderableFtlVisitor.java
index 6dd65d9bcd..d6bcc7f941 100644
---
a/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/renderable/RenderableFtlVisitor.java
+++
b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/renderable/RenderableFtlVisitor.java
@@ -23,5 +23,5 @@ import java.util.Locale;
public interface RenderableFtlVisitor {
void visit(RenderableFtlMacroCall renderableFtlMacroCall, Locale locale);
void visit(RenderableFtlNoop renderableFtlNoop, Locale locale);
- void visit(RenderableFtlString renderableFtlString, Locale locale);
+ void visit(RenderableFtlVerbatim renderableFtlVerbatim, Locale locale);
}
diff --git
a/framework/widget/src/test/java/org/apache/ofbiz/widget/renderer/macro/FtlWriterTest.java
b/framework/widget/src/test/java/org/apache/ofbiz/widget/renderer/macro/FtlWriterTest.java
index e51d501c76..3e6737ec1e 100644
---
a/framework/widget/src/test/java/org/apache/ofbiz/widget/renderer/macro/FtlWriterTest.java
+++
b/framework/widget/src/test/java/org/apache/ofbiz/widget/renderer/macro/FtlWriterTest.java
@@ -32,7 +32,7 @@ import static org.mockito.Mockito.verify;
import java.io.StringWriter;
import org.apache.ofbiz.base.util.template.FreeMarkerWorker;
-import org.apache.ofbiz.widget.renderer.macro.renderable.RenderableFtlString;
+import org.apache.ofbiz.widget.renderer.macro.renderable.RenderableFtlVerbatim;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -61,12 +61,12 @@ public final class FtlWriterTest {
}
@Test
- public void renderableFtlStringIsWrittenAsItIsAndNeverCompiled() throws
Exception {
+ public void renderableFtlVerbatimIsWrittenAsItIsAndNeverCompiled() throws
Exception {
final FtlWriter ftlWriter = new FtlWriter("LIBRARY");
final StringWriter writer = new StringWriter();
final String html = "<a href=\"#\">${1+1} <#stop \"stopped\">
#{2}</a>";
- ftlWriter.processFtl(writer, null, new RenderableFtlString(html));
+ ftlWriter.processFtl(writer, null, new RenderableFtlVerbatim(html));
assertThat(writer.toString(), equalTo(html));
// a compiled string would have been included in the environment of
the writer
diff --git
a/framework/widget/src/test/java/org/apache/ofbiz/widget/renderer/macro/MacroRenderersTemplateTextGuardTest.java
b/framework/widget/src/test/java/org/apache/ofbiz/widget/renderer/macro/MacroRenderersTemplateTextGuardTest.java
new file mode 100644
index 0000000000..194239cecc
--- /dev/null
+++
b/framework/widget/src/test/java/org/apache/ofbiz/widget/renderer/macro/MacroRenderersTemplateTextGuardTest.java
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+ *
+ * http://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.apache.ofbiz.widget.renderer.macro;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.jupiter.api.Assumptions.assumeTrue;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Guards the rule that the macro renderers pass the values to the macros as
data and never turn them into template
+ * text: the only template text that is compiled is the constant text of
{@link FtlWriter} that calls a macro.
+ */
+public final class MacroRenderersTemplateTextGuardTest {
+
+ private static final String MACRO_PACKAGE_PATH =
"org/apache/ofbiz/widget/renderer/macro";
+ private static final String FTL_WRITER = "FtlWriter.java";
+
+ private static Path macroSources() {
+ for (final String root : new String[]
{"framework/widget/src/main/java/", "src/main/java/"}) {
+ final Path path = Paths.get(root + MACRO_PACKAGE_PATH);
+ if (Files.isDirectory(path)) {
+ return path;
+ }
+ }
+ return null;
+ }
+
+ private static List<Path> javaFiles(final Path directory) throws
IOException {
+ try (Stream<Path> files = Files.walk(directory)) {
+ return files.filter(file ->
file.toString().endsWith(".java")).collect(Collectors.toList());
+ }
+ }
+
+ private static String read(final Path file) throws IOException {
+ return new String(Files.readAllBytes(file), StandardCharsets.UTF_8);
+ }
+
+ @Test
+ public void onlyFtlWriterBuildsMacroCallsOrCompilesTemplates() throws
IOException {
+ final Path sources = macroSources();
+ assumeTrue(sources != null, "the sources of the macro renderers are
not available from the working directory");
+
+ final List<String> offenders = new ArrayList<>();
+ for (final Path file : javaFiles(sources)) {
+ final String name = file.getFileName().toString();
+ if (FTL_WRITER.equals(name)) {
+ continue;
+ }
+ final String content = read(file);
+ if (content.contains("\"<@")) {
+ offenders.add(name + " builds a macro call as text");
+ }
+ if (content.contains("new Template(")) {
+ offenders.add(name + " compiles a template");
+ }
+ }
+ assertThat(offenders, empty());
+ }
+
+ @Test
+ public void ftlWriterCompilesOnlyTheConstantMacroCallTemplate() throws
IOException {
+ final Path sources = macroSources();
+ assumeTrue(sources != null, "the sources of the macro renderers are
not available from the working directory");
+
+ final String content = read(sources.resolve(FTL_WRITER));
+ assertThat("FtlWriter compiles a single template", content.split("new
Template\\(", -1).length - 1, equalTo(1));
+ assertThat(content.contains("MACRO_CALL_TEMPLATE"), equalTo(true));
+ }
+}
diff --git
a/framework/widget/src/test/java/org/apache/ofbiz/widget/renderer/macro/RenderableFtlFormElementsBuilderTest.java
b/framework/widget/src/test/java/org/apache/ofbiz/widget/renderer/macro/RenderableFtlFormElementsBuilderTest.java
index 6d63f6699f..d7c29bcb5a 100644
---
a/framework/widget/src/test/java/org/apache/ofbiz/widget/renderer/macro/RenderableFtlFormElementsBuilderTest.java
+++
b/framework/widget/src/test/java/org/apache/ofbiz/widget/renderer/macro/RenderableFtlFormElementsBuilderTest.java
@@ -33,7 +33,7 @@ import org.apache.ofbiz.widget.renderer.VisualTheme;
import
org.apache.ofbiz.widget.renderer.macro.renderable.RenderableFtlMacroCall;
import org.apache.ofbiz.widget.renderer.macro.renderable.RenderableFtl;
import org.apache.ofbiz.widget.renderer.macro.renderable.RenderableFtlNoop;
-import org.apache.ofbiz.widget.renderer.macro.renderable.RenderableFtlString;
+import org.apache.ofbiz.widget.renderer.macro.renderable.RenderableFtlVerbatim;
import org.mockito.MockedStatic;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -1068,9 +1068,9 @@ public final class RenderableFtlFormElementsBuilderTest {
final RenderableFtl link =
renderableFtlFormElementsBuilder.makeHyperlinkByType("hidden-form", "STYLE",
null, "#",
new HashMap<>(), "A ${description}", "", "", modelFormField,
request, response, context);
- assertThat(link instanceof RenderableFtlString, equalTo(true));
- assertThat(((RenderableFtlString) link).getFtlString().contains("A
${description}"), equalTo(true));
-
assertThat(renderableFtlFormElementsBuilder.postMultiFormContent(context)
instanceof RenderableFtlString, equalTo(true));
+ assertThat(link instanceof RenderableFtlVerbatim, equalTo(true));
+ assertThat(((RenderableFtlVerbatim) link).getOutput().contains("A
${description}"), equalTo(true));
+
assertThat(renderableFtlFormElementsBuilder.postMultiFormContent(context)
instanceof RenderableFtlVerbatim, equalTo(true));
}
private static void labelsReturningTheirKey(final
MockedStatic<UtilProperties> utilProperties) {