001/*
002 *  Copyright 2023 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.actions;
017
018import java.util.List;
019import java.util.Map;
020import java.util.Optional;
021
022import org.apache.avalon.framework.service.ServiceException;
023import org.apache.avalon.framework.service.ServiceManager;
024import org.apache.cocoon.acting.ServiceableAction;
025
026import org.ametys.core.right.RightManager;
027import org.ametys.plugins.forms.dao.FormDAO;
028import org.ametys.plugins.forms.dao.FormEntryDAO;
029import org.ametys.plugins.forms.dao.FormQuestionDAO;
030import org.ametys.plugins.forms.dao.FormQuestionDAO.FormEntryValues;
031import org.ametys.plugins.forms.question.FormQuestionType;
032import org.ametys.plugins.forms.question.sources.ChoiceSourceType;
033import org.ametys.plugins.forms.question.types.ChoicesListQuestionType;
034import org.ametys.plugins.forms.question.types.ComputedQuestionType;
035import org.ametys.plugins.forms.repository.Form;
036import org.ametys.plugins.forms.repository.FormEntry;
037import org.ametys.plugins.forms.repository.FormQuestion;
038import org.ametys.plugins.repository.AmetysObjectResolver;
039import org.ametys.runtime.model.View;
040import org.ametys.runtime.model.ViewItem;
041import org.ametys.web.FOAmetysObjectCreationHelper;
042
043/**
044 * Abstract action to process form entry inputs
045 */
046public abstract class AbstractProcessFormAction extends ServiceableAction
047{
048    /** The ametys object resolver */
049    protected AmetysObjectResolver _resolver;
050    
051    /** The FO ametys object creation helper */
052    protected FOAmetysObjectCreationHelper _foAmetysObjectCreationHelper;
053
054    /** The form DAO */
055    protected FormDAO _formDAO;
056    
057    /** The form entry DAO */
058    protected FormEntryDAO _entryDAO;
059    
060    /** The form question DAO */
061    protected FormQuestionDAO _formQuestionDAO;
062    
063    /** The right manager */
064    protected RightManager _rightManager;
065    
066    @Override
067    public void service(ServiceManager smanager) throws ServiceException
068    {
069        super.service(smanager);
070        _foAmetysObjectCreationHelper = (FOAmetysObjectCreationHelper) smanager.lookup(FOAmetysObjectCreationHelper.ROLE);
071        _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE);
072        _formDAO = (FormDAO) smanager.lookup(FormDAO.ROLE);
073        _entryDAO = (FormEntryDAO) smanager.lookup(FormEntryDAO.ROLE);
074        _formQuestionDAO = (FormQuestionDAO) smanager.lookup(FormQuestionDAO.ROLE);
075        _rightManager = (RightManager) smanager.lookup(RightManager.ROLE);        
076    }
077    
078    /**
079     * Remove empty value for choice list because empty value is an other value
080     * @param form the form
081     * @param formInputValues the form inputs to change
082     */
083    protected void _adaptFormValuesForChoiceList(Form form, Map<String, Object> formInputValues)
084    {
085        List<FormQuestion> choiceListQuestions = form.getQuestions()
086            .stream()
087            .filter(q -> q.getType() instanceof ChoicesListQuestionType)
088            .toList();
089        
090        for (FormQuestion question : choiceListQuestions)
091        {
092            ChoicesListQuestionType type = (ChoicesListQuestionType) question.getType();
093            ChoiceSourceType sourceType = type.getSourceType(question);
094            
095            String nameForForm = question.getNameForForm();
096            if (formInputValues.containsKey(nameForForm))
097            {
098                Object object = formInputValues.get(nameForForm);
099                Object newValue = sourceType.removeEmptyOrOtherValue(object);
100                if (newValue == null)
101                {
102                    formInputValues.remove(nameForForm);
103                }
104                else
105                {
106                    formInputValues.put(nameForForm, newValue);
107                }
108                
109            }
110        }
111    }
112    
113    /**
114     * Get a view without elements hidden by a rule
115     * @param form The current form
116     * @param entryView The entry view with possibly unwanted viewItems
117     * @param entryValues The entry values 
118     * @param currentStepId current step of the entry. Can be empty if the form has no workflow
119     * @return a view with filtered items
120     */
121    protected View _getRuleFilteredEntryView(Form form, View entryView, FormEntryValues entryValues, Optional<Long> currentStepId)
122    {
123        View filteredEntryView = new View();
124        for (FormQuestion target : _getRuleFilteredQuestions(form, entryValues, currentStepId))
125        {
126            ViewItem viewItem = entryView.getViewItem(target.getNameForForm());
127            filteredEntryView.addViewItem(viewItem);
128            _manageOtherOption(entryView, filteredEntryView, target);
129        }
130        return filteredEntryView;
131    }
132    
133    /**
134     * Get the list of active question depending of the form rules
135     * @param form the form
136     * @param entryValues the entry values to compute rules
137     * @param currentStepId the current step id. Can be empty if the form has no workflow
138     * @return the list of active question depending of the form rules
139     */
140    protected abstract List<FormQuestion> _getRuleFilteredQuestions(Form form, FormEntryValues entryValues, Optional<Long> currentStepId);
141    
142    private void _manageOtherOption(View entryView, View filteredEntryView, FormQuestion target)
143    {
144        if (target.getType() instanceof ChoicesListQuestionType type && type.hasOtherOption(target))
145        {
146            ViewItem viewOtherItem = entryView.getViewItem(ChoicesListQuestionType.OTHER_PREFIX_DATA_NAME + target.getNameForForm());
147            filteredEntryView.addViewItem(viewOtherItem);
148        }
149    }
150    
151    /**
152     * Handle computed values
153     * @param questions the form questions
154     * @param entry the entry
155     * @param forEdition <code>true</code> to handle edition
156     */
157    protected void _handleComputedValues(List<FormQuestion> questions, FormEntry entry, boolean forEdition)
158    {
159        for (FormQuestion question : questions)
160        {
161            FormQuestionType questionType = question.getType();
162            if (questionType instanceof ComputedQuestionType type)
163            {
164                if (!forEdition || type.getComputingType(question).canEdit())
165                {
166                    Object computedValue = type.getComputingType(question).getComputedValue(question, entry);
167                    if (computedValue != null)
168                    {
169                        entry.setValue(question.getNameForForm(), computedValue);
170                    }
171                }
172            }
173        }
174    }
175}