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