com.liferay.dynamic.data.mapping.internal.render.DDMFormFieldFreeMarkerRenderer.java Source code

Java tutorial

Introduction

Here is the source code for com.liferay.dynamic.data.mapping.internal.render.DDMFormFieldFreeMarkerRenderer.java

Source

/**
 * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 */

package com.liferay.dynamic.data.mapping.internal.render;

import com.liferay.dynamic.data.mapping.constants.DDMPortletKeys;
import com.liferay.dynamic.data.mapping.internal.util.DDMFieldsCounter;
import com.liferay.dynamic.data.mapping.internal.util.DDMFormFieldFreeMarkerRendererHelper;
import com.liferay.dynamic.data.mapping.internal.util.DDMImpl;
import com.liferay.dynamic.data.mapping.model.DDMForm;
import com.liferay.dynamic.data.mapping.model.DDMFormField;
import com.liferay.dynamic.data.mapping.model.DDMFormFieldOptions;
import com.liferay.dynamic.data.mapping.model.DDMTemplateConstants;
import com.liferay.dynamic.data.mapping.model.LocalizedValue;
import com.liferay.dynamic.data.mapping.render.DDMFormFieldRenderer;
import com.liferay.dynamic.data.mapping.render.DDMFormFieldRenderingContext;
import com.liferay.dynamic.data.mapping.storage.Field;
import com.liferay.dynamic.data.mapping.storage.Fields;
import com.liferay.petra.string.CharPool;
import com.liferay.petra.string.StringPool;
import com.liferay.portal.kernel.editor.Editor;
import com.liferay.portal.kernel.exception.PortalException;
import com.liferay.portal.kernel.io.unsync.UnsyncStringWriter;
import com.liferay.portal.kernel.language.LanguageConstants;
import com.liferay.portal.kernel.language.LanguageUtil;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.security.auth.AuthTokenUtil;
import com.liferay.portal.kernel.template.ClassLoaderTemplateResource;
import com.liferay.portal.kernel.template.Template;
import com.liferay.portal.kernel.template.TemplateConstants;
import com.liferay.portal.kernel.template.TemplateManager;
import com.liferay.portal.kernel.template.TemplateManagerUtil;
import com.liferay.portal.kernel.template.TemplateResource;
import com.liferay.portal.kernel.theme.ThemeDisplay;
import com.liferay.portal.kernel.util.ArrayUtil;
import com.liferay.portal.kernel.util.GetterUtil;
import com.liferay.portal.kernel.util.ParamUtil;
import com.liferay.portal.kernel.util.PortalUtil;
import com.liferay.portal.kernel.util.PortletKeys;
import com.liferay.portal.kernel.util.StringBundler;
import com.liferay.portal.kernel.util.StringUtil;
import com.liferay.portal.kernel.util.Validator;
import com.liferay.portal.kernel.util.WebKeys;

import java.io.Writer;

import java.net.URL;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author Pablo Carvalho
 */
public class DDMFormFieldFreeMarkerRenderer implements DDMFormFieldRenderer {

    public DDMFormFieldFreeMarkerRenderer() {
        String defaultTemplateId = _TPL_PATH + "alloy/text.ftl";

        _defaultTemplateResource = getTemplateResource(defaultTemplateId);

        String defaultReadOnlyTemplateId = _TPL_PATH + "readonly/default.ftl";

        _defaultReadOnlyTemplateResource = getTemplateResource(defaultReadOnlyTemplateId);
    }

    @Override
    public String[] getSupportedDDMFormFieldTypes() {
        return _SUPPORTED_DDM_FORM_FIELD_TYPES;
    }

    @Override
    public String render(DDMFormField ddmFormField, DDMFormFieldRenderingContext ddmFormFieldRenderingContext)
            throws PortalException {

        try {
            HttpServletRequest request = ddmFormFieldRenderingContext.getHttpServletRequest();
            HttpServletResponse response = ddmFormFieldRenderingContext.getHttpServletResponse();
            Fields fields = ddmFormFieldRenderingContext.getFields();
            String portletNamespace = ddmFormFieldRenderingContext.getPortletNamespace();
            String namespace = ddmFormFieldRenderingContext.getNamespace();
            String mode = ddmFormFieldRenderingContext.getMode();
            boolean readOnly = ddmFormFieldRenderingContext.isReadOnly();
            boolean showEmptyFieldLabel = ddmFormFieldRenderingContext.isShowEmptyFieldLabel();
            Locale locale = ddmFormFieldRenderingContext.getLocale();

            return getFieldHTML(request, response, ddmFormField, fields, null, portletNamespace, namespace, mode,
                    readOnly, showEmptyFieldLabel, locale);
        } catch (Exception e) {
            throw new PortalException(e);
        }
    }

    protected void addLayoutProperties(DDMFormField ddmFormField, Map<String, Object> fieldContext, Locale locale) {

        LocalizedValue label = ddmFormField.getLabel();

        fieldContext.put("label", label.getString(locale));

        LocalizedValue predefinedValue = ddmFormField.getPredefinedValue();

        fieldContext.put("predefinedValue", predefinedValue.getString(locale));

        LocalizedValue style = ddmFormField.getStyle();

        fieldContext.put("style", style.getString(locale));

        LocalizedValue tip = ddmFormField.getTip();

        fieldContext.put("tip", tip.getString(locale));
    }

    protected void addStructureProperties(DDMFormField ddmFormField, Map<String, Object> fieldContext) {

        fieldContext.put("dataType", ddmFormField.getDataType());
        fieldContext.put("indexType", ddmFormField.getIndexType());
        fieldContext.put("localizable", Boolean.toString(ddmFormField.isLocalizable()));
        fieldContext.put("multiple", Boolean.toString(ddmFormField.isMultiple()));
        fieldContext.put("name", ddmFormField.getName());
        fieldContext.put("readOnly", Boolean.toString(ddmFormField.isReadOnly()));
        fieldContext.put("repeatable", Boolean.toString(ddmFormField.isRepeatable()));
        fieldContext.put("required", Boolean.toString(ddmFormField.isRequired()));
        fieldContext.put("showLabel", Boolean.toString(ddmFormField.isShowLabel()));
        fieldContext.put("type", ddmFormField.getType());
    }

    protected int countFieldRepetition(String[] fieldsDisplayValues, String parentFieldName, int offset) {

        int total = 0;

        String fieldName = fieldsDisplayValues[offset];

        for (; offset < fieldsDisplayValues.length; offset++) {
            String fieldNameValue = fieldsDisplayValues[offset];

            if (fieldNameValue.equals(fieldName)) {
                total++;
            }

            if (fieldNameValue.equals(parentFieldName)) {
                break;
            }
        }

        return total;
    }

    protected String getDDMFormFieldOptionHTML(HttpServletRequest request, HttpServletResponse response,
            DDMFormField ddmFormField, String mode, boolean readOnly, Locale locale,
            Map<String, Object> freeMarkerContext) throws Exception {

        StringBundler sb = new StringBundler();

        DDMFormFieldOptions ddmFormFieldOptions = ddmFormField.getDDMFormFieldOptions();

        for (String value : ddmFormFieldOptions.getOptionsValues()) {
            Map<String, Object> fieldStructure = new HashMap<>();

            fieldStructure.put("children", StringPool.BLANK);
            fieldStructure.put("fieldNamespace", StringUtil.randomId());

            LocalizedValue label = ddmFormFieldOptions.getOptionLabels(value);

            fieldStructure.put("label", label.getString(locale));

            fieldStructure.put("name", StringUtil.randomId());
            fieldStructure.put("value", value);

            freeMarkerContext.put("fieldStructure", fieldStructure);

            sb.append(processFTL(request, response, ddmFormField.getFieldNamespace(), "option", mode, readOnly,
                    freeMarkerContext));
        }

        return sb.toString();
    }

    protected Map<String, Object> getFieldContext(HttpServletRequest request, HttpServletResponse response,
            String portletNamespace, String namespace, DDMFormField ddmFormField, Locale locale) {

        Map<String, Map<String, Object>> fieldsContext = getFieldsContext(request, response, portletNamespace,
                namespace);

        String name = ddmFormField.getName();

        Map<String, Object> fieldContext = fieldsContext.get(name);

        if (fieldContext != null) {
            return fieldContext;
        }

        DDMForm ddmForm = ddmFormField.getDDMForm();

        Set<Locale> availableLocales = ddmForm.getAvailableLocales();

        Locale defaultLocale = ddmForm.getDefaultLocale();

        Locale structureLocale = locale;

        if (!availableLocales.contains(locale)) {
            structureLocale = defaultLocale;
        }

        fieldContext = new HashMap<>();

        addLayoutProperties(ddmFormField, fieldContext, structureLocale);

        addStructureProperties(ddmFormField, fieldContext);

        boolean checkRequired = GetterUtil.getBoolean(request.getAttribute("checkRequired"), true);

        if (!checkRequired) {
            fieldContext.put("required", Boolean.FALSE.toString());
        }

        fieldsContext.put(name, fieldContext);

        return fieldContext;
    }

    protected String getFieldHTML(HttpServletRequest request, HttpServletResponse response,
            DDMFormField ddmFormField, Fields fields, DDMFormField parentDDMFormField, String portletNamespace,
            String namespace, String mode, boolean readOnly, boolean showEmptyFieldLabel, Locale locale)
            throws Exception {

        Map<String, Object> freeMarkerContext = getFreeMarkerContext(request, response, portletNamespace, namespace,
                ddmFormField, parentDDMFormField, showEmptyFieldLabel, locale);

        if (fields != null) {
            freeMarkerContext.put("fields", fields);
        }

        Map<String, Object> fieldStructure = (Map<String, Object>) freeMarkerContext.get("fieldStructure");

        int fieldRepetition = 1;
        int offset = 0;

        DDMFieldsCounter ddmFieldsCounter = getFieldsCounter(request, response, fields, portletNamespace,
                namespace);

        String name = ddmFormField.getName();

        String fieldDisplayValue = getFieldsDisplayValue(request, response, fields);

        String[] fieldsDisplayValues = getFieldsDisplayValues(fieldDisplayValue);

        boolean fieldDisplayable = ArrayUtil.contains(fieldsDisplayValues, name);

        if (fieldDisplayable) {
            Map<String, Object> parentFieldStructure = (Map<String, Object>) freeMarkerContext
                    .get("parentFieldStructure");

            String parentFieldName = (String) parentFieldStructure.get("name");

            offset = getFieldOffset(fieldsDisplayValues, name, ddmFieldsCounter.get(name));

            if (offset == fieldsDisplayValues.length) {
                return StringPool.BLANK;
            }

            fieldRepetition = countFieldRepetition(fieldsDisplayValues, parentFieldName, offset);
        }

        StringBundler sb = new StringBundler(fieldRepetition);

        while (fieldRepetition > 0) {
            offset = getFieldOffset(fieldsDisplayValues, name, ddmFieldsCounter.get(name));

            String fieldNamespace = StringUtil.randomId();

            if (fieldDisplayable) {
                fieldNamespace = getFieldNamespace(fieldDisplayValue, ddmFieldsCounter, offset);
            }

            fieldStructure.put("fieldNamespace", fieldNamespace);
            fieldStructure.put("valueIndex", ddmFieldsCounter.get(name));

            if (fieldDisplayable) {
                ddmFieldsCounter.incrementKey(name);
            }

            StringBundler childrenHTMLSB = new StringBundler(2);

            childrenHTMLSB.append(getHTML(request, response, ddmFormField.getNestedDDMFormFields(), fields,
                    ddmFormField, portletNamespace, namespace, mode, readOnly, showEmptyFieldLabel, locale));

            if (Objects.equals(ddmFormField.getType(), "select")
                    || Objects.equals(ddmFormField.getType(), "radio")) {

                Map<String, Object> optionFreeMarkerContext = new HashMap<>(freeMarkerContext);

                optionFreeMarkerContext.put("parentFieldStructure", fieldStructure);

                childrenHTMLSB.append(getDDMFormFieldOptionHTML(request, response, ddmFormField, mode, readOnly,
                        locale, optionFreeMarkerContext));
            }

            fieldStructure.put("children", childrenHTMLSB.toString());

            sb.append(processFTL(request, response, ddmFormField.getFieldNamespace(), ddmFormField.getType(), mode,
                    readOnly, freeMarkerContext));

            fieldRepetition--;
        }

        return sb.toString();
    }

    protected String getFieldNamespace(String fieldDisplayValue, DDMFieldsCounter ddmFieldsCounter, int offset) {

        String[] fieldsDisplayValues = StringUtil.split(fieldDisplayValue);

        String fieldsDisplayValue = fieldsDisplayValues[offset];

        return StringUtil.extractLast(fieldsDisplayValue, DDMImpl.INSTANCE_SEPARATOR);
    }

    protected int getFieldOffset(String[] fieldsDisplayValues, String name, int index) {

        int offset = 0;

        for (; offset < fieldsDisplayValues.length; offset++) {
            if (name.equals(fieldsDisplayValues[offset])) {
                index--;

                if (index < 0) {
                    break;
                }
            }
        }

        return offset;
    }

    protected Map<String, Map<String, Object>> getFieldsContext(HttpServletRequest request,
            HttpServletResponse response, String portletNamespace, String namespace) {

        String fieldsContextKey = portletNamespace + namespace + "fieldsContext";

        Map<String, Map<String, Object>> fieldsContext = (Map<String, Map<String, Object>>) request
                .getAttribute(fieldsContextKey);

        if (fieldsContext == null) {
            fieldsContext = new HashMap<>();

            request.setAttribute(fieldsContextKey, fieldsContext);
        }

        return fieldsContext;
    }

    protected DDMFieldsCounter getFieldsCounter(HttpServletRequest request, HttpServletResponse response,
            Fields fields, String portletNamespace, String namespace) {

        String fieldsCounterKey = portletNamespace + namespace + "fieldsCount";

        DDMFieldsCounter ddmFieldsCounter = (DDMFieldsCounter) request.getAttribute(fieldsCounterKey);

        if (ddmFieldsCounter == null) {
            ddmFieldsCounter = new DDMFieldsCounter();

            request.setAttribute(fieldsCounterKey, ddmFieldsCounter);
        }

        return ddmFieldsCounter;
    }

    protected String getFieldsDisplayValue(HttpServletRequest request, HttpServletResponse response,
            Fields fields) {

        String defaultFieldsDisplayValue = null;

        if (fields != null) {
            Field fieldsDisplayField = fields.get(DDMImpl.FIELDS_DISPLAY_NAME);

            if (fieldsDisplayField != null) {
                defaultFieldsDisplayValue = (String) fieldsDisplayField.getValue();
            }
        }

        return ParamUtil.getString(request, DDMImpl.FIELDS_DISPLAY_NAME, defaultFieldsDisplayValue);
    }

    protected String[] getFieldsDisplayValues(String fieldDisplayValue) {
        List<String> fieldsDisplayValues = new ArrayList<>();

        for (String value : StringUtil.split(fieldDisplayValue)) {
            String fieldName = StringUtil.extractFirst(value, DDMImpl.INSTANCE_SEPARATOR);

            fieldsDisplayValues.add(fieldName);
        }

        return fieldsDisplayValues.toArray(new String[fieldsDisplayValues.size()]);
    }

    protected Map<String, Object> getFreeMarkerContext(HttpServletRequest request, HttpServletResponse response,
            String portletNamespace, String namespace, DDMFormField ddmFormField, DDMFormField parentDDMFormField,
            boolean showEmptyFieldLabel, Locale locale) {

        Map<String, Object> freeMarkerContext = new HashMap<>();

        Map<String, Object> fieldContext = getFieldContext(request, response, portletNamespace, namespace,
                ddmFormField, locale);

        Map<String, Object> parentFieldContext = new HashMap<>();

        if (parentDDMFormField != null) {
            parentFieldContext = getFieldContext(request, response, portletNamespace, namespace, parentDDMFormField,
                    locale);
        }

        freeMarkerContext.put("ddmPortletId", DDMPortletKeys.DYNAMIC_DATA_MAPPING);

        Editor editor = DDMFormFieldFreeMarkerRendererHelper.getEditor(request);

        freeMarkerContext.put("editorName", editor.getName());

        freeMarkerContext.put("fieldStructure", fieldContext);

        ThemeDisplay themeDisplay = (ThemeDisplay) request.getAttribute(WebKeys.THEME_DISPLAY);

        try {
            String itemSelectorAuthToken = AuthTokenUtil.getToken(request,
                    PortalUtil.getControlPanelPlid(themeDisplay.getCompanyId()), PortletKeys.ITEM_SELECTOR);

            freeMarkerContext.put("itemSelectorAuthToken", itemSelectorAuthToken);
        } catch (PortalException pe) {
            _log.error("Unable to generate item selector auth token ", pe);
        }

        freeMarkerContext.put("namespace", namespace);
        freeMarkerContext.put("parentFieldStructure", parentFieldContext);
        freeMarkerContext.put("portletNamespace", portletNamespace);
        freeMarkerContext.put("requestedLanguageDir", LanguageUtil.get(locale, LanguageConstants.KEY_DIR));
        freeMarkerContext.put("requestedLocale", locale);
        freeMarkerContext.put("showEmptyFieldLabel", showEmptyFieldLabel);

        return freeMarkerContext;
    }

    protected String getHTML(HttpServletRequest request, HttpServletResponse response,
            List<DDMFormField> ddmFormFields, Fields fields, DDMFormField parentDDMFormField,
            String portletNamespace, String namespace, String mode, boolean readOnly, boolean showEmptyFieldLabel,
            Locale locale) throws Exception {

        StringBundler sb = new StringBundler(ddmFormFields.size());

        for (DDMFormField ddmFormField : ddmFormFields) {
            sb.append(getFieldHTML(request, response, ddmFormField, fields, parentDDMFormField, portletNamespace,
                    namespace, mode, readOnly, showEmptyFieldLabel, locale));
        }

        return sb.toString();
    }

    protected URL getResource(String name) {
        Class<?> clazz = getClass();

        ClassLoader classLoader = clazz.getClassLoader();

        return classLoader.getResource(name);
    }

    protected TemplateResource getTemplateResource(String resource) {
        Class<?> clazz = getClass();

        TemplateResource templateResource = new ClassLoaderTemplateResource(clazz.getClassLoader(), resource);

        return templateResource;
    }

    protected String processFTL(HttpServletRequest request, HttpServletResponse response, String fieldNamespace,
            String type, String mode, boolean readOnly, Map<String, Object> freeMarkerContext) throws Exception {

        if (Validator.isNull(fieldNamespace)) {
            fieldNamespace = _DEFAULT_NAMESPACE;
        }

        TemplateResource templateResource = _defaultTemplateResource;

        Map<String, Object> fieldStructure = (Map<String, Object>) freeMarkerContext.get("fieldStructure");

        boolean fieldReadOnly = GetterUtil.getBoolean(fieldStructure.get("readOnly"));

        if ((fieldReadOnly && Validator.isNotNull(mode)
                && StringUtil.equalsIgnoreCase(mode, DDMTemplateConstants.TEMPLATE_MODE_EDIT)) || readOnly) {

            fieldNamespace = _DEFAULT_READ_ONLY_NAMESPACE;

            templateResource = _defaultReadOnlyTemplateResource;
        }

        String templateName = StringUtil.replaceFirst(type, fieldNamespace.concat(StringPool.DASH),
                StringPool.BLANK);

        StringBundler sb = new StringBundler(5);

        sb.append(_TPL_PATH);
        sb.append(StringUtil.toLowerCase(fieldNamespace));
        sb.append(CharPool.SLASH);
        sb.append(templateName);
        sb.append(_TPL_EXT);

        String resource = sb.toString();

        URL url = getResource(resource);

        if (url != null) {
            templateResource = getTemplateResource(resource);
        }

        if (templateResource == null) {
            throw new Exception("Unable to load template resource " + resource);
        }

        Template template = TemplateManagerUtil.getTemplate(TemplateConstants.LANG_TYPE_FTL, templateResource,
                false);

        for (Map.Entry<String, Object> entry : freeMarkerContext.entrySet()) {
            template.put(entry.getKey(), entry.getValue());
        }

        TemplateManager templateManager = TemplateManagerUtil.getTemplateManager(TemplateConstants.LANG_TYPE_FTL);

        templateManager.addTaglibSupport(template, request, response);

        return processFTL(request, response, template);
    }

    /**
     * @see com.liferay.taglib.util.ThemeUtil#includeFTL
     */
    protected String processFTL(HttpServletRequest request, HttpServletResponse response, Template template)
            throws Exception {

        template.prepare(request);

        Writer writer = new UnsyncStringWriter();

        template.processTemplate(writer);

        return writer.toString();
    }

    private static final String _DEFAULT_NAMESPACE = "alloy";

    private static final String _DEFAULT_READ_ONLY_NAMESPACE = "readonly";

    private static final String[] _SUPPORTED_DDM_FORM_FIELD_TYPES = { "checkbox", "ddm-color", "ddm-date",
            "ddm-decimal", "ddm-documentlibrary", "ddm-geolocation", "ddm-image", "ddm-integer",
            "ddm-journal-article", "ddm-link-to-page", "ddm-number", "ddm-paragraph", "ddm-separator",
            "ddm-text-html", "fieldset", "option", "radio", "select", "text", "textarea" };

    private static final String _TPL_EXT = ".ftl";

    private static final String _TPL_PATH = "com/liferay/dynamic/data/mapping/dependencies/";

    private static final Log _log = LogFactoryUtil.getLog(DDMFormFieldFreeMarkerRenderer.class);

    private final TemplateResource _defaultReadOnlyTemplateResource;
    private final TemplateResource _defaultTemplateResource;

}