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.util.ContentUtil;
34  import com.liferay.portal.util.PropsValues;
35  import com.liferay.portal.velocity.VelocityResourceListener;
36  import com.liferay.portlet.journal.TransformException;
37  import com.liferay.util.PwdGenerator;
38  import com.liferay.util.xml.CDATAUtil;
39  
40  import java.io.IOException;
41  
42  import java.util.ArrayList;
43  import java.util.HashMap;
44  import java.util.List;
45  import java.util.Map;
46  
47  import org.apache.velocity.exception.ParseErrorException;
48  import org.apache.velocity.exception.VelocityException;
49  
50  /**
51   * <a href="VelocityTemplateParser.java.html"><b><i>View Source</i></b></a>
52   *
53   * @author Alexander Chow
54   * @author Brian Wing Shun Chan
55   * @author Raymond Augé
56   */
57  public class VelocityTemplateParser extends BaseTemplateParser {
58  
59      protected String doTransform(
60              Map<String, String> tokens, String viewMode, String languageId,
61              String xml, String script)
62          throws Exception {
63  
64          UnsyncStringWriter unsyncStringWriter = new UnsyncStringWriter(true);
65  
66          boolean load = false;
67  
68          try {
69              VelocityContext velocityContext =
70                  VelocityEngineUtil.getWrappedRestrictedToolsContext();
71  
72              Document doc = SAXReaderUtil.read(xml);
73  
74              Element root = doc.getRootElement();
75  
76              List<TemplateNode> nodes = extractDynamicContents(root);
77  
78              for (TemplateNode node : nodes) {
79                  velocityContext.put(node.getName(), node);
80              }
81  
82              velocityContext.put("xmlRequest", root.element("request").asXML());
83              velocityContext.put(
84                  "request", insertRequestVariables(root.element("request")));
85  
86              long companyId = GetterUtil.getLong(tokens.get("company_id"));
87              Company company = CompanyLocalServiceUtil.getCompanyById(companyId);
88              long groupId = GetterUtil.getLong(tokens.get("group_id"));
89              String templateId = tokens.get("template_id");
90              String journalTemplatesPath =
91                  VelocityResourceListener.JOURNAL_SEPARATOR + StringPool.SLASH +
92                      companyId + StringPool.SLASH + groupId;
93              String randomNamespace =
94                  PwdGenerator.getPassword(PwdGenerator.KEY3, 4) +
95                      StringPool.UNDERLINE;
96  
97              velocityContext.put("company", company);
98              velocityContext.put("companyId", String.valueOf(companyId));
99              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(Element parent)
174         throws TransformException {
175 
176         List<TemplateNode> nodes = new ArrayList<TemplateNode>();
177 
178         Map<String, TemplateNode> prototypeNodes =
179             new HashMap<String, TemplateNode>();
180 
181         for (Element el : parent.elements("dynamic-element")) {
182             Element content = el.element("dynamic-content");
183 
184             if (content == null) {
185                 throw new TransformException(
186                     "Element missing \"dynamic-content\"");
187             }
188 
189             String name = el.attributeValue("name", "");
190 
191             if (name.length() == 0) {
192                 throw new TransformException(
193                     "Element missing \"name\" attribute");
194             }
195 
196             String type = el.attributeValue("type", "");
197 
198             TemplateNode node = new TemplateNode(
199                 name, CDATAUtil.strip(content.getText()), type);
200 
201             if (el.element("dynamic-element") != null) {
202                 node.appendChildren(extractDynamicContents(el));
203             }
204             else if (content.element("option") != null) {
205                 for (Element option : content.elements("option")) {
206                     node.appendOption(CDATAUtil.strip(option.getText()));
207                 }
208             }
209 
210             TemplateNode prototypeNode = prototypeNodes.get(name);
211 
212             if (prototypeNode == null) {
213                 prototypeNode = node;
214 
215                 prototypeNodes.put(name, prototypeNode);
216 
217                 nodes.add(node);
218             }
219 
220             prototypeNode.appendSibling(node);
221         }
222 
223         return nodes;
224     }
225 
226     protected String injectEditInPlace(String xml, String script)
227         throws DocumentException {
228 
229         Document doc = SAXReaderUtil.read(xml);
230 
231         List<Node> nodes = doc.selectNodes("//dynamic-element");
232 
233         for (Node node : nodes) {
234             Element el = (Element)node;
235 
236             String name = GetterUtil.getString(el.attributeValue("name"));
237             String type = GetterUtil.getString(el.attributeValue("type"));
238 
239             if ((!name.startsWith("reserved-")) &&
240                 (type.equals("text") || type.equals("text_box") ||
241                  type.equals("text_area"))) {
242 
243                 script = wrapField(script, name, type, "data");
244                 script = wrapField(script, name, type, "getData()");
245             }
246         }
247 
248         return script;
249     }
250 
251     protected Map<String, Object> insertRequestVariables(Element parent) {
252         Map<String, Object> map = new HashMap<String, Object>();
253 
254         if (parent == null) {
255             return map;
256         }
257 
258         for (Element el : parent.elements()) {
259             String name = el.getName();
260 
261             if (name.equals("attribute")) {
262                 map.put(el.elementText("name"), el.elementText("value"));
263             }
264             else if (name.equals("parameter")) {
265                 name = el.element("name").getText();
266 
267                 List<Element> valueEls = el.elements("value");
268 
269                 if (valueEls.size() == 1) {
270                     map.put(name, (valueEls.get(0)).getText());
271                 }
272                 else {
273                     List<String> values = new ArrayList<String>();
274 
275                     for (Element valueEl : valueEls) {
276                         values.add(valueEl.getText());
277                     }
278 
279                     map.put(name, values);
280                 }
281             }
282             else if (el.elements().size() > 0) {
283                 map.put(name, insertRequestVariables(el));
284             }
285             else {
286                 map.put(name, el.getText());
287             }
288         }
289 
290         return map;
291     }
292 
293     protected String wrapField(
294         String script, String name, String type, String call) {
295 
296         String field = "$" + name + "." + call;
297         String wrappedField =
298             "<span class=\"journal-content-eip-" + type + "\" " +
299                 "id=\"journal-content-field-name-" + name + "\">" + field +
300                     "</span>";
301 
302         return StringUtil.replace(
303             script, "$editInPlace(" + field + ")", wrappedField);
304     }
305 
306 }