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