001/* 002 * Copyright 2017 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.plugins.contentio.synchronize; 017 018import java.util.ArrayList; 019import java.util.HashMap; 020import java.util.LinkedHashMap; 021import java.util.List; 022import java.util.Map; 023import java.util.Objects; 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.ContentType; 033import org.ametys.cms.contenttype.ContentTypeExtensionPoint; 034import org.ametys.cms.repository.ContentDAO; 035import org.ametys.plugins.contentio.synchronize.search.SCCSearchModelConfiguration; 036import org.ametys.plugins.repository.AmetysObjectResolver; 037import org.ametys.plugins.repository.model.CompositeDefinition; 038import org.ametys.plugins.repository.model.RepeaterDefinition; 039import org.ametys.runtime.i18n.I18nizableText; 040import org.ametys.runtime.model.ElementDefinition; 041import org.ametys.runtime.model.ModelItem; 042import org.ametys.runtime.model.ModelItemAccessor; 043 044/** 045 * Configuration is separated from {@link AbstractSynchronizableContentsCollection} 046 */ 047public abstract class AbstractStaticSynchronizableContentsCollection implements SynchronizableContentsCollection, Configurable, Serviceable 048{ 049 /** The Ametys object resolver */ 050 protected AmetysObjectResolver _resolver; 051 /** The SCC helper */ 052 protected SynchronizableContentsCollectionHelper _sccHelper; 053 /** The content DAO */ 054 protected ContentDAO _contentDAO; 055 /** The content type extension point */ 056 protected ContentTypeExtensionPoint _contentTypeEP; 057 058 /** The id */ 059 protected String _id; 060 /** The label */ 061 protected I18nizableText _label; 062 /** The path to the metadata holding the 'restricted' property */ 063 protected String _restrictedField; 064 /** The handled content type */ 065 protected String _contentType; 066 /** The handled languages */ 067 protected List<String> _languages; 068 /** The id of controller */ 069 protected String _modelId; 070 /** The untyped values of controller's parameters */ 071 protected Map<String, Object> _modelParamValues; 072 /** True if removal sync */ 073 protected boolean _removalSync; 074 /** True to ignore restrictions on attributes while synchronizing */ 075 protected boolean _ignoreRestrictions; 076 /** The name of the workflow */ 077 protected String _workflowName; 078 /** The id of the initial action of the workflow */ 079 protected int _initialActionId; 080 /** The id of the synchronize action of the workflow */ 081 protected int _synchronizeActionId; 082 /** The id of the validate action of the workflow */ 083 protected int _validateActionId; 084 /** The prefix of the contents */ 085 protected String _contentPrefix; 086 /** True to validate contents after import */ 087 protected boolean _validateAfterImport; 088 /** The report mails */ 089 protected String _reportMails; 090 /** The id of the content operator to use */ 091 protected String _synchronizingContentOperator; 092 /** The id of the content operator to use */ 093 protected boolean _synchronizeExistingContentsOnly; 094 /** <code>true</code> to check the collection while synchronizing */ 095 protected boolean _checkCollection; 096 /** List of compatible SCC with the current SCC (excepting the current one) */ 097 protected List<String> _compatibleSCC; 098 /** Search model configuration for search tool */ 099 protected SCCSearchModelConfiguration _searchModelConfiguration; 100 101 public void service(ServiceManager manager) throws ServiceException 102 { 103 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 104 _sccHelper = (SynchronizableContentsCollectionHelper) manager.lookup(SynchronizableContentsCollectionHelper.ROLE); 105 _contentDAO = (ContentDAO) manager.lookup(ContentDAO.ROLE); 106 _contentTypeEP = (ContentTypeExtensionPoint) manager.lookup(ContentTypeExtensionPoint.ROLE); 107 } 108 109 public void configure(Configuration configuration) throws ConfigurationException 110 { 111 configureStaticParams(configuration); 112 configureDataSource(configuration); 113 _searchModelConfiguration = new SCCSearchModelConfiguration(); 114 configureSearchModel(); 115 } 116 117 /** 118 * Called in {@link #configure(Configuration)} for first configurations needed. 119 * @param configuration Configuration to read 120 * @throws ConfigurationException If an error occurs 121 */ 122 protected void configureStaticParams(Configuration configuration) throws ConfigurationException 123 { 124 _id = configuration.getAttribute("id"); 125 _label = I18nizableText.parseI18nizableText(configuration.getChild("label"), null); 126 _contentType = configuration.getChild("contentType").getValue(); 127 _removalSync = configuration.getChild("removalSync").getValueAsBoolean(false); 128 _ignoreRestrictions = configuration.getChild("ignoreRestrictions").getValueAsBoolean(true); 129 _workflowName = configuration.getChild("workflowName").getValue(); 130 _initialActionId = configuration.getChild("initialActionId").getValueAsInteger(); 131 _synchronizeActionId = configuration.getChild("synchronizeActionId").getValueAsInteger(); 132 _validateActionId = configuration.getChild("validateActionId").getValueAsInteger(); 133 _contentPrefix = configuration.getChild("contentPrefix").getValue(); 134 _restrictedField = configuration.getChild("restrictedField").getValue(null); 135 _validateAfterImport = configuration.getChild("validateAfterImport").getValueAsBoolean(false); 136 _reportMails = configuration.getChild("reportMails").getValue(""); 137 _synchronizingContentOperator = configuration.getChild("contentOperator").getValue(); 138 _modelId = configuration.getChild("model").getAttribute("id"); 139 _languages = _parseMultipleValuesConf(configuration.getChild("languages")); 140 _modelParamValues = _parseParameters(configuration.getChild("model")); 141 _synchronizeExistingContentsOnly = configuration.getChild("synchronizeExistingContentsOnly").getValueAsBoolean(false); 142 _checkCollection = configuration.getChild("checkCollection").getValueAsBoolean(true); 143 _compatibleSCC = _parseMultipleValuesConf(configuration.getChild("compatibleSCC")); 144 } 145 146 /** 147 * Configure the data source parameters. 148 * @param configuration Configuration to read 149 * @throws ConfigurationException If an error occurs 150 */ 151 protected abstract void configureDataSource(Configuration configuration) throws ConfigurationException; 152 153 /** 154 * Configure the search model used by SCCSearchTool. 155 */ 156 protected abstract void configureSearchModel(); 157 158 public String getId() 159 { 160 return _id; 161 } 162 163 public I18nizableText getLabel() 164 { 165 return _label; 166 } 167 168 public String getContentType() 169 { 170 return _contentType; 171 } 172 173 public List<String> getLanguages() 174 { 175 return _languages; 176 } 177 178 public String getRestrictedField() 179 { 180 return _restrictedField; 181 } 182 183 public String getSynchronizeCollectionModelId() 184 { 185 return _modelId; 186 } 187 188 public Map<String, Object> getParameterValues() 189 { 190 return _modelParamValues; 191 } 192 193 public boolean removalSync() 194 { 195 return _removalSync; 196 } 197 198 public boolean ignoreRestrictions() 199 { 200 return _ignoreRestrictions; 201 } 202 203 public boolean checkCollection() 204 { 205 return _checkCollection; 206 } 207 208 public List<String> getCompatibleSCC(boolean includeCurrent) 209 { 210 if (includeCurrent) 211 { 212 List<String> compatibleSCC = new ArrayList<>(_compatibleSCC); 213 compatibleSCC.add(getId()); 214 return compatibleSCC; 215 } 216 return _compatibleSCC; 217 } 218 219 public String getWorkflowName() 220 { 221 return _workflowName; 222 } 223 224 public int getInitialActionId() 225 { 226 return _initialActionId; 227 } 228 229 public int getSynchronizeActionId() 230 { 231 return _synchronizeActionId; 232 } 233 234 public int getValidateActionId() 235 { 236 return _validateActionId; 237 } 238 239 public String getContentPrefix() 240 { 241 return _contentPrefix; 242 } 243 244 public boolean validateAfterImport() 245 { 246 return _validateAfterImport; 247 } 248 249 public String getReportMails() 250 { 251 return _reportMails; 252 } 253 254 public String getSynchronizingContentOperator() 255 { 256 return _synchronizingContentOperator; 257 } 258 259 public boolean synchronizeExistingContentsOnly() 260 { 261 return _synchronizeExistingContentsOnly; 262 } 263 264 public SCCSearchModelConfiguration getSearchModelConfiguration() 265 { 266 return _searchModelConfiguration; 267 } 268 269 /** 270 * Parse parameters' values 271 * @param configuration The root configuration 272 * @return The parameters 273 * @throws ConfigurationException if an error occurred 274 */ 275 protected Map<String, Object> _parseParameters(Configuration configuration) throws ConfigurationException 276 { 277 Map<String, Object> values = new LinkedHashMap<>(); 278 279 Configuration[] params = configuration.getChildren("param"); 280 for (Configuration paramConfig : params) 281 { 282 values.put(paramConfig.getAttribute("name"), paramConfig.getValue("")); 283 } 284 return values; 285 } 286 287 /** 288 * Parse multiple values configuration 289 * @param configuration the configuration 290 * @return the list of handled values 291 * @throws ConfigurationException if an error occurred 292 */ 293 protected List<String> _parseMultipleValuesConf(Configuration configuration) throws ConfigurationException 294 { 295 List<String> values = new ArrayList<>(); 296 for (Configuration conf : configuration.getChildren("value")) 297 { 298 values.add(conf.getValue()); 299 } 300 return values; 301 } 302 303 /** 304 * Transform the remote values to take each attribute cardinality into account 305 * @param remoteValues the remote values 306 * @param contentTypeId the content type ID from which attributes come from 307 * @return the transformed values 308 */ 309 protected Map<String, Object> _transformRemoteValuesCardinality(Map<String, List<Object>> remoteValues, String contentTypeId) 310 { 311 ContentType contentType = _contentTypeEP.getExtension(contentTypeId); 312 return _transformRemoteValuesCardinality(remoteValues, contentType); 313 } 314 315 @SuppressWarnings("unchecked") 316 private Map<String, Object> _transformRemoteValuesCardinality(Map<String, List<Object>> remoteValues, ModelItemAccessor modelItemAccessor) 317 { 318 Map<String, Object> transformedContentValues = new HashMap<>(); 319 for (String attributeName : remoteValues.keySet()) 320 { 321 if (modelItemAccessor.hasModelItem(attributeName)) 322 { 323 List<Object> attributeValues = remoteValues.get(attributeName); 324 Object transformedAttributeValue = attributeValues; 325 326 ModelItem modelItem = modelItemAccessor.getModelItem(attributeName); 327 if (modelItem instanceof ElementDefinition definition && !definition.isMultiple()) 328 { 329 transformedAttributeValue = attributeValues.stream() 330 .filter(Objects::nonNull) 331 .findFirst() 332 .orElse(null); 333 } 334 else if (modelItem instanceof CompositeDefinition composite) 335 { 336 transformedAttributeValue = attributeValues.stream() 337 .filter(Objects::nonNull) 338 .findFirst() 339 .map(object -> (Map<String, List<Object>>) object) 340 .map(values -> _transformRemoteValuesCardinality(values, composite)) 341 .orElse(null); 342 } 343 else if (modelItem instanceof RepeaterDefinition repeater) 344 { 345 transformedAttributeValue = attributeValues.stream() 346 .filter(Objects::nonNull) 347 .map(object -> (Map<String, List<Object>>) object) 348 .map(values -> _transformRemoteValuesCardinality(values, repeater)) 349 .toList(); 350 } 351 352 transformedContentValues.put(attributeName, transformedAttributeValue); 353 } 354 } 355 356 return transformedContentValues; 357 } 358}