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 <[hidden email]> 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) { |
Free forum by Nabble | Edit this page |