001/*
002 *  Copyright 2018 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.validator;
017
018import java.util.ArrayList;
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.configuration.Configurable;
026import org.apache.avalon.framework.configuration.Configuration;
027import org.apache.avalon.framework.configuration.ConfigurationException;
028import org.apache.avalon.framework.service.ServiceException;
029import org.apache.avalon.framework.service.ServiceManager;
030import org.apache.avalon.framework.service.Serviceable;
031
032import org.ametys.cms.contenttype.MetadataDefinition;
033import org.ametys.cms.contenttype.MetadataSet;
034import org.ametys.cms.contenttype.validation.AbstractContentValidator;
035import org.ametys.cms.form.Form;
036import org.ametys.cms.form.SimpleField;
037import org.ametys.cms.repository.Content;
038import org.ametys.odf.ODFHelper;
039import org.ametys.odf.ProgramItem;
040import org.ametys.odf.course.Course;
041import org.ametys.odf.courselist.CourseList;
042import org.ametys.odf.program.ProgramPart;
043import org.ametys.runtime.i18n.I18nizableText;
044import org.ametys.runtime.parameter.Errors;
045
046/**
047 * Global validator for {@link ProgramItem} content.
048 * Check that structure to not create an infinite loop
049 */
050public class ProgramItemHierarchyValidator extends AbstractContentValidator implements Serviceable, Configurable
051{
052    /** The ODF helper */
053    protected ODFHelper _odfHelper;
054    
055    private Set<String> _childMetadataNames;
056    
057    @Override
058    public void service(ServiceManager smanager) throws ServiceException
059    {
060        _odfHelper = (ODFHelper) smanager.lookup(ODFHelper.ROLE);
061    }
062    
063    @Override
064    public void validate(Content content, Errors errors)
065    {
066        // Nothing
067    }
068    
069    public void configure(Configuration configuration) throws ConfigurationException
070    {
071        _childMetadataNames = new HashSet<>();
072        
073        Configuration[] children = configuration.getChildren("childMetadata");
074        for (Configuration childConf : children)
075        {
076            _childMetadataNames.add(childConf.getAttribute("name"));
077        }
078    }
079    
080    @Override
081    public void validate(Content content, Form form, MetadataSet metadataSet, Errors errors)
082    {
083        Set<String> childMetadataNames = getChildMetadataNames();
084        for (String chilMetadataName : childMetadataNames)
085        {
086            if (metadataSet.hasMetadataDefinitionReference(chilMetadataName) && content instanceof ProgramItem)
087            {
088                MetadataDefinition childDefinition = getContentType().getMetadataDefinitionByPath(chilMetadataName);
089                
090                SimpleField<Content> values = form.getContentArray(chilMetadataName);
091                
092                Content[] childContents = values.getValues();
093                for (Content childContent : childContents)
094                {
095                    if (childContent instanceof ProgramItem)
096                    {
097                        if (!checkAncestors((ProgramItem) content, (ProgramItem) childContent))
098                        {
099                            addHierarchyError(errors, childDefinition, content, childContent);
100                        }
101                    }
102                }
103            }
104        }
105    }
106    
107    /**
108     * Get the names of child metadata to be checked
109     * @return the child metadata's names
110     */
111    protected Set<String> getChildMetadataNames()
112    {
113        return _childMetadataNames;
114    }
115    
116    /**
117     * Check if the hierarchy of a program item will be still valid if adding the given program item as child.
118     * Return false if the {@link ProgramItem} to add is in the hierarchy of the given {@link ProgramItem}
119     * @param programItem The content to start search
120     * @param childProgramItem The child program item to search in ancestors
121     * @return true if child program item is already part of the hierarchy (ascendant search)
122     */
123    protected boolean checkAncestors(ProgramItem programItem, ProgramItem childProgramItem)
124    {
125        if (programItem.equals(childProgramItem))
126        {
127            return false;
128        }
129
130        List<? extends ProgramItem> parents = new ArrayList<>();
131        if (programItem instanceof Course)
132        {
133            parents = ((Course) programItem).getParentCourseLists();
134        }
135        else if (programItem instanceof CourseList)
136        {
137            parents = ((CourseList) programItem).getParentCourses();
138        }
139        else if (programItem instanceof ProgramPart)
140        {
141            parents = ((ProgramPart) programItem).getProgramPartParents();
142        }
143        
144        for (ProgramItem parent : parents)
145        {
146            if (parent.equals(childProgramItem))
147            {
148                return false;
149            }
150            else if (!checkAncestors(parent, childProgramItem))
151            {
152                return false;
153            }
154        }
155        
156        return true;
157    }
158    
159    /**
160     * Add an error the ascendant hierarchy is invalid (infinite loop)
161     * @param errors The list of errors
162     * @param childDefinition The child metadata definition
163     * @param content The content being edited
164     * @param childContent The content to add as child content
165     */
166    protected void addHierarchyError(Errors errors, MetadataDefinition childDefinition, Content content, Content childContent)
167    {
168        Map<String, I18nizableText> i18nParams = new HashMap<>();
169        i18nParams.put("title", new I18nizableText(content.getTitle()));
170        i18nParams.put("childTitle", new I18nizableText(childContent.getTitle()));
171        i18nParams.put("fieldLabel", childDefinition.getLabel());
172        errors.addError(new I18nizableText("plugin.odf", "PLUGINS_ODF_CONTENT_VALIDATOR_HIERARCHY_ERROR", i18nParams));
173    }
174}