001/*
002 *  Copyright 2015 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.data;
017
018import java.util.ArrayList;
019import java.util.HashMap;
020import java.util.Iterator;
021import java.util.LinkedHashMap;
022import java.util.List;
023import java.util.Map;
024import java.util.regex.Matcher;
025import java.util.regex.Pattern;
026
027import org.apache.avalon.framework.parameters.Parameters;
028import org.apache.avalon.framework.service.ServiceException;
029import org.apache.avalon.framework.service.ServiceManager;
030import org.apache.cocoon.ProcessingException;
031import org.apache.cocoon.acting.ServiceableAction;
032import org.apache.cocoon.environment.ObjectModelHelper;
033import org.apache.cocoon.environment.Redirector;
034import org.apache.cocoon.environment.Request;
035import org.apache.cocoon.environment.SourceResolver;
036
037import org.ametys.core.cocoon.JSonReader;
038import org.ametys.core.util.I18nUtils;
039import org.ametys.plugins.forms.Field;
040import org.ametys.plugins.forms.Field.FieldType;
041import org.ametys.plugins.forms.Form;
042import org.ametys.plugins.forms.FormsException;
043import org.ametys.plugins.forms.jcr.FormPropertiesManager;
044import org.ametys.plugins.forms.table.FormTableManager;
045import org.ametys.plugins.repository.AmetysObjectResolver;
046import org.ametys.plugins.workflow.store.JdbcWorkflowStore;
047import org.ametys.plugins.workflow.support.WorkflowProvider;
048import org.ametys.runtime.i18n.I18nizableText;
049import org.ametys.runtime.parameter.ParameterHelper;
050
051import com.opensymphony.workflow.Workflow;
052import com.opensymphony.workflow.loader.StepDescriptor;
053import com.opensymphony.workflow.loader.WorkflowDescriptor;
054import com.opensymphony.workflow.spi.Step;
055
056/**
057 * Get the submitted entries of a form
058 *
059 */
060public class GetFormEntriesAction extends ServiceableAction
061{
062    /** Pattern for options value */
063    protected static final Pattern __OPTION_VALUE_PATTERN = Pattern.compile("^option-([0-9]+)-value$");
064            
065    /** The internationalizable text symbolizing the absence of workflow step */
066    protected static final I18nizableText __MESSAGE_NO_STEP = new I18nizableText("plugin.forms", "PLUGINS_FORMS_UITOOL_ENTRY_WORKFLOW_NO_WORKFLOW");
067    
068    /** The form properties manager. */
069    protected FormPropertiesManager _formPropertiesManager;
070    
071    /** The form data manager. */
072    protected FormTableManager _formTableManager;
073    
074    /** The ametys object resolver. */
075    protected AmetysObjectResolver _resolver;
076    
077    /** The workflow provider */
078    protected WorkflowProvider _workflowProvider;
079    
080    /** Utility component for internationalizable text */
081    protected I18nUtils _i18nUtils;
082    
083    @Override
084    public void service(ServiceManager smanager) throws ServiceException
085    {
086        super.service(smanager);
087        _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE);
088        _formPropertiesManager = (FormPropertiesManager) smanager.lookup(FormPropertiesManager.ROLE);
089        _formTableManager = (FormTableManager) smanager.lookup(FormTableManager.ROLE);
090        _workflowProvider = (WorkflowProvider) smanager.lookup(WorkflowProvider.ROLE);
091        _i18nUtils = (I18nUtils) smanager.lookup(I18nUtils.ROLE);
092    }
093    
094    @Override
095    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
096    {
097        @SuppressWarnings("unchecked")
098        Map<String, Object> jsParameters = (Map<String, Object>) objectModel.get(ObjectModelHelper.PARENT_CONTEXT);
099        
100        String siteName = (String) jsParameters.get("siteName");
101        String formId = (String) jsParameters.get("id");
102        
103        int start = jsParameters.containsKey("start") ? (int) jsParameters.get("start") : 0;
104        int limit = jsParameters.containsKey("limit") ? (int) jsParameters.get("limit") : Integer.MAX_VALUE;
105        
106        Form form = _formPropertiesManager.getForm(siteName, formId);
107        
108        if (form == null)
109        {
110            throw new ProcessingException("The form of ID '" + formId + " can't be found in the site '" + siteName + "'.");
111        }
112        
113        Workflow workflow = _workflowProvider.getExternalWorkflow(JdbcWorkflowStore.ROLE);
114        Map<String, Object> result = new HashMap<>();
115        List<Map<String, Object>> entries2json = new ArrayList<>();
116        try
117        {
118            Map<String, FieldValue> columns = _formTableManager.getColumns(form);
119            List<UserEntry> entries = _formTableManager.getSubmissions(form, columns, start, limit, null);
120            int totalSubmissions = _formTableManager.getTotalSubmissions(form.getId());
121            
122            result.put("id", form.getId());
123            result.put("label", form.getLabel());
124            result.put("total", totalSubmissions);
125            
126            for (UserEntry entry : entries)
127            {
128                entries2json.add(_entry2json(entry, workflow));
129            }
130        }
131        catch (FormsException e)
132        {
133            getLogger().error("Failed to get SQL table for form '" + form.getId() + "' for content of id.", e);
134        }
135        
136        result.put("entries", entries2json);
137        
138        Request request = ObjectModelHelper.getRequest(objectModel);
139        request.setAttribute(JSonReader.OBJECT_TO_READ, result);
140        return EMPTY_MAP;
141    }
142    
143    private Map<String, Object> _entry2json(UserEntry entry, Workflow workflow)
144    {
145        Map<String, Object> entryAsMap = new HashMap<>();
146        
147        entryAsMap.put("id", entry.getId());
148        entryAsMap.put("submission-date", ParameterHelper.valueToString(entry.getCreationDate()));
149        
150        if (entry.getWorkflowId() != null)
151        {
152            entryAsMap.put("workflowStep", _workflow2json (entry, workflow));
153        }
154        
155        for (FieldValue fdValue : entry.getValues())
156        {
157            Object rawValue = fdValue.getValue();
158            if (rawValue != null)
159            {
160                entryAsMap.put(fdValue.getColumnName() + "_raw", rawValue);
161                entryAsMap.put(fdValue.getColumnName(), _getReadableValue(fdValue.getField(), rawValue));
162            }
163        }
164        
165        return entryAsMap;
166    }
167    
168    private Map<String, Object> _workflow2json (UserEntry entry, Workflow workflow)
169    {
170        Map<String, Object> workflowInfos = new LinkedHashMap<>();
171        
172        int currentStepId = 0;
173        
174        long workflowId = entry.getWorkflowId();
175        Iterator<Step> currentSteps = workflow.getCurrentSteps(workflowId).iterator();
176        
177        while (currentSteps.hasNext())
178        {
179            Step step = currentSteps.next();
180            currentStepId = step.getStepId();
181        }
182        
183        String workflowName = workflow.getWorkflowName(workflowId);
184        WorkflowDescriptor workflowDescriptor = workflow.getWorkflowDescriptor(workflowName);
185        StepDescriptor stepDescriptor = workflowDescriptor.getStep(currentStepId);
186        
187        if (stepDescriptor != null)
188        {
189            I18nizableText workflowStepName = new I18nizableText("application", stepDescriptor.getName());
190            
191            workflowInfos.put("stepId", currentStepId);
192            workflowInfos.put("name", workflowStepName);
193            
194            String[] icons = new String[] {"small", "medium", "large"};
195            for (String icon : icons)
196            {
197                if ("application".equals(workflowStepName.getCatalogue()))
198                {
199                    workflowInfos.put(icon + "Icon", "/plugins/cms/resources_workflow/" + workflowStepName.getKey() + "-" + icon + ".png");
200                }
201                else
202                {
203                    String pluginName = workflowStepName.getCatalogue().substring("plugin.".length());
204                    workflowInfos.put(icon + "Icon", "/plugins/" + pluginName + "/resources/img/workflow/" + workflowStepName.getKey() + "-" + icon + ".png");
205                }
206            }
207        }
208        
209        return workflowInfos;
210    }
211    
212    /**
213     * Get label to display for field
214     * @param field The field
215     * @param value The value
216     * @return The value to display
217     */
218    protected String _getReadableValue (Field field, Object value)
219    {
220        if (field.getType().equals(FieldType.SELECT) || field.getType().equals(FieldType.RADIO))
221        {
222            Map<String, String> properties = field.getProperties();
223            for (String key : properties.keySet())
224            {
225                Matcher matcher = __OPTION_VALUE_PATTERN.matcher(key);
226                if (matcher.matches())
227                {
228                    if (value.equals(properties.get(key)))
229                    {
230                        String index = matcher.group(1);
231                        if (properties.containsKey("option-" + index + "-label"))
232                        {
233                            return properties.get("option-" + index + "-label");
234                        }
235                    }
236                }
237            }
238        }
239        
240        return ParameterHelper.valueToString(value);
241    }
242
243}