1   /**
2    * Copyright (c) 2000-2009 Liferay, Inc. All rights reserved.
3    *
4    *
5    *
6    *
7    * The contents of this file are subject to the terms of the Liferay Enterprise
8    * Subscription License ("License"). You may not use this file except in
9    * compliance with the License. You can obtain a copy of the License by
10   * contacting Liferay, Inc. See the License for the specific language governing
11   * permissions and limitations under the License, including but not limited to
12   * distribution rights 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  public class JournalVmUtil {
66  
67      public static List<TemplateNode> extractDynamicContents(Element parent)
68          throws TransformException {
69  
70          List<TemplateNode> nodes = new ArrayList<TemplateNode>();
71  
72          Map<String, TemplateNode> prototypeNodes =
73              new HashMap<String, TemplateNode>();
74  
75          for (Element el : parent.elements("dynamic-element")) {
76              Element content = el.element("dynamic-content");
77  
78              if (content == null) {
79                  throw new TransformException(
80                      "Element missing \"dynamic-content\"");
81              }
82  
83              String name = el.attributeValue("name", "");
84  
85              if (name.length() == 0) {
86                  throw new TransformException(
87                      "Element missing \"name\" attribute");
88              }
89  
90              String type = el.attributeValue("type", "");
91  
92              TemplateNode node = new TemplateNode(
93                  name, CDATAUtil.strip(content.getText()), type);
94  
95              if (el.element("dynamic-element") != null) {
96                  node.appendChildren(extractDynamicContents(el));
97              }
98              else if (content.element("option") != null) {
99                  for (Element option : content.elements("option")) {
100                     node.appendOption(CDATAUtil.strip(option.getText()));
101                 }
102             }
103 
104             TemplateNode prototypeNode = prototypeNodes.get(name);
105 
106             if (prototypeNode == null) {
107                 prototypeNode = node;
108 
109                 prototypeNodes.put(name, prototypeNode);
110 
111                 nodes.add(node);
112             }
113 
114             prototypeNode.appendSibling(node);
115         }
116 
117         return nodes;
118     }
119 
120     public static String transform(
121             Map<String, String> tokens, String viewMode, String languageId,
122             String xml, String script)
123         throws TransformException {
124 
125         StringWriter output = new StringWriter();
126 
127         boolean load = false;
128 
129         try {
130             VelocityContext velocityContext =
131                 VelocityEngineUtil.getWrappedRestrictedToolsContext();
132 
133             Document doc = SAXReaderUtil.read(xml);
134 
135             Element root = doc.getRootElement();
136 
137             List<TemplateNode> nodes = extractDynamicContents(root);
138 
139             for (TemplateNode node : nodes) {
140                 velocityContext.put(node.getName(), node);
141             }
142 
143             velocityContext.put("xmlRequest", root.element("request").asXML());
144             velocityContext.put(
145                 "request", insertRequestVariables(root.element("request")));
146 
147             long companyId = GetterUtil.getLong(tokens.get("company_id"));
148             Company company = CompanyLocalServiceUtil.getCompanyById(companyId);
149             long groupId = GetterUtil.getLong(tokens.get("group_id"));
150             String templateId = tokens.get("template_id");
151             String journalTemplatesPath =
152                 VelocityResourceListener.JOURNAL_SEPARATOR + StringPool.SLASH +
153                     companyId + StringPool.SLASH + groupId;
154             String randomNamespace =
155                 PwdGenerator.getPassword(PwdGenerator.KEY3, 4) +
156                     StringPool.UNDERLINE;
157 
158             velocityContext.put("company", company);
159             velocityContext.put("companyId", String.valueOf(companyId));
160             velocityContext.put("groupId", String.valueOf(groupId));
161             velocityContext.put("journalTemplatesPath", journalTemplatesPath);
162             velocityContext.put("viewMode", viewMode);
163             velocityContext.put(
164                 "locale", LocaleUtil.fromLanguageId(languageId));
165             velocityContext.put(
166                 "permissionChecker",
167                 PermissionThreadLocal.getPermissionChecker());
168             velocityContext.put("randomNamespace", randomNamespace);
169 
170             script = injectEditInPlace(xml, script);
171 
172             try {
173                 String velocityTemplateId = companyId + groupId + templateId;
174 
175                 load = VelocityEngineUtil.mergeTemplate(
176                     velocityTemplateId, script, velocityContext, output);
177             }
178             catch (VelocityException ve) {
179                 velocityContext.put("exception", ve.getMessage());
180                 velocityContext.put("script", script);
181 
182                 if (ve instanceof ParseErrorException) {
183                     ParseErrorException pe = (ParseErrorException)ve;
184 
185                     velocityContext.put(
186                         "column", new Integer(pe.getColumnNumber()));
187                     velocityContext.put(
188                         "line", new Integer(pe.getLineNumber()));
189                 }
190 
191                 String velocityTemplateId =
192                     PropsValues.JOURNAL_ERROR_TEMPLATE_VELOCITY;
193                 String velocityTemplateContent = ContentUtil.get(
194                     PropsValues.JOURNAL_ERROR_TEMPLATE_VELOCITY);
195 
196                 load = VelocityEngineUtil.mergeTemplate(
197                     velocityTemplateId, velocityTemplateContent,
198                     velocityContext, output);
199             }
200         }
201         catch (Exception e) {
202             if (e instanceof DocumentException) {
203                 throw new TransformException("Unable to read XML document", e);
204             }
205             else if (e instanceof VelocityException) {
206                 VelocityException pex = (VelocityException)e;
207 
208                 throw new TransformException(
209                     "Unable to parse velocity template: " +
210                         HtmlUtil.escape(pex.getMessage()),
211                     e);
212             }
213             else if (e instanceof IOException) {
214                 throw new TransformException(
215                     "Error reading velocity template", e);
216             }
217             else if (e instanceof TransformException) {
218                 throw (TransformException)e;
219             }
220             else {
221                 throw new TransformException("Unhandled exception", e);
222             }
223         }
224 
225         if (!load) {
226             throw new TransformException(
227                 "Unable to dynamically load velocity transform script");
228         }
229 
230         return output.toString();
231     }
232 
233     protected static String injectEditInPlace(String xml, String script)
234         throws DocumentException {
235 
236         Document doc = SAXReaderUtil.read(xml);
237 
238         List<Node> nodes = doc.selectNodes("//dynamic-element");
239 
240         for (Node node : nodes) {
241             Element el = (Element)node;
242 
243             String name = GetterUtil.getString(el.attributeValue("name"));
244             String type = GetterUtil.getString(el.attributeValue("type"));
245 
246             if ((!name.startsWith("reserved-")) &&
247                 (type.equals("text") || type.equals("text_box") ||
248                  type.equals("text_area"))) {
249 
250                 script = wrapField(script, name, type, "data");
251                 script = wrapField(script, name, type, "getData()");
252             }
253         }
254 
255         return script;
256     }
257 
258     protected static Map<String, Object> insertRequestVariables(
259         Element parent) {
260 
261         Map<String, Object> map = new HashMap<String, Object>();
262 
263         if (parent == null) {
264             return map;
265         }
266 
267         for (Element el : parent.elements()) {
268             String name = el.getName();
269 
270             if (name.equals("attribute")) {
271                 map.put(el.elementText("name"), el.elementText("value"));
272             }
273             else if (name.equals("parameter")) {
274                 name = el.element("name").getText();
275 
276                 List<Element> valueEls = el.elements("value");
277 
278                 if (valueEls.size() == 1) {
279                     map.put(name, (valueEls.get(0)).getText());
280                 }
281                 else {
282                     List<String> values = new ArrayList<String>();
283 
284                     for (Element valueEl : valueEls) {
285                         values.add(valueEl.getText());
286                     }
287 
288                     map.put(name, values);
289                 }
290             }
291             else if (el.elements().size() > 0) {
292                 map.put(name, insertRequestVariables(el));
293             }
294             else {
295                 map.put(name, el.getText());
296             }
297         }
298 
299         return map;
300     }
301 
302     protected static String wrapField(
303         String script, String name, String type, String call) {
304 
305         String field = "$" + name + "." + call;
306         String wrappedField =
307             "<span class=\"journal-content-eip-" + type + "\" " +
308                 "id=\"journal-content-field-name-" + name + "\">" + field +
309                     "</span>";
310 
311         return StringUtil.replace(
312             script, "$editInPlace(" + field + ")", wrappedField);
313     }
314 
315 }