svn commit: r880683 - in /ofbiz/trunk/framework/widget: src/org/ofbiz/widget/screen/MacroScreenViewHandler.java src/org/ofbiz/widget/screen/ModelScreenWidget.java src/org/ofbiz/widget/tree/MacroTreeRenderer.java templates/htmlTreeMacroLibrary.ftl

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

svn commit: r880683 - in /ofbiz/trunk/framework/widget: src/org/ofbiz/widget/screen/MacroScreenViewHandler.java src/org/ofbiz/widget/screen/ModelScreenWidget.java src/org/ofbiz/widget/tree/MacroTreeRenderer.java templates/htmlTreeMacroLibrary.ftl

bibryam
Author: bibryam
Date: Mon Nov 16 09:16:24 2009
New Revision: 880683

URL: http://svn.apache.org/viewvc?rev=880683&view=rev
Log:
Added FTL macro for the tree widget.
https://issues.apache.org/jira/browse/OFBIZ-2550

Added:
    ofbiz/trunk/framework/widget/src/org/ofbiz/widget/tree/MacroTreeRenderer.java
    ofbiz/trunk/framework/widget/templates/htmlTreeMacroLibrary.ftl
Modified:
    ofbiz/trunk/framework/widget/src/org/ofbiz/widget/screen/MacroScreenViewHandler.java
    ofbiz/trunk/framework/widget/src/org/ofbiz/widget/screen/ModelScreenWidget.java

Modified: ofbiz/trunk/framework/widget/src/org/ofbiz/widget/screen/MacroScreenViewHandler.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/widget/src/org/ofbiz/widget/screen/MacroScreenViewHandler.java?rev=880683&r1=880682&r2=880683&view=diff
==============================================================================
--- ofbiz/trunk/framework/widget/src/org/ofbiz/widget/screen/MacroScreenViewHandler.java (original)
+++ ofbiz/trunk/framework/widget/src/org/ofbiz/widget/screen/MacroScreenViewHandler.java Mon Nov 16 09:16:24 2009
@@ -38,6 +38,8 @@
 import org.ofbiz.webapp.view.ViewHandlerException;
 import org.ofbiz.widget.form.FormStringRenderer;
 import org.ofbiz.widget.form.MacroFormRenderer;
+import org.ofbiz.widget.tree.TreeStringRenderer;
+import org.ofbiz.widget.tree.MacroTreeRenderer;
 import org.xml.sax.SAXException;
 
 import freemarker.template.TemplateException;
@@ -87,16 +89,15 @@
 
             ScreenStringRenderer screenStringRenderer = new MacroScreenRenderer(UtilProperties.getPropertyValue("widget", getName() + ".name"), UtilProperties.getPropertyValue("widget", getName() + ".screenrenderer"), writer);
             FormStringRenderer formStringRenderer = new MacroFormRenderer(UtilProperties.getPropertyValue("widget", getName() + ".formrenderer"), writer, request, response);
+            TreeStringRenderer treeStringRenderer = new MacroTreeRenderer(UtilProperties.getPropertyValue("widget", getName() + ".treerenderer"), writer);
             // TODO: uncomment these lines when the renderers are implemented
-            //TreeStringRenderer treeStringRenderer = new MacroTreeRenderer(UtilProperties.getPropertyValue("widget", getName() + ".treerenderer"), writer);
             //MenuStringRenderer menuStringRenderer = new MacroMenuRenderer(UtilProperties.getPropertyValue("widget", getName() + ".menurenderer"), writer);
 
             ScreenRenderer screens = new ScreenRenderer(writer, null, screenStringRenderer);
             screens.populateContextForRequest(request, response, servletContext);
             // this is the object used to render forms from their definitions
             screens.getContext().put("formStringRenderer", formStringRenderer);
-            // TODO: uncomment these lines when the renderers are implemented
-            //screens.getContext().put("treeStringRenderer", treeStringRenderer);
+            screens.getContext().put("treeStringRenderer", treeStringRenderer);
             //screens.getContext().put("menuStringRenderer", menuStringRenderer);
             screens.getContext().put("simpleEncoder", StringUtil.getEncoder(UtilProperties.getPropertyValue("widget", getName() + ".encoder")));
             screenStringRenderer.renderScreenBegin(writer, screens.getContext());

Modified: ofbiz/trunk/framework/widget/src/org/ofbiz/widget/screen/ModelScreenWidget.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/widget/src/org/ofbiz/widget/screen/ModelScreenWidget.java?rev=880683&r1=880682&r2=880683&view=diff
==============================================================================
--- ofbiz/trunk/framework/widget/src/org/ofbiz/widget/screen/ModelScreenWidget.java (original)
+++ ofbiz/trunk/framework/widget/src/org/ofbiz/widget/screen/ModelScreenWidget.java Mon Nov 16 09:16:24 2009
@@ -911,23 +911,10 @@
                 Debug.logError(e, errMsg, module);
                 throw new RuntimeException(errMsg);
             }
-
-            // try finding the treeStringRenderer by name in the context in case one was prepared and put there
+
             TreeStringRenderer treeStringRenderer = (TreeStringRenderer) context.get("treeStringRenderer");
-            // if there was no treeStringRenderer put in place, now try finding the request/response in the context and creating a new one
-            if (treeStringRenderer == null) {
-                treeStringRenderer = new HtmlTreeRenderer();
-                /*
-                String renderClassStyle = modelTree.getRenderStyle();
-                if (UtilValidate.isNotEmpty(renderClassStyle) && renderClassStyle.equals("simple"))
-                    treeStringRenderer = new HtmlTreeRenderer();
-                else
-                    treeStringRenderer = new HtmlTreeExpandCollapseRenderer();
-                */
-            }
-            // still null, throw an error
             if (treeStringRenderer == null) {
-                throw new IllegalArgumentException("Could not find a treeStringRenderer in the context, and could not find HTTP request/response objects need to create one.");
+                throw new IllegalArgumentException("Could not find a treeStringRenderer in the context");
             }
 
             StringBuffer renderBuffer = new StringBuffer();

Added: ofbiz/trunk/framework/widget/src/org/ofbiz/widget/tree/MacroTreeRenderer.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/widget/src/org/ofbiz/widget/tree/MacroTreeRenderer.java?rev=880683&view=auto
==============================================================================
--- ofbiz/trunk/framework/widget/src/org/ofbiz/widget/tree/MacroTreeRenderer.java (added)
+++ ofbiz/trunk/framework/widget/src/org/ofbiz/widget/tree/MacroTreeRenderer.java Mon Nov 16 09:16:24 2009
@@ -0,0 +1,382 @@
+/*******************************************************************************
+ * 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.ofbiz.widget.tree;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.ofbiz.base.util.Debug;
+import org.ofbiz.base.util.StringUtil;
+import org.ofbiz.base.util.UtilGenerics;
+import org.ofbiz.base.util.UtilMisc;
+import org.ofbiz.base.util.UtilValidate;
+import org.ofbiz.base.util.template.FreeMarkerWorker;
+import org.ofbiz.webapp.control.RequestHandler;
+import org.ofbiz.webapp.taglib.ContentUrlTag;
+import org.ofbiz.widget.ModelWidget;
+import org.ofbiz.widget.WidgetWorker;
+import org.ofbiz.widget.screen.ScreenRenderer;
+import org.ofbiz.widget.screen.ScreenStringRenderer;
+import org.ofbiz.widget.tree.ModelTree;
+import org.ofbiz.widget.tree.TreeStringRenderer;
+
+import freemarker.core.Environment;
+import freemarker.template.Template;
+import freemarker.template.TemplateException;
+
+/**
+ * Widget Library - Tree Renderer implementation based on Freemarker macros
+ *
+ */
+public class MacroTreeRenderer implements TreeStringRenderer {
+
+    public static final String module = MacroTreeRenderer.class.getName();
+    ScreenStringRenderer screenStringRenderer = null;
+    private Template macroLibrary;
+    private Environment environment;
+
+    public MacroTreeRenderer(String macroLibraryPath, Appendable writer) throws TemplateException, IOException {
+        this.macroLibrary = FreeMarkerWorker.getTemplate(macroLibraryPath);
+        Map<String, Object> input = UtilMisc.toMap("key", null);
+        this.environment = FreeMarkerWorker.renderTemplate(this.macroLibrary, input, writer);
+    }
+
+    private void executeMacro(Appendable writer, String macro) throws IOException {
+        try {
+            Reader templateReader = new StringReader(macro);
+            // FIXME: I am using a Date as an hack to provide a unique name for the template...
+            Template template = new Template((new java.util.Date()).toString(), templateReader, FreeMarkerWorker.getDefaultOfbizConfig());
+            templateReader.close();
+            if (writer != null) {
+                Map<String, Object> input = UtilMisc.toMap("key", null);
+                Environment tmpEnvironment = FreeMarkerWorker.renderTemplate(this.macroLibrary, input, writer);
+                tmpEnvironment.include(template);
+            } else {
+                this.environment.include(template);
+            }
+        } catch (TemplateException e) {
+            Debug.logError(e, "Error rendering screen thru ftl", module);
+        } catch (IOException e) {
+            Debug.logError(e, "Error rendering screen thru ftl", module);
+        }
+    }
+    
+    private void executeMacro(String macro) throws IOException {
+        executeMacro(null, macro);
+    }
+
+    /**
+     * Renders the beginning boundary comment string.
+     * @param writer The writer to write to
+     * @param widgetType The widget type: "Screen Widget", "Tree Widget", etc.
+     * @param modelWidget The widget
+     */
+    public void renderBeginningBoundaryComment(Appendable writer, String widgetType, ModelWidget modelWidget) throws IOException {
+        if (modelWidget.boundaryCommentsEnabled()) {
+            StringWriter sr = new StringWriter();
+            sr.append("<@formatBoundaryComment ");
+            sr.append(" boundaryType=\"");
+            sr.append("Begin");
+            sr.append("\" widgetType=\"");
+            sr.append(widgetType);
+            sr.append("\" widgetName=\"");
+            sr.append(modelWidget.getBoundaryCommentName());
+            sr.append("\" />");
+            executeMacro(sr.toString());
+        }
+    }
+    
+    /**
+     * Renders the ending boundary comment string.
+     * @param writer The writer to write to
+     * @param widgetType The widget type: "Screen Widget", "Tree Widget", etc.
+     * @param modelWidget The widget
+     */
+    public void renderEndingBoundaryComment(Appendable writer, String widgetType, ModelWidget modelWidget) throws IOException {
+        if (modelWidget.boundaryCommentsEnabled()) {
+            StringWriter sr = new StringWriter();
+            sr.append("<@formatBoundaryComment ");
+            sr.append(" boundaryType=\"");
+            sr.append("End");
+            sr.append("\" widgetType=\"");
+            sr.append(widgetType);
+            sr.append("\" widgetName=\"");
+            sr.append(modelWidget.getBoundaryCommentName());
+            sr.append("\" />");
+            executeMacro(sr.toString());
+        }
+    }    
+    
+    public void renderNodeBegin(Appendable writer, Map<String, Object> context, ModelTree.ModelNode node, int depth) throws IOException {
+        String currentNodeTrailPiped = null;
+        List<String> currentNodeTrail = UtilGenerics.toList(context.get("currentNodeTrail"));
+        
+        String style = "";
+        if (node.isRootNode()) {          
+            renderBeginningBoundaryComment(writer, "Tree Widget", node.getModelTree());
+            style = "basic-tree";
+        }
+
+        StringWriter sr = new StringWriter();
+        sr.append("<@renderNodeBegin ");
+        sr.append(" style=\"");
+        sr.append(style);
+        sr.append("\" />");
+        executeMacro(sr.toString());
+
+        String pkName = node.getPkName();
+        String entityId = null;
+        String entryName = node.getEntryName();
+        if (UtilValidate.isNotEmpty(entryName)) {
+            Map map = (Map)context.get(entryName);
+            entityId = (String)map.get(pkName);
+        } else {
+            entityId = (String) context.get(pkName);
+        }
+        boolean hasChildren = node.hasChildren(context);
+
+        ModelTree.ModelNode.Link expandCollapseLink = new ModelTree.ModelNode.Link();
+        // check to see if this node needs to be expanded.
+        if (hasChildren && node.isExpandCollapse()) {
+            String targetEntityId = null;
+            List<String> targetNodeTrail = UtilGenerics.toList(context.get("targetNodeTrail"));
+            if (depth < targetNodeTrail.size()) {
+                targetEntityId = targetNodeTrail.get(depth);
+            }
+
+            int openDepth = node.getModelTree().getOpenDepth();
+            if (depth >= openDepth && (targetEntityId == null || !targetEntityId.equals(entityId))) {
+                // Not on the trail
+                if (node.showPeers(depth, context)) {
+                    context.put("processChildren", Boolean.FALSE);
+                    //expandCollapseLink.setText("&nbsp;+&nbsp;");
+                    currentNodeTrailPiped = StringUtil.join(currentNodeTrail, "|");
+                    expandCollapseLink.setStyle("collapsed");
+                    expandCollapseLink.setText(" ");
+                    StringBuilder target = new StringBuilder(node.getModelTree().getExpandCollapseRequest(context));
+                    String trailName = node.getModelTree().getTrailName(context);
+                    if (target.indexOf("?") < 0) {
+                        target.append("?");
+                    } else {
+                        target.append("&");
+                    }
+                    target.append(trailName).append("=").append(currentNodeTrailPiped);
+                    expandCollapseLink.setTarget(target.toString());
+                }
+            } else {
+                context.put("processChildren", Boolean.TRUE);
+                //expandCollapseLink.setText("&nbsp;-&nbsp;");
+                String lastContentId = currentNodeTrail.remove(currentNodeTrail.size() - 1);
+                currentNodeTrailPiped = StringUtil.join(currentNodeTrail, "|");
+                if (currentNodeTrailPiped == null) {
+                    currentNodeTrailPiped = "";
+                }
+                expandCollapseLink.setStyle("expanded");
+                expandCollapseLink.setText(" ");
+                StringBuilder target = new StringBuilder(node.getModelTree().getExpandCollapseRequest(context));
+                String trailName = node.getModelTree().getTrailName(context);
+                if (target.indexOf("?") < 0) {
+                    target.append("?");
+                } else {
+                    target.append("&");
+                }
+                target.append(trailName).append("=").append(currentNodeTrailPiped);
+                expandCollapseLink.setTarget(target.toString());
+                // add it so it can be remove in renderNodeEnd
+                currentNodeTrail.add(lastContentId);
+            }
+            renderLink(writer, context, expandCollapseLink);
+        } else if (!hasChildren) {
+            context.put("processChildren", Boolean.FALSE);
+            expandCollapseLink.setStyle("leafnode");
+            expandCollapseLink.setText(" ");
+            renderLink(writer, context, expandCollapseLink);
+        }
+    }
+
+    public void renderNodeEnd(Appendable writer, Map<String, Object> context, ModelTree.ModelNode node) throws IOException {
+        Boolean processChildren = (Boolean) context.get("processChildren");
+        if (node.isRootNode()) {            
+            renderEndingBoundaryComment(writer, "Tree Widget", node.getModelTree());
+        }
+        
+        StringWriter sr = new StringWriter();
+        sr.append("<@renderNodeEnd ");        
+        sr.append(" processChildren=");
+        sr.append(Boolean.toString(processChildren.booleanValue()));
+        sr.append(" isRootNode=");
+        sr.append(Boolean.toString(node.isRootNode()));
+        sr.append(" />");
+        executeMacro(sr.toString());        
+    }
+
+    public void renderLastElement(Appendable writer, Map<String, Object> context, ModelTree.ModelNode node) throws IOException {
+        Boolean processChildren = (Boolean) context.get("processChildren");
+        if (processChildren.booleanValue()) {            
+            StringWriter sr = new StringWriter();
+            sr.append("<@renderLastElement ");
+            sr.append("style=\"");
+            sr.append("basic-tree");
+            sr.append("\" />");
+            executeMacro(sr.toString());
+        }
+    }
+
+    public void renderLabel(Appendable writer, Map<String, Object> context, ModelTree.ModelNode.Label label) throws IOException {
+        String id = label.getId(context);
+        String style = label.getStyle(context);
+        String labelText = label.getText(context);
+
+        StringWriter sr = new StringWriter();
+        sr.append("<@renderLabel ");
+        sr.append("\" id=\"");
+        sr.append(id);
+        sr.append("\" style=\"");
+        sr.append(style);
+        sr.append("\" labelText=\"");
+        sr.append(labelText);        
+        sr.append("\" />");
+        executeMacro(sr.toString());        
+    }
+
+    public void renderLink(Appendable writer, Map<String, Object> context, ModelTree.ModelNode.Link link) throws IOException {
+        String target = link.getTarget(context);
+        StringBuilder linkUrl = new StringBuilder();
+        HttpServletResponse response = (HttpServletResponse) context.get("response");
+        HttpServletRequest request = (HttpServletRequest) context.get("request");
+        
+        if (target != null && target.length() > 0) {
+            WidgetWorker.buildHyperlinkUrl(linkUrl, target, link.getUrlMode(), link.getParameterList(), link.getPrefix(context),
+                    link.getFullPath(), link.getSecure(), link.getEncode(), request, response, context);            
+        }        
+        
+        String id = link.getId(context);
+        String style = link.getStyle(context);
+        String name = link.getName(context);
+        String title = link.getTitle(context);
+        String targetWindow = link.getTargetWindow(context);
+        String linkText = link.getText(context);
+        
+        String imgStr = "";
+        ModelTree.ModelNode.Image img = link.getImage();
+        if (img != null) {
+            StringWriter sw = new StringWriter();
+            renderImage(sw, context, img);
+            imgStr = sw.toString();
+        }
+        
+        StringWriter sr = new StringWriter();
+        sr.append("<@renderLink ");
+        sr.append("id=\"");
+        sr.append(id);
+        sr.append("\" style=\"");
+        sr.append(style);
+        sr.append("\" name=\"");
+        sr.append(name);
+        sr.append("\" title=\"");
+        sr.append(title);
+        sr.append("\" targetWindow=\"");
+        sr.append(targetWindow);  
+        sr.append("\" linkUrl=\"");
+        sr.append(linkUrl);    
+        sr.append("\" linkText=\"");
+        sr.append(linkText);          
+        sr.append("\" imgStr=\"");
+        sr.append(imgStr.replaceAll("\"", "\\\\\""));
+        sr.append("\" />");
+        executeMacro(sr.toString());
+    }
+  
+    public void renderImage(Appendable writer, Map<String, Object> context, ModelTree.ModelNode.Image image) throws IOException {
+        if (image == null) {
+            return ;            
+        }
+        HttpServletResponse response = (HttpServletResponse) context.get("response");
+        HttpServletRequest request = (HttpServletRequest) context.get("request");
+        
+        String urlMode = image.getUrlMode();
+        String src = image.getSrc(context);
+        String id = image.getId(context);
+        String style = image.getStyle(context);
+        String wid = image.getWidth(context);
+        String hgt = image.getHeight(context);
+        String border = image.getBorder(context);
+        String alt = ""; //TODO add alt to tree images image.getAlt(context);
+
+        boolean fullPath = false;
+        boolean secure = false;
+        boolean encode = false;
+        String urlString = "";
+        
+        if (urlMode != null && urlMode.equalsIgnoreCase("intra-app")) {
+            if (request != null && response != null) {
+                ServletContext ctx = (ServletContext) request.getAttribute("servletContext");
+                RequestHandler rh = (RequestHandler) ctx.getAttribute("_REQUEST_HANDLER_");
+                urlString = rh.makeLink(request, response, src, fullPath, secure, encode);
+            } else {
+                urlString = src;
+            }
+        } else  if (urlMode != null && urlMode.equalsIgnoreCase("content")) {
+            if (request != null && response != null) {
+                StringBuilder newURL = new StringBuilder();
+                ContentUrlTag.appendContentPrefix(request, newURL);
+                newURL.append(src);
+                urlString = newURL.toString();
+            }
+        } else {
+            urlString = src;
+        }
+        StringWriter sr = new StringWriter();
+        sr.append("<@renderImage ");
+        sr.append("src=\"");
+        sr.append(src);
+        sr.append("\" id=\"");
+        sr.append(id);
+        sr.append("\" style=\"");
+        sr.append(style);
+        sr.append("\" wid=\"");
+        sr.append(wid);
+        sr.append("\" hgt=\"");
+        sr.append(hgt);
+        sr.append("\" border=\"");
+        sr.append(border);
+        sr.append("\" alt=\"");
+        sr.append(alt);
+        sr.append("\" urlString=\"");
+        sr.append(urlString);
+        sr.append("\" />");
+        executeMacro(writer, sr.toString());        
+    }
+
+    public ScreenStringRenderer getScreenStringRenderer(Map<String, Object> context) {
+        ScreenRenderer screenRenderer = (ScreenRenderer)context.get("screens");
+        if (screenRenderer != null) {
+            this.screenStringRenderer = screenRenderer.getScreenStringRenderer();
+        }
+        return this.screenStringRenderer;
+    }
+}

Added: ofbiz/trunk/framework/widget/templates/htmlTreeMacroLibrary.ftl
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/widget/templates/htmlTreeMacroLibrary.ftl?rev=880683&view=auto
==============================================================================
--- ofbiz/trunk/framework/widget/templates/htmlTreeMacroLibrary.ftl (added)
+++ ofbiz/trunk/framework/widget/templates/htmlTreeMacroLibrary.ftl Mon Nov 16 09:16:24 2009
@@ -0,0 +1,64 @@
+<#--
+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.
+-->
+
+<#macro renderNodeBegin style>
+<#if style?has_content><ul class="${style}"></#if>
+<li><#rt/>
+</#macro>
+
+<#macro renderLastElement style>
+<ul<#if style?has_content> class="${style}"</#if>>
+<li><#rt/>
+</#macro>
+  
+<#macro renderNodeEnd processChildren isRootNode>
+<#if processChildren?has_content && processChildren>
+</ul><#lt/>
+</#if>
+</li><#rt/>
+<#if isRootNode?has_content && isRootNode>
+</ul><#lt/>
+</#if>
+</#macro>
+
+<#macro renderLabel id style labelText>
+<span<#if id?has_content> id="${id}"</#if><#if style?has_content> class="${style}"</#if>><#rt/>
+<#if id?has_content>{labelText}</#if><#rt/>
+</span>    
+</#macro>
+
+<#macro formatBoundaryComment boundaryType widgetType widgetName>
+<!-- ${boundaryType}  ${widgetType}  ${widgetName} -->
+</#macro>
+
+<#macro renderLink id style name title targetWindow linkUrl linkText imgStr>
+<a<#if id?has_content> id="${id}"</#if><#rt/>
+<#if style?has_content> class="${style}"</#if><#rt/>
+<#if name?has_content> name="${name}"</#if><#rt/>
+<#if title?has_content> title="${title}"</#if><#rt/>
+<#if targetWindow?has_content> target="${targetWindow}</#if> href="${linkUrl}"><#rt/>
+<#if imgStr?has_content>${imgStr}<#else><#if linkText?has_content>${linkText}</#if></#if></a><#rt/>
+</#macro>
+
+<#macro renderImage src id style wid hgt border alt urlString>
+<#if src?has_content>
+<img <#if id?has_content>id="${id}"</#if><#if style?has_content> class="${style}"</#if><#if wid?has_content> width="${wid}"</#if><#if hgt?has_content> height="${hgt}"</#if><#if border?has_content> border="${border}"</#if><#if alt?has_content> alt="${alt}"</#if> src="${urlString}" /><#rt/>
+</#if>
+</#macro>
+
\ No newline at end of file