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}