001    /**
002     * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
013     */
014    
015    package com.liferay.portlet.journal.util;
016    
017    import com.liferay.portal.kernel.io.unsync.UnsyncStringWriter;
018    import com.liferay.portal.kernel.util.GetterUtil;
019    import com.liferay.portal.kernel.util.HtmlUtil;
020    import com.liferay.portal.kernel.util.LocaleUtil;
021    import com.liferay.portal.kernel.util.StringPool;
022    import com.liferay.portal.kernel.util.StringUtil;
023    import com.liferay.portal.kernel.velocity.VelocityContext;
024    import com.liferay.portal.kernel.velocity.VelocityEngineUtil;
025    import com.liferay.portal.kernel.xml.Document;
026    import com.liferay.portal.kernel.xml.DocumentException;
027    import com.liferay.portal.kernel.xml.Element;
028    import com.liferay.portal.kernel.xml.Node;
029    import com.liferay.portal.kernel.xml.SAXReaderUtil;
030    import com.liferay.portal.model.Company;
031    import com.liferay.portal.security.permission.PermissionThreadLocal;
032    import com.liferay.portal.service.CompanyLocalServiceUtil;
033    import com.liferay.portal.theme.ThemeDisplay;
034    import com.liferay.portal.util.ContentUtil;
035    import com.liferay.portal.util.PropsValues;
036    import com.liferay.portal.velocity.VelocityResourceListener;
037    import com.liferay.portlet.journal.TransformException;
038    import com.liferay.util.PwdGenerator;
039    import com.liferay.util.xml.CDATAUtil;
040    
041    import java.io.IOException;
042    
043    import java.util.ArrayList;
044    import java.util.HashMap;
045    import java.util.List;
046    import java.util.Map;
047    
048    import org.apache.velocity.exception.ParseErrorException;
049    import org.apache.velocity.exception.VelocityException;
050    
051    /**
052     * @author Alexander Chow
053     * @author Brian Wing Shun Chan
054     * @author Raymond Augé
055     */
056    public class VelocityTemplateParser extends BaseTemplateParser {
057    
058            protected String doTransform(
059                            ThemeDisplay themeDisplay, Map<String, String> tokens,
060                            String viewMode, String languageId,     String xml, String script)
061                    throws Exception {
062    
063                    UnsyncStringWriter unsyncStringWriter = new UnsyncStringWriter();
064    
065                    boolean load = false;
066    
067                    try {
068                            VelocityContext velocityContext =
069                                    VelocityEngineUtil.getWrappedRestrictedToolsContext();
070    
071                            Document doc = SAXReaderUtil.read(xml);
072    
073                            Element root = doc.getRootElement();
074    
075                            List<TemplateNode> nodes = extractDynamicContents(
076                                    themeDisplay, root);
077    
078                            for (TemplateNode node : nodes) {
079                                    velocityContext.put(node.getName(), node);
080                            }
081    
082                            velocityContext.put("xmlRequest", root.element("request").asXML());
083                            velocityContext.put(
084                                    "request", insertRequestVariables(root.element("request")));
085    
086                            long companyId = GetterUtil.getLong(tokens.get("company_id"));
087                            Company company = CompanyLocalServiceUtil.getCompanyById(companyId);
088                            long groupId = GetterUtil.getLong(tokens.get("group_id"));
089                            String templateId = tokens.get("template_id");
090                            String journalTemplatesPath =
091                                    VelocityResourceListener.JOURNAL_SEPARATOR + StringPool.SLASH +
092                                            companyId + StringPool.SLASH + groupId;
093                            String randomNamespace =
094                                    PwdGenerator.getPassword(PwdGenerator.KEY3, 4) +
095                                            StringPool.UNDERLINE;
096    
097                            velocityContext.put("company", company);
098                            velocityContext.put("companyId", String.valueOf(companyId));
099                            velocityContext.put("groupId", String.valueOf(groupId));
100                            velocityContext.put("journalTemplatesPath", journalTemplatesPath);
101                            velocityContext.put("viewMode", viewMode);
102                            velocityContext.put(
103                                    "locale", LocaleUtil.fromLanguageId(languageId));
104                            velocityContext.put(
105                                    "permissionChecker",
106                                    PermissionThreadLocal.getPermissionChecker());
107                            velocityContext.put("randomNamespace", randomNamespace);
108    
109                            script = injectEditInPlace(xml, script);
110    
111                            try {
112                                    String velocityTemplateId = companyId + groupId + templateId;
113    
114                                    load = VelocityEngineUtil.mergeTemplate(
115                                            velocityTemplateId, script, velocityContext,
116                                            unsyncStringWriter);
117                            }
118                            catch (VelocityException ve) {
119                                    velocityContext.put("exception", ve.getMessage());
120                                    velocityContext.put("script", script);
121    
122                                    if (ve instanceof ParseErrorException) {
123                                            ParseErrorException pe = (ParseErrorException)ve;
124    
125                                            velocityContext.put(
126                                                    "column", new Integer(pe.getColumnNumber()));
127                                            velocityContext.put(
128                                                    "line", new Integer(pe.getLineNumber()));
129                                    }
130    
131                                    String velocityTemplateId =
132                                            PropsValues.JOURNAL_ERROR_TEMPLATE_VELOCITY;
133                                    String velocityTemplateContent = ContentUtil.get(
134                                            PropsValues.JOURNAL_ERROR_TEMPLATE_VELOCITY);
135    
136                                    load = VelocityEngineUtil.mergeTemplate(
137                                            velocityTemplateId, velocityTemplateContent,
138                                            velocityContext, unsyncStringWriter);
139                            }
140                    }
141                    catch (Exception e) {
142                            if (e instanceof DocumentException) {
143                                    throw new TransformException("Unable to read XML document", e);
144                            }
145                            else if (e instanceof VelocityException) {
146                                    VelocityException pex = (VelocityException)e;
147    
148                                    throw new TransformException(
149                                            "Unable to parse velocity template: " +
150                                                    HtmlUtil.escape(pex.getMessage()),
151                                            e);
152                            }
153                            else if (e instanceof IOException) {
154                                    throw new TransformException(
155                                            "Error reading velocity template", e);
156                            }
157                            else if (e instanceof TransformException) {
158                                    throw (TransformException)e;
159                            }
160                            else {
161                                    throw new TransformException("Unhandled exception", e);
162                            }
163                    }
164    
165                    if (!load) {
166                            throw new TransformException(
167                                    "Unable to dynamically load velocity transform script");
168                    }
169    
170                    return unsyncStringWriter.toString();
171            }
172    
173            protected List<TemplateNode> extractDynamicContents(
174                            ThemeDisplay themeDisplay, Element parent)
175                    throws TransformException {
176    
177                    List<TemplateNode> nodes = new ArrayList<TemplateNode>();
178    
179                    Map<String, TemplateNode> prototypeNodes =
180                            new HashMap<String, TemplateNode>();
181    
182                    for (Element el : parent.elements("dynamic-element")) {
183                            Element content = el.element("dynamic-content");
184    
185                            String data = StringPool.BLANK;
186    
187                            if (content != null) {
188                                    data = content.getText();
189                            }
190    
191                            String name = el.attributeValue("name", "");
192    
193                            if (name.length() == 0) {
194                                    throw new TransformException(
195                                            "Element missing \"name\" attribute");
196                            }
197    
198                            String type = el.attributeValue("type", "");
199    
200                            TemplateNode node = new TemplateNode(
201                                    themeDisplay, name, CDATAUtil.strip(data), type);
202    
203                            if (el.element("dynamic-element") != null) {
204                                    node.appendChildren(extractDynamicContents(themeDisplay, el));
205                            }
206                            else if ((content != null) && (content.element("option") != null)) {
207                                    for (Element option : content.elements("option")) {
208                                            node.appendOption(CDATAUtil.strip(option.getText()));
209                                    }
210                            }
211    
212                            TemplateNode prototypeNode = prototypeNodes.get(name);
213    
214                            if (prototypeNode == null) {
215                                    prototypeNode = node;
216    
217                                    prototypeNodes.put(name, prototypeNode);
218    
219                                    nodes.add(node);
220                            }
221    
222                            prototypeNode.appendSibling(node);
223                    }
224    
225                    return nodes;
226            }
227    
228            protected String injectEditInPlace(String xml, String script)
229                    throws DocumentException {
230    
231                    Document doc = SAXReaderUtil.read(xml);
232    
233                    List<Node> nodes = doc.selectNodes("//dynamic-element");
234    
235                    for (Node node : nodes) {
236                            Element el = (Element)node;
237    
238                            String name = GetterUtil.getString(el.attributeValue("name"));
239                            String type = GetterUtil.getString(el.attributeValue("type"));
240    
241                            if ((!name.startsWith("reserved-")) &&
242                                    (type.equals("text") || type.equals("text_box") ||
243                                     type.equals("text_area"))) {
244    
245                                    script = wrapField(script, name, type, "data");
246                                    script = wrapField(script, name, type, "getData()");
247                            }
248                    }
249    
250                    return script;
251            }
252    
253            protected Map<String, Object> insertRequestVariables(Element parent) {
254                    Map<String, Object> map = new HashMap<String, Object>();
255    
256                    if (parent == null) {
257                            return map;
258                    }
259    
260                    for (Element el : parent.elements()) {
261                            String name = el.getName();
262    
263                            if (name.equals("attribute")) {
264                                    map.put(el.elementText("name"), el.elementText("value"));
265                            }
266                            else if (name.equals("parameter")) {
267                                    name = el.element("name").getText();
268    
269                                    List<Element> valueEls = el.elements("value");
270    
271                                    if (valueEls.size() == 1) {
272                                            map.put(name, (valueEls.get(0)).getText());
273                                    }
274                                    else {
275                                            List<String> values = new ArrayList<String>();
276    
277                                            for (Element valueEl : valueEls) {
278                                                    values.add(valueEl.getText());
279                                            }
280    
281                                            map.put(name, values);
282                                    }
283                            }
284                            else if (el.elements().size() > 0) {
285                                    map.put(name, insertRequestVariables(el));
286                            }
287                            else {
288                                    map.put(name, el.getText());
289                            }
290                    }
291    
292                    return map;
293            }
294    
295            protected String wrapField(
296                    String script, String name, String type, String call) {
297    
298                    String field = "$" + name + "." + call;
299                    String wrappedField =
300                            "<span class=\"journal-content-eip-" + type + "\" " +
301                                    "id=\"journal-content-field-name-" + name + "\">" + field +
302                                            "</span>";
303    
304                    return StringUtil.replace(
305                            script, "$editInPlace(" + field + ")", wrappedField);
306            }
307    
308    }