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.cms.search.systemprop;
017
018import java.util.ArrayList;
019import java.util.LinkedHashMap;
020import java.util.List;
021import java.util.Map;
022
023import org.apache.avalon.framework.configuration.Configuration;
024import org.apache.avalon.framework.configuration.DefaultConfiguration;
025import org.apache.avalon.framework.service.ServiceException;
026import org.apache.avalon.framework.service.ServiceManager;
027import org.apache.cocoon.xml.AttributesImpl;
028import org.apache.cocoon.xml.XMLUtils;
029import org.xml.sax.ContentHandler;
030import org.xml.sax.SAXException;
031
032import org.ametys.cms.contenttype.MetadataType;
033import org.ametys.cms.repository.Content;
034import org.ametys.cms.repository.WorkflowAwareContent;
035import org.ametys.cms.search.SearchField;
036import org.ametys.cms.search.model.SystemProperty;
037import org.ametys.cms.search.query.OrQuery;
038import org.ametys.cms.search.query.Query;
039import org.ametys.cms.search.query.Query.Operator;
040import org.ametys.cms.search.query.WorkflowStepQuery;
041import org.ametys.cms.search.solr.field.WorkflowStepSearchField;
042import org.ametys.cms.workflow.DefaultWorkflowStepEnumerator;
043import org.ametys.plugins.workflow.support.WorkflowHelper;
044import org.ametys.plugins.workflow.support.WorkflowProvider;
045import org.ametys.plugins.workflow.support.WorkflowProvider.AmetysObjectWorkflow;
046import org.ametys.runtime.i18n.I18nizableText;
047
048import com.opensymphony.workflow.loader.StepDescriptor;
049import com.opensymphony.workflow.loader.WorkflowDescriptor;
050import com.opensymphony.workflow.spi.Step;
051
052/**
053 * {@link SystemProperty} which represents the current workflow step ID of a content.
054 */
055public class WorkflowStepSystemProperty extends AbstractSystemProperty
056{
057    /** The workflow provider */
058    protected WorkflowProvider _workflowProvider;
059    
060    /** The workflow helper */
061    protected WorkflowHelper _workflowHelper;
062    
063    @Override
064    public void service(ServiceManager manager) throws ServiceException
065    {
066        super.service(manager);
067        _workflowProvider = (WorkflowProvider) manager.lookup(WorkflowProvider.ROLE);
068        _workflowHelper = (WorkflowHelper) manager.lookup(WorkflowHelper.ROLE);
069    }
070    
071    @Override
072    public MetadataType getType()
073    {
074        return MetadataType.LONG;
075    }
076    
077    @Override
078    public boolean isMultiple()
079    {
080        return false;
081    }
082    
083    @Override
084    public boolean isSortable()
085    {
086        return true;
087    }
088    
089    @SuppressWarnings("unchecked")
090    @Override
091    public Query getQuery(Object value, Operator operator, String language, Map<String, Object> contextualParameters)
092    {
093        if (value == null)
094        {
095            return new WorkflowStepQuery(Operator.EXISTS);
096        }
097        
098        List<Object> values;
099        if (value instanceof List)
100        {
101            values = (List<Object>) value;
102        }
103        else
104        {
105            values = List.of(value);
106        }
107        
108        List<Query> queries = new ArrayList<>();
109        for (Object val : values)
110        {
111            if (!"".equals(val)) // empty value when selecting "All" entry in the widget
112            {
113                long stepId = parseLong(val);
114                if (stepId != 0)
115                {
116                    queries.add(new WorkflowStepQuery(operator, (int) stepId));
117                }
118            }
119        }
120        
121        switch (queries.size())
122        {
123            case 0: return null;
124            case 1: return queries.get(0);
125            default: return new OrQuery(queries);
126        }
127    }
128    
129    @Override
130    public SearchField getSearchField()
131    {
132        return new WorkflowStepSearchField();
133    }
134    
135    @Override
136    public String getRenderer()
137    {
138        return "Ametys.plugins.cms.search.SearchGridHelper.renderWorkflowStep";
139    }
140    
141    @Override
142    public String getConverter()
143    {
144        return "Ametys.plugins.cms.search.SearchGridHelper.convertWorkflowStep";
145    }
146    
147    @Override
148    public Integer getColumnWidth()
149    {
150        return 60;
151    }
152    
153    @Override
154    public Object getValue(Content content)
155    {
156        if (content instanceof WorkflowAwareContent)
157        {
158            WorkflowAwareContent waContent = (WorkflowAwareContent) content;
159            AmetysObjectWorkflow workflow = _workflowProvider.getAmetysObjectWorkflow(waContent);
160            
161            long workflowId = waContent.getWorkflowId();
162            List<Step> currentSteps = workflow.getCurrentSteps(workflowId);
163            
164            if (!currentSteps.isEmpty())
165            {
166                return Long.valueOf(currentSteps.iterator().next().getStepId());
167            }
168        }
169        
170        return null;
171    }
172    
173    public Object getJsonValue(Content content, boolean full)
174    {
175        Long value = (Long) getValue(content);
176        if (value != null && full)
177        {
178            Map<String, Object> workflowInfos = new LinkedHashMap<>();
179            
180            if (content instanceof WorkflowAwareContent)
181            {
182                WorkflowAwareContent waContent = (WorkflowAwareContent) content;
183                int currentStepId = Math.toIntExact(value);
184                
185                StepDescriptor stepDescriptor = _getStepDescriptor(waContent, 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            
210            return workflowInfos;
211        }
212        return value;
213    }
214    
215    @Override
216    public void saxValue(ContentHandler handler, Content content) throws SAXException
217    {
218        Long value = (Long) getValue(content);
219        if (value != null && content instanceof WorkflowAwareContent)
220        {
221            WorkflowAwareContent waContent = (WorkflowAwareContent) content;
222            int currentStepId = Math.toIntExact(value);
223            
224            StepDescriptor stepDescriptor = _getStepDescriptor(waContent, currentStepId);
225            
226            if (stepDescriptor != null)
227            {
228                I18nizableText workflowStepName = new I18nizableText("application", stepDescriptor.getName());
229                
230                AttributesImpl attr = new AttributesImpl();
231                attr.addCDATAAttribute("id", String.valueOf(currentStepId));
232                XMLUtils.startElement(handler, getId(), attr);
233                workflowStepName.toSAX(handler);
234                XMLUtils.endElement(handler, getId());
235            }
236        }
237    }
238    
239    private StepDescriptor _getStepDescriptor(WorkflowAwareContent content, int stepId)
240    {
241        long workflowId = content.getWorkflowId();
242        
243        AmetysObjectWorkflow workflow = _workflowProvider.getAmetysObjectWorkflow(content);
244        
245        String workflowName = workflow.getWorkflowName(workflowId);
246        WorkflowDescriptor workflowDescriptor = workflow.getWorkflowDescriptor(workflowName);
247        
248        if (workflowDescriptor != null)
249        {
250            StepDescriptor stepDescriptor = workflowDescriptor.getStep(stepId);
251            if (stepDescriptor != null)
252            {
253                return stepDescriptor;
254            }
255            else if (getLogger().isWarnEnabled())
256            {
257                getLogger().warn("Unknown step id '" + stepId + "' for workflow for name : " + workflowName);
258            }
259        }
260        else if (getLogger().isWarnEnabled())
261        {
262            getLogger().warn("Unknown workflow for name : " + workflowName);
263        }
264        
265        return null;
266    }
267    
268    @Override
269    public EnumeratorDefinition getEnumeratorDefinition(Configuration configuration)
270    {
271        DefaultConfiguration conf = new DefaultConfiguration("criteria");
272        
273        String workflowName = configuration.getChild("workflow").getAttribute("name", "content");
274        
275        DefaultConfiguration enumConf = new DefaultConfiguration("enumeration");
276        
277        DefaultConfiguration customEnumerator = new DefaultConfiguration("custom-enumerator");
278        customEnumerator.setAttribute("class", DefaultWorkflowStepEnumerator.class.getName());
279        enumConf.addChild(customEnumerator);
280        
281        DefaultConfiguration wfNameConf = new DefaultConfiguration("workflow-name");
282        wfNameConf.setValue(workflowName);
283        customEnumerator.addChild(wfNameConf);
284        
285        DefaultConfiguration excludeConf = new DefaultConfiguration("exclude-workflow-steps");
286        
287        // Exclude workflow steps greater than 9000
288        List<StepDescriptor> steps = _workflowHelper.getWorkflowDescriptor(workflowName).getSteps();
289        for (StepDescriptor stepDescriptor : steps)
290        {
291            if (stepDescriptor.getId() >= 9000)
292            {
293                DefaultConfiguration stepId = new DefaultConfiguration("id");
294                stepId.setValue(stepDescriptor.getId());
295                excludeConf.addChild(stepId);
296            }
297        }
298        
299        customEnumerator.addChild(excludeConf);
300        
301        conf.addChild(enumConf);
302        
303        return new EnumeratorDefinition(DefaultWorkflowStepEnumerator.class, conf);
304    }
305
306}