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 f85040f Improved: Headerize external script in multi-block html template (OFBIZ-11741) f85040f is described below commit f85040ff04a70ea17facfd312c329101c3fe817b Author: James Yong <[hidden email]> AuthorDate: Sun May 31 17:42:42 2020 +0800 Improved: Headerize external script in multi-block html template (OFBIZ-11741) Allow expression in template location. --- build.gradle | 2 +- framework/widget/dtd/widget-screen.xsd | 2 +- .../widget/model/HtmlImportWidgetVisitor.java | 236 --------------------- .../org/apache/ofbiz/widget/model/HtmlWidget.java | 19 ++ .../ofbiz/widget/model/ModelScreenWidget.java | 19 -- .../widget/model/MultiBlockHtmlTemplateUtil.java | 132 +++++++++--- 6 files changed, 128 insertions(+), 282 deletions(-) diff --git a/build.gradle b/build.gradle index 7ad21a2..1e55676 100644 --- a/build.gradle +++ b/build.gradle @@ -286,7 +286,7 @@ checkstyle { // the sum of errors found last time it was changed after using the // ‘checkstyle’ tool present in the framework and in the official // plugins. - tasks.checkstyleMain.maxErrors = 26985 + tasks.checkstyleMain.maxErrors = 26908 // Currently there are a lot of errors so we need to temporarily // hide them to avoid polluting the terminal output. showViolations = false diff --git a/framework/widget/dtd/widget-screen.xsd b/framework/widget/dtd/widget-screen.xsd index 6e3fe97..3bf4f1b 100644 --- a/framework/widget/dtd/widget-screen.xsd +++ b/framework/widget/dtd/widget-screen.xsd @@ -524,7 +524,7 @@ under the License. <xs:documentation> Multi-block processing of template targeted for the html body. Inline script will be rendered as external script after html body tag. - External script tag with attribute data-import='head' will be rendered within html head tag if the template location is static. + External script tag with attribute data-import='head' will be rendered within html head tag. </xs:documentation> </xs:annotation> </xs:attribute> diff --git a/framework/widget/src/main/java/org/apache/ofbiz/widget/model/HtmlImportWidgetVisitor.java b/framework/widget/src/main/java/org/apache/ofbiz/widget/model/HtmlImportWidgetVisitor.java deleted file mode 100644 index 9e6add0..0000000 --- a/framework/widget/src/main/java/org/apache/ofbiz/widget/model/HtmlImportWidgetVisitor.java +++ /dev/null @@ -1,236 +0,0 @@ -/******************************************************************************* - * 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.model; - -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.apache.ofbiz.base.util.FileUtil; -import org.apache.ofbiz.base.util.UtilValidate; -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.jsoup.select.Elements; - -public final class HtmlImportWidgetVisitor implements ModelWidgetVisitor { - - private Set<String> jsImports = new LinkedHashSet<String>(); - - /** - * Get the script source locations collected by HtmlImportWidgetVisitor - * @return - */ - public Set<String> getJsImports() { - return jsImports; - } - - @Override - public void visit(HtmlWidget htmlWidget) throws Exception { - List<ModelScreenWidget> widgetList = htmlWidget.getSubWidgets(); - for (ModelScreenWidget widget: widgetList) { - // HtmlTemplate - widget.accept(this); - } - } - - @Override - public void visit(HtmlWidget.HtmlTemplate htmlTemplate) throws Exception { - String fileLocation = htmlTemplate.locationExdr.getOriginal(); - boolean isStaticLocation = !fileLocation.contains("${"); - if (isStaticLocation && htmlTemplate.isMultiBlock()) { - String template = FileUtil.readString("UTF-8", FileUtil.getFile(fileLocation)); - Document doc = Jsoup.parseBodyFragment(template); - Elements scriptElements = doc.select("script"); - if (scriptElements != null && scriptElements.size() > 0) { - for (org.jsoup.nodes.Element script : scriptElements) { - String src = script.attr("src"); - if (UtilValidate.isNotEmpty(src)) { - String dataImport = script.attr("data-import"); - if ("head".equals(dataImport)) { - jsImports.add(src); - } - } - } - } - } - } - - @Override - public void visit(HtmlWidget.HtmlTemplateDecorator htmlTemplateDecorator) throws Exception { - - } - - @Override - public void visit(HtmlWidget.HtmlTemplateDecoratorSection htmlTemplateDecoratorSection) throws Exception { - - } - - @Override - public void visit(IterateSectionWidget iterateSectionWidget) throws Exception { - - } - - @Override - public void visit(ModelSingleForm modelForm) throws Exception { - - } - - @Override - public void visit(ModelGrid modelGrid) throws Exception { - - } - - @Override - public void visit(ModelMenu modelMenu) throws Exception { - - } - - @Override - public void visit(ModelMenuItem modelMenuItem) throws Exception { - - } - - @Override - public void visit(ModelScreen modelScreen) throws Exception { - - } - - @Override - public void visit(ModelScreenWidget.ColumnContainer columnContainer) throws Exception { - - } - - @Override - public void visit(ModelScreenWidget.Container container) throws Exception { - - } - - @Override - public void visit(ModelScreenWidget.Content content) throws Exception { - - } - - @Override - public void visit(ModelScreenWidget.DecoratorScreen decoratorScreen) throws Exception { - - } - - @Override - public void visit(ModelScreenWidget.DecoratorSection decoratorSection) throws Exception { - - } - - @Override - public void visit(ModelScreenWidget.DecoratorSectionInclude decoratorSectionInclude) throws Exception { - - } - - @Override - public void visit(ModelScreenWidget.Form form) throws Exception { - - } - - @Override - public void visit(ModelScreenWidget.Grid grid) throws Exception { - - } - - @Override - public void visit(ModelScreenWidget.HorizontalSeparator horizontalSeparator) throws Exception { - - } - - @Override - public void visit(ModelScreenWidget.ScreenImage image) throws Exception { - - } - - @Override - public void visit(ModelScreenWidget.IncludeScreen includeScreen) throws Exception { - - } - - @Override - public void visit(ModelScreenWidget.Label label) throws Exception { - - } - - @Override - public void visit(ModelScreenWidget.ScreenLink link) throws Exception { - - } - - @Override - public void visit(ModelScreenWidget.Menu menu) throws Exception { - - } - - @Override - public void visit(ModelScreenWidget.PlatformSpecific platformSpecific) throws Exception { - Map<String, ModelScreenWidget> widgetMap = platformSpecific.getSubWidgets(); - for (Map.Entry<String, ModelScreenWidget> entry : widgetMap.entrySet()) { - if (entry.getKey().equals("html")) { - // HtmlWidget - entry.getValue().accept(this); - } - } - - } - - @Override - public void visit(ModelScreenWidget.PortalPage portalPage) throws Exception { - - } - - @Override - public void visit(ModelScreenWidget.Screenlet screenlet) throws Exception { - - } - - @Override - public void visit(ModelScreenWidget.Section section) throws Exception { - - } - - @Override - public void visit(ModelScreenWidget.Tree tree) throws Exception { - - } - - @Override - public void visit(ModelTree modelTree) throws Exception { - - } - - @Override - public void visit(ModelTree.ModelNode modelNode) throws Exception { - - } - - @Override - public void visit(ModelTree.ModelNode.ModelSubNode modelSubNode) throws Exception { - - } - - @Override - public void visit(ModelScreenWidget.Column column) throws Exception { - - } -} 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 4817a37..3e4f3cd 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 @@ -24,8 +24,10 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; import org.apache.ofbiz.base.util.Debug; import org.apache.ofbiz.base.util.GeneralException; @@ -261,6 +263,23 @@ public class HtmlWidget extends ModelScreenWidget { super(modelScreen, htmlTemplateElement); this.locationExdr = FlexibleStringExpander.getInstance(htmlTemplateElement.getAttribute("location")); this.multiBlock = !"false".equals(htmlTemplateElement.getAttribute("multi-block")); + + if (this.isMultiBlock()) { + String origLoc = this.locationExdr.getOriginal(); + Set<String> urls = null; + if (origLoc.contains("${")) { + urls = new LinkedHashSet<>(); + urls.add(origLoc); + } else { + try { + urls = MultiBlockHtmlTemplateUtil.getHtmlImportsFromHtmlTemplate(origLoc); + } catch (IOException e) { + String errMsg = "Error getting html imports from template at location [" + origLoc + "]: " + e.toString(); + Debug.logError(e, errMsg, MODULE); + } + } + MultiBlockHtmlTemplateUtil.addLinksToHtmlImportCache(modelScreen.getSourceLocation(), modelScreen.getName(), urls); + } } public String getLocation(Map<String, Object> context) { diff --git a/framework/widget/src/main/java/org/apache/ofbiz/widget/model/ModelScreenWidget.java b/framework/widget/src/main/java/org/apache/ofbiz/widget/model/ModelScreenWidget.java index a460679..c3701f3 100644 --- a/framework/widget/src/main/java/org/apache/ofbiz/widget/model/ModelScreenWidget.java +++ b/framework/widget/src/main/java/org/apache/ofbiz/widget/model/ModelScreenWidget.java @@ -259,25 +259,6 @@ public abstract class ModelScreenWidget extends ModelWidget { this.failWidgets = Collections.emptyList(); } this.isMainSection = isMainSection; - - String screenLocation = modelScreen.getSourceLocation(); - String screenName = modelScreen.getName(); - - // check required html import from the widgets - HtmlImportWidgetVisitor visitor = new HtmlImportWidgetVisitor(); - try { - for (ModelScreenWidget widget : this.subWidgets) { - widget.accept(visitor); - } - for (ModelScreenWidget widget : this.failWidgets) { - widget.accept(visitor); - } - MultiBlockHtmlTemplateUtil.addLinksToHtmlImportCache(screenLocation, screenName, visitor.getJsImports()); - } catch (Exception e) { - String errMsg = "Error adding links to Html Import Cache"; - Debug.logError(e, errMsg, MODULE); - throw new RuntimeException(errMsg); - } } @Override diff --git a/framework/widget/src/main/java/org/apache/ofbiz/widget/model/MultiBlockHtmlTemplateUtil.java b/framework/widget/src/main/java/org/apache/ofbiz/widget/model/MultiBlockHtmlTemplateUtil.java index 7404963..a7bc33e 100644 --- a/framework/widget/src/main/java/org/apache/ofbiz/widget/model/MultiBlockHtmlTemplateUtil.java +++ b/framework/widget/src/main/java/org/apache/ofbiz/widget/model/MultiBlockHtmlTemplateUtil.java @@ -18,6 +18,7 @@ *******************************************************************************/ package org.apache.ofbiz.widget.model; +import java.io.IOException; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; @@ -28,42 +29,69 @@ import java.util.Set; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; +import org.apache.ofbiz.base.util.Debug; +import org.apache.ofbiz.base.util.FileUtil; import org.apache.ofbiz.base.util.UtilGenerics; import org.apache.ofbiz.base.util.UtilValidate; +import org.apache.ofbiz.base.util.string.FlexibleStringExpander; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.select.Elements; public final class MultiBlockHtmlTemplateUtil { + private static final String MODULE = MultiBlockHtmlTemplateUtil.class.getName(); public static final String MULTI_BLOCK_WRITER = "multiBlockWriter"; private static final String HTML_LINKS_FOR_HEAD = "htmlLinksForHead"; private static final String SCRIPT_LINKS_FOR_FOOT = "ScriptLinksForFoot"; - private static int maxScriptCacheSizePerSession = 10; - // store inline script from freemarker template by user session + private static int maxScriptCacheSizePerUserSession = 10; + private static int estimatedConcurrentUserSessions = 250; + private static int estimatedScreensWithMultiBlockHtmlTemplate = 200; + private static int estimatedScreensWithChildScreen = 200; + /** + * Store inline script extracted from freemarker template for a user session. + * Number of inline scripts for a user session will be constraint by {@link MultiBlockHtmlTemplateUtil#maxScriptCacheSizePerUserSession} + * {@link MultiBlockHtmlTemplateUtil#cleanupScriptCache(HttpSession)} will be called to remove entry when session ends. + */ private static LinkedHashMap<String, Map<String, String>> scriptCache = new LinkedHashMap<String, Map<String, String>>() { private static final long serialVersionUID = 1L; protected boolean removeEldestEntry(Map.Entry<String, Map<String, String>> eldest) { - return size() > 100; // TODO probably set to max number of concurrent user + return size() > estimatedConcurrentUserSessions; } }; - // store the additional html import by screen location + /** + * For each screen containing html-template, store a set of html imports headerized from html-template. + * The set may contain entry of an expression of the the html-template's location. + * In this case, we need to expand the location expression, read the html-template for any html imports. + */ private static LinkedHashMap<String, Set<String>> htmlImportCache = new LinkedHashMap<String, Set<String>>() { private static final long serialVersionUID = 1L; protected boolean removeEldestEntry(Map.Entry<String, Set<String>> eldest) { - return size() > 300; // TODO probably set to max number of screens + return size() > estimatedScreensWithMultiBlockHtmlTemplate; } }; - // store the child screen + /** + * Store set of dependent screens info, in form of screen location + hash + name, of a given parent screen. + * The set may contain entry of an expression of the dependent screen location and will need to be expanded before use. + */ private static LinkedHashMap<String, Set<String>> dependentScreenCache = new LinkedHashMap<String, Set<String>>() { private static final long serialVersionUID = 1L; protected boolean removeEldestEntry(Map.Entry<String, Set<String>> eldest) { - return size() > 100; + return size() > estimatedScreensWithChildScreen; } }; private MultiBlockHtmlTemplateUtil() { } + /** + * Add child screen info to {@link MultiBlockHtmlTemplateUtil#dependentScreenCache}. + * @param parentModelScreen parent screen. + * @param location screen location. Expression is allowed. + * @param name screen name. Expression is allowed. + */ public static void collectChildScreenInfo(ModelScreen parentModelScreen, String location, String name) { String key = parentModelScreen.getSourceLocation() + "#" + parentModelScreen.getName(); Set<String> childList = dependentScreenCache.get(key); @@ -78,7 +106,14 @@ public final class MultiBlockHtmlTemplateUtil { } } - public static void addLinksToHtmlImportCache(String location, String name, Set<String> urls) throws Exception { + /** + * Add Html Imports to {@link MultiBlockHtmlTemplateUtil#htmlImportCache}. + * @param location screen location. Expression is not allowed. + * @param name screen name. Expression is not allowed. + * @param urls Set of html links associated with the screen. May contain expression to html-template location. + * @throws Exception + */ + public static void addLinksToHtmlImportCache(String location, String name, Set<String> urls) { if (UtilValidate.isEmpty(urls)) { return; } @@ -91,6 +126,37 @@ public final class MultiBlockHtmlTemplateUtil { existingUrls.addAll(urls); } + /** + * Get html import scr location from html template + * @param fileLocation Location to html template. Expression is not allowed. + * @return + */ + public static Set<String> getHtmlImportsFromHtmlTemplate(String fileLocation) throws IOException { + Set<String> imports = new LinkedHashSet<>(); + String template = FileUtil.readString("UTF-8", FileUtil.getFile(fileLocation)); + Document doc = Jsoup.parseBodyFragment(template); + Elements scriptElements = doc.select("script"); + if (scriptElements != null && scriptElements.size() > 0) { + for (org.jsoup.nodes.Element script : scriptElements) { + String src = script.attr("src"); + if (UtilValidate.isNotEmpty(src)) { + String dataImport = script.attr("data-import"); + if ("head".equals(dataImport)) { + imports.add(src); + } + } + } + } + return imports; + } + + /** + * Add html links to the header + * @param context + * @param location screen location. Expression is not allowed. + * @param name screen name. Expression is not allowed. + * @throws Exception + */ public static void addLinksToLayoutSettings(final Map<String, Object> context, String location, String name) throws Exception { HttpServletRequest request = (HttpServletRequest) context.get("request"); if (request.getAttribute(HTML_LINKS_FOR_HEAD) == null) { @@ -107,18 +173,37 @@ public final class MultiBlockHtmlTemplateUtil { if (UtilValidate.isEmpty(layoutSettingsJsList)) { return; } + // ensure initTheme.groovy has run. + Map<String, String> commonScreenLocations = UtilGenerics.cast(context.get("commonScreenLocations")); + if (UtilValidate.isEmpty(commonScreenLocations)) { + return; + } Object objValue = request.getAttribute(HTML_LINKS_FOR_HEAD); if (objValue instanceof String) { String currentLocationHashName = (String) request.getAttribute(HTML_LINKS_FOR_HEAD); Set<String> htmlLinks = new LinkedHashSet<>(); - Set<String> locHashNameList = getRelatedScreenLocationHashName(currentLocationHashName); + Set<String> locHashNameList = getRelatedScreenLocationHashName(currentLocationHashName, context); for (String locHashName:locHashNameList) { - Set<String> urls = htmlImportCache.get(locHashName); + String expandLocHashName = ""; + if (locHashName.contains("${")) { + expandLocHashName = FlexibleStringExpander.expandString(locHashName, context); + } else { + expandLocHashName = locHashName; + } + Set<String> urls = htmlImportCache.get(expandLocHashName); if (UtilValidate.isNotEmpty(urls)) { - // check url is not already in layoutSettings.javaScripts for (String url : urls) { - if (!htmlLinks.contains(url)) { - htmlLinks.add(url); + if (url.contains("${")) { + String expandUrl = FlexibleStringExpander.expandString(url, context); + if (UtilValidate.isNotEmpty(expandUrl)) { + htmlLinks.addAll(getHtmlImportsFromHtmlTemplate(expandUrl)); + } else { + Debug.log("Unable to expand " + url, MODULE); + } + } else { + if (!htmlLinks.contains(url)) { + htmlLinks.add(url); + } } } } @@ -141,28 +226,25 @@ public final class MultiBlockHtmlTemplateUtil { * @param locationHashName * @return */ - private static Set<String> getRelatedScreenLocationHashName(String locationHashName) { + private static Set<String> getRelatedScreenLocationHashName(String locationHashName, final Map<String, Object> context) { Set<String> resultList = new HashSet<>(); resultList.add(locationHashName); Set<String> locHashNameList = dependentScreenCache.get(locationHashName); if (locHashNameList != null) { for (String locHashName : locHashNameList) { - resultList.addAll(getRelatedScreenLocationHashName(locHashName)); + String exLocHashName = ""; + if (locHashName.contains("${")) { + exLocHashName = FlexibleStringExpander.expandString(locHashName, context); + } else { + exLocHashName = locHashName; + } + resultList.addAll(getRelatedScreenLocationHashName(exLocHashName, context)); } } return resultList; } /** - * add the script links that should be in the head tag - * @param context - * @param urls - */ - private static void addJsLinkToLayoutSettings(final Map<String, Object> context, final Set<String> urls) { - - } - - /** * add script link for page footer. * @param context * @param filePath @@ -203,7 +285,7 @@ public final class MultiBlockHtmlTemplateUtil { scriptMap = new LinkedHashMap<String, String>() { private static final long serialVersionUID = 1L; protected boolean removeEldestEntry(Map.Entry<String, String> eldest) { - return size() > maxScriptCacheSizePerSession; + return size() > maxScriptCacheSizePerUserSession; } }; scriptCache.put(sessionId, scriptMap); |
Free forum by Nabble | Edit this page |