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