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 a key 'validContents' for the ids of valid contents for the move, a key 'invalidContents' for the ids of invalid contents for the move and key 'noRightContents' for the ids of contents with no read access, among contentIdsToMove 060 */ 061 @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION) 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 List<String> noRightContents = new ArrayList<>(); 070 result.put("noRightContents", noRightContents); 071 072 if ("root".equals(newParentContentId)) 073 { 074 ContentType leafContentType = _contentTypeExtensionPoint.getExtension(leafContentTypeId); 075 String topLevelType = _hierarchicalSimpleContentsHelper.getTopLevelType(leafContentType).getId(); 076 077 for (String contentIdToMove : contentIdsToMove) 078 { 079 try 080 { 081 Content contentToMove = _resolver.resolveById(contentIdToMove); 082 083 if (!_rightManager.currentUserHasReadAccess(contentToMove)) 084 { 085 noRightContents.add(contentIdToMove); 086 break; 087 } 088 089 ContentAttributeDefinition parentAttributeDefinition = _getParentAttributeDefinition(contentToMove); 090 if (parentAttributeDefinition == null) 091 { 092 invalidContents.add(contentIdToMove); 093 break; 094 } 095 096 // 1) As move towards the root node means removing the "parent" attribute, check it is not mandatory 097 Validator validator = parentAttributeDefinition.getValidator(); 098 if (validator != null && (Boolean) validator.getConfiguration().get("mandatory")) 099 { 100 invalidContents.add(contentIdToMove); 101 break; 102 } 103 104 // 2) Check the content is a potential child of root node, i.e. has good content type 105 if (Arrays.asList(contentToMove.getTypes()).contains(topLevelType)) 106 { 107 validContents.add(_validContentToMap(contentToMove, Collections.singletonList(topLevelType))); 108 } 109 else 110 { 111 invalidContents.add(contentIdToMove); 112 } 113 } 114 catch (AmetysRepositoryException e) 115 { 116 getLogger().error("An error occured while retrieving the content to move.", e); 117 invalidContents.add(contentIdToMove); 118 } 119 } 120 } 121 else 122 { 123 Content newParent; 124 try 125 { 126 newParent = _resolver.resolveById(newParentContentId); 127 } 128 catch (AmetysRepositoryException e) 129 { 130 getLogger().error("An error occured while retrieving the new parent content.", e); 131 result.put("error", "unknown-parent"); 132 return result; 133 } 134 List<String> childContentTypes = _hierarchicalSimpleContentsHelper.getChildContentTypes(newParent).stream() 135 .map(ContentType::getId) 136 .collect(Collectors.toList()); 137 for (String contentIdToMove : contentIdsToMove) 138 { 139 try 140 { 141 Content contentToMove = _resolver.resolveById(contentIdToMove); 142 if (!_rightManager.currentUserHasReadAccess(contentToMove)) 143 { 144 noRightContents.add(contentIdToMove); 145 } 146 else if (CollectionUtils.containsAny(Arrays.asList(contentToMove.getTypes()), childContentTypes)) 147 { 148 // The content types of the content is valid with the new parent 149 Map<String, String> validContent = _validContentToMap(contentToMove, childContentTypes); 150 validContents.add(validContent); 151 } 152 else 153 { 154 invalidContents.add(contentIdToMove); 155 } 156 } 157 catch (AmetysRepositoryException e) 158 { 159 getLogger().error("An error occured while retrieving the content to move.", e); 160 invalidContents.add(contentIdToMove); 161 } 162 } 163 } 164 165 return result; 166 } 167 168 private ContentAttributeDefinition _getParentAttributeDefinition(Content content) 169 { 170 for (String cTypeId : content.getTypes()) 171 { 172 ContentType cType = _contentTypeExtensionPoint.getExtension(cTypeId); 173 Optional<ContentAttributeDefinition> parentAttribute = cType.getParentAttributeDefinition(); 174 if (!parentAttribute.isEmpty()) 175 { 176 return parentAttribute.get(); 177 } 178 } 179 180 return null; 181 } 182 183 private Map<String, String> _validContentToMap(Content validContent, List<String> childContentTypes) 184 { 185 Map<String, String> contentMap = new HashMap<>(); 186 contentMap.put("id", validContent.getId()); 187 188 List<String> types = Arrays.asList(validContent.getTypes()); 189 for (String childContentTypeId : childContentTypes) 190 { 191 if (types.contains(childContentTypeId)) 192 { 193 ContentType childContentType = _contentTypeExtensionPoint.getExtension(childContentTypeId); 194 childContentType.getParentAttributeDefinition() 195 .ifPresent(parentAttribute -> contentMap.put("parentAttributeName", parentAttribute.getName())); 196 } 197 } 198 199 contentMap.put("parentAttributeValue", _hierarchicalSimpleContentsHelper.getParent(validContent)); 200 201 return contentMap; 202 } 203}