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.Optional; 021import java.util.stream.Collectors; 022 023import org.apache.avalon.framework.activity.Initializable; 024import org.apache.commons.lang3.ArrayUtils; 025 026import org.ametys.cms.contenttype.ContentType; 027import org.ametys.cms.contenttype.ContentTypeExtensionPoint; 028import org.ametys.cms.contenttype.ContentValidator; 029import org.ametys.cms.repository.Content; 030import org.ametys.plugins.repository.AmetysRepositoryException; 031import org.ametys.plugins.repository.data.holder.ModelAwareDataHolder; 032import org.ametys.plugins.repository.data.holder.group.impl.ModelAwareComposite; 033import org.ametys.plugins.repository.data.holder.group.impl.ModelAwareRepeater; 034import org.ametys.plugins.repository.data.holder.group.impl.ModelAwareRepeaterEntry; 035import org.ametys.plugins.repository.model.CompositeDefinition; 036import org.ametys.plugins.repository.model.RepeaterDefinition; 037import org.ametys.runtime.model.ElementDefinition; 038import org.ametys.runtime.model.ModelHelper; 039import org.ametys.runtime.model.ModelItem; 040import org.ametys.runtime.parameter.Errors; 041 042import com.opensymphony.module.propertyset.PropertySet; 043import com.opensymphony.workflow.Condition; 044import com.opensymphony.workflow.WorkflowException; 045 046/** 047 * OSWorkflow condition to check all content metadata are valid 048 */ 049public class ValidateContentCondition extends AbstractContentWorkflowComponent implements Condition, Initializable 050{ 051 private ContentTypeExtensionPoint _cTypeEP; 052 053 @Override 054 public void initialize() throws Exception 055 { 056 _cTypeEP = (ContentTypeExtensionPoint) _manager.lookup(ContentTypeExtensionPoint.ROLE); 057 } 058 059 @Override 060 public boolean passesCondition(Map transientVars, Map args, PropertySet ps) throws WorkflowException 061 { 062 try 063 { 064 Content content = getContent(transientVars); 065 List<String> conditionFailures = getConditionFailures(transientVars); 066 067 List<ModelItem> modelItems = content.getModel().stream().flatMap(model -> model.getModelItems().stream()).collect(Collectors.toList()); 068 069 if (!_validateValues(modelItems, Optional.of(content), "", content, conditionFailures)) 070 { 071 return false; 072 } 073 074 String[] allContentTypes = ArrayUtils.addAll(content.getTypes(), content.getMixinTypes()); 075 for (String cTypeId : allContentTypes) 076 { 077 ContentType contentType = _cTypeEP.getExtension(cTypeId); 078 079 for (ContentValidator contentValidator : contentType.getGlobalValidators()) 080 { 081 Errors errors = new Errors(); 082 contentValidator.validate(content, errors); 083 if (errors.hasErrors()) 084 { 085 conditionFailures.add(String.format("Validate condition failed for content on global validators", content.getId())); 086 return false; 087 } 088 } 089 } 090 091 return true; 092 } 093 catch (AmetysRepositoryException e) 094 { 095 getConditionFailures(transientVars).add("Validate metadata condition failed (" + e.getMessage() + ")"); 096 _logger.error("Cannot check condition (" + e.getMessage() + "). Assuming it is false.", e); 097 return false; 098 } 099 } 100 101 private boolean _validateValues(List<ModelItem> modelItems, Optional<? extends ModelAwareDataHolder> dataHolder, String dataPath, Content content, List<String> conditionFailures) 102 { 103 for (ModelItem modelItem : modelItems) 104 { 105 String name = modelItem.getName(); 106 if (modelItem instanceof ElementDefinition) 107 { 108 // simple element 109 ElementDefinition definition = (ElementDefinition) modelItem; 110 Object value = dataHolder.map(holder -> holder.getValue(name)).orElse(null); 111 112 if (!ModelHelper.validateValue(definition, value).isEmpty()) 113 { 114 conditionFailures.add(String.format("Validate attribute condition failed for content %s on attribute %s with value %s", content.getId(), dataPath + name, String.valueOf(value))); 115 return false; 116 } 117 } 118 else if (modelItem instanceof CompositeDefinition) 119 { 120 // composite 121 Optional<ModelAwareComposite> composite = dataHolder.map(holder -> holder.getComposite(name)); 122 if (!_validateValues(((CompositeDefinition) modelItem).getChildren(), composite, dataPath + name + "/", content, conditionFailures)) 123 { 124 return false; 125 } 126 } 127 else if (modelItem instanceof RepeaterDefinition) 128 { 129 // repeater 130 RepeaterDefinition definition = (RepeaterDefinition) modelItem; 131 Optional<ModelAwareRepeater> repeater = dataHolder.map(holder -> holder.getRepeater(name)); 132 133 Optional<List<? extends ModelAwareRepeaterEntry>> entries = repeater.map(ModelAwareRepeater::getEntries); 134 135 int repeaterSize = repeater.map(ModelAwareRepeater::getSize).orElse(0); 136 int minSize = definition.getMinSize(); 137 int maxSize = definition.getMaxSize(); 138 139 if ((repeaterSize < minSize) || (maxSize > 0 && repeaterSize > maxSize)) 140 { 141 conditionFailures.add(String.format("Validate repeater size condition failed for content %s on repeater %s with size %s", content.getId(), dataPath + name, repeaterSize)); 142 return false; 143 } 144 145 if (entries.isPresent()) 146 { 147 for (ModelAwareRepeaterEntry entry : entries.get()) 148 { 149 if (!_validateValues(definition.getChildren(), Optional.of(entry), dataPath + name + "[" + (entry.getPosition()) + "]/", content, conditionFailures)) 150 { 151 return false; 152 } 153 } 154 } 155 } 156 } 157 158 return true; 159 } 160}