001    /**
002     * Copyright (c) 2000-2013 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            @Override
077            public String getHTML(
078                            PageContext pageContext, DDMStructure ddmStructure, Fields fields,
079                            String namespace, boolean readOnly, Locale locale)
080                    throws Exception {
081    
082                    return getHTML(
083                            pageContext, ddmStructure.getXsd(), fields, namespace, readOnly,
084                            locale);
085            }
086    
087            @Override
088            public String getHTML(
089                            PageContext pageContext, DDMTemplate ddmTemplate, Fields fields,
090                            String namespace, boolean readOnly, Locale locale)
091                    throws Exception {
092    
093                    return getHTML(
094                            pageContext, ddmTemplate.getScript(), fields, namespace,
095                            ddmTemplate.getMode(), readOnly, locale);
096            }
097    
098            public String getHTML(
099                            PageContext pageContext, Element element, Fields fields,
100                            Locale locale)
101                    throws Exception {
102    
103                    return getHTML(
104                            pageContext, element, fields, StringPool.BLANK, null, false,
105                            locale);
106            }
107    
108            public String getHTML(
109                            PageContext pageContext, Element element, Fields fields,
110                            String namespace, String mode, boolean readOnly, Locale locale)
111                    throws Exception {
112    
113                    StringBundler sb = new StringBundler();
114    
115                    HttpServletRequest request =
116                            (HttpServletRequest)pageContext.getRequest();
117    
118                    String portletId = PortalUtil.getPortletId(request);
119    
120                    String portletNamespace = PortalUtil.getPortletNamespace(portletId);
121    
122                    List<Element> dynamicElementElements = element.elements(
123                            "dynamic-element");
124    
125                    for (Element dynamicElementElement : dynamicElementElements) {
126                            FreeMarkerContext freeMarkerContext = getFreeMarkerContext(
127                                    dynamicElementElement, locale);
128    
129                            freeMarkerContext.put("portletNamespace", portletNamespace);
130                            freeMarkerContext.put("namespace", namespace);
131    
132                            if (fields != null) {
133                                    freeMarkerContext.put("fields", fields);
134                            }
135    
136                            Map<String, Object> field =
137                                    (Map<String, Object>)freeMarkerContext.get("fieldStructure");
138    
139                            String childrenHTML = getHTML(
140                                    pageContext, dynamicElementElement, fields, namespace, mode,
141                                    readOnly, locale);
142    
143                            field.put("children", childrenHTML);
144    
145                            String fieldNamespace = dynamicElementElement.attributeValue(
146                                    "fieldNamespace", _DEFAULT_NAMESPACE);
147    
148                            String defaultResourcePath = _TPL_DEFAULT_PATH;
149    
150                            boolean fieldReadOnly = GetterUtil.getBoolean(
151                                    field.get("readOnly"));
152    
153                            if ((fieldReadOnly && Validator.isNotNull(mode) &&
154                                     mode.equalsIgnoreCase(
155                                            DDMTemplateConstants.TEMPLATE_MODE_EDIT)) || readOnly) {
156    
157                                    fieldNamespace = _DEFAULT_READ_ONLY_NAMESPACE;
158    
159                                    defaultResourcePath = _TPL_DEFAULT_READ_ONLY_PATH;
160                            }
161    
162                            String type = dynamicElementElement.attributeValue("type");
163    
164                            String templateName = StringUtil.replaceFirst(
165                                    type, fieldNamespace.concat(StringPool.DASH), StringPool.BLANK);
166    
167                            StringBundler resourcePath = new StringBundler(5);
168    
169                            resourcePath.append(_TPL_PATH);
170                            resourcePath.append(fieldNamespace.toLowerCase());
171                            resourcePath.append(CharPool.SLASH);
172                            resourcePath.append(templateName);
173                            resourcePath.append(_TPL_EXT);
174    
175                            sb.append(
176                                    processFTL(
177                                            pageContext, freeMarkerContext, resourcePath.toString(),
178                                            defaultResourcePath));
179                    }
180    
181                    return sb.toString();
182            }
183    
184            public String getHTML(
185                            PageContext pageContext, Element element, Locale locale)
186                    throws Exception {
187    
188                    return getHTML(pageContext, element, null, locale);
189            }
190    
191            @Override
192            public String getHTML(
193                            PageContext pageContext, String xml, Fields fields, Locale locale)
194                    throws Exception {
195    
196                    return getHTML(pageContext, xml, fields, StringPool.BLANK, locale);
197            }
198    
199            @Override
200            public String getHTML(
201                            PageContext pageContext, String xml, Fields fields,
202                            String namespace, boolean readOnly, Locale locale)
203                    throws Exception {
204    
205                    return getHTML(
206                            pageContext, xml, fields, namespace, null, readOnly, locale);
207            }
208    
209            @Override
210            public String getHTML(
211                            PageContext pageContext, String xml, Fields fields,
212                            String namespace, Locale locale)
213                    throws Exception {
214    
215                    return getHTML(pageContext, xml, fields, namespace, false, locale);
216            }
217    
218            public String getHTML(
219                            PageContext pageContext, String xml, Fields fields,
220                            String namespace, String mode, boolean readOnly, Locale locale)
221                    throws Exception {
222    
223                    Document document = SAXReaderUtil.read(xml);
224    
225                    return getHTML(
226                            pageContext, document.getRootElement(), fields, namespace, mode,
227                            readOnly, locale);
228            }
229    
230            @Override
231            public String getHTML(PageContext pageContext, String xml, Locale locale)
232                    throws Exception {
233    
234                    return getHTML(pageContext, xml, null, locale);
235            }
236    
237            @Override
238            public JSONArray getJSONArray(Document document) throws JSONException {
239                    return getJSONArray(document.getRootElement());
240            }
241    
242            @Override
243            public JSONArray getJSONArray(Element element) throws JSONException {
244                    JSONArray jsonArray = JSONFactoryUtil.createJSONArray();
245    
246                    Document document = element.getDocument();
247    
248                    String defaultLocale = LocalizationUtil.getDefaultLocale(
249                            document.asXML());
250    
251                    List<Element> dynamicElementElements = element.elements(
252                            "dynamic-element");
253    
254                    for (Element dynamicElementElement : dynamicElementElements) {
255                            JSONObject jsonObject = JSONFactoryUtil.createJSONObject();
256                            JSONObject localizationMapJSONObject =
257                                    JSONFactoryUtil.createJSONObject();
258    
259                            for (Attribute attribute : dynamicElementElement.attributes()) {
260                                    jsonObject.put(attribute.getName(), attribute.getValue());
261                            }
262    
263                            jsonObject.put("id", dynamicElementElement.attributeValue("name"));
264    
265                            String type = jsonObject.getString("type");
266    
267                            List<Element> metadataElements = dynamicElementElement.elements(
268                                    "meta-data");
269    
270                            for (Element metadataElement : metadataElements) {
271                                    if (metadataElement == null) {
272                                            continue;
273                                    }
274    
275                                    String locale = metadataElement.attributeValue("locale");
276    
277                                    JSONObject localeMap = JSONFactoryUtil.createJSONObject();
278    
279                                    for (Element metadataEntryElement :
280                                                    metadataElement.elements()) {
281    
282                                            String attributeName = metadataEntryElement.attributeValue(
283                                                    "name");
284                                            String attributeValue = metadataEntryElement.getTextTrim();
285    
286                                            putMetadataValue(
287                                                    localeMap, attributeName, attributeValue, type);
288    
289                                            if (defaultLocale.equals(locale)) {
290                                                    putMetadataValue(
291                                                            jsonObject, attributeName, attributeValue, type);
292                                            }
293                                    }
294    
295                                    localizationMapJSONObject.put(locale, localeMap);
296                            }
297    
298                            jsonObject.put("localizationMap", localizationMapJSONObject);
299    
300                            JSONArray hiddenAttributesJSONArray =
301                                    JSONFactoryUtil.createJSONArray();
302    
303                            if (type.equals(DDMImpl.TYPE_CHECKBOX)) {
304                                    hiddenAttributesJSONArray.put("required");
305                            }
306    
307                            if (type.equals(DDMImpl.TYPE_DDM_FILEUPLOAD)) {
308                                    hiddenAttributesJSONArray.put("predefinedValue");
309                            }
310    
311                            hiddenAttributesJSONArray.put("readOnly");
312    
313                            jsonObject.put("hiddenAttributes", hiddenAttributesJSONArray);
314    
315                            String key = "fields";
316    
317                            if (type.equals(DDMImpl.TYPE_RADIO) ||
318                                    type.equals(DDMImpl.TYPE_SELECT)) {
319    
320                                    key = "options";
321                            }
322    
323                            jsonObject.put(key, getJSONArray(dynamicElementElement));
324    
325                            jsonArray.put(jsonObject);
326                    }
327    
328                    return jsonArray;
329            }
330    
331            @Override
332            public JSONArray getJSONArray(String xml)
333                    throws DocumentException, JSONException {
334    
335                    return getJSONArray(SAXReaderUtil.read(xml));
336            }
337    
338            protected Map<String, Object> getFieldContext(
339                    Element dynamicElementElement, Locale locale) {
340    
341                    Document document = dynamicElementElement.getDocument();
342    
343                    String xml = document.asXML();
344    
345                    String[] availableLocales = LocalizationUtil.getAvailableLocales(xml);
346    
347                    String defaultLanguageId = LocalizationUtil.getDefaultLocale(xml);
348    
349                    String languageId = LocaleUtil.toLanguageId(locale);
350    
351                    if (!ArrayUtil.contains(availableLocales, languageId)) {
352                            languageId = defaultLanguageId;
353                    }
354    
355                    Element metadataElement =
356                            (Element)dynamicElementElement.selectSingleNode(
357                                    "meta-data[@locale='" + languageId + "']");
358    
359                    Map<String, Object> field = new HashMap<String, Object>();
360    
361                    if (metadataElement != null) {
362                            for (Element metadataEntry : metadataElement.elements()) {
363                                    field.put(
364                                            metadataEntry.attributeValue("name"),
365                                            metadataEntry.getText());
366                            }
367                    }
368    
369                    for (Attribute attribute : dynamicElementElement.attributes()) {
370                            field.put(attribute.getName(), attribute.getValue());
371                    }
372    
373                    return field;
374            }
375    
376            protected FreeMarkerContext getFreeMarkerContext(
377                    Element dynamicElementElement, Locale locale) {
378    
379                    FreeMarkerContext freeMarkerContext =
380                            FreeMarkerEngineUtil.getWrappedStandardToolsContext();
381    
382                    Map<String, Object> fieldContext = getFieldContext(
383                            dynamicElementElement, locale);
384    
385                    Map<String, Object> parentFieldContext = new HashMap<String, Object>();
386    
387                    Element parentElement = dynamicElementElement.getParent();
388    
389                    if (parentElement != null) {
390                            parentFieldContext = getFieldContext(parentElement, locale);
391                    }
392    
393                    freeMarkerContext.put("fieldStructure", fieldContext);
394                    freeMarkerContext.put("parentFieldStructure", parentFieldContext);
395    
396                    return freeMarkerContext;
397            }
398    
399            /**
400             * @see com.liferay.taglib.util.ThemeUtil#includeFTL
401             */
402            protected String processFTL(
403                            PageContext pageContext, FreeMarkerContext freeMarkerContext,
404                            String resourcePath, String defaultResourcePath)
405                    throws Exception {
406    
407                    if (!FreeMarkerEngineUtil.resourceExists(resourcePath)) {
408                            resourcePath = defaultResourcePath;
409                    }
410    
411                    HttpServletRequest request =
412                            (HttpServletRequest)pageContext.getRequest();
413    
414                    // FreeMarker variables
415    
416                    FreeMarkerVariablesUtil.insertVariables(freeMarkerContext, request);
417    
418                    // Tag libraries
419    
420                    HttpServletResponse response =
421                            (HttpServletResponse)pageContext.getResponse();
422    
423                    Writer writer = new UnsyncStringWriter();
424    
425                    // Portal JSP tag library factory
426    
427                    TemplateHashModel portalTaglib =
428                            FreeMarkerTaglibFactoryUtil.createTaglibFactory(
429                                    pageContext.getServletContext());
430    
431                    freeMarkerContext.put("PortalJspTagLibs", portalTaglib);
432    
433                    // FreeMarker JSP tag library support
434    
435                    final Servlet servlet = (Servlet)pageContext.getPage();
436    
437                    GenericServlet genericServlet = null;
438    
439                    if (servlet instanceof GenericServlet) {
440                            genericServlet = (GenericServlet)servlet;
441                    }
442                    else {
443                            genericServlet = new GenericServlet() {
444    
445                                    @Override
446                                    public void service(
447                                                    ServletRequest servletRequest,
448                                                    ServletResponse servletResponse)
449                                            throws IOException, ServletException {
450    
451                                            servlet.service(servletRequest, servletResponse);
452                                    }
453    
454                            };
455    
456                            genericServlet.init(pageContext.getServletConfig());
457                    }
458    
459                    ServletContextHashModel servletContextHashModel =
460                            new ServletContextHashModel(
461                                    genericServlet, ObjectWrapper.DEFAULT_WRAPPER);
462    
463                    freeMarkerContext.put("Application", servletContextHashModel);
464    
465                    HttpRequestHashModel httpRequestHashModel = new HttpRequestHashModel(
466                            request, response, ObjectWrapper.DEFAULT_WRAPPER);
467    
468                    freeMarkerContext.put("Request", httpRequestHashModel);
469    
470                    // Merge templates
471    
472                    FreeMarkerEngineUtil.mergeTemplate(
473                            resourcePath, freeMarkerContext, writer);
474    
475                    return writer.toString();
476            }
477    
478            protected void putMetadataValue(
479                    JSONObject jsonObject, String attributeName, String attributeValue,
480                    String type) {
481    
482                    if (type.equals(DDMImpl.TYPE_RADIO) ||
483                            type.equals(DDMImpl.TYPE_SELECT)) {
484    
485                            if (attributeName.equals("predefinedValue")) {
486                                    try {
487                                            jsonObject.put(
488                                                    attributeName,
489                                                    JSONFactoryUtil.createJSONArray(attributeValue));
490                                    }
491                                    catch (Exception e) {
492                                    }
493    
494                                    return;
495                            }
496                    }
497    
498                    jsonObject.put(attributeName, attributeValue);
499            }
500    
501            private static final String _DEFAULT_NAMESPACE = "alloy";
502    
503            private static final String _DEFAULT_READ_ONLY_NAMESPACE = "readonly";
504    
505            private static final String _TPL_DEFAULT_PATH =
506                    "com/liferay/portlet/dynamicdatamapping/dependencies/alloy/text.ftl";
507    
508            private static final String _TPL_DEFAULT_READ_ONLY_PATH =
509                    "com/liferay/portlet/dynamicdatamapping/dependencies/readonly/" +
510                            "default.ftl";
511    
512            private static final String _TPL_EXT = ".ftl";
513    
514            private static final String _TPL_PATH =
515                    "com/liferay/portlet/dynamicdatamapping/dependencies/";
516    
517    }