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