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