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}