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.util.ArrayList;
019import java.util.HashMap;
020import java.util.List;
021import java.util.Map;
022import java.util.Optional;
023
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.Request;
028import org.apache.cocoon.xml.AttributesImpl;
029import org.apache.cocoon.xml.XMLUtils;
030import org.apache.commons.lang3.StringUtils;
031import org.xml.sax.SAXException;
032
033import org.ametys.cms.workflow.AmetysObjectCheckRightsCondition;
034import org.ametys.core.user.User;
035import org.ametys.core.user.UserIdentity;
036import org.ametys.core.user.UserManager;
037import org.ametys.plugins.forms.data.Answer;
038import org.ametys.plugins.forms.helper.FormAdminDashboardHelper;
039import org.ametys.plugins.forms.helper.FormMailHelper;
040import org.ametys.plugins.forms.repository.Form;
041import org.ametys.plugins.forms.repository.FormEntry;
042import org.ametys.plugins.forms.repository.FormQuestion;
043import org.ametys.runtime.i18n.I18nizableText;
044import org.ametys.web.WebHelper;
045
046import com.opensymphony.workflow.Workflow;
047import com.opensymphony.workflow.loader.ActionDescriptor;
048import com.opensymphony.workflow.loader.WorkflowDescriptor;
049
050/**
051 * This class generates all the forms process information for current user
052 */
053public class FormAdminDashboardGenerator extends FormDashboardGenerator
054{
055    /** The default number of entries by page */
056    private static int __DEFAULT_NB_ENTRIES_BY_PAGE = 10;
057    
058    /** The user manager */
059    protected UserManager _userManager;
060    
061    /** The form mail helper */
062    protected FormMailHelper _formMailHelper;
063    
064    /** The form admin dashboard helper */
065    protected FormAdminDashboardHelper _formAdminDashboardHelper;
066    
067    @Override
068    public void service(ServiceManager smanager) throws ServiceException
069    {
070        super.service(smanager);
071        _userManager = (UserManager) smanager.lookup(UserManager.ROLE);
072        _formMailHelper = (FormMailHelper) smanager.lookup(FormMailHelper.ROLE);
073        _formAdminDashboardHelper = (FormAdminDashboardHelper) smanager.lookup(FormAdminDashboardHelper.ROLE);
074    }
075    
076    @Override
077    protected void _saxFormDashboardData(UserIdentity user) throws SAXException
078    {
079        XMLUtils.startElement(contentHandler, "dashboard");
080        
081        Request request = ObjectModelHelper.getRequest(objectModel);
082        String siteName = WebHelper.getSiteName(request);
083        
084        String formId = request.getParameter("formId");
085        // No need to sax the forms if a form is send in the request
086        if (StringUtils.isBlank(formId))
087        {
088            List<Form> forms = _formAdminDashboardHelper.getFormToAdmin(siteName, user);
089            // Sax the forms for user selection
090            _saxForms(forms);
091        }
092        else
093        {
094            // Sax form entries
095            _saxFormEntries(request, formId, user);
096        }
097        
098
099        XMLUtils.endElement(contentHandler, "dashboard");
100    }
101    
102    /**
103     * Sax the forms
104     * @param forms the list of form
105     * @throws SAXException if a saxing error occurred
106     */
107    protected void _saxForms(List<Form> forms) throws SAXException
108    {
109        XMLUtils.startElement(contentHandler, "forms");
110        for (Form form : forms)
111        {
112            AttributesImpl attrs = new AttributesImpl();
113            attrs.addCDATAAttribute("id", form.getId());
114            XMLUtils.startElement(contentHandler, "form", attrs);
115            
116            XMLUtils.createElement(contentHandler, "title", form.getTitle());
117            
118            String description = form.getDescription();
119            if (description != null)
120            {
121                XMLUtils.createElement(contentHandler, "description", description);
122            }
123            
124            XMLUtils.endElement(contentHandler, "form");
125        }
126        XMLUtils.endElement(contentHandler, "forms");
127    }
128    
129    /**
130     * Get available form entries for given page
131     * @param formId the form id
132     * @param page the page
133     * @param nbEntriesByPage the number of entries by page
134     * @return the entries record
135     */
136    protected EntriesRecord _getAvailableFormEntries(String formId, int page, int nbEntriesByPage)
137    {
138        List<FormEntry> availableEntries = new ArrayList<>();
139
140        Form form = _resolver.resolveById(formId);
141        List<FormEntry> entries = form.getEntries()
142                .stream()
143                .sorted((e1, e2) -> Long.compare(e1.getEntryId(), e2.getEntryId()))
144                .toList();
145        
146        int activeEntriesSize = entries.size();
147        int nbAnswer = 0;
148        int position = 0;
149        int lastEntryIndex = page * nbEntriesByPage;
150        boolean hasNextPage = false;
151        while (nbAnswer <= lastEntryIndex && position < activeEntriesSize)
152        {
153            FormEntry entry = entries.get(position);
154            if (_formAdminDashboardHelper.hasAvailableActions(entry))
155            {
156                if (nbAnswer == lastEntryIndex)
157                {
158                    hasNextPage = true;
159                }
160                else
161                {
162                    availableEntries.add(entry);
163                }
164                nbAnswer++;
165            }
166            position++;
167        }
168        
169        return new EntriesRecord(availableEntries, hasNextPage);
170    }
171    
172    /**
173     * Sax the form entries
174     * @param request the request
175     * @param formId the form id
176     * @param user the current user
177     * @throws SAXException if a saxing error occurred
178     */
179    protected void _saxFormEntries(Request request, String formId, UserIdentity user) throws SAXException
180    {
181        Form form = _resolver.resolveById(formId);
182        
183        int nbAnswerByPage = StringUtils.isNotBlank(request.getParameter("nbEntriesByPage")) ? Integer.parseInt(request.getParameter("nbEntriesByPage")) : __DEFAULT_NB_ENTRIES_BY_PAGE;
184        int page = request.getParameter("page") != null ? Integer.parseInt(request.getParameter("page")) : 1;
185        
186        EntriesRecord availableFormEntries = _getAvailableFormEntries(formId, page, nbAnswerByPage);
187        List<FormEntry> entries = availableFormEntries.entries();
188        
189        // Recalculate the page
190        int size = entries.size();
191        int mod = size % nbAnswerByPage;
192        page = mod == 0 && size != 0 ? (size / nbAnswerByPage) : (size / nbAnswerByPage) + 1;
193        List<FormEntry> entriesToSax = entries.subList((page - 1) * nbAnswerByPage, Math.min(page * nbAnswerByPage, size));
194        
195        // Sax entries
196        AttributesImpl attrs = new AttributesImpl();
197        attrs.addCDATAAttribute("formId", formId);
198        attrs.addCDATAAttribute("formTitle", form.getTitle());
199        attrs.addCDATAAttribute("page", String.valueOf(page));
200        attrs.addCDATAAttribute("hasNextPage", String.valueOf(availableFormEntries.hasNextEntries()));
201        XMLUtils.startElement(contentHandler, "answers", attrs);
202        try
203        {
204            for (FormEntry entry : entriesToSax)
205            {
206                _saxAnswer(_formEntry2Answer(entry), user);
207            }
208        }
209        catch (Exception e) 
210        {
211            getLogger().error("An error occurred saxing answers", e);
212        }
213        XMLUtils.endElement(contentHandler, "answers");
214    }
215    
216    @Override
217    protected void _saxWorkflowInformations(Answer answer, UserIdentity user) throws Exception
218    {
219        super._saxWorkflowInformations(answer, user);
220        
221        Workflow workflow = _getWorkflow(answer);
222        
223        FormEntry formEntry = _resolver.resolveById(answer.getId());
224        _saxMailUsersInformations(formEntry, user);
225        
226        Map<String, Object> inputs = new HashMap<>();
227        inputs.put(AmetysObjectCheckRightsCondition.AMETYS_OBJECT_KEY, formEntry);
228        
229        WorkflowDescriptor workflowDescriptor = _workflowHelper.getWorkflowDescriptor(answer.getWorkflowName());
230        int[] availableActions = workflow.getAvailableActions(answer.getWorkflowId(), inputs);
231        for (int actionId : availableActions)
232        {
233            ActionDescriptor action = workflowDescriptor.getAction(actionId);
234            
235            AttributesImpl attrs = new AttributesImpl();
236            attrs.addCDATAAttribute("id", String.valueOf(actionId));
237            
238            Map<String, Object> metaAttributes = action.getMetaAttributes();
239            String actionStatus = (String) metaAttributes.getOrDefault("status", String.valueOf(actionId));
240            attrs.addCDATAAttribute("status", actionStatus);
241            
242            _saxActionType(action, attrs);
243            
244            I18nizableText workflowActionLabel = new I18nizableText("application", action.getName());
245            
246            XMLUtils.startElement(contentHandler, "action", attrs);
247            workflowActionLabel.toSAX(contentHandler);
248            XMLUtils.endElement(contentHandler, "action");
249            
250        }
251    }
252    
253    private void _saxActionType(ActionDescriptor action, AttributesImpl attrs)
254    {
255        Map<String, Object> metaAttributes = action.getMetaAttributes();
256        
257        String actionType = (String) metaAttributes.get("action-type");
258        if (actionType != null)
259        {
260            attrs.addCDATAAttribute("type", actionType);
261        }
262    }
263    
264    private void _saxMailUsersInformations(FormEntry formEntry, UserIdentity user) throws SAXException
265    {
266        User currentUser = _userManager.getUser(user);
267        
268        XMLUtils.createElement(contentHandler, "sender-mail", currentUser.getEmail());
269        XMLUtils.createElement(contentHandler, "sender-fullname", currentUser.getFullName() + " <" + currentUser.getEmail() + ">");
270        
271        XMLUtils.startElement(contentHandler, "receivers");
272        User entryUser = _userManager.getUser(formEntry.getUser());
273
274        XMLUtils.startElement(contentHandler, "receiver");
275        XMLUtils.createElement(contentHandler, "receiver-mail", entryUser.getEmail());
276        XMLUtils.createElement(contentHandler, "receiver-fullname", entryUser.getFullName() + " <" + entryUser.getEmail() + ">");
277        XMLUtils.endElement(contentHandler, "receiver");
278        
279        for (FormQuestion question : _formMailHelper.getQuestionWithMail(formEntry.getForm().getId()))
280        {
281            Optional<String> receiver = _formMailHelper.getReceiver(formEntry, Optional.of(question.getNameForForm()));
282            if (receiver.isPresent())
283            {
284                XMLUtils.startElement(contentHandler, "receiver");
285                XMLUtils.createElement(contentHandler, "receiver-mail", receiver.get());
286                XMLUtils.createElement(contentHandler, "receiver-fullname", question.getTitle() + " <" + receiver.get() + ">");
287                XMLUtils.endElement(contentHandler, "receiver");
288            }
289        }
290        XMLUtils.endElement(contentHandler, "receivers");
291    }
292    
293    private record EntriesRecord(List<FormEntry> entries, boolean hasNextEntries) { /** empty */ }
294}