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