001/*
002 *  Copyright 2022 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.plugins.forms.question.types;
017
018import java.time.LocalDate;
019import java.time.ZonedDateTime;
020import java.util.List;
021import java.util.Map;
022
023import org.apache.commons.lang3.StringUtils;
024
025import org.ametys.core.util.DateUtils;
026import org.ametys.plugins.forms.helper.FormElementDefinitionHelper;
027import org.ametys.plugins.forms.question.validators.DateTimeIntervalFormValidator;
028import org.ametys.plugins.forms.question.validators.LocalDateIntervalFormValidator;
029import org.ametys.plugins.forms.repository.FormQuestion;
030import org.ametys.runtime.config.DisableCondition;
031import org.ametys.runtime.config.DisableCondition.OPERATOR;
032import org.ametys.runtime.config.DisableConditions;
033import org.ametys.runtime.i18n.I18nizableText;
034import org.ametys.runtime.model.ElementDefinition;
035import org.ametys.runtime.model.ModelItem;
036import org.ametys.runtime.model.SimpleViewItemGroup;
037import org.ametys.runtime.model.StaticEnumerator;
038import org.ametys.runtime.model.ViewElement;
039import org.ametys.runtime.model.type.ModelItemTypeConstants;
040import org.ametys.runtime.parameter.DefaultValidator;
041
042/**
043 * Class for creating date time questions
044 */
045public class DateTimeQuestionType extends AbstractFormQuestionType
046{
047    /** Constant for minimum Date attribute. */
048    public static final String ATTRIBUTE_MIN_DATE = "min-date";
049
050    /** Constant for maximum Date attribute. */
051    public static final String ATTRIBUTE_MAX_DATE = "max-date";
052
053    /** Constant for minimum Date attribute. */
054    public static final String ATTRIBUTE_MIN_DATETIME = "min-datetime";
055
056    /** Constant for maximum Date attribute. */
057    public static final String ATTRIBUTE_MAX_DATETIME = "max-datetime";
058
059    /** Constant for date format attribute. */
060    public static final String ATTRIBUTE_DATE_FORMAT = "date-format";
061
062    /** Name of datetimeStaticEnumerator entry that enable min and max datetime fields */
063    public static final String DATE_TIME_FORMAT_VALUE = "datetime";
064
065    /** Name of datetimeStaticEnumerator entry that enable min and max date fields */
066    public static final String DATE_FORMAT_VALUE = "date";
067
068    /** Constant for default title */
069    public static final String DEFAULT_TITLE = "PLUGIN_FORMS_QUESTION_DEFAULT_TITLE_DATE";
070    
071    @Override
072    protected List<ModelItem> _getModelItems()
073    {
074        List<ModelItem> modelItems = super._getModelItems();
075        
076        // DATETIME Conditions
077        DisableConditions datetimeDisableConditions = new DisableConditions();
078        DisableCondition conditionDatetime = new DisableCondition(ATTRIBUTE_DATE_FORMAT, OPERATOR.NEQ, DATE_TIME_FORMAT_VALUE);
079        datetimeDisableConditions.getConditions().add(conditionDatetime);
080
081        // MIN datetime
082        ElementDefinition minDateTime = FormElementDefinitionHelper.getElementDefinition(ATTRIBUTE_MIN_DATETIME, ModelItemTypeConstants.DATETIME_TYPE_ID, "PLUGINS_FORMS_QUESTIONS_DIALOG_DATETIME_MIN_DATE", "PLUGINS_FORMS_QUESTIONS_DIALOG_DATETIME_MIN_DATE_DESC", null);
083        minDateTime.setDisableConditions(datetimeDisableConditions);
084        modelItems.add(minDateTime);
085
086        // MAX datetime
087        ElementDefinition maxDateTime = FormElementDefinitionHelper.getElementDefinition(ATTRIBUTE_MAX_DATETIME, ModelItemTypeConstants.DATETIME_TYPE_ID, "PLUGINS_FORMS_QUESTIONS_DIALOG_DATETIME_MAX_DATE", "PLUGINS_FORMS_QUESTIONS_DIALOG_DATETIME_MAX_DATE_DESC", null);
088        maxDateTime.setDisableConditions(datetimeDisableConditions);
089        modelItems.add(maxDateTime);
090
091        // DATE Conditions
092        DisableConditions dateDisableConditions = new DisableConditions();
093        DisableCondition conditionDate = new DisableCondition(ATTRIBUTE_DATE_FORMAT, OPERATOR.NEQ, DATE_FORMAT_VALUE);
094        dateDisableConditions.getConditions().add(conditionDate);
095
096        // MIN date
097        ElementDefinition minDate = FormElementDefinitionHelper.getElementDefinition(ATTRIBUTE_MIN_DATE, ModelItemTypeConstants.DATE_TYPE_ID, "PLUGINS_FORMS_QUESTIONS_DIALOG_DATETIME_MIN_DATE", "PLUGINS_FORMS_QUESTIONS_DIALOG_DATETIME_MIN_DATE_DESC", null);
098        minDate.setDisableConditions(dateDisableConditions);
099        modelItems.add(minDate);
100
101        // MAX date
102        ElementDefinition maxDate = FormElementDefinitionHelper.getElementDefinition(ATTRIBUTE_MAX_DATE, ModelItemTypeConstants.DATE_TYPE_ID, "PLUGINS_FORMS_QUESTIONS_DIALOG_DATETIME_MAX_DATE", "PLUGINS_FORMS_QUESTIONS_DIALOG_DATETIME_MAX_DATE_DESC", null);
103        maxDate.setDisableConditions(dateDisableConditions);
104        modelItems.add(maxDate);
105
106        // FORMAT
107        ElementDefinition<String> format = FormElementDefinitionHelper.getElementDefinition(ATTRIBUTE_DATE_FORMAT, ModelItemTypeConstants.STRING_TYPE_ID, "PLUGINS_FORMS_QUESTIONS_DIALOG_DATETIME_FORMAT", "PLUGINS_FORMS_QUESTIONS_DIALOG_DATETIME_FORMAT_DESC", new DefaultValidator(null, true));
108        StaticEnumerator<String> datetimeStaticEnumerator = new StaticEnumerator<>();
109        datetimeStaticEnumerator.add(new I18nizableText("plugin.forms", "PLUGINS_FORMS_QUESTIONS_DIALOG_DATETIME_FORMAT_DATETIME"), DATE_TIME_FORMAT_VALUE);
110        datetimeStaticEnumerator.add(new I18nizableText("plugin.forms", "PLUGINS_FORMS_QUESTIONS_DIALOG_DATETIME_FORMAT_DATE"), DATE_FORMAT_VALUE);
111        format.setEnumerator(datetimeStaticEnumerator);
112        format.setDefaultValue(DATE_FORMAT_VALUE);
113        modelItems.add(format);
114        
115        return modelItems;
116    }
117    
118    @Override
119    protected SimpleViewItemGroup _getMainTab()
120    {
121        SimpleViewItemGroup mainFieldset = super._getMainTab();
122        
123        ViewElement format = new ViewElement();
124        format.setDefinition((ElementDefinition< ? >) getModel().getModelItem(ATTRIBUTE_DATE_FORMAT));
125        mainFieldset.addViewItem(format);
126        
127        return mainFieldset;
128    }
129    
130    @Override
131    protected SimpleViewItemGroup _getAdvancedTab()
132    {
133        SimpleViewItemGroup advancedFieldset = super._getAdvancedTab();
134        
135        ViewElement minDate = new ViewElement();
136        minDate.setDefinition((ElementDefinition< ? >) getModel().getModelItem(ATTRIBUTE_MIN_DATE));
137        advancedFieldset.addViewItem(minDate);
138
139        ViewElement maxDate = new ViewElement();
140        maxDate.setDefinition((ElementDefinition< ? >) getModel().getModelItem(ATTRIBUTE_MAX_DATE));
141        advancedFieldset.addViewItem(maxDate);
142
143        ViewElement minDateTime = new ViewElement();
144        minDateTime.setDefinition((ElementDefinition< ? >) getModel().getModelItem(ATTRIBUTE_MIN_DATETIME));
145        advancedFieldset.addViewItem(minDateTime);
146
147        ViewElement maxDateTime = new ViewElement();
148        maxDateTime.setDefinition((ElementDefinition< ? >) getModel().getModelItem(ATTRIBUTE_MAX_DATETIME));
149        advancedFieldset.addViewItem(maxDateTime);
150        
151        return advancedFieldset;
152    }
153    
154    public String getStorageType(FormQuestion question)
155    {
156        String type = question.getValue(ATTRIBUTE_DATE_FORMAT);
157        return DATE_TIME_FORMAT_VALUE.equals(type) ? ModelItemTypeConstants.DATETIME_TYPE_ID : ModelItemTypeConstants.DATE_TYPE_ID;
158    }
159    
160    @Override
161    protected ModelItem _getEntryModelItem(FormQuestion question)
162    {
163        ModelItem item = super._getEntryModelItem(question);
164        if (DATE_TIME_FORMAT_VALUE.equals(question.getValue(ATTRIBUTE_DATE_FORMAT)))
165        {
166            ZonedDateTime minDate = question.getValue(ATTRIBUTE_MIN_DATETIME);
167            ZonedDateTime maxDate = question.getValue(ATTRIBUTE_MAX_DATETIME);
168            ((ElementDefinition) item).setValidator(new DateTimeIntervalFormValidator(null, isMandatory(question), minDate, maxDate));
169        }
170        else
171        {
172            LocalDate minDate = question.getValue(ATTRIBUTE_MIN_DATE);
173            LocalDate maxDate = question.getValue(ATTRIBUTE_MAX_DATE);
174            ((ElementDefinition) item).setValidator(new LocalDateIntervalFormValidator(null, isMandatory(question), minDate, maxDate));
175        }
176        return item;
177    }
178    
179    @Override
180    public void validateQuestionValues(Map<String, Object> values, Map<String, I18nizableText> errors)
181    {
182        super.validateQuestionValues(values, errors);
183
184        String format = (String) values.get(ATTRIBUTE_DATE_FORMAT);
185        switch (format)
186        {
187            case DATE_FORMAT_VALUE:
188                String minDateAsString = (String) values.getOrDefault(ATTRIBUTE_MIN_DATE, null);
189                String maxDateAsString = (String) values.getOrDefault(ATTRIBUTE_MAX_DATE, null);
190                if (StringUtils.isNotBlank(minDateAsString) && StringUtils.isNotBlank(maxDateAsString))
191                {
192                    LocalDate minDate = LocalDate.parse(minDateAsString);
193                    LocalDate maxDate = LocalDate.parse(maxDateAsString);
194                    
195                    if (minDate != null && maxDate != null && minDate.isAfter(maxDate))
196                    {
197                        errors.put(ATTRIBUTE_MIN_DATE, new I18nizableText("plugin.forms", "PLUGINS_FORMS_QUESTIONS_DIALOG_DATE_GAP_ERROR"));
198                        errors.put(ATTRIBUTE_MAX_DATE, new I18nizableText("plugin.forms", "PLUGINS_FORMS_QUESTIONS_DIALOG_DATE_GAP_ERROR"));
199                    }
200                }
201                break; 
202            case DATE_TIME_FORMAT_VALUE:
203                String minDateTimeAsString = (String) values.getOrDefault(ATTRIBUTE_MIN_DATETIME, null);
204                String maxDateTimeAsString = (String) values.getOrDefault(ATTRIBUTE_MAX_DATETIME, null);
205                if (StringUtils.isNotBlank(minDateTimeAsString) && StringUtils.isNotBlank(maxDateTimeAsString))
206                {
207                    ZonedDateTime minDateTime = DateUtils.parseZonedDateTime(minDateTimeAsString);
208                    ZonedDateTime maxDateTime = DateUtils.parseZonedDateTime(maxDateTimeAsString);
209                    if (minDateTime.isAfter(maxDateTime))
210                    {
211                        errors.put(ATTRIBUTE_MIN_DATETIME, new I18nizableText("plugin.forms", "PLUGINS_FORMS_QUESTIONS_DIALOG_DATE_GAP_ERROR"));
212                        errors.put(ATTRIBUTE_MAX_DATETIME, new I18nizableText("plugin.forms", "PLUGINS_FORMS_QUESTIONS_DIALOG_DATE_GAP_ERROR"));
213                    }
214                }
215                break;
216            default:
217                getLogger().error("Date format " + format + " is not supported");
218                break;
219        }
220    }
221    
222    public I18nizableText getDefaultTitle()
223    {
224        return new I18nizableText("plugin.forms", DEFAULT_TITLE);
225    }
226    
227    @Override
228    public List<String> getFieldToDisableIfFormPublished(FormQuestion question)
229    {
230        List<String> fieldNames =  super.getFieldToDisableIfFormPublished(question);
231        fieldNames.add(ATTRIBUTE_DATE_FORMAT);
232        return fieldNames;
233    }
234}