1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * This library is free software; you can redistribute it and/or modify it under
5    * the terms of the GNU Lesser General Public License as published by the Free
6    * Software Foundation; either version 2.1 of the License, or (at your option)
7    * any later version.
8    *
9    * This library is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11   * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12   * details.
13   */
14  
15  package com.liferay.portlet.journal.util;
16  
17  import com.liferay.portal.kernel.io.unsync.UnsyncStringWriter;
18  import com.liferay.portal.kernel.util.GetterUtil;
19  import com.liferay.portal.kernel.util.HtmlUtil;
20  import com.liferay.portal.kernel.util.LocaleUtil;
21  import com.liferay.portal.kernel.util.StringPool;
22  import com.liferay.portal.kernel.util.StringUtil;
23  import com.liferay.portal.kernel.velocity.VelocityContext;
24  import com.liferay.portal.kernel.velocity.VelocityEngineUtil;
25  import com.liferay.portal.kernel.xml.Document;
26  import com.liferay.portal.kernel.xml.DocumentException;
27  import com.liferay.portal.kernel.xml.Element;
28  import com.liferay.portal.kernel.xml.Node;
29  import com.liferay.portal.kernel.xml.SAXReaderUtil;
30  import com.liferay.portal.model.Company;
31  import com.liferay.portal.security.permission.PermissionThreadLocal;
32  import com.liferay.portal.service.CompanyLocalServiceUtil;
33  import com.liferay.portal.theme.ThemeDisplay;
34  import com.liferay.portal.util.ContentUtil;
35  import com.liferay.portal.util.PropsValues;
36  import com.liferay.portal.velocity.VelocityResourceListener;
37  import com.liferay.portlet.journal.TransformException;
38  import com.liferay.util.PwdGenerator;
39  import com.liferay.util.xml.CDATAUtil;
40  
41  import java.io.IOException;
42  
43  import java.util.ArrayList;
44  import java.util.HashMap;
45  import java.util.List;
46  import java.util.Map;
47  
48  import org.apache.velocity.exception.ParseErrorException;
49  import org.apache.velocity.exception.VelocityException;
50  
51  /**
52   * <a href="VelocityTemplateParser.java.html"><b><i>View Source</i></b></a>
53   *
54   * @author Alexander Chow
55   * @author Brian Wing Shun Chan
56   * @author Raymond Augé
57   */
58  public class VelocityTemplateParser extends BaseTemplateParser {
59  
60      protected String doTransform(
61              ThemeDisplay themeDisplay, Map<String, String> tokens,
62              String viewMode, String languageId, String xml, String script)
63          throws Exception {
64  
65          UnsyncStringWriter unsyncStringWriter = new UnsyncStringWriter();
66  
67          boolean load = false;
68  
69          try {
70              VelocityContext velocityContext =
71                  VelocityEngineUtil.getWrappedRestrictedToolsContext();
72  
73              Document doc = SAXReaderUtil.read(xml);
74  
75              Element root = doc.getRootElement();
76  
77              List<TemplateNode> nodes = extractDynamicContents(
78                  themeDisplay, root);
79  
80              for (TemplateNode node : nodes) {
81                  velocityContext.put(node.getName(), node);
82              }
83  
84              velocityContext.put("xmlRequest", root.element("request").asXML());
85              velocityContext.put(
86                  "request", insertRequestVariables(root.element("request")));
87  
88              long companyId = GetterUtil.getLong(tokens.get("company_id"));
89              Company company = CompanyLocalServiceUtil.getCompanyById(companyId);
90              long groupId = GetterUtil.getLong(tokens.get("group_id"));
91              String templateId = tokens.get("template_id");
92              String journalTemplatesPath =
93                  VelocityResourceListener.JOURNAL_SEPARATOR + StringPool.SLASH +
94                      companyId + StringPool.SLASH + groupId;
95              String randomNamespace =
96                  PwdGenerator.getPassword(PwdGenerator.KEY3, 4) +
97                      StringPool.UNDERLINE;
98  
99              velocityContext.put("company", company);
100             velocityContext.put("companyId", String.valueOf(companyId));
101             velocityContext.put("groupId", String.valueOf(groupId));
102             velocityContext.put("journalTemplatesPath", journalTemplatesPath);
103             velocityContext.put("viewMode", viewMode);
104             velocityContext.put(
105                 "locale", LocaleUtil.fromLanguageId(languageId));
106             velocityContext.put(
107                 "permissionChecker",
108                 PermissionThreadLocal.getPermissionChecker());
109             velocityContext.put("randomNamespace", randomNamespace);
110 
111             script = injectEditInPlace(xml, script);
112 
113             try {
114                 String velocityTemplateId = companyId + groupId + templateId;
115 
116                 load = VelocityEngineUtil.mergeTemplate(
117                     velocityTemplateId, script, velocityContext,
118                     unsyncStringWriter);
119             }
120             catch (VelocityException ve) {
121                 velocityContext.put("exception", ve.getMessage());
122                 velocityContext.put("script", script);
123 
124                 if (ve instanceof ParseErrorException) {
125                     ParseErrorException pe = (ParseErrorException)ve;
126 
127                     velocityContext.put(
128                         "column", new Integer(pe.getColumnNumber()));
129                     velocityContext.put(
130                         "line", new Integer(pe.getLineNumber()));
131                 }
132 
133                 String velocityTemplateId =
134                     PropsValues.JOURNAL_ERROR_TEMPLATE_VELOCITY;
135                 String velocityTemplateContent = ContentUtil.get(
136                     PropsValues.JOURNAL_ERROR_TEMPLATE_VELOCITY);
137 
138                 load = VelocityEngineUtil.mergeTemplate(
139                     velocityTemplateId, velocityTemplateContent,
140                     velocityContext, unsyncStringWriter);
141             }
142         }
143         catch (Exception e) {
144             if (e instanceof DocumentException) {
145                 throw new TransformException("Unable to read XML document", e);
146             }
147             else if (e instanceof VelocityException) {
148                 VelocityException pex = (VelocityException)e;
149 
150                 throw new TransformException(
151                     "Unable to parse velocity template: " +
152                         HtmlUtil.escape(pex.getMessage()),
153                     e);
154             }
155             else if (e instanceof IOException) {
156                 throw new TransformException(
157                     "Error reading velocity template", e);
158             }
159             else if (e instanceof TransformException) {
160                 throw (TransformException)e;
161             }
162             else {
163                 throw new TransformException("Unhandled exception", e);
164             }
165         }
166 
167         if (!load) {
168             throw new TransformException(
169                 "Unable to dynamically load velocity transform script");
170         }
171 
172         return unsyncStringWriter.toString();
173     }
174 
175     protected List<TemplateNode> extractDynamicContents(
176             ThemeDisplay themeDisplay, Element parent)
177         throws TransformException {
178 
179         List<TemplateNode> nodes = new ArrayList<TemplateNode>();
180 
181         Map<String, TemplateNode> prototypeNodes =
182             new HashMap<String, TemplateNode>();
183 
184         for (Element el : parent.elements("dynamic-element")) {
185             Element content = el.element("dynamic-content");
186 
187             if (content == null) {
188                 throw new TransformException(
189                     "Element missing \"dynamic-content\"");
190             }
191 
192             String name = el.attributeValue("name", "");
193 
194             if (name.length() == 0) {
195                 throw new TransformException(
196                     "Element missing \"name\" attribute");
197             }
198 
199             String type = el.attributeValue("type", "");
200 
201             TemplateNode node = new TemplateNode(
202                 themeDisplay, name, CDATAUtil.strip(content.getText()), type);
203 
204             if (el.element("dynamic-element") != null) {
205                 node.appendChildren(extractDynamicContents(themeDisplay, el));
206             }
207             else if (content.element("option") != null) {
208                 for (Element option : content.elements("option")) {
209                     node.appendOption(CDATAUtil.strip(option.getText()));
210                 }
211             }
212 
213             TemplateNode prototypeNode = prototypeNodes.get(name);
214 
215             if (prototypeNode == null) {
216                 prototypeNode = node;
217 
218                 prototypeNodes.put(name, prototypeNode);
219 
220                 nodes.add(node);
221             }
222 
223             prototypeNode.appendSibling(node);
224         }
225 
226         return nodes;
227     }
228 
229     protected String injectEditInPlace(String xml, String script)
230         throws DocumentException {
231 
232         Document doc = SAXReaderUtil.read(xml);
233 
234         List<Node> nodes = doc.selectNodes("//dynamic-element");
235 
236         for (Node node : nodes) {
237             Element el = (Element)node;
238 
239             String name = GetterUtil.getString(el.attributeValue("name"));
240             String type = GetterUtil.getString(el.attributeValue("type"));
241 
242             if ((!name.startsWith("reserved-")) &&
243                 (type.equals("text") || type.equals("text_box") ||
244                  type.equals("text_area"))) {
245 
246                 script = wrapField(script, name, type, "data");
247                 script = wrapField(script, name, type, "getData()");
248             }
249         }
250 
251         return script;
252     }
253 
254     protected Map<String, Object> insertRequestVariables(Element parent) {
255         Map<String, Object> map = new HashMap<String, Object>();
256 
257         if (parent == null) {
258             return map;
259         }
260 
261         for (Element el : parent.elements()) {
262             String name = el.getName();
263 
264             if (name.equals("attribute")) {
265                 map.put(el.elementText("name"), el.elementText("value"));
266             }
267             else if (name.equals("parameter")) {
268                 name = el.element("name").getText();
269 
270                 List<Element> valueEls = el.elements("value");
271 
272                 if (valueEls.size() == 1) {
273                     map.put(name, (valueEls.get(0)).getText());
274                 }
275                 else {
276                     List<String> values = new ArrayList<String>();
277 
278                     for (Element valueEl : valueEls) {
279                         values.add(valueEl.getText());
280                     }
281 
282                     map.put(name, values);
283                 }
284             }
285             else if (el.elements().size() > 0) {
286                 map.put(name, insertRequestVariables(el));
287             }
288             else {
289                 map.put(name, el.getText());
290             }
291         }
292 
293         return map;
294     }
295 
296     protected String wrapField(
297         String script, String name, String type, String call) {
298 
299         String field = "$" + name + "." + call;
300         String wrappedField =
301             "<span class=\"journal-content-eip-" + type + "\" " +
302                 "id=\"journal-content-field-name-" + name + "\">" + field +
303                     "</span>";
304 
305         return StringUtil.replace(
306             script, "$editInPlace(" + field + ")", wrappedField);
307     }
308 
309 }