001/*
002 *  Copyright 2019 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.tree;
017
018import java.util.HashMap;
019import java.util.Map;
020
021import org.apache.avalon.framework.service.ServiceException;
022import org.apache.avalon.framework.service.ServiceManager;
023import org.apache.commons.lang3.StringUtils;
024
025import org.ametys.cms.repository.Content;
026import org.ametys.cms.repository.WorkflowAwareContent;
027import org.ametys.core.ui.Callable;
028import org.ametys.odf.ODFHelper;
029import org.ametys.odf.ProgramItem;
030import org.ametys.odf.course.Course;
031import org.ametys.odf.course.ShareableCourseHelper;
032import org.ametys.odf.course.ShareableCourseStatusHelper;
033import org.ametys.odf.course.ShareableCourseStatusHelper.ShareableStatus;
034import org.ametys.odf.courselist.CourseList;
035import org.ametys.odf.orgunit.OrgUnit;
036import org.ametys.plugins.contentstree.ContentsTreeHelper;
037import org.ametys.plugins.workflow.repository.WorkflowAwareAmetysObject;
038import org.ametys.plugins.workflow.support.WorkflowProvider;
039import org.ametys.plugins.workflow.support.WorkflowProvider.AmetysObjectWorkflow;
040import org.ametys.runtime.i18n.I18nizableText;
041
042import com.opensymphony.workflow.loader.StepDescriptor;
043import com.opensymphony.workflow.loader.WorkflowDescriptor;
044
045/**
046 * Helper ODF contents tree
047 *
048 */
049public class ODFContentsTreeHelper extends ContentsTreeHelper
050{
051    /** The Avalon role */
052    @SuppressWarnings("hiding")
053    public static final String ROLE = ODFContentsTreeHelper.class.getName();
054 
055    /** The ODF helper */
056    protected ODFHelper _odfHelper;
057    /** The workflow provider */
058    protected WorkflowProvider _workflowProvider;
059    /** The shareable course status helper */
060    protected ShareableCourseStatusHelper _shareableStatusHelper;
061    /** The shareable course helper */
062    protected ShareableCourseHelper _shareableCourseHelper;
063    
064    @Override
065    public void service(ServiceManager smanager) throws ServiceException
066    {
067        super.service(smanager);
068        
069        _workflowProvider = (WorkflowProvider) smanager.lookup(WorkflowProvider.ROLE);
070        _odfHelper = (ODFHelper) smanager.lookup(ODFHelper.ROLE);
071        _shareableCourseHelper = (ShareableCourseHelper) smanager.lookup(ShareableCourseHelper.ROLE);
072        _shareableStatusHelper = (ShareableCourseStatusHelper) smanager.lookup(ShareableCourseStatusHelper.ROLE);
073    }
074    
075    @Override
076    @Callable
077    public Map<String, Object> getRootNodeInformations(String contentId)
078    {
079        Map<String, Object> infos = super.getRootNodeInformations(contentId);
080        infos.put("handleShareableCourse", _shareableCourseHelper.handleShareableCourse());
081        return infos;
082    }
083    
084    @Override
085    protected Map<String, Object> content2Json(Content content)
086    {
087        Map<String, Object> infos = super.content2Json(content);
088        if (content instanceof ProgramItem)
089        {
090            boolean isShared = isShared((ProgramItem) content);
091            boolean isSharedByParents = !isShared ? isSharedByParents((ProgramItem) content) : false;
092            infos.put("code", getProgramItemDisplayCode((Content & ProgramItem) content));
093            infos.put("workflowStep", getWorkflowStep(content));
094            infos.put("isShared", isShared);
095            infos.put("isParentShared", isSharedByParents);
096            
097            if (content instanceof Course)
098            {
099                infos.put("shareableStatus", getShareableStatus((Course) content));
100            }
101            else if (content instanceof CourseList)
102            {
103                CourseList courseList = (CourseList) content;
104                infos.put("courseList-type", courseList.getType());
105            }
106        }
107        else if (content instanceof OrgUnit)
108        {
109            infos.put("code", ((OrgUnit) content).getUAICode());
110        }
111        
112        return infos;
113    }
114    
115    /**
116     * Get the ProgramItem code we want to display.
117     * @param <T> The type of the program element, should be a subclasse of {@link ProgramItem} and {@link Content}
118     * @param programItem The program item
119     * @return The code to display
120     */
121    protected <T extends ProgramItem & Content> Object getProgramItemDisplayCode(T programItem)
122    {
123        return programItem.getCode();
124    }
125    
126    @Override
127    protected boolean isContentMatching(Content content, String value)
128    {
129        boolean matchTitle = super.isContentMatching(content, value);
130        if (!matchTitle)
131        {
132            if (content instanceof ProgramItem)
133            {
134                // Try with code
135                String code =  StringUtils.stripAccents(((ProgramItem) content).getCode().toLowerCase());
136                return code.contains(value);
137            }
138            else if (content instanceof OrgUnit)
139            {
140                // Try with code
141                String code =  StringUtils.stripAccents(((OrgUnit) content).getUAICode().toLowerCase());
142                return code.contains(value);
143            }
144        }
145        
146        return matchTitle;
147    }
148    
149    /**
150     * Get workflow step information
151     * @param content the content the content
152     * @return the workflow step information
153     */
154    protected Map<String, Object> getWorkflowStep(Content content)
155    {
156        Map<String, Object> workflowInfos = new HashMap<>();
157                
158        if (content instanceof WorkflowAwareAmetysObject)
159        {
160            WorkflowAwareContent waContent = (WorkflowAwareContent) content;
161            
162            long workflowId = waContent.getWorkflowId();
163            int currentStepId = Math.toIntExact(waContent.getCurrentStepId());
164        
165            AmetysObjectWorkflow workflow = _workflowProvider.getAmetysObjectWorkflow(waContent);
166        
167            String workflowName = workflow.getWorkflowName(workflowId);
168            WorkflowDescriptor workflowDescriptor = workflow.getWorkflowDescriptor(workflowName);
169        
170            if (workflowDescriptor != null)
171            {
172                StepDescriptor stepDescriptor = workflowDescriptor.getStep(currentStepId);
173                if (stepDescriptor != null)
174                {
175                    I18nizableText workflowStepName = new I18nizableText("application", stepDescriptor.getName());
176                    
177                    workflowInfos.put("stepId", currentStepId);
178                    workflowInfos.put("name", workflowStepName);
179                    
180                    String[] icons = new String[] {"small", "medium", "large"};
181                    for (String icon : icons)
182                    {
183                        if ("application".equals(workflowStepName.getCatalogue()))
184                        {
185                            workflowInfos.put(icon + "Icon", "/plugins/cms/resources_workflow/" + workflowStepName.getKey() + "-" + icon + ".png");
186                        }
187                        else
188                        {
189                            String pluginName = workflowStepName.getCatalogue().substring("plugin.".length());
190                            workflowInfos.put(icon + "Icon", "/plugins/" + pluginName + "/resources/img/workflow/" + workflowStepName.getKey() + "-" + icon + ".png");
191                        }
192                    }
193                }
194            }
195        }
196        
197        return workflowInfos;
198    }
199    
200    /**
201     * Get the shared status of this program item
202     * @param programItem the program item
203     * @return true if the program item belongs to more than one program item
204     */
205    protected boolean isShared(ProgramItem programItem)
206    {
207        return _odfHelper.getParentProgramItems(programItem).size() > 1;
208    }
209    
210    /**
211     * Check if the parent of a program item is shared 
212     * @param programItem the program item
213     * @return true if the parent of the program item is shared
214     */
215    protected boolean isSharedByParents(ProgramItem programItem)
216    {
217        for (ProgramItem parent : _odfHelper.getParentProgramItems(programItem))
218        {
219            return _odfHelper.getParentProgramItems(parent).size() > 1 ? true : isSharedByParents(parent);
220        }
221        return false;
222    }
223
224    /**
225     * Get the shareable course status as String
226     * @param course the course
227     * @return the shareable status
228     */
229    protected String getShareableStatus(Course course)
230    {
231        if (_shareableCourseHelper.handleShareableCourse())
232        {
233            ShareableStatus shareableStatus = _shareableStatusHelper.getShareableStatus(course);
234            return shareableStatus.name().toLowerCase();
235        }
236        
237        return null;
238    }
239}