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.runtime.model.ModelItemContainer; 034import org.ametys.runtime.model.ViewItemContainer; 035import org.ametys.runtime.model.exception.BadItemTypeException; 036import org.ametys.runtime.model.exception.UndefinedItemPathException; 037 038/** 039 * Implementation modifiable data holder for content 040 */ 041public class ModifiableContentDataHolder extends DefaultModifiableModelAwareDataHolder 042{ 043 /** the content */ 044 protected ModifiableContent _content; 045 046 /** the content data helper */ 047 protected ContentDataHelper _contentDataHelper; 048 049 /** 050 * Creates a modifiable content data holder 051 * @param content the content 052 * @param contentDataHelper the content data helper 053 * @param repositoryData the repository data to use 054 * @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. 055 */ 056 public ModifiableContentDataHolder(ModifiableContent content, ContentDataHelper contentDataHelper, ModifiableRepositoryData repositoryData, Collection<? extends ModelItemContainer> itemContainers) 057 { 058 super(repositoryData, Optional.empty(), Optional.of(content), itemContainers); 059 _content = content; 060 _contentDataHelper = contentDataHelper; 061 } 062 063 @Override 064 public <T extends SynchronizationResult> T synchronizeValues(ViewItemContainer viewItemContainer, Map<String, Object> values, SynchronizationContext context) throws UndefinedItemPathException, BadItemTypeException 065 { 066 Collection<ReferencedContents> allReferencedContents = new ArrayList<>(); 067 if (context instanceof ContentSynchronizationContext) 068 { 069 ContentSynchronizationContext contentContext = (ContentSynchronizationContext) context; 070 if (contentContext.invertRelationsEnabled()) 071 { 072 allReferencedContents.addAll(contentContext.getReferencedContents() 073 .orElseGet(() -> _contentDataHelper.collectReferencedContents(viewItemContainer, _content, values, contentContext))); 074 } 075 } 076 077 T result = super.synchronizeValues(viewItemContainer, values, context); 078 079 if (result instanceof ContentSynchronizationResult) 080 { 081 Map<String, ModifiableContent> modifiedContents = new HashMap<>(); 082 083 for (ReferencedContents referencedContents : allReferencedContents) 084 { 085 String invertRelationPath = referencedContents.getDefinition().getInvertRelationPath(); 086 modifiedContents.putAll(ContentDataHelper.manageInvertRelations(invertRelationPath, referencedContents.getAddedContents(), _content.getId(), ContentDataHelper::addInvertRelation, modifiedContents, context.getExternalizableDataContext())); 087 modifiedContents.putAll(ContentDataHelper.manageInvertRelations(invertRelationPath, referencedContents.getRemovedContents(), _content.getId(), ContentDataHelper::removeInvertRelation, modifiedContents, context.getExternalizableDataContext())); 088 089 Map<ContentValue, Collection<String>> thirdPartyContents = new HashMap<>(); 090 for (Map.Entry<ContentValue, ContentValue> thirdPartyContent : referencedContents.getThirdPartyContents().entrySet()) 091 { 092 thirdPartyContents.computeIfAbsent(thirdPartyContent.getValue(), __ -> new ArrayList<>()).add(thirdPartyContent.getKey().getContentId()); 093 } 094 modifiedContents.putAll(ContentDataHelper.manageInvertRelations(referencedContents.getDefinition().getPath(), thirdPartyContents, ContentDataHelper::removeInvertRelation, modifiedContents, context.getExternalizableDataContext())); 095 } 096 097 ((ContentSynchronizationResult) result).addModifiedContents(modifiedContents.values()); 098 } 099 100 return result; 101 } 102 103 @SuppressWarnings("unchecked") 104 @Override 105 protected <T extends SynchronizationContext> T _createSynchronizationContextInstance() 106 { 107 return (T) ContentSynchronizationContext.newInstance(); 108 } 109 110 @SuppressWarnings("unchecked") 111 @Override 112 protected <T extends SynchronizationResult> T _createSetValueResultInstance() 113 { 114 return (T) new ContentSynchronizationResult(); 115 } 116}