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.cms.content.referencetable; 017 018import java.util.ArrayList; 019import java.util.Arrays; 020import java.util.Collections; 021import java.util.HashMap; 022import java.util.List; 023import java.util.Map; 024import java.util.Optional; 025import java.util.stream.Collectors; 026 027import org.apache.avalon.framework.service.ServiceException; 028import org.apache.avalon.framework.service.ServiceManager; 029import org.apache.commons.collections.CollectionUtils; 030 031import org.ametys.cms.clientsideelement.relations.SetContentAttributeClientSideElement; 032import org.ametys.cms.contenttype.ContentAttributeDefinition; 033import org.ametys.cms.contenttype.ContentType; 034import org.ametys.cms.repository.Content; 035import org.ametys.core.ui.Callable; 036import org.ametys.plugins.repository.AmetysRepositoryException; 037import org.ametys.runtime.parameter.Validator; 038 039/** 040 * Set the "parent" attribute of a simple content 041 */ 042public class SetParentContentClientSideElement extends SetContentAttributeClientSideElement 043{ 044 /** The helper component for hierarchical simple contents */ 045 protected HierarchicalReferenceTablesHelper _hierarchicalSimpleContentsHelper; 046 047 @Override 048 public void service(ServiceManager manager) throws ServiceException 049 { 050 super.service(manager); 051 _hierarchicalSimpleContentsHelper = (HierarchicalReferenceTablesHelper) manager.lookup(HierarchicalReferenceTablesHelper.ROLE); 052 } 053 054 /** 055 * Tells if the given simple contents can have their parent attribute changed for the given new simple content parent. 056 * @param contentIdsToMove The identifiers of the simple contents willing to change their parent attribute 057 * @param newParentContentId The identifier of the new parent simple content. Can be "root" if it is the root of simple contents 058 * @param leafContentTypeId If newParentContentId is equal to "root", is the identifier of the content type of the leaves of the tree. Otherwise, must be null 059 * @return A map with key 'validContents' and key 'invalidContents' 060 */ 061 @Callable 062 public Map<String, Object> areValidMoves(List<String> contentIdsToMove, String newParentContentId, String leafContentTypeId) 063 { 064 Map<String, Object> result = new HashMap<>(); 065 List<Map<String, String>> validContents = new ArrayList<>(); 066 result.put("validContents", validContents); 067 List<String> invalidContents = new ArrayList<>(); 068 result.put("invalidContents", invalidContents); 069 070 if ("root".equals(newParentContentId)) 071 { 072 ContentType leafContentType = _contentTypeExtensionPoint.getExtension(leafContentTypeId); 073 String topLevelType = _hierarchicalSimpleContentsHelper.getTopLevelType(leafContentType).getId(); 074 075 for (String contentIdToMove : contentIdsToMove) 076 { 077 try 078 { 079 Content contentToMove = _resolver.resolveById(contentIdToMove); 080 ContentAttributeDefinition parentAttributeDefinition = _getParentAttributeDefinition(contentToMove); 081 if (parentAttributeDefinition == null) 082 { 083 invalidContents.add(contentIdToMove); 084 break; 085 } 086 087 // 1) As move towards the root node means removing the "parent" attribute, check it is not mandatory 088 Validator validator = parentAttributeDefinition.getValidator(); 089 if (validator != null && (Boolean) validator.getConfiguration().get("mandatory")) 090 { 091 invalidContents.add(contentIdToMove); 092 break; 093 } 094 095 // 2) Check the content is a potential child of root node, i.e. has good content type 096 if (Arrays.asList(contentToMove.getTypes()).contains(topLevelType)) 097 { 098 validContents.add(_validContentToMap(contentToMove, Collections.singletonList(topLevelType))); 099 } 100 else 101 { 102 invalidContents.add(contentIdToMove); 103 } 104 } 105 catch (AmetysRepositoryException e) 106 { 107 getLogger().error("An error occured while retrieving the content to move.", e); 108 invalidContents.add(contentIdToMove); 109 } 110 } 111 } 112 else 113 { 114 Content newParent; 115 try 116 { 117 newParent = _resolver.resolveById(newParentContentId); 118 } 119 catch (AmetysRepositoryException e) 120 { 121 getLogger().error("An error occured while retrieving the new parent content.", e); 122 result.put("error", "unknown-parent"); 123 return result; 124 } 125 List<String> childContentTypes = _hierarchicalSimpleContentsHelper.getChildContentTypes(newParent).stream() 126 .map(ContentType::getId) 127 .collect(Collectors.toList()); 128 for (String contentIdToMove : contentIdsToMove) 129 { 130 try 131 { 132 Content contentToMove = _resolver.resolveById(contentIdToMove); 133 if (CollectionUtils.containsAny(Arrays.asList(contentToMove.getTypes()), childContentTypes)) 134 { 135 // The content types of the content is valid with the new parent 136 Map<String, String> validContent = _validContentToMap(contentToMove, childContentTypes); 137 validContents.add(validContent); 138 } 139 else 140 { 141 invalidContents.add(contentIdToMove); 142 } 143 } 144 catch (AmetysRepositoryException e) 145 { 146 getLogger().error("An error occured while retrieving the content to move.", e); 147 invalidContents.add(contentIdToMove); 148 } 149 } 150 } 151 152 return result; 153 } 154 155 private ContentAttributeDefinition _getParentAttributeDefinition(Content content) 156 { 157 for (String cTypeId : content.getTypes()) 158 { 159 ContentType cType = _contentTypeExtensionPoint.getExtension(cTypeId); 160 Optional<ContentAttributeDefinition> parentAttribute = cType.getParentAttributeDefinition(); 161 if (!parentAttribute.isEmpty()) 162 { 163 return parentAttribute.get(); 164 } 165 } 166 167 return null; 168 } 169 170 private Map<String, String> _validContentToMap(Content validContent, List<String> childContentTypes) 171 { 172 Map<String, String> contentMap = new HashMap<>(); 173 contentMap.put("id", validContent.getId()); 174 175 List<String> types = Arrays.asList(validContent.getTypes()); 176 for (String childContentTypeId : childContentTypes) 177 { 178 if (types.contains(childContentTypeId)) 179 { 180 ContentType childContentType = _contentTypeExtensionPoint.getExtension(childContentTypeId); 181 childContentType.getParentAttributeDefinition() 182 .ifPresent(parentAttribute -> contentMap.put("parentAttributeName", parentAttribute.getName())); 183 } 184 } 185 186 contentMap.put("parentAttributeValue", _hierarchicalSimpleContentsHelper.getParent(validContent)); 187 188 return contentMap; 189 } 190}