001/* 002 * Copyright 2010 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.cms.workflow; 017 018import java.util.List; 019import java.util.Map; 020import java.util.Set; 021 022import org.apache.avalon.framework.activity.Initializable; 023import org.apache.commons.lang3.ArrayUtils; 024 025import org.ametys.cms.contenttype.ContentType; 026import org.ametys.cms.contenttype.ContentTypeExtensionPoint; 027import org.ametys.cms.contenttype.ContentValidator; 028import org.ametys.cms.contenttype.MetadataDefinition; 029import org.ametys.cms.contenttype.MetadataType; 030import org.ametys.cms.contenttype.RepeaterDefinition; 031import org.ametys.cms.repository.Content; 032import org.ametys.plugins.repository.AmetysRepositoryException; 033import org.ametys.plugins.repository.metadata.CompositeMetadata; 034import org.ametys.plugins.repository.metadata.UnknownMetadataException; 035import org.ametys.runtime.parameter.Errors; 036 037import com.opensymphony.module.propertyset.PropertySet; 038import com.opensymphony.workflow.Condition; 039import com.opensymphony.workflow.WorkflowException; 040 041 042/** 043 * OSWorkflow condition to check all content metadata are valid 044 */ 045public class ValidateMetadataCondition extends AbstractContentWorkflowComponent implements Condition, Initializable 046{ 047 private ContentTypeExtensionPoint _cTypeEP; 048 049 @Override 050 public void initialize() throws Exception 051 { 052 _cTypeEP = (ContentTypeExtensionPoint) _manager.lookup(ContentTypeExtensionPoint.ROLE); 053 } 054 055 @Override 056 public boolean passesCondition(Map transientVars, Map args, PropertySet ps) throws WorkflowException 057 { 058 try 059 { 060 Content content = getContent(transientVars); 061 062 String[] allContentTypes = ArrayUtils.addAll(content.getTypes(), content.getMixinTypes()); 063 for (String cTypeId : allContentTypes) 064 { 065 ContentType contentType = _cTypeEP.getExtension(cTypeId); 066 067 Set<String> metadataNames = contentType.getMetadataNames(); 068 for (String metadataName : metadataNames) 069 { 070 MetadataDefinition metadataDef = contentType.getMetadataDefinition(metadataName); 071 if (!validateMetadata(content.getMetadataHolder(), metadataDef, metadataName, new Errors())) 072 { 073 List<String> conditionFailures = getConditionFailures(transientVars); 074 if (conditionFailures != null) 075 { 076 conditionFailures.add(String.format("Validate metadata condition failed for content %s on metadata ", content.getId(), metadataName)); 077 } 078 return false; 079 } 080 } 081 082 for (ContentValidator contentValidator : contentType.getGlobalValidators()) 083 { 084 Errors errors = new Errors(); 085 contentValidator.validate(content, errors); 086 if (errors.hasErrors()) 087 { 088 getConditionFailures(transientVars).add(String.format("Validate metadata condition failed for content on global validators", content.getId())); 089 return false; 090 } 091 } 092 } 093 094 return true; 095 } 096 catch (AmetysRepositoryException e) 097 { 098 getConditionFailures(transientVars).add("Validate metadata condition failed (" + e.getMessage() + ")"); 099 _logger.error("Cannot check condition (" + e.getMessage() + "). Assuming it is false.", e); 100 return false; 101 } 102 } 103 104 /** 105 * Validate a metadata 106 * @param parentMetadata The parent metadata 107 * @param metadataDef The metadata definition 108 * @param metadataName The metadata name 109 * @param errors The errors 110 * @return <code>true</code> if metadata is valid 111 */ 112 protected boolean validateMetadata (CompositeMetadata parentMetadata, MetadataDefinition metadataDef, String metadataName, Errors errors) 113 { 114 MetadataType type = metadataDef.getType(); 115 116 if (metadataDef instanceof RepeaterDefinition) 117 { 118 return _validateRepeaterMetadata(parentMetadata, metadataDef, metadataName, errors); 119 } 120 else if (type.equals(MetadataType.COMPOSITE)) 121 { 122 return _validateCompositeMetadata(parentMetadata, metadataDef, metadataName, errors); 123 } 124 else 125 { 126 return _validateOtherMetadata(parentMetadata, metadataDef, metadataName, errors); 127 } 128 } 129 130 private boolean _validateOtherMetadata(CompositeMetadata parentMetadata, MetadataDefinition metadataDef, String metadataName, Errors errors) 131 { 132 if (metadataDef.getValidator() == null) 133 { 134 // no validator, the metadata is valid 135 return true; 136 } 137 138 Object value = getValue(parentMetadata, metadataDef, metadataName); 139 140 metadataDef.getValidator().validate(value, errors); 141 142 // the metadata is valid is there is no errors 143 return !errors.hasErrors(); 144 } 145 146 private boolean _validateCompositeMetadata(CompositeMetadata parentMetadata, MetadataDefinition metadataDef, String metadataName, Errors errors) 147 { 148 CompositeMetadata metadata = null; 149 if (parentMetadata != null && parentMetadata.hasMetadata(metadataName)) 150 { 151 metadata = parentMetadata.getCompositeMetadata(metadataName); 152 } 153 154 Set<String> subMetadataNames = metadataDef.getMetadataNames(); 155 for (String subMetadataName : subMetadataNames) 156 { 157 MetadataDefinition subMetadataDef = metadataDef.getMetadataDefinition(subMetadataName); 158 if (!validateMetadata(metadata, subMetadataDef, subMetadataName, errors)) 159 { 160 return false; 161 } 162 } 163 164 return true; 165 } 166 167 private boolean _validateRepeaterMetadata(CompositeMetadata parentMetadata, MetadataDefinition metadataDef, String metadataName, Errors errors) 168 { 169 RepeaterDefinition repeaterDef = (RepeaterDefinition) metadataDef; 170 171 if (parentMetadata != null && parentMetadata.hasMetadata(metadataName)) 172 { 173 CompositeMetadata metadata = parentMetadata.getCompositeMetadata(metadataName); 174 175 // Metadata exists 176 String[] entries = metadata.getMetadataNames(); 177 178 // Check size 179 if (entries.length < repeaterDef.getMinSize() || (repeaterDef.getMaxSize() != -1 && entries.length > repeaterDef.getMaxSize())) 180 { 181 return false; 182 } 183 184 // Check inside repeater values 185 for (String entryName : entries) 186 { 187 CompositeMetadata entry = metadata.getCompositeMetadata(entryName); 188 Set<String> subMetadataNames = metadataDef.getMetadataNames(); 189 for (String subMetadataName : subMetadataNames) 190 { 191 MetadataDefinition subMetadataDef = metadataDef.getMetadataDefinition(subMetadataName); 192 if (!validateMetadata(entry, subMetadataDef, subMetadataName, errors)) 193 { 194 return false; 195 } 196 } 197 } 198 } 199 200 return repeaterDef.getMinSize() == 0; 201 } 202 203 /** 204 * Get the metadata value 205 * @param metadata The metadata 206 * @param metadataDef The metadata definition 207 * @param metadataName The metadata name 208 * @return The metadata value 209 */ 210 protected Object getValue (CompositeMetadata metadata, MetadataDefinition metadataDef, String metadataName) 211 { 212 if (metadata == null) 213 { 214 return null; 215 } 216 217 try 218 { 219 switch (metadataDef.getType()) 220 { 221 case STRING: 222 return metadata.getStringArray(metadataName); 223 case MULTILINGUAL_STRING: 224 return metadata.getMultilingualString(metadataName).getValues().stream().toArray(String[]::new); 225 case DOUBLE: 226 return ArrayUtils.toObject(metadata.getDoubleArray(metadataName)); 227 case LONG: 228 return ArrayUtils.toObject(metadata.getLongArray(metadataName)); 229 case BOOLEAN: 230 return ArrayUtils.toObject(metadata.getBooleanArray(metadataName)); 231 case DATE: 232 case DATETIME: 233 return metadata.getDateArray(metadataName); 234 case BINARY: 235 return metadata.getBinaryMetadata(metadataName); 236 case FILE: 237 return metadata.getType(metadataName) == org.ametys.plugins.repository.metadata.CompositeMetadata.MetadataType.BINARY ? metadata.getBinaryMetadata(metadataName) : metadata.getString(metadataName); 238 case RICH_TEXT: 239 return metadata.getRichText(metadataName); 240 case COMPOSITE: 241 case USER: 242 case GEOCODE: 243 case REFERENCE: 244 return metadata.getCompositeMetadata(metadataName); 245 case CONTENT: 246 return metadata.getStringArray(metadataName); 247 default: 248 return null; 249 } 250 } 251 catch (UnknownMetadataException e) 252 { 253 return null; 254 } 255 } 256}