1   /**
2    * Copyright (c) 2000-2008 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.xml.Document;
31  import com.liferay.portal.kernel.xml.DocumentException;
32  import com.liferay.portal.kernel.xml.Element;
33  import com.liferay.portal.kernel.xml.Node;
34  import com.liferay.portal.kernel.xml.SAXReaderUtil;
35  import com.liferay.portal.model.Company;
36  import com.liferay.portal.service.CompanyLocalServiceUtil;
37  import com.liferay.portal.util.ContentUtil;
38  import com.liferay.portal.util.PropsKeys;
39  import com.liferay.portal.util.PropsUtil;
40  import com.liferay.portal.util.PropsValues;
41  import com.liferay.portal.velocity.VelocityResourceListener;
42  import com.liferay.portal.velocity.VelocityVariables;
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.VelocityContext;
56  import org.apache.velocity.app.Velocity;
57  import org.apache.velocity.exception.ParseErrorException;
58  import org.apache.velocity.exception.VelocityException;
59  
60  /**
61   * <a href="JournalVmUtil.java.html"><b><i>View Source</i></b></a>
62   *
63   * @author Alexander Chow
64   * @author Brian Wing Shun Chan
65   * @author Raymond Augé
66   *
67   */
68  public class JournalVmUtil {
69  
70      public static final String[] _TEMPLATE_VELOCITY_RESTRICTED_VARIABLES =
71          PropsUtil.getArray(
72              PropsKeys.JOURNAL_TEMPLATE_VELOCITY_RESTRICTED_VARIABLES);
73  
74      public static String transform(
75              Map<String, String> tokens, String languageId, String xml,
76              String script)
77          throws TransformException {
78  
79          StringWriter output = new StringWriter();
80  
81          boolean load = false;
82  
83          try {
84              VelocityContext context = new VelocityContext();
85  
86              Document doc = SAXReaderUtil.read(xml);
87  
88              Element root = doc.getRootElement();
89  
90              List<TemplateNode> nodes = _extractDynamicContents(root);
91  
92              for (TemplateNode node : nodes) {
93                  context.put(node.getName(), node);
94              }
95  
96              context.put("xmlRequest", root.element("request").asXML());
97              context.put(
98                  "request", _insertRequestVariables(root.element("request")));
99  
100             long companyId = GetterUtil.getLong(tokens.get("company_id"));
101             Company company = CompanyLocalServiceUtil.getCompanyById(companyId);
102             long groupId = GetterUtil.getLong(tokens.get("group_id"));
103             String journalTemplatesPath =
104                 VelocityResourceListener.JOURNAL_SEPARATOR + StringPool.SLASH +
105                     companyId + StringPool.SLASH + groupId;
106             String randomNamespace =
107                 PwdGenerator.getPassword(PwdGenerator.KEY3, 4) +
108                     StringPool.UNDERLINE;
109 
110             context.put("company", company);
111             context.put("companyId", String.valueOf(companyId));
112             context.put("groupId", String.valueOf(groupId));
113             context.put("journalTemplatesPath", journalTemplatesPath);
114             context.put("locale", LocaleUtil.fromLanguageId(languageId));
115             context.put("randomNamespace", randomNamespace);
116 
117             VelocityVariables.insertHelperUtilities(
118                 context, _TEMPLATE_VELOCITY_RESTRICTED_VARIABLES);
119 
120             script = _injectEditInPlace(xml, script);
121 
122             try {
123                 load = Velocity.evaluate(
124                     context, output, JournalVmUtil.class.getName(), script);
125             }
126             catch (VelocityException ve) {
127                 context.put("exception", ve.getMessage());
128                 context.put("script", script);
129 
130                 if (ve instanceof ParseErrorException) {
131                     ParseErrorException pe = (ParseErrorException)ve;
132 
133                     context.put("column", new Integer(pe.getColumnNumber()));
134                     context.put("line", new Integer(pe.getLineNumber()));
135                 }
136 
137                 String errorTemplate = ContentUtil.get(
138                     PropsValues.JOURNAL_ERROR_TEMPLATE_VELOCITY);
139 
140                 load = Velocity.evaluate(
141                     context, output, JournalVmUtil.class.getName(),
142                     errorTemplate);
143             }
144         }
145         catch (Exception e) {
146             if (e instanceof DocumentException) {
147                 throw new TransformException("Unable to read XML document", e);
148             }
149             else if (e instanceof VelocityException) {
150                 VelocityException pex = (VelocityException)e;
151 
152                 throw new TransformException(
153                     "Unable to parse velocity template: " +
154                         HtmlUtil.escape(pex.getMessage()),
155                     e);
156             }
157             else if (e instanceof IOException) {
158                 throw new TransformException(
159                     "Error reading velocity template", e);
160             }
161             else if (e instanceof TransformException) {
162                 throw (TransformException)e;
163             }
164             else {
165                 throw new TransformException("Unhandled exception", e);
166             }
167         }
168 
169         if (!load) {
170             throw new TransformException(
171                 "Unable to dynamically load velocity transform script");
172         }
173 
174         return output.toString();
175     }
176 
177     private static List<TemplateNode> _extractDynamicContents(Element parent)
178         throws TransformException {
179 
180         List<TemplateNode> nodes = new ArrayList<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                 name, CDATAUtil.strip(content.getText()), type);
201 
202             if (el.element("dynamic-element") != null) {
203                 node.appendChildren(_extractDynamicContents(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             nodes.add(node);
212         }
213 
214         return nodes;
215     }
216 
217     private static String _injectEditInPlace(String xml, String script)
218         throws DocumentException {
219 
220         Document doc = SAXReaderUtil.read(xml);
221 
222         List<Node> nodes = doc.selectNodes("//dynamic-element");
223 
224         for (Node node : nodes) {
225             Element el = (Element)node;
226 
227             String name = GetterUtil.getString(el.attributeValue("name"));
228             String type = GetterUtil.getString(el.attributeValue("type"));
229 
230             if ((!name.startsWith("reserved-")) &&
231                 (type.equals("text") || type.equals("text_box") ||
232                  type.equals("text_area"))) {
233 
234                 script = _wrapField(script, name, type, "data");
235                 script = _wrapField(script, name, type, "getData()");
236             }
237         }
238 
239         return script;
240     }
241 
242     private static Map<String, Object> _insertRequestVariables(
243         Element parent) {
244 
245         Map<String, Object> map = new HashMap<String, Object>();
246 
247         if (parent == null) {
248             return map;
249         }
250 
251         for (Element el : parent.elements()) {
252             String name = el.getName();
253 
254             if (name.equals("attribute")) {
255                 map.put(el.elementText("name"), el.elementText("value"));
256             }
257             else if (name.equals("parameter")) {
258                 name = el.element("name").getText();
259 
260                 List<Element> valueEls = el.elements("value");
261 
262                 if (valueEls.size() == 1) {
263                     map.put(name, (valueEls.get(0)).getText());
264                 }
265                 else {
266                     List<String> values = new ArrayList<String>();
267 
268                     for (Element valueEl : valueEls) {
269                         values.add(valueEl.getText());
270                     }
271 
272                     map.put(name, values);
273                 }
274             }
275             else if (el.elements().size() > 0) {
276                 map.put(name, _insertRequestVariables(el));
277             }
278             else {
279                 map.put(name, el.getText());
280             }
281         }
282 
283         return map;
284     }
285 
286     private static String _wrapField(
287         String script, String name, String type, String call) {
288 
289         String field = "$" + name + "." + call;
290         String wrappedField =
291             "<span class=\"journal-content-eip-" + type + "\" " +
292                 "id=\"journal-content-field-name-" + name + "\">" + field +
293                     "</span>";
294 
295         return StringUtil.replace(
296             script, "$editInPlace(" + field + ")", wrappedField);
297     }
298 
299 }