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 c0e527e Improved: Headerize external script in multi-block html template (OFBIZ-11741) c0e527e is described below commit c0e527efb2bc63803298138a415699dee507ee02 Author: James Yong <[hidden email]> AuthorDate: Sat May 30 12:00:21 2020 +0800 Improved: Headerize external script in multi-block html template (OFBIZ-11741) Allow external scripts within the multi-block html template, to be rendered within the html head tag, when a new attribute data-import is set to “head” Thanks: Jacques for review --- .../humanres/template/category/CategoryTree.ftl | 2 + applications/humanres/widget/CommonScreens.xml | 1 - .../product/template/category/CategoryTree.ftl | 2 + .../template/store/ProductStoreGroupTree.ftl | 2 + .../product/widget/catalog/CommonScreens.xml | 1 - .../java/org/apache/ofbiz/common/CommonEvents.java | 4 +- ...ansform.java => ScriptTagsFooterTransform.java} | 10 +- .../ofbiz/webapp/freemarkerTransforms.properties | 2 +- framework/widget/dtd/widget-screen.xsd | 10 +- .../widget/model/HtmlImportWidgetVisitor.java | 236 +++++++++++++++++++++ .../org/apache/ofbiz/widget/model/HtmlWidget.java | 116 +++++----- .../ofbiz/widget/model/ModelScreenWidget.java | 139 ++++++++---- .../widget/model/MultiBlockHtmlTemplateUtil.java | 228 ++++++++++++++++++++ .../ofbiz/widget/model/ScriptTemplateUtil.java | 105 --------- .../ofbiz/widget/renderer/ScreenRenderer.java | 5 +- .../template/includes/CloseHtmlBody.ftl | 2 +- .../template/includes/LookupFooter.ftl | 2 +- themes/common-theme/widget/Theme.xml | 1 - themes/flatgrey/template/Footer.ftl | 2 +- themes/rainbowstone/template/includes/Footer.ftl | 2 +- themes/tomahawk/template/Footer.ftl | 2 +- 21 files changed, 666 insertions(+), 208 deletions(-) diff --git a/applications/humanres/template/category/CategoryTree.ftl b/applications/humanres/template/category/CategoryTree.ftl index f14bbfc..b1b7465 100644 --- a/applications/humanres/template/category/CategoryTree.ftl +++ b/applications/humanres/template/category/CategoryTree.ftl @@ -17,6 +17,8 @@ specific language governing permissions and limitations under the License. --> +<script src="/common/js/jquery/plugins/jsTree/jquery.jstree.js" type="application/javascript" data-import="head"></script> + <script type="application/javascript"> <#-- some labels are not unescaped in the JSON object so we have to do this manualy --> function unescapeHtmlText(text) { diff --git a/applications/humanres/widget/CommonScreens.xml b/applications/humanres/widget/CommonScreens.xml index 965b590..cf5db20 100644 --- a/applications/humanres/widget/CommonScreens.xml +++ b/applications/humanres/widget/CommonScreens.xml @@ -39,7 +39,6 @@ under the License. <set field="layoutSettings.styleSheets[]" value="/partymgr/static/partymgr.css" global="true"/> <set field="layoutSettings.styleSheets[]" value="/images/humanres/humanres.css" global="true"/> <set field="layoutSettings.javaScripts[+0]" value="/common/js/jquery/ui/js/jquery.cookie-1.4.0.js" global="true"/> - <set field="layoutSettings.javaScripts[+0]" value="/common/js/jquery/plugins/jsTree/jquery.jstree.js" global="true"/> <set field="applicationMenuName" value="HumanResAppBar" global="true"/> <set field="applicationMenuLocation" value="component://humanres/widget/HumanresMenus.xml" global="true"/> <set field="applicationTitle" from-field="uiLabelMap.HumanResManagerApplication" global="true"/> diff --git a/applications/product/template/category/CategoryTree.ftl b/applications/product/template/category/CategoryTree.ftl index dd4ca21..dcb6a47 100644 --- a/applications/product/template/category/CategoryTree.ftl +++ b/applications/product/template/category/CategoryTree.ftl @@ -17,6 +17,8 @@ specific language governing permissions and limitations under the License. --> +<script src="/common/js/jquery/plugins/jsTree/jquery.jstree.js" type="application/javascript" data-import="head"></script> + <script type="application/javascript"> <#-- some labels are not unescaped in the JSON object so we have to do this manualy --> function unescapeHtmlText(text) { diff --git a/applications/product/template/store/ProductStoreGroupTree.ftl b/applications/product/template/store/ProductStoreGroupTree.ftl index 039f981..91c0027 100644 --- a/applications/product/template/store/ProductStoreGroupTree.ftl +++ b/applications/product/template/store/ProductStoreGroupTree.ftl @@ -17,6 +17,8 @@ specific language governing permissions and limitations under the License. --> +<script src="/common/js/jquery/plugins/jsTree/jquery.jstree.js" type="application/javascript" data-import="head"></script> + <script type="application/javascript"> <#-- some labels are not unescaped in the JSON object so we have to do this manualy --> function unescapeHtmlText(text) { diff --git a/applications/product/widget/catalog/CommonScreens.xml b/applications/product/widget/catalog/CommonScreens.xml index c3842fd..cbaf3e6 100644 --- a/applications/product/widget/catalog/CommonScreens.xml +++ b/applications/product/widget/catalog/CommonScreens.xml @@ -42,7 +42,6 @@ under the License. <set field="applicationMenuLocation" value="component://product/widget/catalog/CatalogMenus.xml" global="true"/> <set field="applicationTitle" from-field="uiLabelMap.ProductCatalogManagerApplication" global="true"/> <set field="layoutSettings.javaScripts[+0]" value="/common/js/jquery/ui/js/jquery.cookie-1.4.0.js" global="true"/> - <set field="layoutSettings.javaScripts[+0]" value="/common/js/jquery/plugins/jsTree/jquery.jstree.js" global="true"/> </actions> <widgets> <include-screen name="ApplicationDecorator" location="component://commonext/widget/CommonScreens.xml"/> diff --git a/framework/common/src/main/java/org/apache/ofbiz/common/CommonEvents.java b/framework/common/src/main/java/org/apache/ofbiz/common/CommonEvents.java index aeda1ec..9450567 100644 --- a/framework/common/src/main/java/org/apache/ofbiz/common/CommonEvents.java +++ b/framework/common/src/main/java/org/apache/ofbiz/common/CommonEvents.java @@ -53,7 +53,7 @@ import org.apache.ofbiz.entity.GenericValue; import org.apache.ofbiz.entity.util.EntityUtilProperties; import org.apache.ofbiz.webapp.control.JWTManager; import org.apache.ofbiz.webapp.control.LoginWorker; -import org.apache.ofbiz.widget.model.ScriptTemplateUtil; +import org.apache.ofbiz.widget.model.MultiBlockHtmlTemplateUtil; import org.apache.ofbiz.widget.model.ThemeFactory; import org.apache.ofbiz.widget.renderer.VisualTheme; @@ -180,7 +180,7 @@ public class CommonEvents { public static String jsResponseFromRequest(HttpServletRequest request, HttpServletResponse response) { String fileName = request.getParameter("name"); - String script = ScriptTemplateUtil.getScriptFromSession(request.getSession(), fileName); + String script = MultiBlockHtmlTemplateUtil.getScriptFromCache(request.getSession(), fileName); // return the JS String Writer out; diff --git a/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/ScriptTemplateListTransform.java b/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/ScriptTagsFooterTransform.java similarity index 86% rename from framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/ScriptTemplateListTransform.java rename to framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/ScriptTagsFooterTransform.java index f8c67fa..8288ab5 100644 --- a/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/ScriptTemplateListTransform.java +++ b/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/ScriptTagsFooterTransform.java @@ -24,7 +24,7 @@ import java.util.Map; import java.util.Set; import javax.servlet.http.HttpServletRequest; -import org.apache.ofbiz.widget.model.ScriptTemplateUtil; +import org.apache.ofbiz.widget.model.MultiBlockHtmlTemplateUtil; import freemarker.core.Environment; import freemarker.ext.beans.BeanModel; @@ -32,14 +32,14 @@ import freemarker.template.TemplateModelException; import freemarker.template.TemplateTransformModel; /** - * Render the script tags collected from the "script-template" tag + * Render the externalized script tags collected from the "html-template" tag where multi-block = true */ -public class ScriptTemplateListTransform implements TemplateTransformModel { +public class ScriptTagsFooterTransform implements TemplateTransformModel { private static final String MODULE = CsrfTokenAjaxTransform.class.getName(); @Override - public Writer getWriter(Writer out, @SuppressWarnings("rawtypes") Map args) + public final Writer getWriter(Writer out, @SuppressWarnings("rawtypes") Map args) throws TemplateModelException, IOException { return new Writer(out) { @@ -51,7 +51,7 @@ public class ScriptTemplateListTransform implements TemplateTransformModel { BeanModel req = (BeanModel) env.getVariable("request"); if (req != null) { HttpServletRequest request = (HttpServletRequest) req.getWrappedObject(); - Set<String> scriptSrcSet = ScriptTemplateUtil.getScriptSrcLinksFromRequest(request); + Set<String> scriptSrcSet = MultiBlockHtmlTemplateUtil.getScriptLinksForFoot(request); if (scriptSrcSet != null) { String srcList = ""; for (String scriptSrc : scriptSrcSet) { diff --git a/framework/webapp/src/main/resources/org/apache/ofbiz/webapp/freemarkerTransforms.properties b/framework/webapp/src/main/resources/org/apache/ofbiz/webapp/freemarkerTransforms.properties index b4f4d8b..caf421d 100644 --- a/framework/webapp/src/main/resources/org/apache/ofbiz/webapp/freemarkerTransforms.properties +++ b/framework/webapp/src/main/resources/org/apache/ofbiz/webapp/freemarkerTransforms.properties @@ -31,4 +31,4 @@ renderWrappedText=org.apache.ofbiz.webapp.ftl.RenderWrappedTextTransform setContextField=org.apache.ofbiz.webapp.ftl.SetContextFieldTransform csrfTokenAjax=org.apache.ofbiz.webapp.ftl.CsrfTokenAjaxTransform csrfTokenPair=org.apache.ofbiz.webapp.ftl.CsrfTokenPairNonAjaxTransform -scriptTemplateList=org.apache.ofbiz.webapp.ftl.ScriptTemplateListTransform +scriptTagsFooter=org.apache.ofbiz.webapp.ftl.ScriptTagsFooterTransform diff --git a/framework/widget/dtd/widget-screen.xsd b/framework/widget/dtd/widget-screen.xsd index 087809b..6e3fe97 100644 --- a/framework/widget/dtd/widget-screen.xsd +++ b/framework/widget/dtd/widget-screen.xsd @@ -519,7 +519,15 @@ under the License. </xs:element> <xs:attributeGroup name="attlist.html-template"> <xs:attribute type="xs:string" name="location" use="required" /> - <xs:attribute type="xs:boolean" name="multi-block" use="optional" default="false" /> + <xs:attribute type="xs:boolean" name="multi-block" use="optional" default="false"> + <xs:annotation> + <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. + </xs:documentation> + </xs:annotation> + </xs:attribute> </xs:attributeGroup> <xs:element name="html-template-decorator" substitutionGroup="HtmlWidgets"> <xs:annotation> 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 new file mode 100644 index 0000000..9e6add0 --- /dev/null +++ b/framework/widget/src/main/java/org/apache/ofbiz/widget/model/HtmlImportWidgetVisitor.java @@ -0,0 +1,236 @@ +/******************************************************************************* + * 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 13449db..4817a37 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 @@ -179,6 +179,72 @@ public class HtmlWidget extends ModelScreenWidget { } } + public static void renderHtmlTemplateMultiBlock(Appendable writer, FlexibleStringExpander locationExdr, Map<String, Object> context) throws IOException { + String location = locationExdr.expandString(context); + + StringWriter stringWriter = new StringWriter(); + context.put(MultiBlockHtmlTemplateUtil.MULTI_BLOCK_WRITER, stringWriter); + renderHtmlTemplate(stringWriter, locationExdr, context); + context.remove(MultiBlockHtmlTemplateUtil.MULTI_BLOCK_WRITER); + String data = stringWriter.toString(); + stringWriter.close(); + + Document doc = Jsoup.parseBodyFragment(data); + + // extract scripts + Elements scriptElements = doc.select("script"); + if (scriptElements != null && scriptElements.size() > 0) { + StringBuilder scripts = new StringBuilder(); + + // check if location contains variable + String originalLocation = locationExdr.getOriginal(); + boolean isStaticLocation = !originalLocation.contains("${"); + + for (org.jsoup.nodes.Element script : scriptElements) { + String type = script.attr("type"); + String src = script.attr("src"); + if (UtilValidate.isEmpty(src)) { + if (UtilValidate.isEmpty(type) || type.equals("application/javascript")) { + scripts.append(script.data()); + script.remove(); + } + } else { + String dataImport = script.attr("data-import"); + if ("head".equals(dataImport)) { + if (isStaticLocation) { + // remove external script in the template that is meant to be imported in the html header + script.remove(); + } else { + // throw error to the browser + writer.append("<script>alert('Unable to headerize " + + UtilCodec.getEncoder("html").encode(script.toString()) + + " when template location not is static');</script>"); + } + } + } + } + + if (scripts.length() > 0) { + // store script for retrieval by the browser + String fileName = location; + fileName = fileName.substring(fileName.lastIndexOf('/') + 1); + if (fileName.endsWith(".ftl")) { + fileName = fileName.substring(0, fileName.length() - 4); + } + MultiBlockHtmlTemplateUtil.putScriptInCache(context, fileName, scripts.toString()); + + // store value to be used by scriptTagsFooter freemarker macro + String webappName = (String) context.get("webappName"); + MultiBlockHtmlTemplateUtil.addScriptLinkForFoot(context, "/" + webappName + "/control/getJs?name=" + + fileName); + } + } + + // the 'template' block + String body = doc.body().html(); + writer.append(body); + } + // TODO: We can make this more fancy, but for now this is very functional public static void writeError(Appendable writer, String message) { try { @@ -208,54 +274,8 @@ public class HtmlWidget extends ModelScreenWidget { @Override public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws IOException { - if (false && isMultiBlock()) { - - StringWriter stringWriter = new StringWriter(); - context.put("MultiBlockWriter", stringWriter); - renderHtmlTemplate(stringWriter, this.locationExdr, context); - context.remove("MultiBlockWriter"); - String data = stringWriter.toString(); - stringWriter.close(); - - Document doc = Jsoup.parseBodyFragment(data); - - // extract scripts - Elements scriptElements = doc.select("script"); - if (scriptElements != null && scriptElements.size()>0) { - StringBuilder scripts = new StringBuilder(); - - for (org.jsoup.nodes.Element script : scriptElements) { - String type = script.attr("type"); - String src = script.attr("src"); - if (UtilValidate.isEmpty(src)) { - if (UtilValidate.isEmpty(type) || type.equals("application/javascript")) { - scripts.append(script.data()); - script.remove(); - } - } - } - - // store script for retrieval by the browser - String fileName = this.getLocation(context); - fileName = fileName.substring(fileName.lastIndexOf("/") + 1); - if (fileName.endsWith(".ftl")) { - fileName = fileName.substring(0, fileName.length() - 4); - } - ScriptTemplateUtil.putScriptInSession(context, fileName, scripts.toString()); - - // store value to be used by ScriptTemplateList freemarker macro - String webappName = (String) context.get("webappName"); - ScriptTemplateUtil.addScriptSrcToRequest(context, "/" + webappName + "/control/getJs?name=" - + fileName); - } - - // check for external script - String externalScripts = doc.body().select("script").toString(); - writer.append(externalScripts); - - // the 'template' block - String body = doc.body().html(); - writer.append(body); + if (isMultiBlock()) { + renderHtmlTemplateMultiBlock(writer, this.locationExdr, context); } else { renderHtmlTemplate(writer, this.locationExdr, 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 75d84cc..a460679 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 @@ -77,7 +77,8 @@ public abstract class ModelScreenWidget extends ModelWidget { } } - public abstract void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException; + public abstract void renderWidgetString(Appendable writer, Map<String, Object> context, + ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException; protected static List<ModelScreenWidget> readSubWidgets(ModelScreen modelScreen, List<? extends Element> subElementList) { if (subElementList.isEmpty()) { @@ -90,13 +91,16 @@ public abstract class ModelScreenWidget extends ModelWidget { return Collections.unmodifiableList(subWidgets); } - protected static void renderSubWidgetsString(List<ModelScreenWidget> subWidgets, Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { + protected static void renderSubWidgetsString(List<ModelScreenWidget> subWidgets, Appendable writer, + Map<String, Object> context, ScreenStringRenderer screenStringRenderer) + throws GeneralException, IOException { if (subWidgets == null) { return; } for (ModelScreenWidget subWidget: subWidgets) { if (Debug.verboseOn()) { - Debug.logVerbose("Rendering screen " + subWidget.getModelScreen().getName() + "; widget class is " + subWidget.getClass().getName(), MODULE); + Debug.logVerbose("Rendering screen " + subWidget.getModelScreen().getName() + + "; widget class is " + subWidget.getClass().getName(), MODULE); } // render the sub-widget itself @@ -255,6 +259,25 @@ 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 @@ -263,7 +286,17 @@ public abstract class ModelScreenWidget extends ModelWidget { } @Override - public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { + public void renderWidgetString(Appendable writer, Map<String, Object> context, + ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { + + try { + String location = getModelScreen().getSourceLocation(); + String name = getModelScreen().getName(); + MultiBlockHtmlTemplateUtil.addLinksToLayoutSettings(context, location, name); + } catch (Exception e) { + throw new GeneralException(e); + } + // check the condition, if there is one boolean condTrue = true; if (this.condition != null) { @@ -286,7 +319,8 @@ public abstract class ModelScreenWidget extends ModelWidget { screenStringRenderer.renderSectionEnd(writer, context, this); } catch (IOException e) { - String errMsg = "Error rendering widgets section [" + getName() + "] in screen named [" + getModelScreen().getName() + "]: " + e.toString(); + String errMsg = "Error rendering widgets section [" + getName() + "] in screen named [" + + getModelScreen().getName() + "]: " + e.toString(); Debug.logError(e, errMsg, MODULE); throw new RuntimeException(errMsg); } @@ -300,7 +334,8 @@ public abstract class ModelScreenWidget extends ModelWidget { screenStringRenderer.renderSectionEnd(writer, context, this); } catch (IOException e) { - String errMsg = "Error rendering fail-widgets section [" + this.getName() + "] in screen named [" + getModelScreen().getName() + "]: " + e.toString(); + String errMsg = "Error rendering fail-widgets section [" + this.getName() + "] in screen named [" + + getModelScreen().getName() + "]: " + e.toString(); Debug.logError(e, errMsg, MODULE); throw new RuntimeException(errMsg); } @@ -361,7 +396,8 @@ public abstract class ModelScreenWidget extends ModelWidget { } @Override - public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { + public void renderWidgetString(Appendable writer, Map<String, Object> context, + ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { try { screenStringRenderer.renderColumnContainer(writer, context, this); } catch (IOException e) { @@ -457,7 +493,8 @@ public abstract class ModelScreenWidget extends ModelWidget { } @Override - public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { + public void renderWidgetString(Appendable writer, Map<String, Object> context, + ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { try { screenStringRenderer.renderContainerBegin(writer, context, this); @@ -596,7 +633,8 @@ public abstract class ModelScreenWidget extends ModelWidget { } @Override - public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { + public void renderWidgetString(Appendable writer, Map<String, Object> context, + ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { boolean collapsed = getInitiallyCollapsed(context); if (this.collapsible) { String preferenceKey = getPreferenceKey(context) + "_collapsed"; @@ -722,7 +760,8 @@ public abstract class ModelScreenWidget extends ModelWidget { } @Override - public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { + public void renderWidgetString(Appendable writer, Map<String, Object> context, + ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { screenStringRenderer.renderHorizontalSeparator(writer, context, this); } @@ -759,10 +798,12 @@ public abstract class ModelScreenWidget extends ModelWidget { this.nameExdr = FlexibleStringExpander.getInstance(includeScreenElement.getAttribute("name")); this.locationExdr = FlexibleStringExpander.getInstance(includeScreenElement.getAttribute("location")); this.shareScopeExdr = FlexibleStringExpander.getInstance(includeScreenElement.getAttribute("share-scope")); + MultiBlockHtmlTemplateUtil.collectChildScreenInfo(modelScreen, this.locationExdr.getOriginal(), this.nameExdr.getOriginal()); } @Override - public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { + public void renderWidgetString(Appendable writer, Map<String, Object> context, + ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { // if we are not sharing the scope, protect it using the MapStack boolean protectScope = !shareScope(context); if (protectScope) { @@ -784,13 +825,15 @@ public abstract class ModelScreenWidget extends ModelWidget { context.put("_WIDGETTRAIL_", widgetTrail); } - // don't need the renderer here, will just pass this on down to another screen call; screenStringRenderer.renderContainerBegin(writer, context, this); + // don't need the renderer here, will just pass this on down to another screen call; + // screenStringRenderer.renderContainerBegin(writer, context, this); String name = this.getName(context); String location = this.getLocation(context); if (name.isEmpty()) { if (Debug.verboseOn()) { - Debug.logVerbose("In the include-screen tag the screen name was empty, ignoring include; in screen [" + getModelScreen().getName() + "]", MODULE); + Debug.logVerbose("In the include-screen tag the screen name was empty, ignoring include; in screen [" + + getModelScreen().getName() + "]", MODULE); } return; } @@ -851,10 +894,12 @@ public abstract class ModelScreenWidget extends ModelWidget { sectionMap.put(decoratorSection.getName(), decoratorSection); } this.sectionMap = Collections.unmodifiableMap(sectionMap); + MultiBlockHtmlTemplateUtil.collectChildScreenInfo(modelScreen, this.locationExdr.getOriginal(), this.nameExdr.getOriginal()); } @Override - public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { + public void renderWidgetString(Appendable writer, Map<String, Object> context, + ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { // isolate the scope if (!(context instanceof MapStack)) { context = MapStack.create(context); @@ -862,7 +907,8 @@ public abstract class ModelScreenWidget extends ModelWidget { MapStack<String> contextMs = UtilGenerics.cast(context); - // create a standAloneStack, basically a "save point" for this SectionsRenderer, and make a new "screens" object just for it so it is isolated and doesn't follow the stack down + // create a standAloneStack, basically a "save point" for this SectionsRenderer, + // and make a new "screens" object just for it so it is isolated and doesn't follow the stack down MapStack<String> standAloneStack = contextMs.standAloneChildStack(); standAloneStack.put("screens", new ScreenRenderer(writer, standAloneStack, screenStringRenderer)); SectionsRenderer sections = new SectionsRenderer(this.sectionMap, standAloneStack, writer, screenStringRenderer); @@ -918,7 +964,8 @@ public abstract class ModelScreenWidget extends ModelWidget { } @Override - public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { + public void renderWidgetString(Appendable writer, Map<String, Object> context, + ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { // render sub-widgets renderSubWidgetsString(this.subWidgets, writer, context, screenStringRenderer); } @@ -941,7 +988,8 @@ public abstract class ModelScreenWidget extends ModelWidget { } @Override - public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { + public void renderWidgetString(Appendable writer, Map<String, Object> context, + ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { Map<String, ? extends Object> preRenderedContent = UtilGenerics.cast(context.get("preRenderedContent")); if (preRenderedContent != null && preRenderedContent.containsKey(getName())) { try { @@ -955,7 +1003,8 @@ public abstract class ModelScreenWidget extends ModelWidget { SectionsRenderer sections = (SectionsRenderer) context.get("sections"); // for now if sections is null, just log a warning; may be permissible to make the screen for flexible if (sections == null) { - Debug.logWarning("In decorator-section-include could not find sections object in the context, not rendering section with name [" + getName() + "]", MODULE); + Debug.logWarning("In decorator-section-include could not find sections object in the context, " + + "not rendering section with name [" + getName() + "]", MODULE); } else { sections.render(getName()); } @@ -1054,7 +1103,9 @@ public abstract class ModelScreenWidget extends ModelWidget { // Output format might not support forms, so make form rendering optional. FormStringRenderer formStringRenderer = (FormStringRenderer) context.get("formStringRenderer"); if (formStringRenderer == null) { - if (Debug.verboseOn()) Debug.logVerbose("FormStringRenderer instance not found in rendering context, form not rendered.", MODULE); + if (Debug.verboseOn()) { + Debug.logVerbose("FormStringRenderer instance not found in rendering context, form not rendered.", MODULE); + } return; } boolean protectScope = !shareScope(context); @@ -1069,7 +1120,8 @@ public abstract class ModelScreenWidget extends ModelWidget { FormRenderer renderer = new FormRenderer(modelForm, formStringRenderer); renderer.render(writer, context); } catch (Exception e) { - String errMsg = "Error rendering included form named [" + getName() + "] at location [" + this.getLocation(context) + "]: " + e.toString(); + String errMsg = "Error rendering included form named [" + getName() + "] at location [" + + this.getLocation(context) + "]: " + e.toString(); Debug.logError(e, errMsg, MODULE); throw new RuntimeException(errMsg + e); } @@ -1141,7 +1193,9 @@ public abstract class ModelScreenWidget extends ModelWidget { // Output format might not support forms, so make form rendering optional. FormStringRenderer formStringRenderer = (FormStringRenderer) context.get("formStringRenderer"); if (formStringRenderer == null) { - if (Debug.verboseOn()) Debug.logVerbose("FormStringRenderer instance not found in rendering context, form not rendered.", MODULE); + if (Debug.verboseOn()) { + Debug.logVerbose("FormStringRenderer instance not found in rendering context, form not rendered.", MODULE); + } return; } boolean protectScope = !shareScope(context); @@ -1156,7 +1210,8 @@ public abstract class ModelScreenWidget extends ModelWidget { try { renderer.render(writer, context); } catch (Exception e) { - String errMsg = "Error rendering included grid named [" + getName() + "] at location [" + this.getLocation(context) + "]: " + e.toString(); + String errMsg = "Error rendering included grid named [" + getName() + "] at location [" + + this.getLocation(context) + "]: " + e.toString(); Debug.logError(e, errMsg, MODULE); throw new RuntimeException(errMsg + e); } @@ -1233,11 +1288,14 @@ public abstract class ModelScreenWidget extends ModelWidget { } @Override - public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { + public void renderWidgetString(Appendable writer, Map<String, Object> context, + ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { // Output format might not support trees, so make tree rendering optional. TreeStringRenderer treeStringRenderer = (TreeStringRenderer) context.get("treeStringRenderer"); if (treeStringRenderer == null) { - if (Debug.verboseOn()) Debug.logVerbose("TreeStringRenderer instance not found in rendering context, tree not rendered.", MODULE); + if (Debug.verboseOn()) { + Debug.logVerbose("TreeStringRenderer instance not found in rendering context, tree not rendered.", MODULE); + } return; } boolean protectScope = !shareScope(context); @@ -1252,7 +1310,8 @@ public abstract class ModelScreenWidget extends ModelWidget { String location = this.getLocation(context); ModelTree modelTree = null; try { - modelTree = TreeFactory.getTreeFromLocation(this.getLocation(context), this.getName(context), getModelScreen().getDelegator(context), getModelScreen().getDispatcher(context)); + modelTree = TreeFactory.getTreeFromLocation(this.getLocation(context), this.getName(context), + getModelScreen().getDelegator(context), getModelScreen().getDispatcher(context)); } catch (IOException | SAXException | ParserConfigurationException e) { String errMsg = "Error rendering included tree named [" + name + "] at location [" + location + "]: " + e.toString(); Debug.logError(e, errMsg, MODULE); @@ -1315,7 +1374,8 @@ public abstract class ModelScreenWidget extends ModelWidget { } else if ("xls".equals(childElement.getNodeName())) { subWidgets.put("xls", new HtmlWidget(modelScreen, childElement)); } else { - throw new IllegalArgumentException("Tag not supported under the platform-specific tag with name: " + childElement.getNodeName()); + throw new IllegalArgumentException("Tag not supported under the platform-specific tag with name: " + + childElement.getNodeName()); } } } @@ -1323,12 +1383,14 @@ public abstract class ModelScreenWidget extends ModelWidget { } @Override - public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { + public void renderWidgetString(Appendable writer, Map<String, Object> context, + ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { ModelScreenWidget subWidget = null; subWidget = subWidgets.get(screenStringRenderer.getRendererName()); if (subWidget == null) { // This is here for backward compatibility - Debug.logWarning("In platform-dependent could not find template for " + screenStringRenderer.getRendererName() + ", using the one for html.", MODULE); + Debug.logWarning("In platform-dependent could not find template for " + + screenStringRenderer.getRendererName() + ", using the one for html.", MODULE); subWidget = subWidgets.get("html"); } if (subWidget != null) { @@ -1591,7 +1653,9 @@ public abstract class ModelScreenWidget extends ModelWidget { // Output format might not support menus, so make menu rendering optional. MenuStringRenderer menuStringRenderer = (MenuStringRenderer) context.get("menuStringRenderer"); if (menuStringRenderer == null) { - if (Debug.verboseOn()) Debug.logVerbose("MenuStringRenderer instance not found in rendering context, menu not rendered.", MODULE); + if (Debug.verboseOn()) { + Debug.logVerbose("MenuStringRenderer instance not found in rendering context, menu not rendered.", MODULE); + } return; } ModelMenu modelMenu = getModelMenu(context); @@ -1911,7 +1975,8 @@ public abstract class ModelScreenWidget extends ModelWidget { } @Override - public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { + public void renderWidgetString(Appendable writer, Map<String, Object> context, + ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { try { Delegator delegator = (Delegator) context.get("delegator"); List<GenericValue> portalPageColumns = null; @@ -1933,8 +1998,8 @@ public abstract class ModelScreenWidget extends ModelWidget { String prevColumnSeqId = ""; // Iterates through the PortalPage columns - ListIterator <GenericValue>columnsIterator = portalPageColumns.listIterator(); - while(columnsIterator.hasNext()) { + ListIterator<GenericValue> columnsIterator = portalPageColumns.listIterator(); + while (columnsIterator.hasNext()) { GenericValue columnValue = columnsIterator.next(); String columnSeqId = columnValue.getString("columnSeqId"); @@ -1958,8 +2023,8 @@ public abstract class ModelScreenWidget extends ModelWidget { } // Iterates through the Portlets in the Column - ListIterator <GenericValue>portletsIterator = portalPagePortlets.listIterator(); - while(portletsIterator.hasNext()) { + ListIterator<GenericValue> portletsIterator = portalPagePortlets.listIterator(); + while (portletsIterator.hasNext()) { GenericValue portletValue = portletsIterator.next(); // If not the last portlet in the column, get the next nextPortletId and nextPortletSeqId @@ -1981,10 +2046,12 @@ public abstract class ModelScreenWidget extends ModelWidget { // Get portlet's attributes portletAttributes = EntityQuery.use(delegator) .from("PortletAttribute") - .where("portalPageId", portletValue.get("portalPageId"), "portalPortletId", portletValue.get("portalPortletId"), "portletSeqId", portletValue.get("portletSeqId")) + .where("portalPageId", portletValue.get("portalPageId"), + "portalPortletId", portletValue.get("portalPortletId"), + "portletSeqId", portletValue.get("portletSeqId")) .queryList(); - ListIterator <GenericValue>attributesIterator = portletAttributes.listIterator(); + ListIterator<GenericValue> attributesIterator = portletAttributes.listIterator(); while (attributesIterator.hasNext()) { GenericValue attribute = attributesIterator.next(); context.put(attribute.getString("attrName"), attribute.getString("attrValue")); 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 new file mode 100644 index 0000000..ee29af5 --- /dev/null +++ b/framework/widget/src/main/java/org/apache/ofbiz/widget/model/MultiBlockHtmlTemplateUtil.java @@ -0,0 +1,228 @@ +/******************************************************************************* + * 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.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +import org.apache.ofbiz.base.util.UtilGenerics; +import org.apache.ofbiz.base.util.UtilValidate; + +public final class MultiBlockHtmlTemplateUtil { + + 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 maxNumOfScriptInCache = 10; + private static int cacheSize = 100; + // store inline script from freemarker template by user session + 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 + } + }; + // store the additional html import by screen location + 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 + } + }; + // store the child screen + 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; + } + }; + + private MultiBlockHtmlTemplateUtil() { } + + public static void collectChildScreenInfo(ModelScreen parentModelScreen, String location, String name) { + String key = parentModelScreen.getSourceLocation() + "#" + parentModelScreen.getName(); + Set<String> childList = dependentScreenCache.get(key); + if (childList == null) { + childList = new LinkedHashSet<>(); + dependentScreenCache.put(key, childList); + } + if (UtilValidate.isNotEmpty(location)) { + childList.add(location + "#" + name); + } else { + childList.add(parentModelScreen.getSourceLocation() + "#" + name); + } + } + + public static void addLinksToHtmlImportCache(String location, String name, Set<String> urls) throws Exception { + if (UtilValidate.isEmpty(urls)) { + return; + } + String locHashName = location + "#" + name; + Set<String> existingUrls = htmlImportCache.get(locHashName); + if (existingUrls == null) { + existingUrls = new LinkedHashSet<>(); + htmlImportCache.put(locHashName, existingUrls); + } + existingUrls.addAll(urls); + } + + 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) { + String currentLocationHashName = location + "#" + name; + request.setAttribute(HTML_LINKS_FOR_HEAD, currentLocationHashName); + return; + } + // check "layoutSettings.javaScripts" is not empty + Map<String, Object> layoutSettings = UtilGenerics.cast(context.get("layoutSettings")); + if (UtilValidate.isEmpty(layoutSettings)) { + return; + } + List<String> layoutSettingsJsList = UtilGenerics.cast(layoutSettings.get("javaScripts")); + if (UtilValidate.isEmpty(layoutSettingsJsList)) { + 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); + for (String locHashName:locHashNameList) { + Set<String> urls = htmlImportCache.get(locHashName); + if (UtilValidate.isNotEmpty(urls)) { + // check url is not already in layoutSettings.javaScripts + for (String url : urls) { + if (!htmlLinks.contains(url)) { + htmlLinks.add(url); + } + } + } + } + if (UtilValidate.isNotEmpty(htmlLinks)) { + // check url is not already in layoutSettings.javaScripts + for (String url : htmlLinks) { + if (!layoutSettingsJsList.contains(url)) { + layoutSettingsJsList.add(url); + } + } + } + request.setAttribute(HTML_LINKS_FOR_HEAD, true); + } + + } + + /** + * Get all the child screens including itself + * @param locationHashName + * @return + */ + private static Set<String> getRelatedScreenLocationHashName(String locationHashName) { + 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)); + } + } + 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 + */ + public static void addScriptLinkForFoot(final Map<String, Object> context, final String filePath) { + HttpServletRequest request = (HttpServletRequest) context.get("request"); + Set<String> scriptLinks = UtilGenerics.cast(request.getAttribute(SCRIPT_LINKS_FOR_FOOT)); + if (scriptLinks == null) { + // use of LinkedHashSet to maintain insertion order + scriptLinks = new LinkedHashSet<String>(); + request.setAttribute(SCRIPT_LINKS_FOR_FOOT, scriptLinks); + } + scriptLinks.add(filePath); + } + + /** + * get the script links for page footer. Also @see {@link ScriptTagsFooterTransform} + * @param request + * @return + */ + public static Set<String> getScriptLinksForFoot(HttpServletRequest request) { + Set<String> scriptLinks = UtilGenerics.cast(request.getAttribute(SCRIPT_LINKS_FOR_FOOT)); + return scriptLinks; + } + + /** + * put script in cache for retrieval by the browser + * @param context + * @param fileName + * @param fileContent + */ + public static void putScriptInCache(Map<String, Object> context, String fileName, String fileContent) { + HttpSession session = (HttpSession) context.get("session"); + String sessionId = session.getId(); + Map<String, String> scriptMap = UtilGenerics.cast(scriptCache.get(sessionId)); + if (scriptMap == null) { + // use of LinkedHashMap to limit size of the map + scriptMap = new LinkedHashMap<String, String>() { + private static final long serialVersionUID = 1L; + protected boolean removeEldestEntry(Map.Entry<String, String> eldest) { + return size() > maxNumOfScriptInCache; + } + }; + scriptCache.put(sessionId, scriptMap); + } + scriptMap.put(fileName, fileContent); + } + + /** + * Get the script stored in cache. + * @param session + * @param fileName + * @return script to be sent back to browser + */ + public static String getScriptFromCache(HttpSession session, final String fileName) { + Map<String, String> scriptMap = UtilGenerics.cast(scriptCache.get(session.getId())); + if (scriptMap != null) { + return scriptMap.get(fileName); + } + return ""; + } +} diff --git a/framework/widget/src/main/java/org/apache/ofbiz/widget/model/ScriptTemplateUtil.java b/framework/widget/src/main/java/org/apache/ofbiz/widget/model/ScriptTemplateUtil.java deleted file mode 100644 index 7bce317..0000000 --- a/framework/widget/src/main/java/org/apache/ofbiz/widget/model/ScriptTemplateUtil.java +++ /dev/null @@ -1,105 +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.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; - -import org.apache.ofbiz.base.util.UtilGenerics; - -public class ScriptTemplateUtil { - - private static String sessionKey = "ScriptTemplateMap"; - private static String requestKey = "ScriptTemplateList"; - private static int maxNumOfScriptInCache = 10; - - private ScriptTemplateUtil() { } - - /** - * add script src link for use by @see {@link org.apache.ofbiz.webapp.ftl.ScriptTemplateListTransform} - * @param context - * @param filePath - */ - public static void addScriptSrcToRequest(final Map<String, Object> context, final String filePath) { - HttpServletRequest request = (HttpServletRequest) context.get("request"); - Set<String> scriptTemplates = UtilGenerics.cast(request.getAttribute(requestKey)); - if (scriptTemplates == null) { - // use of LinkedHashSet to maintain insertion order - scriptTemplates = new LinkedHashSet<String>(); - request.setAttribute(requestKey, scriptTemplates); - } - scriptTemplates.add(filePath); - } - - /** - * get the script src links collected from the html-template tags where multi-block=true. - * @param request - * @return - */ - public static Set<String> getScriptSrcLinksFromRequest(HttpServletRequest request) { - Set<String> scriptTemplates = UtilGenerics.cast(request.getAttribute(requestKey)); - return scriptTemplates; - } - - /** - * put script in user session for retrieval by the browser - * @param context - * @param fileName - * @param fileContent - */ - public static void putScriptInSession(Map<String, Object> context, String fileName, String fileContent) { - HttpSession session = (HttpSession) context.get("session"); - Map<String, String> scriptTemplateMap = UtilGenerics.cast(session.getAttribute(sessionKey)); - if (scriptTemplateMap == null) { - synchronized (session) { - scriptTemplateMap = UtilGenerics.cast(session.getAttribute(sessionKey)); - if (scriptTemplateMap == null) { - // use of LinkedHashMap to limit size of the map - scriptTemplateMap = new LinkedHashMap<String, String>() { - private static final long serialVersionUID = 1L; - protected boolean removeEldestEntry(Map.Entry<String, String> eldest) { - return size() > maxNumOfScriptInCache; - } - }; - session.setAttribute(sessionKey, scriptTemplateMap); - } - } - } - scriptTemplateMap.put(fileName, fileContent); - } - - /** - * Get the script stored in user session. - * @param session - * @param fileName - * @return script to be sent back to browser - */ - public static String getScriptFromSession(HttpSession session, final String fileName) { - Map<String, String> scriptTemplateMap = UtilGenerics.cast(session.getAttribute(sessionKey)); - if (scriptTemplateMap != null) { - return scriptTemplateMap.get(fileName); - } - return null; - } -} diff --git a/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/ScreenRenderer.java b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/ScreenRenderer.java index 9f4bd7e..6724ad7 100644 --- a/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/ScreenRenderer.java +++ b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/ScreenRenderer.java @@ -58,6 +58,7 @@ import org.apache.ofbiz.widget.cache.GenericWidgetOutput; import org.apache.ofbiz.widget.cache.ScreenCache; import org.apache.ofbiz.widget.cache.WidgetContextCacheKey; import org.apache.ofbiz.widget.model.ModelScreen; +import org.apache.ofbiz.widget.model.MultiBlockHtmlTemplateUtil; import org.apache.ofbiz.widget.model.ScreenFactory; import org.apache.ofbiz.widget.model.ThemeFactory; import org.xml.sax.SAXException; @@ -137,8 +138,8 @@ public class ScreenRenderer { } } else { context.put("renderFormSeqNumber", String.valueOf(renderFormSeqNumber)); - if (context.get("MultiBlockWriter") != null) { - StringWriter stringWriter = (StringWriter) context.get("MultiBlockWriter"); + if (context.get(MultiBlockHtmlTemplateUtil.MULTI_BLOCK_WRITER) != null) { + StringWriter stringWriter = (StringWriter) context.get(MultiBlockHtmlTemplateUtil.MULTI_BLOCK_WRITER); modelScreen.renderScreenString(stringWriter, context, screenStringRenderer); } else { modelScreen.renderScreenString(writer, context, screenStringRenderer); diff --git a/themes/common-theme/template/includes/CloseHtmlBody.ftl b/themes/common-theme/template/includes/CloseHtmlBody.ftl index 6390ae7..9ad25cb 100644 --- a/themes/common-theme/template/includes/CloseHtmlBody.ftl +++ b/themes/common-theme/template/includes/CloseHtmlBody.ftl @@ -17,5 +17,5 @@ specific language governing permissions and limitations under the License. --> </body> -<@scriptTemplateList/> +<@scriptTagsFooter/> </html> diff --git a/themes/common-theme/template/includes/LookupFooter.ftl b/themes/common-theme/template/includes/LookupFooter.ftl index 1c9f388..e00660c 100644 --- a/themes/common-theme/template/includes/LookupFooter.ftl +++ b/themes/common-theme/template/includes/LookupFooter.ftl @@ -17,5 +17,5 @@ specific language governing permissions and limitations under the License. --> </body> - <@scriptTemplateList/> + <@scriptTagsFooter/> </html> diff --git a/themes/common-theme/widget/Theme.xml b/themes/common-theme/widget/Theme.xml index 9b708c5..3fee75f 100644 --- a/themes/common-theme/widget/Theme.xml +++ b/themes/common-theme/widget/Theme.xml @@ -76,7 +76,6 @@ under the License. <property name="VT_HDR_JAVASCRIPT['add']" value="/common/js/util/miscAjaxFunctions.js"/> <property name="VT_HDR_JAVASCRIPT['add']" value="/common/js/util/selectMultipleRelatedValues.js"/> <property name="VT_HDR_JAVASCRIPT['add']" value="/common/js/util/util.js"/> - <property name="VT_HDR_JAVASCRIPT['add']" value="/common/js/jquery/plugins/jsTree/jquery.jstree.js"/> <property name="VT_HDR_JAVASCRIPT['add']" value="/common/js/jquery/ui/js/jquery.cookie-1.4.0.js"/> <property name="VT_HDR_JAVASCRIPT['add']" value="/common/js/plugins/date/FromThruDateCheck.js"/> <property name="VT_HDR_JAVASCRIPT['add']" value="/common/js/util/application.js"/> diff --git a/themes/flatgrey/template/Footer.ftl b/themes/flatgrey/template/Footer.ftl index 3a65a83..67d70f9 100644 --- a/themes/flatgrey/template/Footer.ftl +++ b/themes/flatgrey/template/Footer.ftl @@ -42,5 +42,5 @@ under the License. </#list> </#if> </body> -<@scriptTemplateList/> +<@scriptTagsFooter/> </html> diff --git a/themes/rainbowstone/template/includes/Footer.ftl b/themes/rainbowstone/template/includes/Footer.ftl index 057ad23..2741617 100644 --- a/themes/rainbowstone/template/includes/Footer.ftl +++ b/themes/rainbowstone/template/includes/Footer.ftl @@ -34,5 +34,5 @@ under the License. </#list> </#if> </body> -<@scriptTemplateList/> +<@scriptTagsFooter/> </html> diff --git a/themes/tomahawk/template/Footer.ftl b/themes/tomahawk/template/Footer.ftl index baf94a8..1f27daa 100644 --- a/themes/tomahawk/template/Footer.ftl +++ b/themes/tomahawk/template/Footer.ftl @@ -42,5 +42,5 @@ under the License. </div> </body> -<@scriptTemplateList/> +<@scriptTagsFooter/> </html> |
Free forum by Nabble | Edit this page |