1   /**
2    * Copyright (c) 2000-2009 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portlet.journal.util;
24  
25  import com.liferay.portal.kernel.util.GetterUtil;
26  import com.liferay.portal.kernel.util.HtmlUtil;
27  import com.liferay.portal.kernel.util.LocaleUtil;
28  import com.liferay.portal.kernel.util.StringPool;
29  import com.liferay.portal.kernel.util.StringUtil;
30  import com.liferay.portal.kernel.velocity.VelocityContext;
31  import com.liferay.portal.kernel.velocity.VelocityEngineUtil;
32  import com.liferay.portal.kernel.xml.Document;
33  import com.liferay.portal.kernel.xml.DocumentException;
34  import com.liferay.portal.kernel.xml.Element;
35  import com.liferay.portal.kernel.xml.Node;
36  import com.liferay.portal.kernel.xml.SAXReaderUtil;
37  import com.liferay.portal.model.Company;
38  import com.liferay.portal.security.permission.PermissionThreadLocal;
39  import com.liferay.portal.service.CompanyLocalServiceUtil;
40  import com.liferay.portal.util.ContentUtil;
41  import com.liferay.portal.util.PropsValues;
42  import com.liferay.portal.velocity.VelocityResourceListener;
43  import com.liferay.portlet.journal.TransformException;
44  import com.liferay.util.PwdGenerator;
45  import com.liferay.util.xml.CDATAUtil;
46  
47  import java.io.IOException;
48  import java.io.StringWriter;
49  
50  import java.util.ArrayList;
51  import java.util.HashMap;
52  import java.util.List;
53  import java.util.Map;
54  
55  import org.apache.velocity.exception.ParseErrorException;
56  import org.apache.velocity.exception.VelocityException;
57  
58  /**
59   * <a href="JournalVmUtil.java.html"><b><i>View Source</i></b></a>
60   *
61   * @author Alexander Chow
62   * @author Brian Wing Shun Chan
63   * @author Raymond Augé
64   *
65   */
66  public class JournalVmUtil {
67  
68      public static List<TemplateNode> extractDynamicContents(Element parent)
69          throws TransformException {
70  
71          List<TemplateNode> nodes = new ArrayList<TemplateNode>();
72  
73          Map<String, TemplateNode> prototypeNodes =
74              new HashMap<String, TemplateNode>();
75  
76          for (Element el : parent.elements("dynamic-element")) {
77              Element content = el.element("dynamic-content");
78  
79              if (content == null) {
80                  throw new TransformException(
81                      "Element missing \"dynamic-content\"");
82              }
83  
84              String name = el.attributeValue("name", "");
85  
86              if (name.length() == 0) {
87                  throw new TransformException(
88                      "Element missing \"name\" attribute");
89              }
90  
91              String type = el.attributeValue("type", "");
92  
93              TemplateNode node = new TemplateNode(
94                  name, CDATAUtil.strip(content.getText()), type);
95  
96              if (el.element("dynamic-element") != null) {
97                  node.appendChildren(extractDynamicContents(el));
98              }
99              else if (content.element("option") != null) {
100                 for (Element option : content.elements("option")) {
101                     node.appendOption(CDATAUtil.strip(option.getText()));
102                 }
103             }
104 
105             TemplateNode prototypeNode = prototypeNodes.get(name);
106 
107             if (prototypeNode == null) {
108                 prototypeNode = node;
109 
110                 prototypeNodes.put(name, prototypeNode);
111 
112                 nodes.add(node);
113             }
114 
115             prototypeNode.appendSibling(node);
116         }
117 
118         return nodes;
119     }
120 
121     public static String transform(
122             Map<String, String> tokens, String viewMode, String languageId,
123             String xml, String script)
124         throws TransformException {
125 
126         StringWriter output = new StringWriter();
127 
128         boolean load = false;
129 
130         try {
131             VelocityContext velocityContext =
132                 VelocityEngineUtil.getWrappedRestrictedToolsContext();
133 
134             Document doc = SAXReaderUtil.read(xml);
135 
136             Element root = doc.getRootElement();
137 
138             List<TemplateNode> nodes = extractDynamicContents(root);
139 
140             for (TemplateNode node : nodes) {
141                 velocityContext.put(node.getName(), node);
142             }
143 
144             velocityContext.put("xmlRequest", root.element("request").asXML());
145             velocityContext.put(
146                 "request", insertRequestVariables(root.element("request")));
147 
148             long companyId = GetterUtil.getLong(tokens.get("company_id"));
149             Company company = CompanyLocalServiceUtil.getCompanyById(companyId);
150             long groupId = GetterUtil.getLong(tokens.get("group_id"));
151             String templateId = tokens.get("template_id");
152             String journalTemplatesPath =
153                 VelocityResourceListener.JOURNAL_SEPARATOR + StringPool.SLASH +
154                     companyId + StringPool.SLASH + groupId;
155             String randomNamespace =
156                 PwdGenerator.getPassword(PwdGenerator.KEY3, 4) +
157                     StringPool.UNDERLINE;
158 
159             velocityContext.put("company", company);
160             velocityContext.put("companyId", String.valueOf(companyId));
161             velocityContext.put("groupId", String.valueOf(groupId));
162             velocityContext.put("journalTemplatesPath", journalTemplatesPath);
163             velocityContext.put("viewMode", viewMode);
164             velocityContext.put(
165                 "locale", LocaleUtil.fromLanguageId(languageId));
166             velocityContext.put(
167                 "permissionChecker",
168                 PermissionThreadLocal.getPermissionChecker());
169             velocityContext.put("randomNamespace", randomNamespace);
170 
171             script = injectEditInPlace(xml, script);
172 
173             try {
174                 String velocityTemplateId = companyId + groupId + templateId;
175 
176                 load = VelocityEngineUtil.mergeTemplate(
177                     velocityTemplateId, script, velocityContext, output);
178             }
179             catch (VelocityException ve) {
180                 velocityContext.put("exception", ve.getMessage());
181                 velocityContext.put("script", script);
182 
183                 if (ve instanceof ParseErrorException) {
184                     ParseErrorException pe = (ParseErrorException)ve;
185 
186                     velocityContext.put(
187                         "column", new Integer(pe.getColumnNumber()));
188                     velocityContext.put(
189                         "line", new Integer(pe.getLineNumber()));
190                 }
191 
192                 String velocityTemplateId =
193                     PropsValues.JOURNAL_ERROR_TEMPLATE_VELOCITY;
194                 String velocityTemplateContent = ContentUtil.get(
195                     PropsValues.JOURNAL_ERROR_TEMPLATE_VELOCITY);
196 
197                 load = VelocityEngineUtil.mergeTemplate(
198                     velocityTemplateId, velocityTemplateContent,
199                     velocityContext, output);
200             }
201         }
202         catch (Exception e) {
203             if (e instanceof DocumentException) {
204                 throw new TransformException("Unable to read XML document", e);
205             }
206             else if (e instanceof VelocityException) {
207                 VelocityException pex = (VelocityException)e;
208 
209                 throw new TransformException(
210                     "Unable to parse velocity template: " +
211                         HtmlUtil.escape(pex.getMessage()),
212                     e);
213             }
214             else if (e instanceof IOException) {
215                 throw new TransformException(
216                     "Error reading velocity template", e);
217             }
218             else if (e instanceof TransformException) {
219                 throw (TransformException)e;
220             }
221             else {
222                 throw new TransformException("Unhandled exception", e);
223             }
224         }
225 
226         if (!load) {
227             throw new TransformException(
228                 "Unable to dynamically load velocity transform script");
229         }
230 
231         return output.toString();
232     }
233 
234     protected static String injectEditInPlace(String xml, String script)
235         throws DocumentException {
236 
237         Document doc = SAXReaderUtil.read(xml);
238 
239         List<Node> nodes = doc.selectNodes("//dynamic-element");
240 
241         for (Node node : nodes) {
242             Element el = (Element)node;
243 
244             String name = GetterUtil.getString(el.attributeValue("name"));
245             String type = GetterUtil.getString(el.attributeValue("type"));
246 
247             if ((!name.startsWith("reserved-")) &&
248                 (type.equals("text") || type.equals("text_box") ||
249                  type.equals("text_area"))) {
250 
251                 script = wrapField(script, name, type, "data");
252                 script = wrapField(script, name, type, "getData()");
253             }
254         }
255 
256         return script;
257     }
258 
259     protected static Map<String, Object> insertRequestVariables(
260         Element parent) {
261 
262         Map<String, Object> map = new HashMap<String, Object>();
263 
264         if (parent == null) {
265             return map;
266         }
267 
268         for (Element el : parent.elements()) {
269             String name = el.getName();
270 
271             if (name.equals("attribute")) {
272                 map.put(el.elementText("name"), el.elementText("value"));
273             }
274             else if (name.equals("parameter")) {
275                 name = el.element("name").getText();
276 
277                 List<Element> valueEls = el.elements("value");
278 
279                 if (valueEls.size() == 1) {
280                     map.put(name, (valueEls.get(0)).getText());
281                 }
282                 else {
283                     List<String> values = new ArrayList<String>();
284 
285                     for (Element valueEl : valueEls) {
286                         values.add(valueEl.getText());
287                     }
288 
289                     map.put(name, values);
290                 }
291             }
292             else if (el.elements().size() > 0) {
293                 map.put(name, insertRequestVariables(el));
294             }
295             else {
296                 map.put(name, el.getText());
297             }
298         }
299 
300         return map;
301     }
302 
303     protected static String wrapField(
304         String script, String name, String type, String call) {
305 
306         String field = "$" + name + "." + call;
307         String wrappedField =
308             "<span class=\"journal-content-eip-" + type + "\" " +
309                 "id=\"journal-content-field-name-" + name + "\">" + field +
310                     "</span>";
311 
312         return StringUtil.replace(
313             script, "$editInPlace(" + field + ")", wrappedField);
314     }
315 
316 }