001/* 002 * Copyright 2020 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.repository; 017 018import java.util.ArrayList; 019import java.util.Collection; 020import java.util.HashMap; 021import java.util.Map; 022import java.util.Optional; 023 024import org.ametys.cms.data.ContentDataHelper; 025import org.ametys.cms.data.ContentSynchronizationContext; 026import org.ametys.cms.data.ContentSynchronizationResult; 027import org.ametys.cms.data.ContentValue; 028import org.ametys.cms.data.ReferencedContents; 029import org.ametys.cms.data.holder.impl.DefaultModifiableModelAwareDataHolder; 030import org.ametys.plugins.repository.data.holder.values.SynchronizationContext; 031import org.ametys.plugins.repository.data.holder.values.SynchronizationResult; 032import org.ametys.plugins.repository.data.repositorydata.ModifiableRepositoryData; 033import org.ametys.plugins.repository.lock.LockableAmetysObject; 034import org.ametys.runtime.model.ModelItemContainer; 035import org.ametys.runtime.model.ViewItemAccessor; 036import org.ametys.runtime.model.exception.BadItemTypeException; 037import org.ametys.runtime.model.exception.UndefinedItemPathException; 038 039/** 040 * Implementation modifiable data holder for content 041 */ 042public class ModifiableContentDataHolder extends DefaultModifiableModelAwareDataHolder 043{ 044 /** the content */ 045 protected ModifiableContent _content; 046 047 /** the content data helper */ 048 protected ContentDataHelper _contentDataHelper; 049 050 /** 051 * Creates a modifiable content data holder 052 * @param content the content 053 * @param contentDataHelper the content data helper 054 * @param repositoryData the repository data to use 055 * @param itemContainers the model containers to use to get information about definitions. Must match the given repository data. A repository data can have several item containers. For example, a content can have several content types. 056 */ 057 public ModifiableContentDataHolder(ModifiableContent content, ContentDataHelper contentDataHelper, ModifiableRepositoryData repositoryData, Collection<? extends ModelItemContainer> itemContainers) 058 { 059 super(repositoryData, itemContainers, Optional.of(content).filter(LockableAmetysObject.class::isInstance).map(LockableAmetysObject.class::cast), Optional.empty(), Optional.of(content)); 060 _content = content; 061 _contentDataHelper = contentDataHelper; 062 } 063 064 @Override 065 public <T extends SynchronizationResult> T synchronizeValues(ViewItemAccessor viewItemAccessor, Map<String, Object> values, SynchronizationContext context) throws UndefinedItemPathException, BadItemTypeException 066 { 067 Collection<ReferencedContents> allReferencedContents = new ArrayList<>(); 068 if (context instanceof ContentSynchronizationContext) 069 { 070 ContentSynchronizationContext contentContext = (ContentSynchronizationContext) context; 071 if (contentContext.invertRelationsEnabled()) 072 { 073 allReferencedContents.addAll(contentContext.getReferencedContents() 074 .orElseGet(() -> _contentDataHelper.collectReferencedContents(viewItemAccessor, _content, values, contentContext))); 075 } 076 } 077 078 T result = super.synchronizeValues(viewItemAccessor, values, context); 079 080 if (result instanceof ContentSynchronizationResult) 081 { 082 Map<String, ModifiableContent> modifiedContents = new HashMap<>(); 083 084 for (ReferencedContents referencedContents : allReferencedContents) 085 { 086 String invertRelationPath = referencedContents.getDefinition().getInvertRelationPath(); 087 modifiedContents.putAll(ContentDataHelper.manageInvertRelations(invertRelationPath, referencedContents.getAddedContents(), _content.getId(), ContentDataHelper::addInvertRelation, modifiedContents, context.getExternalizableDataContext())); 088 modifiedContents.putAll(ContentDataHelper.manageInvertRelations(invertRelationPath, referencedContents.getRemovedContents(), _content.getId(), ContentDataHelper::removeInvertRelation, modifiedContents, context.getExternalizableDataContext())); 089 090 Map<ContentValue, Collection<String>> thirdPartyContents = new HashMap<>(); 091 for (Map.Entry<ContentValue, ContentValue> thirdPartyContent : referencedContents.getThirdPartyContents().entrySet()) 092 { 093 thirdPartyContents.computeIfAbsent(thirdPartyContent.getValue(), __ -> new ArrayList<>()).add(thirdPartyContent.getKey().getContentId()); 094 } 095 modifiedContents.putAll(ContentDataHelper.manageInvertRelations(referencedContents.getDefinition().getPath(), thirdPartyContents, ContentDataHelper::removeInvertRelation, modifiedContents, context.getExternalizableDataContext())); 096 } 097 098 ((ContentSynchronizationResult) result).addModifiedContents(modifiedContents.values()); 099 } 100 101 return result; 102 } 103 104 @SuppressWarnings("unchecked") 105 @Override 106 protected <T extends SynchronizationContext> T _createSynchronizationContextInstance() 107 { 108 return (T) ContentSynchronizationContext.newInstance(); 109 } 110 111 @SuppressWarnings("unchecked") 112 @Override 113 protected <T extends SynchronizationResult> T _createSetValueResultInstance() 114 { 115 return (T) new ContentSynchronizationResult(); 116 } 117}