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.io.unsync.UnsyncStringWriter;
26  import com.liferay.portal.kernel.util.GetterUtil;
27  import com.liferay.portal.kernel.util.HtmlUtil;
28  import com.liferay.portal.kernel.util.LocaleUtil;
29  import com.liferay.portal.kernel.util.StringPool;
30  import com.liferay.portal.kernel.util.StringUtil;
31  import com.liferay.portal.kernel.velocity.VelocityContext;
32  import com.liferay.portal.kernel.velocity.VelocityEngineUtil;
33  import com.liferay.portal.kernel.xml.Document;
34  import com.liferay.portal.kernel.xml.DocumentException;
35  import com.liferay.portal.kernel.xml.Element;
36  import com.liferay.portal.kernel.xml.Node;
37  import com.liferay.portal.kernel.xml.SAXReaderUtil;
38  import com.liferay.portal.model.Company;
39  import com.liferay.portal.security.permission.PermissionThreadLocal;
40  import com.liferay.portal.service.CompanyLocalServiceUtil;
41  import com.liferay.portal.util.ContentUtil;
42  import com.liferay.portal.util.PropsValues;
43  import com.liferay.portal.velocity.VelocityResourceListener;
44  import com.liferay.portlet.journal.TransformException;
45  import com.liferay.util.PwdGenerator;
46  import com.liferay.util.xml.CDATAUtil;
47  
48  import java.io.IOException;
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="VelocityTemplateParser.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 VelocityTemplateParser extends BaseTemplateParser {
66  
67      protected String doTransform(
68              Map<String, String> tokens, String viewMode, String languageId,
69              String xml, String script)
70          throws Exception {
71  
72          UnsyncStringWriter unsyncStringWriter = new UnsyncStringWriter(true);
73  
74          boolean load = false;
75  
76          try {
77              VelocityContext velocityContext =
78                  VelocityEngineUtil.getWrappedRestrictedToolsContext();
79  
80              Document doc = SAXReaderUtil.read(xml);
81  
82              Element root = doc.getRootElement();
83  
84              List<TemplateNode> nodes = extractDynamicContents(root);
85  
86              for (TemplateNode node : nodes) {
87                  velocityContext.put(node.getName(), node);
88              }
89  
90              velocityContext.put("xmlRequest", root.element("request").asXML());
91              velocityContext.put(
92                  "request", insertRequestVariables(root.element("request")));
93  
94              long companyId = GetterUtil.getLong(tokens.get("company_id"));
95              Company company = CompanyLocalServiceUtil.getCompanyById(companyId);
96              long groupId = GetterUtil.getLong(tokens.get("group_id"));
97              String templateId = tokens.get("template_id");
98              String journalTemplatesPath =
99                  VelocityResourceListener.JOURNAL_SEPARATOR + StringPool.SLASH +
100                     companyId + StringPool.SLASH + groupId;
101             String randomNamespace =
102                 PwdGenerator.getPassword(PwdGenerator.KEY3, 4) +
103                     StringPool.UNDERLINE;
104 
105             velocityContext.put("company", company);
106             velocityContext.put("companyId", String.valueOf(companyId));
107             velocityContext.put("groupId", String.valueOf(groupId));
108             velocityContext.put("journalTemplatesPath", journalTemplatesPath);
109             velocityContext.put("viewMode", viewMode);
110             velocityContext.put(
111                 "locale", LocaleUtil.fromLanguageId(languageId));
112             velocityContext.put(
113                 "permissionChecker",
114                 PermissionThreadLocal.getPermissionChecker());
115             velocityContext.put("randomNamespace", randomNamespace);
116 
117             script = injectEditInPlace(xml, script);
118 
119             try {
120                 String velocityTemplateId = companyId + groupId + templateId;
121 
122                 load = VelocityEngineUtil.mergeTemplate(
123                     velocityTemplateId, script, velocityContext,
124                     unsyncStringWriter);
125             }
126             catch (VelocityException ve) {
127                 velocityContext.put("exception", ve.getMessage());
128                 velocityContext.put("script", script);
129 
130                 if (ve instanceof ParseErrorException) {
131                     ParseErrorException pe = (ParseErrorException)ve;
132 
133                     velocityContext.put(
134                         "column", new Integer(pe.getColumnNumber()));
135                     velocityContext.put(
136                         "line", new Integer(pe.getLineNumber()));
137                 }
138 
139                 String velocityTemplateId =
140                     PropsValues.JOURNAL_ERROR_TEMPLATE_VELOCITY;
141                 String velocityTemplateContent = ContentUtil.get(
142                     PropsValues.JOURNAL_ERROR_TEMPLATE_VELOCITY);
143 
144                 load = VelocityEngineUtil.mergeTemplate(
145                     velocityTemplateId, velocityTemplateContent,
146                     velocityContext, unsyncStringWriter);
147             }
148         }
149         catch (Exception e) {
150             if (e instanceof DocumentException) {
151                 throw new TransformException("Unable to read XML document", e);
152             }
153             else if (e instanceof VelocityException) {
154                 VelocityException pex = (VelocityException)e;
155 
156                 throw new TransformException(
157                     "Unable to parse velocity template: " +
158                         HtmlUtil.escape(pex.getMessage()),
159                     e);
160             }
161             else if (e instanceof IOException) {
162                 throw new TransformException(
163                     "Error reading velocity template", e);
164             }
165             else if (e instanceof TransformException) {
166                 throw (TransformException)e;
167             }
168             else {
169                 throw new TransformException("Unhandled exception", e);
170             }
171         }
172 
173         if (!load) {
174             throw new TransformException(
175                 "Unable to dynamically load velocity transform script");
176         }
177 
178         return unsyncStringWriter.toString();
179     }
180 
181     protected List<TemplateNode> extractDynamicContents(Element parent)
182         throws TransformException {
183 
184         List<TemplateNode> nodes = new ArrayList<TemplateNode>();
185 
186         Map<String, TemplateNode> prototypeNodes =
187             new HashMap<String, TemplateNode>();
188 
189         for (Element el : parent.elements("dynamic-element")) {
190             Element content = el.element("dynamic-content");
191 
192             if (content == null) {
193                 throw new TransformException(
194                     "Element missing \"dynamic-content\"");
195             }
196 
197             String name = el.attributeValue("name", "");
198 
199             if (name.length() == 0) {
200                 throw new TransformException(
201                     "Element missing \"name\" attribute");
202             }
203 
204             String type = el.attributeValue("type", "");
205 
206             TemplateNode node = new TemplateNode(
207                 name, CDATAUtil.strip(content.getText()), type);
208 
209             if (el.element("dynamic-element") != null) {
210                 node.appendChildren(extractDynamicContents(el));
211             }
212             else if (content.element("option") != null) {
213                 for (Element option : content.elements("option")) {
214                     node.appendOption(CDATAUtil.strip(option.getText()));
215                 }
216             }
217 
218             TemplateNode prototypeNode = prototypeNodes.get(name);
219 
220             if (prototypeNode == null) {
221                 prototypeNode = node;
222 
223                 prototypeNodes.put(name, prototypeNode);
224 
225                 nodes.add(node);
226             }
227 
228             prototypeNode.appendSibling(node);
229         }
230 
231         return nodes;
232     }
233 
234     protected 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 Map<String, Object> insertRequestVariables(Element parent) {
260         Map<String, Object> map = new HashMap<String, Object>();
261 
262         if (parent == null) {
263             return map;
264         }
265 
266         for (Element el : parent.elements()) {
267             String name = el.getName();
268 
269             if (name.equals("attribute")) {
270                 map.put(el.elementText("name"), el.elementText("value"));
271             }
272             else if (name.equals("parameter")) {
273                 name = el.element("name").getText();
274 
275                 List<Element> valueEls = el.elements("value");
276 
277                 if (valueEls.size() == 1) {
278                     map.put(name, (valueEls.get(0)).getText());
279                 }
280                 else {
281                     List<String> values = new ArrayList<String>();
282 
283                     for (Element valueEl : valueEls) {
284                         values.add(valueEl.getText());
285                     }
286 
287                     map.put(name, values);
288                 }
289             }
290             else if (el.elements().size() > 0) {
291                 map.put(name, insertRequestVariables(el));
292             }
293             else {
294                 map.put(name, el.getText());
295             }
296         }
297 
298         return map;
299     }
300 
301     protected String wrapField(
302         String script, String name, String type, String call) {
303 
304         String field = "$" + name + "." + call;
305         String wrappedField =
306             "<span class=\"journal-content-eip-" + type + "\" " +
307                 "id=\"journal-content-field-name-" + name + "\">" + field +
308                     "</span>";
309 
310         return StringUtil.replace(
311             script, "$editInPlace(" + field + ")", wrappedField);
312     }
313 
314 }