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