001    /**
002     * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portlet.journal.util;
016    
017    import com.liferay.portal.kernel.io.unsync.UnsyncStringWriter;
018    import com.liferay.portal.kernel.util.GetterUtil;
019    import com.liferay.portal.kernel.util.HtmlUtil;
020    import com.liferay.portal.kernel.util.LocaleUtil;
021    import com.liferay.portal.kernel.util.StringPool;
022    import com.liferay.portal.kernel.util.StringUtil;
023    import com.liferay.portal.kernel.velocity.VelocityContext;
024    import com.liferay.portal.kernel.velocity.VelocityEngineUtil;
025    import com.liferay.portal.kernel.xml.Document;
026    import com.liferay.portal.kernel.xml.DocumentException;
027    import com.liferay.portal.kernel.xml.Element;
028    import com.liferay.portal.kernel.xml.Node;
029    import com.liferay.portal.kernel.xml.SAXReaderUtil;
030    import com.liferay.portal.model.Company;
031    import com.liferay.portal.security.permission.PermissionThreadLocal;
032    import com.liferay.portal.service.CompanyLocalServiceUtil;
033    import com.liferay.portal.theme.ThemeDisplay;
034    import com.liferay.portal.util.ContentUtil;
035    import com.liferay.portal.util.PropsValues;
036    import com.liferay.portal.velocity.VelocityResourceListener;
037    import com.liferay.portlet.journal.TransformException;
038    import com.liferay.util.PwdGenerator;
039    import com.liferay.util.xml.CDATAUtil;
040    
041    import java.io.IOException;
042    
043    import java.util.ArrayList;
044    import java.util.HashMap;
045    import java.util.List;
046    import java.util.Map;
047    
048    import org.apache.velocity.exception.ParseErrorException;
049    import org.apache.velocity.exception.VelocityException;
050    
051    /**
052     * @author Alexander Chow
053     * @author Brian Wing Shun Chan
054     * @author Raymond Augé
055     */
056    public class VelocityTemplateParser extends BaseTemplateParser {
057    
058            protected String doTransform(
059                            ThemeDisplay themeDisplay, Map<String, String> tokens,
060                            String viewMode, String languageId,     String xml, String script)
061                    throws Exception {
062    
063                    UnsyncStringWriter unsyncStringWriter = new UnsyncStringWriter();
064    
065                    boolean load = false;
066    
067                    try {
068                            VelocityContext velocityContext =
069                                    VelocityEngineUtil.getWrappedRestrictedToolsContext();
070    
071                            Document doc = SAXReaderUtil.read(xml);
072    
073                            Element root = doc.getRootElement();
074    
075                            List<TemplateNode> nodes = extractDynamicContents(
076                                    themeDisplay, root);
077    
078                            for (TemplateNode node : nodes) {
079                                    velocityContext.put(node.getName(), node);
080                            }
081    
082                            velocityContext.put("xmlRequest", root.element("request").asXML());
083                            velocityContext.put(
084                                    "request", insertRequestVariables(root.element("request")));
085    
086                            long companyId = GetterUtil.getLong(tokens.get("company_id"));
087                            Company company = CompanyLocalServiceUtil.getCompanyById(companyId);
088                            long groupId = GetterUtil.getLong(tokens.get("group_id"));
089                            String templateId = tokens.get("template_id");
090                            String journalTemplatesPath =
091                                    VelocityResourceListener.JOURNAL_SEPARATOR + StringPool.SLASH +
092                                            companyId + StringPool.SLASH + groupId;
093                            String randomNamespace =
094                                    PwdGenerator.getPassword(PwdGenerator.KEY3, 4) +
095                                            StringPool.UNDERLINE;
096    
097                            velocityContext.put("company", company);
098                            velocityContext.put("companyId", String.valueOf(companyId));
099                            velocityContext.put("groupId", String.valueOf(groupId));
100                            velocityContext.put("journalTemplatesPath", journalTemplatesPath);
101                            velocityContext.put("viewMode", viewMode);
102                            velocityContext.put(
103                                    "locale", LocaleUtil.fromLanguageId(languageId));
104                            velocityContext.put(
105                                    "permissionChecker",
106                                    PermissionThreadLocal.getPermissionChecker());
107                            velocityContext.put("randomNamespace", randomNamespace);
108    
109                            script = injectEditInPlace(xml, script);
110    
111                            try {
112                                    String velocityTemplateId = companyId + groupId + templateId;
113    
114                                    load = VelocityEngineUtil.mergeTemplate(
115                                            velocityTemplateId, script, velocityContext,
116                                            unsyncStringWriter);
117                            }
118                            catch (VelocityException ve) {
119                                    velocityContext.put("exception", ve.getMessage());
120                                    velocityContext.put("script", script);
121    
122                                    if (ve instanceof ParseErrorException) {
123                                            ParseErrorException pe = (ParseErrorException)ve;
124    
125                                            velocityContext.put(
126                                                    "column", new Integer(pe.getColumnNumber()));
127                                            velocityContext.put(
128                                                    "line", new Integer(pe.getLineNumber()));
129                                    }
130    
131                                    String velocityTemplateId =
132                                            PropsValues.JOURNAL_ERROR_TEMPLATE_VELOCITY;
133                                    String velocityTemplateContent = ContentUtil.get(
134                                            PropsValues.JOURNAL_ERROR_TEMPLATE_VELOCITY);
135    
136                                    load = VelocityEngineUtil.mergeTemplate(
137                                            velocityTemplateId, velocityTemplateContent,
138                                            velocityContext, unsyncStringWriter);
139                            }
140                    }
141                    catch (Exception e) {
142                            if (e instanceof DocumentException) {
143                                    throw new TransformException("Unable to read XML document", e);
144                            }
145                            else if (e instanceof VelocityException) {
146                                    VelocityException pex = (VelocityException)e;
147    
148                                    throw new TransformException(
149                                            "Unable to parse velocity template: " +
150                                                    HtmlUtil.escape(pex.getMessage()),
151                                            e);
152                            }
153                            else if (e instanceof IOException) {
154                                    throw new TransformException(
155                                            "Error reading velocity template", e);
156                            }
157                            else if (e instanceof TransformException) {
158                                    throw (TransformException)e;
159                            }
160                            else {
161                                    throw new TransformException("Unhandled exception", e);
162                            }
163                    }
164    
165                    if (!load) {
166                            throw new TransformException(
167                                    "Unable to dynamically load velocity transform script");
168                    }
169    
170                    return unsyncStringWriter.toString();
171            }
172    
173            protected List<TemplateNode> extractDynamicContents(
174                            ThemeDisplay themeDisplay, Element parent)
175                    throws TransformException {
176    
177                    List<TemplateNode> nodes = new ArrayList<TemplateNode>();
178    
179                    Map<String, TemplateNode> prototypeNodes =
180                            new HashMap<String, TemplateNode>();
181    
182                    for (Element el : parent.elements("dynamic-element")) {
183                            Element content = el.element("dynamic-content");
184    
185                            if (content == null) {
186                                    throw new TransformException(
187                                            "Element missing \"dynamic-content\"");
188                            }
189    
190                            String name = el.attributeValue("name", "");
191    
192                            if (name.length() == 0) {
193                                    throw new TransformException(
194                                            "Element missing \"name\" attribute");
195                            }
196    
197                            String type = el.attributeValue("type", "");
198    
199                            TemplateNode node = new TemplateNode(
200                                    themeDisplay, name, CDATAUtil.strip(content.getText()), type);
201    
202                            if (el.element("dynamic-element") != null) {
203                                    node.appendChildren(extractDynamicContents(themeDisplay, el));
204                            }
205                            else if (content.element("option") != null) {
206                                    for (Element option : content.elements("option")) {
207                                            node.appendOption(CDATAUtil.strip(option.getText()));
208                                    }
209                            }
210    
211                            TemplateNode prototypeNode = prototypeNodes.get(name);
212    
213                            if (prototypeNode == null) {
214                                    prototypeNode = node;
215    
216                                    prototypeNodes.put(name, prototypeNode);
217    
218                                    nodes.add(node);
219                            }
220    
221                            prototypeNode.appendSibling(node);
222                    }
223    
224                    return nodes;
225            }
226    
227            protected String injectEditInPlace(String xml, String script)
228                    throws DocumentException {
229    
230                    Document doc = SAXReaderUtil.read(xml);
231    
232                    List<Node> nodes = doc.selectNodes("//dynamic-element");
233    
234                    for (Node node : nodes) {
235                            Element el = (Element)node;
236    
237                            String name = GetterUtil.getString(el.attributeValue("name"));
238                            String type = GetterUtil.getString(el.attributeValue("type"));
239    
240                            if ((!name.startsWith("reserved-")) &&
241                                    (type.equals("text") || type.equals("text_box") ||
242                                     type.equals("text_area"))) {
243    
244                                    script = wrapField(script, name, type, "data");
245                                    script = wrapField(script, name, type, "getData()");
246                            }
247                    }
248    
249                    return script;
250            }
251    
252            protected Map<String, Object> insertRequestVariables(Element parent) {
253                    Map<String, Object> map = new HashMap<String, Object>();
254    
255                    if (parent == null) {
256                            return map;
257                    }
258    
259                    for (Element el : parent.elements()) {
260                            String name = el.getName();
261    
262                            if (name.equals("attribute")) {
263                                    map.put(el.elementText("name"), el.elementText("value"));
264                            }
265                            else if (name.equals("parameter")) {
266                                    name = el.element("name").getText();
267    
268                                    List<Element> valueEls = el.elements("value");
269    
270                                    if (valueEls.size() == 1) {
271                                            map.put(name, (valueEls.get(0)).getText());
272                                    }
273                                    else {
274                                            List<String> values = new ArrayList<String>();
275    
276                                            for (Element valueEl : valueEls) {
277                                                    values.add(valueEl.getText());
278                                            }
279    
280                                            map.put(name, values);
281                                    }
282                            }
283                            else if (el.elements().size() > 0) {
284                                    map.put(name, insertRequestVariables(el));
285                            }
286                            else {
287                                    map.put(name, el.getText());
288                            }
289                    }
290    
291                    return map;
292            }
293    
294            protected String wrapField(
295                    String script, String name, String type, String call) {
296    
297                    String field = "$" + name + "." + call;
298                    String wrappedField =
299                            "<span class=\"journal-content-eip-" + type + "\" " +
300                                    "id=\"journal-content-field-name-" + name + "\">" + field +
301                                            "</span>";
302    
303                    return StringUtil.replace(
304                            script, "$editInPlace(" + field + ")", wrappedField);
305            }
306    
307    }