This is an automated email from the ASF dual-hosted git repository.
jamesyong 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 aab02b5 Improved: Well-formed html in ftl template (OFBIZ-11996)
aab02b5 is described below
commit aab02b514275a096f5bbb2dbb04ead601e0cfc62
Author: James Yong <[email protected]>
AuthorDate: Thu Sep 10 13:10:32 2020 +0800
Improved: Well-formed html in ftl template (OFBIZ-11996)
Add code to check closing tags and refactoring
---
.../java/org/apache/ofbiz/base/util/UtilHtml.java | 109 +++++++++++++++++++++
.../org/apache/ofbiz/base/util/UtilHtmlTest.java | 34 +++++++
.../org/apache/ofbiz/widget/model/HtmlWidget.java | 33 ++-----
3 files changed, 153 insertions(+), 23 deletions(-)
diff --git
a/framework/base/src/main/java/org/apache/ofbiz/base/util/UtilHtml.java
b/framework/base/src/main/java/org/apache/ofbiz/base/util/UtilHtml.java
new file mode 100644
index 0000000..39f185e
--- /dev/null
+++ b/framework/base/src/main/java/org/apache/ofbiz/base/util/UtilHtml.java
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * 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.base.util;
+
+import org.jsoup.parser.ParseError;
+import org.jsoup.parser.Parser;
+
+import javax.xml.stream.XMLEventReader;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.events.EndElement;
+import javax.xml.stream.events.StartElement;
+import javax.xml.stream.events.XMLEvent;
+import java.io.ByteArrayInputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Stack;
+
+public final class UtilHtml {
+
+ private static final String MODULE = UtilHtml.class.getName();
+ private static final Parser JSOUP_HTML_PARSER = createJSoupHtmlParser();
+ private static final String[] TAG_SHOULD_CLOSE_LIST = new String[]{"div"};
+ private UtilHtml() { }
+
+ private static Parser createJSoupHtmlParser() {
+ Parser parser = Parser.htmlParser();
+ parser.setTrackErrors(100);
+ return parser;
+ }
+
+ public static List<ParseError> validateHtmlFragmentWithJSoup(String
content) {
+ if (content != null) {
+ JSOUP_HTML_PARSER.parseInput(content, "");
+ if (JSOUP_HTML_PARSER.isTrackErrors()) {
+ return JSOUP_HTML_PARSER.getErrors();
+ }
+ }
+ return null;
+ }
+
+ /**
+ *
+ * @param content
+ * @param locationInfo for printing location information
+ * @return true if there is error
+ */
+ public static boolean hasUnclosedTag(String content, String locationInfo) {
+ XMLInputFactory inputFactory = XMLInputFactory.newInstance();
+ XMLEventReader eventReader = null;
+ try {
+ eventReader = inputFactory.createXMLEventReader(
+ new
ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)), "utf-8");
+ } catch (XMLStreamException e) {
+ Debug.logError(e.getMessage(), MODULE);
+ return true;
+ }
+
+ Stack<StartElement> stack = new Stack<StartElement>();
+ boolean hasError = false;
+ while (eventReader.hasNext()) {
+ try {
+ XMLEvent event = eventReader.nextEvent();
+ if (event.isStartElement()) {
+ StartElement startElement = event.asStartElement();
+ stack.push(startElement);
+ }
+ if (event.isEndElement()) {
+ EndElement endElement = event.asEndElement();
+ stack.pop();
+ }
+ } catch (XMLStreamException e) {
+ if (!stack.isEmpty()) {
+ StartElement startElement = stack.pop();
+ String elementName = startElement.getName().getLocalPart();
+ if
(Arrays.stream(TAG_SHOULD_CLOSE_LIST).anyMatch(elementName::equals)) {
+ hasError = true;
+ UtilHtml.logFormattedError(content, locationInfo,
e.getMessage(), MODULE);
+ }
+ } else {
+ UtilHtml.logFormattedError(content, locationInfo,
e.getMessage(), MODULE);
+ }
+ break;
+ }
+ }
+ return hasError;
+ }
+
+ public static void logFormattedError(String content, String location,
String error, String module) {
+ Debug.logError("[Parsing " + location + "]" + error, module);
+ }
+}
diff --git
a/framework/base/src/test/java/org/apache/ofbiz/base/util/UtilHtmlTest.java
b/framework/base/src/test/java/org/apache/ofbiz/base/util/UtilHtmlTest.java
new file mode 100644
index 0000000..e5a132e
--- /dev/null
+++ b/framework/base/src/test/java/org/apache/ofbiz/base/util/UtilHtmlTest.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * 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.base.util;
+
+import org.junit.Test;
+
+import javax.xml.stream.XMLStreamException;
+
+import static org.junit.Assert.assertEquals;
+
+public class UtilHtmlTest {
+
+ @Test
+ public void parseHtmlFragment_1() throws XMLStreamException {
+ assertEquals(true, UtilHtml.hasUnclosedTag("<div><div></div>",
"location.ftl"));
+ }
+}
+
diff --git
a/framework/widget/src/main/java/org/apache/ofbiz/widget/model/HtmlWidget.java
b/framework/widget/src/main/java/org/apache/ofbiz/widget/model/HtmlWidget.java
index 163fffd..b3368e0 100644
---
a/framework/widget/src/main/java/org/apache/ofbiz/widget/model/HtmlWidget.java
+++
b/framework/widget/src/main/java/org/apache/ofbiz/widget/model/HtmlWidget.java
@@ -31,6 +31,7 @@ import org.apache.ofbiz.base.util.Debug;
import org.apache.ofbiz.base.util.GeneralException;
import org.apache.ofbiz.base.util.UtilCodec;
import org.apache.ofbiz.base.util.UtilGenerics;
+import org.apache.ofbiz.base.util.UtilHtml;
import org.apache.ofbiz.base.util.UtilValidate;
import org.apache.ofbiz.base.util.UtilXml;
import org.apache.ofbiz.base.util.cache.UtilCache;
@@ -44,8 +45,6 @@ import
org.apache.ofbiz.widget.renderer.html.HtmlWidgetRenderer;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.parser.ParseError;
-import org.jsoup.parser.ParseErrorList;
-import org.jsoup.parser.Parser;
import org.jsoup.select.Elements;
import org.w3c.dom.Element;
@@ -270,32 +269,20 @@ public class HtmlWidget extends ModelScreenWidget {
String data = stringWriter.toString();
stringWriter.close();
- Document doc = null;
if (Debug.verboseOn()) {
- Parser parser = Parser.htmlParser();
- parser.setTrackErrors(100);
- doc = parser.parseInput(data, "");
-
- // check for any error during parsing
- int dataLength = data.length();
- Consumer<ParseError> logError = a ->
Debug.logError(a.toString() + " [Parsing " + location + "]\n"
- + "..."
- + data.substring(Math.max(a.getPosition() -
50, 0), a.getPosition()).replaceAll("^\\s+", "")
- + " ^^^ "
- + data.substring(a.getPosition(),
Math.min(a.getPosition() + 50, dataLength - 1)).replaceAll("\\s+$", "")
- + "...",
- MODULE);
-
- // print any parse error
- if (parser.isTrackErrors()) {
- ParseErrorList list = parser.getErrors();
- list.forEach(logError);
+ // check for unclosed tags
+ boolean hasError = false; //UtilHtml.hasUnclosedTag(data,
location);
+ if (!hasError) {
+ List<ParseError> errList =
UtilHtml.validateHtmlFragmentWithJSoup(data);
+ if (UtilValidate.isNotEmpty(errList)) {
+ Consumer<ParseError> logError = a ->
UtilHtml.logFormattedError(data, location, a.toString(), MODULE);
+ errList.forEach(logError);
+ }
}
- } else {
- doc = Jsoup.parseBodyFragment(data);
}
if (isMultiBlock()) {
+ Document doc = Jsoup.parseBodyFragment(data);
// extract js script tags
Elements scriptElements = doc.select("script");
if (scriptElements != null && scriptElements.size() > 0) {