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.HashMap;
019import java.util.List;
020import java.util.Map;
021import java.util.Optional;
022
023import org.apache.avalon.framework.parameters.Parameters;
024import org.apache.avalon.framework.service.ServiceException;
025import org.apache.avalon.framework.service.ServiceManager;
026import org.apache.cocoon.environment.ObjectModelHelper;
027import org.apache.cocoon.environment.Redirector;
028import org.apache.cocoon.environment.Request;
029import org.apache.cocoon.environment.SourceResolver;
030import org.apache.commons.lang3.StringUtils;
031
032import org.ametys.core.right.RightManager.RightResult;
033import org.ametys.core.user.CurrentUserProvider;
034import org.ametys.plugins.forms.dao.FormEntryDAO;
035import org.ametys.plugins.forms.dao.FormQuestionDAO.FormEntryValues;
036import org.ametys.plugins.forms.helper.FormAdminDashboardHelper;
037import org.ametys.plugins.forms.question.types.FileQuestionType;
038import org.ametys.plugins.forms.repository.Form;
039import org.ametys.plugins.forms.repository.FormEntry;
040import org.ametys.plugins.forms.repository.FormQuestion;
041import org.ametys.plugins.repository.RepositoryConstants;
042import org.ametys.plugins.repository.data.holder.values.UntouchedValue;
043import org.ametys.plugins.repository.provider.RequestAttributeWorkspaceSelector;
044import org.ametys.runtime.i18n.I18nizableText;
045import org.ametys.runtime.model.View;
046
047import com.google.common.collect.ArrayListMultimap;
048import com.google.common.collect.Multimap;
049
050/**
051 * Action to edit the given form entry
052 */
053public class EditFormEntryAction extends AbstractProcessFormAction
054{
055    /** The form admin dashboard helper */
056    protected FormAdminDashboardHelper _formAdminDashboardHelper;
057    
058    /** The current user provider */
059    protected CurrentUserProvider _currentUserProvider;
060    
061    @Override
062    public void service(ServiceManager smanager) throws ServiceException
063    {
064        super.service(smanager);
065        _formAdminDashboardHelper = (FormAdminDashboardHelper) smanager.lookup(FormAdminDashboardHelper.ROLE);
066        _currentUserProvider = (CurrentUserProvider) smanager.lookup(CurrentUserProvider.ROLE);
067    }
068    
069    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
070    {
071        Request request = ObjectModelHelper.getRequest(objectModel);
072        // Retrieve the current workspace.
073        String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request);
074        try
075        {
076            // Force the workspace.
077            RequestAttributeWorkspaceSelector.setForcedWorkspace(request, RepositoryConstants.DEFAULT_WORKSPACE);
078            
079            String entryId = request.getParameter("entryId");
080            if (entryId == null)
081            {
082                throw new IllegalStateException("Impossible to edit entry. No id provided.");
083            }
084            
085            Multimap<String, I18nizableText> formErrors = ArrayListMultimap.create();
086            FormEntry entry = _resolver.resolveById(entryId);
087            boolean canSubmitterEdit = _canSubmitterEdit(entry);
088            if (!canSubmitterEdit && !_canManagerEdit(entry))
089            {
090                formErrors.put("form-access", new I18nizableText("plugin.forms", "PLUGINS_FORMS_EDIT_FORM_ENTRY_ERRORS"));
091                request.setAttribute("form", entry.getForm());
092                request.setAttribute("form-errors", formErrors);
093                return null;
094            }
095            
096            Optional<Long> currentStepId = Optional.of(_entryDAO.getCurrentStepId(entry));
097            
098            View view = View.of(entry.getModel());
099            
100            View filteredEntryView = _getRuleFilteredEntryView(request, entry.getForm(), view, new FormEntryValues(null, entry), currentStepId);
101            
102            Map<String, Object> values = _foAmetysObjectCreationHelper.getFormValues(request, filteredEntryView, "", formErrors);
103            _adaptFormValuesForChoiceList(entry.getForm(), values);
104            _adaptFormValuesForFile(request, entry.getForm(), values);
105            
106            Map<String, Object> additionalParameters = new HashMap<>();
107            additionalParameters.put("ignoreWriteRestriction", canSubmitterEdit);
108
109            formErrors.putAll(_foAmetysObjectCreationHelper.validateValues(values, filteredEntryView));
110            for (FormQuestion question : entry.getForm().getQuestions())
111            {
112                if (filteredEntryView.hasModelViewItem(question.getNameForForm()))
113                {
114                    question.getType().validateEntryValues(question, values, formErrors, currentStepId, additionalParameters);
115                }
116            }
117            
118            if (!formErrors.isEmpty())
119            {
120                request.setAttribute("form", entry.getForm());
121                request.setAttribute("form-errors", formErrors);
122                return null;
123            }
124            
125            entry.synchronizeValues(filteredEntryView, values);
126            _handleComputedValues(entry.getForm().getQuestions(), entry, true);
127            
128            entry.saveChanges();
129        }
130        finally 
131        {
132            // Restore context
133            RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp);
134        }
135        
136        return EMPTY_MAP;
137    }
138
139    @Override
140    protected List<FormQuestion> _getRuleFilteredQuestions(Request request, Form form, FormEntryValues entryValues, Optional<Long> currentStepId)
141    {
142        String isSubmitterAsString = request.getParameter("isSubmitter");
143        boolean isSubmitter = StringUtils.isNotBlank(isSubmitterAsString) ? Boolean.valueOf(isSubmitterAsString) : false;
144        
145        boolean canSubmitterEdit = isSubmitter && _canSubmitterEdit(entryValues.entry());
146        boolean onlyWritableQuestion = !canSubmitterEdit; // Get only writable questions if it's not the submitter
147        boolean onlyReadableQuestion = canSubmitterEdit; // Get only readable questions if it's the submitter
148        
149        return _formQuestionDAO.getRuleFilteredQuestions(form, entryValues, currentStepId, onlyWritableQuestion, onlyReadableQuestion);
150    }
151    
152    private void _adaptFormValuesForFile(Request request, Form form, Map<String, Object> values)
153    {
154        List<FormQuestion> fileQuestions = form.getQuestions()
155                .stream()
156                .filter(q -> q.getType() instanceof FileQuestionType)
157                .toList();
158            
159        for (FormQuestion question : fileQuestions)
160        {
161            String nameForForm = question.getNameForForm();
162            String fileInfo = request.getParameter(nameForForm + "-info");
163            if ("untouched".equals(fileInfo))
164            {
165                values.put(nameForForm, new UntouchedValue());
166            }
167        }
168    }
169    
170    private boolean _canManagerEdit(FormEntry entry)
171    {
172        // Manager can edit if the edit action is available and he has the right to handle the entry
173        return _rightManager.currentUserHasRight(FormEntryDAO.HANDLE_FORMS_ENTRIES_RIGHT_ID, entry) == RightResult.RIGHT_ALLOW  
174                && !_formAdminDashboardHelper.getAvailableActions(entry, List.of("edit")).isEmpty();
175    }
176    
177    private boolean _canSubmitterEdit(FormEntry entry)
178    {
179        // Submitter can edit if the edit-by-submitter action is available and he is the entry submitter
180        return _currentUserProvider.getUser().equals(entry.getUser()) 
181                && !_formAdminDashboardHelper.getAvailableActions(entry, List.of("edit-by-submitter")).isEmpty();
182    }
183}