001/*
002 *  Copyright 2021 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.util.ArrayList;
019import java.util.List;
020import java.util.Map;
021
022import org.apache.avalon.framework.service.ServiceException;
023import org.apache.avalon.framework.service.ServiceManager;
024import org.apache.cocoon.xml.AttributesImpl;
025import org.apache.cocoon.xml.XMLUtils;
026import org.apache.commons.lang.StringUtils;
027import org.xml.sax.ContentHandler;
028import org.xml.sax.SAXException;
029
030import org.ametys.plugins.forms.helper.FormElementDefinitionHelper;
031import org.ametys.plugins.forms.question.computing.ComputingType;
032import org.ametys.plugins.forms.question.computing.ComputingTypeExtensionPoint;
033import org.ametys.plugins.forms.repository.FormEntry;
034import org.ametys.plugins.forms.repository.FormQuestion;
035import org.ametys.runtime.config.DisableCondition;
036import org.ametys.runtime.config.DisableCondition.OPERATOR;
037import org.ametys.runtime.config.DisableConditions;
038import org.ametys.runtime.i18n.I18nizableText;
039import org.ametys.runtime.model.ElementDefinition;
040import org.ametys.runtime.model.ModelItem;
041import org.ametys.runtime.model.SimpleViewItemGroup;
042import org.ametys.runtime.model.StaticEnumerator;
043import org.ametys.runtime.model.ViewElement;
044import org.ametys.runtime.model.ViewItemGroup;
045import org.ametys.runtime.model.type.ModelItemTypeConstants;
046
047/**
048 * Class for creating computed question
049 */
050public class ComputedQuestionType extends AbstractStaticFormQuestionType implements ConfidentialAwareQuestionType
051{
052    /** Constant for computing attribute. */
053    public static final String ATTRIBUTE_COMPUTING_TYPE = "computing-type";
054
055    /** Constant for default title */
056    public static final String DEFAULT_TITLE = "PLUGIN_FORMS_QUESTION_DEFAULT_TITLE_COMPUTED";
057    
058    /** The computing type extension point */
059    private ComputingTypeExtensionPoint _computingTypeExtensionPoint;
060    
061    @Override
062    public void service(ServiceManager manager) throws ServiceException
063    {
064        super.service(manager);
065        _computingTypeExtensionPoint = (ComputingTypeExtensionPoint) manager.lookup(ComputingTypeExtensionPoint.ROLE);
066    }
067    
068    @Override
069    protected List<ModelItem> _getModelItems()
070    {
071        List<ModelItem> modelItems = super._getModelItems();
072        
073        ElementDefinition<String> computingType = FormElementDefinitionHelper.getElementDefinition(ATTRIBUTE_COMPUTING_TYPE, ModelItemTypeConstants.STRING_TYPE_ID, "PLUGINS_FORMS_QUESTIONS_DIALOG_COMPUTED_COMPUTING_TYPE", "PLUGINS_FORMS_QUESTIONS_DIALOG_COMPUTED_COMPUTING_TYPE_DESC", null);
074        StaticEnumerator<String> typesStaticEnumerator = new StaticEnumerator<>();
075        for (ComputingType type : _getAllComputingType())
076        {
077            typesStaticEnumerator.add(type.getLabel(), type.getId());
078            
079            DisableConditions disableConditions = new DisableConditions();
080            DisableCondition condition = new DisableCondition(ATTRIBUTE_COMPUTING_TYPE, OPERATOR.NEQ, type.getId()); 
081            disableConditions.getConditions().add(condition);
082            
083            Map<String, ModelItem> typeModelItems = type.getModelItems();
084            for (ModelItem item : typeModelItems.values())
085            {
086                item.setDisableConditions(disableConditions);
087            }
088            modelItems.addAll(typeModelItems.values());
089        }
090        computingType.setEnumerator(typesStaticEnumerator);
091        modelItems.add(computingType);
092        
093        modelItems.add(getConfidentialityModelItem());
094        
095        return modelItems;
096    }
097    
098    @Override
099    protected List<ViewItemGroup> _getTabs()
100    {
101        return List.of(
102                _getMainTab(),
103                _getAdvancedTab(),
104                _getRulesTab()
105                );
106    }
107    
108    /**
109     * Define the content of the main tab
110     * @return the main tab definition
111     */
112    protected SimpleViewItemGroup _getMainTab()
113    {
114        SimpleViewItemGroup fieldset =  super._createMainTab();
115        
116        ViewElement computingCombobox = new ViewElement();
117        computingCombobox.setDefinition((ElementDefinition< ? >) getModel().getModelItem(ATTRIBUTE_COMPUTING_TYPE));
118        fieldset.addViewItem(computingCombobox);
119
120        for (ComputingType type : _getAllComputingType())
121        {
122            if (!type.getViewElements().isEmpty())
123            {
124                fieldset.addViewItems(type.getViewElements());
125            }
126        }
127        
128        return fieldset;
129    }
130    
131    /**
132     * Define the content of the advanced tab
133     * @return the advanced tab definition
134     */
135    protected SimpleViewItemGroup _getAdvancedTab()
136    {
137        SimpleViewItemGroup advancedFieldset = super._createAdvancedTab();
138        
139        ViewElement title = new ViewElement();
140        title.setDefinition((ElementDefinition< ? >) getModel().getModelItem(FormQuestion.ATTRIBUTE_TITLE));
141        advancedFieldset.addViewItem(title);
142        
143        advancedFieldset.addViewItem(getConfidentialityViewElement(getModel()));
144        
145        return advancedFieldset;
146    }
147    
148    public String getStorageType(FormQuestion question)
149    {
150        ComputingType computingType = getComputingType(question);
151        if (computingType != null)
152        {
153            return computingType.getStorageType(question);
154        }
155        
156        // Return string by default but it not supposed to have no computing type
157        return ModelItemTypeConstants.STRING_TYPE_ID;
158    }
159    
160    @Override
161    public void saxAdditionalInfos(ContentHandler contentHandler, FormQuestion question) throws SAXException
162    {
163        super.saxAdditionalInfos(contentHandler, question);
164        for (ComputingType type : _getAllComputingType())
165        {
166            AttributesImpl attr = new AttributesImpl();
167            String xslEnabled = StringUtils.isBlank(type.getXSLT())  ? "false" : "true";
168            attr.addCDATAAttribute("xsl-enabled", xslEnabled);
169            attr.addCDATAAttribute("id", type.getId());
170            XMLUtils.createElement(contentHandler, "computing-type", attr);
171        }
172    }
173    
174    /**
175     * Get all computing types
176     * @return a list of {@link ComputingType}
177     */
178    private List<ComputingType> _getAllComputingType()
179    {
180        List<ComputingType> computingTypes = new ArrayList<>();
181        for (String id: _computingTypeExtensionPoint.getExtensionsIds())
182        {
183            computingTypes.add(_computingTypeExtensionPoint.getExtension(id));
184        }
185        return computingTypes;
186    }
187    
188    @Override
189    public boolean isQuestionConfigured(FormQuestion question)
190    {
191        return StringUtils.isNotBlank(question.getValue(ATTRIBUTE_COMPUTING_TYPE));
192    }
193    
194    /**
195     * Get the computing type
196     * @param question the question
197     * @return the computing type
198     */
199    public ComputingType getComputingType(FormQuestion question)
200    {
201        String value = question.getValue(ATTRIBUTE_COMPUTING_TYPE);
202        return StringUtils.isNotBlank(value) ? _computingTypeExtensionPoint.getExtension(value) : null;
203    }
204    
205    @Override
206    public boolean onlyForDisplay(FormQuestion question)
207    {
208        ComputingType computingType = getComputingType(question);
209        if (computingType != null)
210        {
211            return !computingType.hasComputedValue();
212        }
213        return true;
214    }
215    
216    @Override
217    public boolean canBeAnsweredByUser(FormQuestion question)
218    {
219        return false;
220    }
221    
222    public I18nizableText getDefaultTitle()
223    {
224        return new I18nizableText("plugin.forms", DEFAULT_TITLE);
225    }
226    
227    @Override
228    public void saxEntryValue(ContentHandler contentHandler, FormQuestion question, FormEntry entry) throws SAXException
229    {
230        super.saxEntryValue(contentHandler, question, entry);
231        AttributesImpl attrs = new AttributesImpl();
232        ComputingType computingType = getComputingType(question);
233        attrs.addCDATAAttribute("computed-type", computingType.getId());
234        
235        XMLUtils.startElement(contentHandler, "additional-infos", attrs);
236
237        computingType.saxAdditionalValue(contentHandler, question, entry);
238        
239        XMLUtils.endElement(contentHandler, "additional-infos");
240        
241    }
242    
243    @Override
244    public List<String> getFieldToDisableIfFormPublished(FormQuestion question)
245    {
246        List<String> fieldNames =  super.getFieldToDisableIfFormPublished(question);
247        fieldNames.add(ATTRIBUTE_COMPUTING_TYPE);
248        
249        ComputingType computingType = getComputingType(question);
250        if (computingType != null)
251        {
252            for (String fieldName : computingType.getFieldToDisableIfFormPublished())
253            {
254                fieldNames.add(fieldName);
255            }
256        }
257        
258        fieldNames.add(ConfidentialAwareQuestionType.ATTRIBUTE_CONFIDENTIALITY);
259        
260        return fieldNames;
261    }
262
263    @Override
264    public String getJSRenderer(FormQuestion question)
265    {
266        ComputingType computingType = getComputingType(question);
267        return computingType.getJSRenderer();
268    }
269    
270    public Object valueToJSONForClient(Object value, FormQuestion question, FormEntry entry, ModelItem modelItem) throws Exception
271    {
272        Object valueToJSONForClient = super.valueToJSONForClient(value, question, entry, modelItem);
273        ComputingType computingType = getComputingType(question);
274        return computingType.valueToJSONForClient(valueToJSONForClient, question, entry, modelItem);
275    }
276}