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