001/*
002 *  Copyright 2022 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.generators;
017
018import java.io.IOException;
019import java.time.ZoneId;
020import java.time.ZonedDateTime;
021import java.util.List;
022import java.util.Map;
023import java.util.stream.Collectors;
024
025import org.apache.avalon.framework.service.ServiceException;
026import org.apache.avalon.framework.service.ServiceManager;
027import org.apache.cocoon.ProcessingException;
028import org.apache.cocoon.environment.ObjectModelHelper;
029import org.apache.cocoon.environment.Request;
030import org.apache.cocoon.generation.ServiceableGenerator;
031import org.apache.cocoon.xml.AttributesImpl;
032import org.apache.cocoon.xml.XMLUtils;
033import org.apache.commons.lang3.StringUtils;
034import org.xml.sax.SAXException;
035
036import org.ametys.core.user.CurrentUserProvider;
037import org.ametys.core.user.UserIdentity;
038import org.ametys.core.user.UserManager;
039import org.ametys.core.util.DateUtils;
040import org.ametys.plugins.forms.helper.FormMailHelper;
041import org.ametys.plugins.forms.question.FormQuestionType;
042import org.ametys.plugins.forms.question.computing.ComputingType;
043import org.ametys.plugins.forms.question.types.ComputedQuestionType;
044import org.ametys.plugins.forms.repository.FormEntry;
045import org.ametys.plugins.forms.repository.FormQuestion;
046import org.ametys.plugins.repository.AmetysObjectResolver;
047import org.ametys.runtime.authentication.AccessDeniedException;
048
049/**
050 * Generate the entry of a form
051 */
052public class FormEntryInformationGenerator extends ServiceableGenerator
053{
054    /** The current user provider */
055    protected CurrentUserProvider _currentUserProvider;
056    
057    /** The Ametys Object resolver */
058    protected AmetysObjectResolver _resolver;
059    
060    /** The user manager */
061    protected UserManager _userManager;
062    
063    @Override
064    public void service(ServiceManager smanager) throws ServiceException
065    {
066        super.service(smanager);
067        _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE);
068        _currentUserProvider = (CurrentUserProvider) smanager.lookup(CurrentUserProvider.ROLE);
069        _userManager = (UserManager) smanager.lookup(UserManager.ROLE);
070    }
071    
072    public void generate() throws IOException, SAXException, ProcessingException
073    {
074        String entryId = _getFormEntryId();
075        
076        contentHandler.startDocument();
077        
078        try
079        {
080            FormEntry entry = _resolver.resolveById(entryId);
081            
082            _checkRights(entry);
083            
084            _saxEntry(entry);
085        }
086        catch (SAXException e) 
087        {
088            getLogger().error("An error occurred saxing entry information for entry '" + entryId + "'", e);
089        }
090        
091        contentHandler.endDocument();
092    }
093    
094    /**
095     * Check right before saxing entry
096     * @param entry the entry
097     */
098    protected void _checkRights(FormEntry entry)
099    {
100        Request request = ObjectModelHelper.getRequest(objectModel);
101        Boolean ignoreRight = (Boolean) request.getAttribute(FormMailHelper.IGNORE_RIGHT_KEY);
102        
103        if (ignoreRight == null || !ignoreRight)
104        {
105            UserIdentity currentUser = _currentUserProvider.getUser();
106            if (!entry.getUser().equals(currentUser))
107            {
108                throw new AccessDeniedException("User '" + currentUser + "' is not allowed to access to user entry data.");
109            }
110        }
111    }
112
113    /**
114     * Sax the entry
115     * @param entry the entry
116     * @throws SAXException if a saxing error occurred
117     */
118    protected void _saxEntry(FormEntry entry) throws SAXException
119    {
120        AttributesImpl formAttrs = new AttributesImpl();
121        formAttrs.addCDATAAttribute("formId", entry.getForm().getId());
122        formAttrs.addCDATAAttribute("label", entry.getForm().getTitle());
123        formAttrs.addCDATAAttribute("entryId", String.valueOf(entry.getEntryId()));
124        
125        ZonedDateTime submittedAt = entry.getSubmitDate().toInstant().atZone(ZoneId.systemDefault());
126        formAttrs.addCDATAAttribute("id", entry.getId());
127        formAttrs.addCDATAAttribute("creationDate", DateUtils.zonedDateTimeToString(submittedAt));
128        UserIdentity user = entry.getUser();
129        if (user != null)
130        {
131            formAttrs.addCDATAAttribute("user", _userManager.getUser(user).getFullName());
132            formAttrs.addCDATAAttribute("userId", UserIdentity.userIdentityToString(user));
133        }
134        
135        Long entryId = entry.getEntryId();
136        if (entryId != null)
137        {
138            formAttrs.addCDATAAttribute("entryId", String.valueOf(entryId));
139        }
140        
141        _addAdditionalEntryAttributes(entry, formAttrs);
142        
143        XMLUtils.startElement(contentHandler, "entry", formAttrs);
144        
145        List<FormQuestion> questions = entry.getForm()
146              .getQuestions()
147              .stream()
148              .filter(this::_displayField)
149              .collect(Collectors.toList());
150        
151        for (FormQuestion question : questions)
152        {
153            AttributesImpl attrs = new AttributesImpl();
154            attrs.addCDATAAttribute("type", question.getType().getStorageType(question));
155            attrs.addCDATAAttribute("typeId", question.getType().getId());
156            attrs.addCDATAAttribute("label", question.getTitle());
157            attrs.addCDATAAttribute("name", question.getNameForForm());
158            
159            XMLUtils.startElement(contentHandler, "field", attrs);
160            question.getType().saxEntryValue(contentHandler, question, entry);
161            XMLUtils.endElement(contentHandler, "field");
162        }
163        
164        XMLUtils.endElement(contentHandler, "entry");
165    }
166    
167    /**
168     * Add additional entry attributes
169     * @param entry the entry
170     * @param attrs the attributes
171     */
172    protected void _addAdditionalEntryAttributes(FormEntry entry, AttributesImpl attrs)
173    {
174        // Do nothing
175    }
176
177    private boolean _displayField(FormQuestion question)
178    {
179        FormQuestionType type = question.getType();
180        // This generator is used for front display or mail. Do not display computed field with only server computed value
181        if (type instanceof ComputedQuestionType questionType)
182        {
183            ComputingType computingType = questionType.getComputingType(question);
184            return StringUtils.isNotBlank(questionType.getDisplayXSLT()) && computingType.hasComputedValue();
185        }
186        return !type.onlyForDisplay(question);
187    }
188    
189    private String _getFormEntryId()
190    {
191        Request request = ObjectModelHelper.getRequest(objectModel);
192        @SuppressWarnings("unchecked")
193        Map<String, Object> params = (Map<String, Object>) objectModel.get(ObjectModelHelper.PARENT_CONTEXT);
194        String entryId = (String) request.get("entryId");
195        if (entryId == null)
196        {
197            entryId = (String) params.get("entryId");
198        }
199        return entryId;
200    }
201}