001    /**
002     * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portlet.dynamicdatamapping.util;
016    
017    import com.liferay.portal.kernel.freemarker.FreeMarkerContext;
018    import com.liferay.portal.kernel.freemarker.FreeMarkerEngineUtil;
019    import com.liferay.portal.kernel.freemarker.FreeMarkerVariablesUtil;
020    import com.liferay.portal.kernel.io.unsync.UnsyncStringWriter;
021    import com.liferay.portal.kernel.json.JSONArray;
022    import com.liferay.portal.kernel.json.JSONException;
023    import com.liferay.portal.kernel.json.JSONFactoryUtil;
024    import com.liferay.portal.kernel.json.JSONObject;
025    import com.liferay.portal.kernel.util.ArrayUtil;
026    import com.liferay.portal.kernel.util.CharPool;
027    import com.liferay.portal.kernel.util.GetterUtil;
028    import com.liferay.portal.kernel.util.LocaleUtil;
029    import com.liferay.portal.kernel.util.LocalizationUtil;
030    import com.liferay.portal.kernel.util.StringBundler;
031    import com.liferay.portal.kernel.util.StringPool;
032    import com.liferay.portal.kernel.util.StringUtil;
033    import com.liferay.portal.kernel.util.Validator;
034    import com.liferay.portal.kernel.xml.Attribute;
035    import com.liferay.portal.kernel.xml.Document;
036    import com.liferay.portal.kernel.xml.DocumentException;
037    import com.liferay.portal.kernel.xml.Element;
038    import com.liferay.portal.kernel.xml.SAXReaderUtil;
039    import com.liferay.portal.util.PortalUtil;
040    import com.liferay.portlet.dynamicdatamapping.model.DDMStructure;
041    import com.liferay.portlet.dynamicdatamapping.model.DDMTemplate;
042    import com.liferay.portlet.dynamicdatamapping.model.DDMTemplateConstants;
043    import com.liferay.portlet.dynamicdatamapping.storage.Fields;
044    import com.liferay.util.freemarker.FreeMarkerTaglibFactoryUtil;
045    
046    import freemarker.ext.servlet.HttpRequestHashModel;
047    import freemarker.ext.servlet.ServletContextHashModel;
048    
049    import freemarker.template.ObjectWrapper;
050    import freemarker.template.TemplateHashModel;
051    
052    import java.io.IOException;
053    import java.io.Writer;
054    
055    import java.util.HashMap;
056    import java.util.List;
057    import java.util.Locale;
058    import java.util.Map;
059    
060    import javax.servlet.GenericServlet;
061    import javax.servlet.Servlet;
062    import javax.servlet.ServletException;
063    import javax.servlet.ServletRequest;
064    import javax.servlet.ServletResponse;
065    import javax.servlet.http.HttpServletRequest;
066    import javax.servlet.http.HttpServletResponse;
067    import javax.servlet.jsp.PageContext;
068    
069    /**
070     * @author Bruno Basto
071     * @author Eduardo Lundgren
072     * @author Brian Wing Shun Chan
073     */
074    public class DDMXSDImpl implements DDMXSD {
075    
076            public String getHTML(
077                            PageContext pageContext, DDMStructure ddmStructure, Fields fields,
078                            String namespace, boolean readOnly, Locale locale)
079                    throws Exception {
080    
081                    return getHTML(
082                            pageContext, ddmStructure.getXsd(), fields, namespace, readOnly,
083                            locale);
084            }
085    
086            public String getHTML(
087                            PageContext pageContext, DDMTemplate ddmTemplate, Fields fields,
088                            String namespace, boolean readOnly, Locale locale)
089                    throws Exception {
090    
091                    return getHTML(
092                            pageContext, ddmTemplate.getScript(), fields, namespace,
093                            ddmTemplate.getMode(), readOnly, locale);
094            }
095    
096            public String getHTML(
097                            PageContext pageContext, Element element, Fields fields,
098                            Locale locale)
099                    throws Exception {
100    
101                    return getHTML(
102                            pageContext, element, fields, StringPool.BLANK, null, false,
103                            locale);
104            }
105    
106            public String getHTML(
107                            PageContext pageContext, Element element, Fields fields,
108                            String namespace, String mode, boolean readOnly, Locale locale)
109                    throws Exception {
110    
111                    StringBundler sb = new StringBundler();
112    
113                    HttpServletRequest request =
114                            (HttpServletRequest)pageContext.getRequest();
115    
116                    String portletId = PortalUtil.getPortletId(request);
117    
118                    String portletNamespace = PortalUtil.getPortletNamespace(portletId);
119    
120                    List<Element> dynamicElementElements = element.elements(
121                            "dynamic-element");
122    
123                    for (Element dynamicElementElement : dynamicElementElements) {
124                            FreeMarkerContext freeMarkerContext = getFreeMarkerContext(
125                                    dynamicElementElement, locale);
126    
127                            freeMarkerContext.put("portletNamespace", portletNamespace);
128                            freeMarkerContext.put("namespace", namespace);
129    
130                            if (fields != null) {
131                                    freeMarkerContext.put("fields", fields);
132                            }
133    
134                            Map<String, Object> field =
135                                    (Map<String, Object>)freeMarkerContext.get("fieldStructure");
136    
137                            String childrenHTML = getHTML(
138                                    pageContext, dynamicElementElement, fields, namespace, mode,
139                                    readOnly, locale);
140    
141                            field.put("children", childrenHTML);
142    
143                            String fieldNamespace = dynamicElementElement.attributeValue(
144                                    "fieldNamespace", _DEFAULT_NAMESPACE);
145    
146                            String defaultResourcePath = _TPL_DEFAULT_PATH;
147    
148                            boolean fieldReadOnly = GetterUtil.getBoolean(
149                                    field.get("readOnly"));
150    
151                            if ((fieldReadOnly && Validator.isNotNull(mode) &&
152                                     mode.equalsIgnoreCase(
153                                            DDMTemplateConstants.TEMPLATE_MODE_EDIT)) || readOnly) {
154    
155                                    fieldNamespace = _DEFAULT_READ_ONLY_NAMESPACE;
156    
157                                    defaultResourcePath = _TPL_DEFAULT_READ_ONLY_PATH;
158                            }
159    
160                            String type = dynamicElementElement.attributeValue("type");
161    
162                            String templateName = StringUtil.replaceFirst(
163                                    type, fieldNamespace.concat(StringPool.DASH), StringPool.BLANK);
164    
165                            StringBundler resourcePath = new StringBundler(5);
166    
167                            resourcePath.append(_TPL_PATH);
168                            resourcePath.append(fieldNamespace.toLowerCase());
169                            resourcePath.append(CharPool.SLASH);
170                            resourcePath.append(templateName);
171                            resourcePath.append(_TPL_EXT);
172    
173                            sb.append(
174                                    processFTL(
175                                            pageContext, freeMarkerContext, resourcePath.toString(),
176                                            defaultResourcePath));
177                    }
178    
179                    return sb.toString();
180            }
181    
182            public String getHTML(
183                            PageContext pageContext, Element element, Locale locale)
184                    throws Exception {
185    
186                    return getHTML(pageContext, element, null, locale);
187            }
188    
189            public String getHTML(
190                            PageContext pageContext, String xml, Fields fields, Locale locale)
191                    throws Exception {
192    
193                    return getHTML(pageContext, xml, fields, StringPool.BLANK, locale);
194            }
195    
196            public String getHTML(
197                            PageContext pageContext, String xml, Fields fields,
198                            String namespace, boolean readOnly, Locale locale)
199                    throws Exception {
200    
201                    return getHTML(
202                            pageContext, xml, fields, namespace, null, readOnly, locale);
203            }
204    
205            public String getHTML(
206                            PageContext pageContext, String xml, Fields fields,
207                            String namespace, Locale locale)
208                    throws Exception {
209    
210                    return getHTML(pageContext, xml, fields, namespace, false, locale);
211            }
212    
213            public String getHTML(
214                            PageContext pageContext, String xml, Fields fields,
215                            String namespace, String mode, boolean readOnly, Locale locale)
216                    throws Exception {
217    
218                    Document document = SAXReaderUtil.read(xml);
219    
220                    return getHTML(
221                            pageContext, document.getRootElement(), fields, namespace, mode,
222                            readOnly, locale);
223            }
224    
225            public String getHTML(PageContext pageContext, String xml, Locale locale)
226                    throws Exception {
227    
228                    return getHTML(pageContext, xml, null, locale);
229            }
230    
231            public JSONArray getJSONArray(Document document) throws JSONException {
232                    return getJSONArray(document.getRootElement());
233            }
234    
235            public JSONArray getJSONArray(Element element) throws JSONException {
236                    JSONArray jsonArray = JSONFactoryUtil.createJSONArray();
237    
238                    Document document = element.getDocument();
239    
240                    String defaultLocale = LocalizationUtil.getDefaultLocale(
241                            document.asXML());
242    
243                    List<Element> dynamicElementElements = element.elements(
244                            "dynamic-element");
245    
246                    for (Element dynamicElementElement : dynamicElementElements) {
247                            JSONObject jsonObject = JSONFactoryUtil.createJSONObject();
248                            JSONObject localizationMapJSONObject =
249                                    JSONFactoryUtil.createJSONObject();
250    
251                            for (Attribute attribute : dynamicElementElement.attributes()) {
252                                    jsonObject.put(attribute.getName(), attribute.getValue());
253                            }
254    
255                            jsonObject.put("id", dynamicElementElement.attributeValue("name"));
256    
257                            String type = jsonObject.getString("type");
258    
259                            List<Element> metadataElements = dynamicElementElement.elements(
260                                    "meta-data");
261    
262                            for (Element metadataElement : metadataElements) {
263                                    if (metadataElement == null) {
264                                            continue;
265                                    }
266    
267                                    String locale = metadataElement.attributeValue("locale");
268    
269                                    JSONObject localeMap = JSONFactoryUtil.createJSONObject();
270    
271                                    for (Element metadataEntryElement :
272                                                    metadataElement.elements()) {
273    
274                                            String attributeName = metadataEntryElement.attributeValue(
275                                                    "name");
276                                            String attributeValue = metadataEntryElement.getText();
277    
278                                            putMetadataValue(
279                                                    localeMap, attributeName, attributeValue, type);
280    
281                                            if (defaultLocale.equals(locale)) {
282                                                    putMetadataValue(
283                                                            jsonObject, attributeName, attributeValue, type);
284                                            }
285                                    }
286    
287                                    localizationMapJSONObject.put(locale, localeMap);
288                            }
289    
290                            jsonObject.put("localizationMap", localizationMapJSONObject);
291    
292                            JSONArray hiddenAttributesJSONArray =
293                                    JSONFactoryUtil.createJSONArray();
294    
295                            if (type.equals(DDMImpl.TYPE_CHECKBOX)) {
296                                    hiddenAttributesJSONArray.put("required");
297                            }
298    
299                            if (type.equals(DDMImpl.TYPE_DDM_FILEUPLOAD)) {
300                                    hiddenAttributesJSONArray.put("predefinedValue");
301                            }
302    
303                            hiddenAttributesJSONArray.put("readOnly");
304    
305                            jsonObject.put("hiddenAttributes", hiddenAttributesJSONArray);
306    
307                            String key = "fields";
308    
309                            if (type.equals(DDMImpl.TYPE_RADIO) ||
310                                    type.equals(DDMImpl.TYPE_SELECT)) {
311    
312                                    key = "options";
313                            }
314    
315                            jsonObject.put(key, getJSONArray(dynamicElementElement));
316    
317                            jsonArray.put(jsonObject);
318                    }
319    
320                    return jsonArray;
321            }
322    
323            public JSONArray getJSONArray(String xml)
324                    throws DocumentException, JSONException {
325    
326                    return getJSONArray(SAXReaderUtil.read(xml));
327            }
328    
329            protected Map<String, Object> getFieldContext(
330                    Element dynamicElementElement, Locale locale) {
331    
332                    Document document = dynamicElementElement.getDocument();
333    
334                    String[] availableLocales = LocalizationUtil.getAvailableLocales(
335                            document.asXML());
336    
337                    String defaultLanguageId = LocalizationUtil.getDefaultLocale(
338                            document.asXML());
339    
340                    String languageId = LocaleUtil.toLanguageId(locale);
341    
342                    if (!ArrayUtil.contains(availableLocales, languageId)) {
343                            languageId = defaultLanguageId;
344                    }
345    
346                    Element metadataElement =
347                            (Element)dynamicElementElement.selectSingleNode(
348                                    "meta-data[@locale='" + languageId + "']");
349    
350                    Map<String, Object> field = new HashMap<String, Object>();
351    
352                    if (metadataElement != null) {
353                            for (Element metadataEntry : metadataElement.elements()) {
354                                    field.put(
355                                            metadataEntry.attributeValue("name"),
356                                            metadataEntry.getText());
357                            }
358                    }
359    
360                    for (Attribute attribute : dynamicElementElement.attributes()) {
361                            field.put(attribute.getName(), attribute.getValue());
362                    }
363    
364                    return field;
365            }
366    
367            protected FreeMarkerContext getFreeMarkerContext(
368                    Element dynamicElementElement, Locale locale) {
369    
370                    FreeMarkerContext freeMarkerContext =
371                            FreeMarkerEngineUtil.getWrappedStandardToolsContext();
372    
373                    Map<String, Object> fieldContext = getFieldContext(
374                            dynamicElementElement, locale);
375    
376                    Map<String, Object> parentFieldContext = new HashMap<String, Object>();
377    
378                    Element parentElement = dynamicElementElement.getParent();
379    
380                    if (parentElement != null) {
381                            parentFieldContext = getFieldContext(parentElement, locale);
382                    }
383    
384                    freeMarkerContext.put("fieldStructure", fieldContext);
385                    freeMarkerContext.put("parentFieldStructure", parentFieldContext);
386    
387                    return freeMarkerContext;
388            }
389    
390            /**
391             * @see com.liferay.taglib.util.ThemeUtil#includeFTL
392             */
393            protected String processFTL(
394                            PageContext pageContext, FreeMarkerContext freeMarkerContext,
395                            String resourcePath, String defaultResourcePath)
396                    throws Exception {
397    
398                    if (!FreeMarkerEngineUtil.resourceExists(resourcePath)) {
399                            resourcePath = defaultResourcePath;
400                    }
401    
402                    HttpServletRequest request =
403                            (HttpServletRequest)pageContext.getRequest();
404    
405                    // FreeMarker variables
406    
407                    FreeMarkerVariablesUtil.insertVariables(freeMarkerContext, request);
408    
409                    // Tag libraries
410    
411                    HttpServletResponse response =
412                            (HttpServletResponse)pageContext.getResponse();
413    
414                    Writer writer = new UnsyncStringWriter();
415    
416                    // Portal JSP tag library factory
417    
418                    TemplateHashModel portalTaglib =
419                            FreeMarkerTaglibFactoryUtil.createTaglibFactory(
420                                    pageContext.getServletContext());
421    
422                    freeMarkerContext.put("PortalJspTagLibs", portalTaglib);
423    
424                    // FreeMarker JSP tag library support
425    
426                    final Servlet servlet = (Servlet)pageContext.getPage();
427    
428                    GenericServlet genericServlet = null;
429    
430                    if (servlet instanceof GenericServlet) {
431                            genericServlet = (GenericServlet)servlet;
432                    }
433                    else {
434                            genericServlet = new GenericServlet() {
435    
436                                    @Override
437                                    public void service(
438                                                    ServletRequest servletRequest,
439                                                    ServletResponse servletResponse)
440                                            throws ServletException, IOException {
441    
442                                            servlet.service(servletRequest, servletResponse);
443                                    }
444    
445                            };
446    
447                            genericServlet.init(pageContext.getServletConfig());
448                    }
449    
450                    ServletContextHashModel servletContextHashModel =
451                            new ServletContextHashModel(
452                                    genericServlet, ObjectWrapper.DEFAULT_WRAPPER);
453    
454                    freeMarkerContext.put("Application", servletContextHashModel);
455    
456                    HttpRequestHashModel httpRequestHashModel = new HttpRequestHashModel(
457                            request, response, ObjectWrapper.DEFAULT_WRAPPER);
458    
459                    freeMarkerContext.put("Request", httpRequestHashModel);
460    
461                    // Merge templates
462    
463                    FreeMarkerEngineUtil.mergeTemplate(
464                            resourcePath, freeMarkerContext, writer);
465    
466                    return writer.toString();
467            }
468    
469            protected void putMetadataValue(
470                    JSONObject jsonObject, String attributeName, String attributeValue,
471                    String type) {
472    
473                    if (type.equals(DDMImpl.TYPE_RADIO) ||
474                            type.equals(DDMImpl.TYPE_SELECT)) {
475    
476                            if (attributeName.equals("predefinedValue")) {
477                                    try {
478                                            jsonObject.put(
479                                                    attributeName,
480                                                    JSONFactoryUtil.createJSONArray(attributeValue));
481                                    }
482                                    catch (Exception e) {
483                                    }
484    
485                                    return;
486                            }
487                    }
488    
489                    jsonObject.put(attributeName, attributeValue);
490            }
491    
492            private static final String _DEFAULT_NAMESPACE = "alloy";
493    
494            private static final String _DEFAULT_READ_ONLY_NAMESPACE = "readonly";
495    
496            private static final String _TPL_DEFAULT_PATH =
497                    "com/liferay/portlet/dynamicdatamapping/dependencies/alloy/text.ftl";
498    
499            private static final String _TPL_DEFAULT_READ_ONLY_PATH =
500                    "com/liferay/portlet/dynamicdatamapping/dependencies/readonly/" +
501                            "default.ftl";
502    
503            private static final String _TPL_EXT = ".ftl";
504    
505            private static final String _TPL_PATH =
506                    "com/liferay/portlet/dynamicdatamapping/dependencies/";
507    
508    }