001/*
002 *  Copyright 2017 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.odf.workflow;
017
018import java.util.Collection;
019import java.util.HashMap;
020import java.util.HashSet;
021import java.util.List;
022import java.util.Map;
023import java.util.Set;
024
025import org.apache.avalon.framework.component.Component;
026import org.apache.avalon.framework.service.ServiceException;
027import org.apache.avalon.framework.service.ServiceManager;
028import org.apache.avalon.framework.service.Serviceable;
029import org.apache.commons.lang3.StringUtils;
030
031import org.ametys.cms.repository.Content;
032import org.ametys.cms.repository.WorkflowAwareContent;
033import org.ametys.core.ui.Callable;
034import org.ametys.odf.ODFHelper;
035import org.ametys.odf.ProgramItem;
036import org.ametys.odf.course.Course;
037import org.ametys.odf.orgunit.OrgUnit;
038import org.ametys.odf.program.AbstractProgram;
039import org.ametys.plugins.repository.AmetysObjectResolver;
040import org.ametys.plugins.repository.UnknownAmetysObjectException;
041import org.ametys.plugins.workflow.support.WorkflowProvider;
042import org.ametys.plugins.workflow.support.WorkflowProvider.AmetysObjectWorkflow;
043import org.ametys.runtime.plugin.component.AbstractLogEnabled;
044
045import com.opensymphony.workflow.spi.Step;
046
047/**
048 * Helper for ODF contents on their workflow
049 *
050 */
051public class ODFWorkflowHelper extends AbstractLogEnabled implements Component, Serviceable
052{
053    /** The component role. */
054    public static final String ROLE = ODFWorkflowHelper.class.getName();
055    
056    /** The validate step id */
057    public static final int VALIDATED_STEP_ID = 3;
058    
059    /** The Ametys object resolver */
060    protected AmetysObjectResolver _resolver;
061    /** The workflow provider */
062    protected WorkflowProvider _workflowProvider;
063    /** The ODF helper */
064    protected ODFHelper _odfHelper;
065    
066    @Override
067    public void service(ServiceManager manager) throws ServiceException
068    {
069        _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
070        _workflowProvider = (WorkflowProvider) manager.lookup(WorkflowProvider.ROLE);
071        _odfHelper = (ODFHelper) manager.lookup(ODFHelper.ROLE);
072    }
073    
074    /**
075     * Check if the contents can be validated. A content can be validated only if all its referenced contents are already validated.
076     * @param contentIds The id of contents to check
077     * @return A map with success key to true if the content can be validated. A map with the invalidated contents otherwise.
078     */
079    @Callable
080    public Map<String, Object> canValidContent(List<String> contentIds)
081    {
082        Map<String, Object> result = new HashMap<>();
083        
084        Map<String, Object> contentsInError = new HashMap<>();
085        
086        for (String contentId : contentIds)
087        {
088            WorkflowAwareContent content = _resolver.resolveById(contentId);
089            
090            Map<String, String> invalidatedContents = new HashMap<>();
091            _checkValidateStep(content, invalidatedContents);
092            
093            if (!invalidatedContents.isEmpty())
094            {
095                contentsInError.put(content.getTitle(), invalidatedContents);
096            }
097        }
098        result.put("contentsInError", contentsInError);
099        result.put("success", contentsInError.isEmpty());
100        return result;
101    }
102    
103    /**
104     * Determines if a content is already in validated step
105     * @param content The content to test
106     * @return true if the content is already validated
107     */
108    public boolean isInValidatedStep (WorkflowAwareContent content)
109    {
110        AmetysObjectWorkflow workflow = _workflowProvider.getAmetysObjectWorkflow(content);
111        long workflowId = content.getWorkflowId();
112        
113        List<Step> steps = workflow.getCurrentSteps(workflowId);
114        for (Step step : steps)
115        {
116            if (step.getStepId() == VALIDATED_STEP_ID)
117            {
118                return true;
119            }
120        }
121        
122        return false;
123    }
124    
125    private void _checkValidateStep (WorkflowAwareContent content, Map<String, String> invalidatedContent)
126    {
127        if (content instanceof ProgramItem)
128        {
129            // Check the structure recursively
130            List<ProgramItem> children = _odfHelper.getChildProgramItems((ProgramItem) content);
131            for (ProgramItem child : children)
132            {
133                WorkflowAwareContent waChild = (WorkflowAwareContent) child;
134                if (!isInValidatedStep(waChild))
135                {
136                    if (!invalidatedContent.containsKey(waChild.getId()))
137                    {
138                        invalidatedContent.put(waChild.getId(), _getTitle(waChild));
139                    }
140                }
141                else
142                {
143                    // The item is already validated. Continue to its own children.
144                    _checkValidateStep(waChild, invalidatedContent);
145                }
146            }
147        }
148        
149        // Validate others referenced contents
150        if (content instanceof AbstractProgram)
151        {
152            _checkReferencedContents(((AbstractProgram) content).getOrgUnits(), invalidatedContent);
153            _checkReferencedContents(((AbstractProgram) content).getContacts(), invalidatedContent);
154
155            Set<String> persons = new HashSet<>();
156            Map<String, String[]> personsInChargeByRole = ((AbstractProgram) content).getPersonsInCharge();
157            for (String role : personsInChargeByRole.keySet())
158            {
159                for (String person : personsInChargeByRole.get(role))
160                {
161                    persons.add(person);
162                }
163            }
164            _checkReferencedContents(persons, invalidatedContent);
165        }
166        else if (content instanceof Course)
167        {
168            _checkReferencedContents(((Course) content).getOrgUnits(), invalidatedContent);
169            _checkReferencedContents(((Course) content).getContacts(), invalidatedContent);
170            _checkReferencedContents(((Course) content).getPersonsInCharge(), invalidatedContent);
171        }
172        else if (content instanceof OrgUnit)
173        {
174            _checkReferencedContents(((OrgUnit) content).getContacts(), invalidatedContent);
175        }
176    }
177    
178    private String _getTitle(Content content)
179    {
180        String title = content.getTitle();
181        if (content instanceof ProgramItem)
182        {
183            title += " (" + ((ProgramItem) content).getCode() + ")";
184        }
185        return title;
186    }
187    
188    private void _checkReferencedContents (Collection<String> refContentIds, Map<String, String> invalidatedContent)
189    {
190        for (String id : refContentIds)
191        {
192            try
193            {
194                if (StringUtils.isNotEmpty(id))
195                {
196                    WorkflowAwareContent refContent = _resolver.resolveById(id);
197                    _checkValidateStep(refContent, invalidatedContent);
198                }
199            }
200            catch (UnknownAmetysObjectException e)
201            {
202                // Nothing
203            }
204        }
205    }
206}