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