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.HashMap;
019import java.util.List;
020import java.util.Map;
021import java.util.Optional;
022import java.util.stream.Collectors;
023
024import org.apache.avalon.framework.service.ServiceException;
025import org.apache.avalon.framework.service.ServiceManager;
026import org.apache.commons.lang3.StringUtils;
027
028import org.ametys.cms.content.ContentHelper;
029import org.ametys.cms.contenttype.ContentAttributeDefinition;
030import org.ametys.cms.contenttype.ContentType;
031import org.ametys.cms.contenttype.ContentTypeExtensionPoint;
032import org.ametys.cms.contenttype.ContentTypesHelper;
033import org.ametys.cms.data.type.ModelItemTypeConstants;
034import org.ametys.cms.repository.Content;
035import org.ametys.cms.rights.ContentRightAssignmentContext;
036import org.ametys.core.ui.Callable;
037import org.ametys.core.ui.StaticClientSideElement;
038import org.ametys.plugins.repository.AmetysObjectIterable;
039import org.ametys.plugins.repository.AmetysObjectResolver;
040import org.ametys.plugins.repository.AmetysRepositoryException;
041import org.ametys.runtime.model.ModelItem;
042
043/**
044 * This element creates a button enabling to do operation on reference table contents which can have a child.
045 */
046public class HierarchicalReferenceTableClientSideElement extends StaticClientSideElement
047{
048    /** The extension point for content types */
049    protected ContentTypeExtensionPoint _contentTypeEP;
050    /** The helper component for hierarchical reference tables */
051    protected HierarchicalReferenceTablesHelper _hierarchicalReferenceTableContentsHelper;
052    /** The Ametys Object resolver */
053    protected AmetysObjectResolver _resolver;
054    /** The content types helper */
055    protected ContentTypesHelper _cTypeHelper;
056    /** The content helper */
057    protected ContentHelper _contentHelper;
058    
059    @Override
060    public void service(ServiceManager smanager) throws ServiceException
061    {
062        super.service(smanager);
063        _contentTypeEP = (ContentTypeExtensionPoint) smanager.lookup(ContentTypeExtensionPoint.ROLE);
064        _hierarchicalReferenceTableContentsHelper = (HierarchicalReferenceTablesHelper) smanager.lookup(HierarchicalReferenceTablesHelper.ROLE);
065        _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE);
066        _cTypeHelper = (ContentTypesHelper) smanager.lookup(ContentTypesHelper.ROLE);
067        _contentHelper = (ContentHelper) smanager.lookup(ContentHelper.ROLE);
068    }
069    
070    /**
071     * Returns true if the given content can have a child, according to its hierarchy. See {@link ContentType#getParentAttributeDefinition()}
072     * @param contentId The id of the content
073     * @return true if the given content can have a child, according to its hierarchy
074     */
075    @Callable(rights = Callable.NO_CHECK_REQUIRED) // Content type definition are public
076    public boolean canHaveChild(String contentId)
077    {
078        try
079        {
080            Content content = _resolver.resolveById(contentId);
081            for (String cTypeId : content.getTypes())
082            {
083                ContentType cType = _contentTypeEP.getExtension(cTypeId);
084                if (cType != null && _hierarchicalReferenceTableContentsHelper.hasChildContentType(cType))
085                {
086                    return true;
087                }
088            }
089        }
090        catch (AmetysRepositoryException e)
091        {
092            // Ignore
093        }
094        
095        return false;
096    }
097    
098    /**
099     * Get the hierarchy properties of the content types tree
100     * @param leafContentTypeId The id of the leaf content type, which defines the hierarchy.
101     * @return the hierarchy properties
102     */
103    @Callable(rights = Callable.NO_CHECK_REQUIRED) // Content type definition are public
104    public Map<String, Object> getReferenceTableProperties(String leafContentTypeId)
105    {
106        Map<String, Object> result = new HashMap<>();
107        
108        result.put("rootContentType", getContentTypeForRoot(leafContentTypeId));
109        result.put("supportedContentTypes", _hierarchicalReferenceTableContentsHelper.getHierarchicalContentTypes(leafContentTypeId));
110        ContentType leafContentType = _contentTypeEP.getExtension(leafContentTypeId);
111        result.put("allowCandidates", _hierarchicalReferenceTableContentsHelper.supportCandidates(leafContentType));
112        return result;
113    }
114    
115    /**
116     * Gets the content types ids for a child of the given content (must be non-leaf in a hierarchy of reference tables contents)
117     * @param parentContentId The id of the parent content
118     * @return the content types ids
119     */
120    @Callable(rights = Callable.NO_CHECK_REQUIRED) // Content type definition are public
121    public List<String> getContentTypesForChild(String parentContentId)
122    {
123        Content parentContent = _resolver.resolveById(parentContentId);
124        List<ContentType> childContentTypes = _hierarchicalReferenceTableContentsHelper.getChildContentTypes(parentContent);
125        
126        return childContentTypes.stream().map(ContentType::getId).collect(Collectors.toList());
127    }
128    
129    /**
130     * Gets the content type id and the default title for a child of the root node of the hierarchy of reference table contents, defined by the given leaf content type
131     * @param leafContentTypeId The id of the leaf content type, which defines the hierarchy.
132     * @return the content type id for the root node of the hierarchy of reference table contents
133     */
134    @Callable(rights = Callable.NO_CHECK_REQUIRED) // Content type definition are public
135    public String getContentTypeForRoot(String leafContentTypeId)
136    {
137        ContentType leafContentType = _contentTypeEP.getExtension(leafContentTypeId);
138        ContentType rootContentType = _hierarchicalReferenceTableContentsHelper.getTopLevelType(leafContentType);
139        
140        if (rootContentType != null)
141        {
142            return rootContentType.getId();
143        }
144        
145        return null;
146    }
147    
148    /**
149     * Return the leafContentType
150     * @param contentTypeId the content type id
151     * @param contentId the content id to get the leaf content type from
152     * @return the map containing the properties of the leaf content type
153     */
154    @Callable(rights = Callable.NO_CHECK_REQUIRED) // Content type definition are public
155    public Map<String, Object> getLeafContentType(String contentTypeId, String contentId)
156    {
157        ContentType cType = _contentTypeEP.getExtension(contentTypeId);
158        if (_hierarchicalReferenceTableContentsHelper.isLeaf(cType))
159        {
160            return _cTypeHelper.getContentTypeProperties(cType);
161        }
162        else
163        {
164            AmetysObjectIterable<Content> contents = _hierarchicalReferenceTableContentsHelper.getDirectChildren(_resolver.resolveById(contentId));
165            for (Content content: contents)
166            {
167                return getLeafContentType(contentTypeId, content.getId());
168            }
169        }
170        return null;
171    }
172    
173    /**
174     * Get the path of attribute holding the parent
175     * @param contentId The id of content
176     * @return the path of parent attribute or null if not found
177     */
178    @Callable(rights = Callable.NO_CHECK_REQUIRED) // Content type definition are public
179    public String getParentAttributePath(String contentId)
180    {
181        Content content = _resolver.resolveById(contentId);
182        List<ContentType> childrenContentTypes = _hierarchicalReferenceTableContentsHelper.getChildContentTypes(content);
183        for (ContentType contentType : childrenContentTypes)
184        {
185            Optional<ContentAttributeDefinition> parentAttribute = contentType.getParentAttributeDefinition();
186            if (!parentAttribute.isEmpty())
187            {
188                return parentAttribute.get().getPath();
189            }
190        }
191        return null;
192    }
193    
194    /**
195     * Get the path of attribute when root is selected
196     * @param leafContentTypeId The id of content
197     * @return the path of parent attribute or null if not found
198     */
199    @Callable(rights = Callable.NO_CHECK_REQUIRED) // Content type definition are public
200    public String getParentAttributePathForRoot(String leafContentTypeId)
201    {
202        ContentType leafContentType = _contentTypeEP.getExtension(leafContentTypeId);
203        return leafContentType.getParentAttributeDefinition()
204                .map(ModelItem::getPath)
205                .filter(StringUtils::isNotEmpty)
206                .orElse(null);
207    }
208    
209    /**
210     * Get the title's properties for edition
211     * @param contentId The id of content being renamed
212     * @return the title's properties
213     */
214    @Callable(rights = Callable.READ_ACCESS, paramIndex = 0, rightContext = ContentRightAssignmentContext.ID)
215    public Map<String, Object> getTitleForEdition(String contentId)
216    {
217        Content content = _resolver.resolveById(contentId);
218        
219        Map<String, Object> titleInfos = new HashMap<>();
220        
221        ModelItem titleDef = content.getDefinition(Content.ATTRIBUTE_TITLE);
222        titleInfos.put("type", titleDef.getType().getId());
223        titleInfos.put("label", titleDef.getLabel());
224        
225        if (ModelItemTypeConstants.MULTILINGUAL_STRING_ELEMENT_TYPE_ID.equals(titleDef.getType().getId()))
226        {
227            titleInfos.put("value", _contentHelper.getTitleVariants(content));
228        }
229        else
230        {
231            titleInfos.put("value", _contentHelper.getTitle(content));
232        }
233        
234        return titleInfos;
235    }
236}