001/* 002 * Copyright 2015 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.plugins.contentstree.ui; 017 018import java.util.ArrayList; 019import java.util.HashMap; 020import java.util.List; 021import java.util.Map; 022 023import org.apache.avalon.framework.parameters.Parameters; 024import org.apache.avalon.framework.service.ServiceException; 025import org.apache.avalon.framework.service.ServiceManager; 026import org.apache.cocoon.acting.ServiceableAction; 027import org.apache.cocoon.environment.ObjectModelHelper; 028import org.apache.cocoon.environment.Redirector; 029import org.apache.cocoon.environment.Request; 030import org.apache.cocoon.environment.SourceResolver; 031import org.apache.commons.lang3.StringUtils; 032 033import org.ametys.cms.contenttype.ContentConstants; 034import org.ametys.cms.contenttype.ContentType; 035import org.ametys.cms.contenttype.ContentTypeExtensionPoint; 036import org.ametys.cms.contenttype.ContentTypesHelper; 037import org.ametys.cms.contenttype.MetadataDefinition; 038import org.ametys.cms.contenttype.MetadataDefinitionHolder; 039import org.ametys.cms.contenttype.MetadataType; 040import org.ametys.cms.contenttype.RepeaterDefinition; 041import org.ametys.cms.repository.Content; 042import org.ametys.core.cocoon.JSonReader; 043import org.ametys.plugins.contentstree.MetadataTreeConfigurationElementsChild; 044import org.ametys.plugins.contentstree.TreeConfiguration; 045import org.ametys.plugins.contentstree.TreeConfigurationContentType; 046import org.ametys.plugins.contentstree.TreeConfigurationElements; 047import org.ametys.plugins.contentstree.TreeConfigurationElementsChild; 048import org.ametys.plugins.contentstree.TreeExtensionPoint; 049import org.ametys.plugins.repository.AmetysObject; 050import org.ametys.plugins.repository.AmetysObjectResolver; 051import org.ametys.plugins.repository.metadata.CompositeMetadata; 052 053/** 054 * Get the node information to build the tree 055 * Put it in the request attribute to use the json reader 056 * 057 */ 058public class GetTreeNodeAction extends ServiceableAction 059{ 060 /** The ametys object resolver instance */ 061 protected AmetysObjectResolver _ametysResolver; 062 /** The tree configuration EP instance */ 063 protected TreeExtensionPoint _treeExtensionPoint; 064 /** The content type EP instance */ 065 protected ContentTypeExtensionPoint _contentTypesEP; 066 /** The content types helper instance */ 067 protected ContentTypesHelper _contentTypesHelper; 068 069 @Override 070 public void service(ServiceManager smanager) throws ServiceException 071 { 072 super.service(smanager); 073 074 _ametysResolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE); 075 _treeExtensionPoint = (TreeExtensionPoint) smanager.lookup(TreeExtensionPoint.ROLE); 076 _contentTypesEP = (ContentTypeExtensionPoint) smanager.lookup(ContentTypeExtensionPoint.ROLE); 077 _contentTypesHelper = (ContentTypesHelper) smanager.lookup(ContentTypesHelper.ROLE); 078 } 079 080 @Override 081 public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception 082 { 083 @SuppressWarnings("unchecked") 084 Map<String, Object> jsParameters = (Map<String, Object>) objectModel.get(ObjectModelHelper.PARENT_CONTEXT); 085 086 TreeConfiguration treeConfiguration = _getTreeConfiguration((String) jsParameters.get("tree")); 087 Content parentContent = _getParentContent((String) jsParameters.get("contentId")); 088 089 Map<String, Object> result = contentToJSON(parentContent, treeConfiguration); 090 091 Request request = ObjectModelHelper.getRequest(objectModel); 092 request.setAttribute(JSonReader.OBJECT_TO_READ, result); 093 094 return EMPTY_MAP; 095 } 096 097 private TreeConfiguration _getTreeConfiguration(String treeId) 098 { 099 if (StringUtils.isBlank(treeId)) 100 { 101 throw new IllegalArgumentException("The tree information cannot be obtain, because 'tree' is null"); 102 } 103 104 TreeConfiguration treeConfiguration = _treeExtensionPoint.getExtension(treeId); 105 if (treeConfiguration == null) 106 { 107 throw new IllegalArgumentException("There is no tree configuration for '" + treeId + "'"); 108 } 109 return treeConfiguration; 110 } 111 112 private Content _getParentContent(String parentId) throws IllegalArgumentException 113 { 114 if (StringUtils.isBlank(parentId)) 115 { 116 throw new IllegalArgumentException("The tree information cannot be obtain, because 'node' is null"); 117 } 118 119 try 120 { 121 return _ametysResolver.resolveById(parentId); 122 } 123 catch (Exception e) 124 { 125 throw new IllegalArgumentException("The tree configuration cannot be used on an object that is not a content: " + parentId, e); 126 } 127 128 } 129 130 /** 131 * Gets the content to JSON format 132 * @param content The content 133 * @param treeConfiguration The configuration of the tree 134 * @return The content properties 135 */ 136 protected Map<String, Object> contentToJSON(Content content, TreeConfiguration treeConfiguration) 137 { 138 Map<String, Object> infos = new HashMap<>(); 139 140 Map<String, List<Content>> childrenContent = _getChildrendContent(content, treeConfiguration); 141 142 List<Map<String, Object>> childrenInfos = new ArrayList<>(); 143 infos.put("children", childrenInfos); 144 145 for (String metadataName : childrenContent.keySet()) 146 { 147 for (Content childContent : childrenContent.get(metadataName)) 148 { 149 Map<String, Object> childInfo = new HashMap<>(); 150 childrenInfos.add(childInfo); 151 152 childInfo.put("contentId", childContent.getId()); 153 childInfo.put("metadataPath", metadataName); 154 childInfo.put("contenttypesIds", childContent.getTypes()); 155 childInfo.put("name", childContent.getName()); 156 childInfo.put("title", childContent.getTitle()); 157 childInfo.put("lang", childContent.getLanguage()); 158 159 childInfo.put("iconGlyph", _contentTypesHelper.getIconGlyph(childContent)); 160 childInfo.put("iconDecorator", _contentTypesHelper.getIconDecorator(childContent)); 161 childInfo.put("iconSmall", _contentTypesHelper.getSmallIcon(childContent)); 162 childInfo.put("iconMedium", _contentTypesHelper.getMediumIcon(childContent)); 163 childInfo.put("iconLarge", _contentTypesHelper.getLargeIcon(childContent)); 164 165 childInfo.putAll(getAdditionalContentInfos(childContent)); 166 167 Map<String, List<Content>> grandchildrenContent = _getChildrendContent(childContent, treeConfiguration); 168 if (grandchildrenContent.size() == 0) 169 { 170 List<Map<String, Object>> subinfos = new ArrayList<>(); 171 childInfo.put("children", subinfos); 172 } 173 } 174 } 175 176 return infos; 177 } 178 179 /** 180 * Get the additional infos on content 181 * @param content The content 182 * @return The additionnal infos in a Map 183 */ 184 protected Map<String, Object> getAdditionalContentInfos (Content content) 185 { 186 return new HashMap<>(); 187 } 188 189 private Map<String, List<Content>> _getChildrendContent(Content parentContent, TreeConfiguration treeConfiguration) 190 { 191 Map<String, List<Content>> childrenContent = new HashMap<>(); 192 193 // For each content type of the content 194 for (String contentTypeId : parentContent.getTypes()) 195 { 196 // Loop over all possible elements for the tree 197 for (TreeConfigurationElements treeConfigurationElements : treeConfiguration.getElements()) 198 { 199 // Check for a match between the element and the content type of the content 200 for (TreeConfigurationContentType treeConfigurationContentType : treeConfigurationElements.getContentTypesConfiguration()) 201 { 202 if (treeConfigurationContentType.getContentTypesIds().contains(contentTypeId)) 203 { 204 ContentType contentType = _contentTypesEP.getExtension(contentTypeId); 205 206 // Add all required children for this element 207 for (TreeConfigurationElementsChild treeConfigurationElementsChild : treeConfigurationElements.getChildren()) 208 { 209 if (treeConfigurationElementsChild instanceof MetadataTreeConfigurationElementsChild) 210 { 211 // Get the metadata 212 Map<String, List<Content>> contents = _handleMetadataTreeConfigurationElementsChild(contentType, parentContent.getMetadataHolder(), (MetadataTreeConfigurationElementsChild) treeConfigurationElementsChild, treeConfiguration); 213 _merge(childrenContent, contents); 214 } 215 else 216 { 217 throw new IllegalArgumentException("The child configuration element class <" + treeConfigurationElementsChild + "> is not supported in tree '" + treeConfiguration.getId() + "'"); 218 } 219 } 220 } 221 } 222 223 } 224 } 225 226 return childrenContent; 227 } 228 229 private void _merge(Map<String, List<Content>> childrenContent, Map<String, List<Content>> contents) 230 { 231 for (String key : contents.keySet()) 232 { 233 if (!childrenContent.containsKey(key)) 234 { 235 childrenContent.put(key, new ArrayList<Content>()); 236 } 237 238 List<Content> contentsList = childrenContent.get(key); 239 contentsList.addAll(contents.get(key)); 240 } 241 } 242 243 private Map<String, List<Content>> _handleMetadataTreeConfigurationElementsChild(ContentType contentType, CompositeMetadata metadataHolder, MetadataTreeConfigurationElementsChild metadataTreeConfigurationElementsChild, TreeConfiguration treeConfiguration) 244 { 245 Map<String, List<Content>> childrenContent = new HashMap<>(); 246 247 String metadataId = metadataTreeConfigurationElementsChild.getId(); 248 249 try 250 { 251 Map<String, List<Content>> contents = _handleMetadata(contentType, metadataHolder, metadataId, ""); 252 _merge(childrenContent, contents); 253 } 254 catch (Exception e) 255 { 256 throw new IllegalArgumentException("An error occured on the tree configuration '" + treeConfiguration.getId() + "' getting for metadata '" + metadataId + "' on content type '" + contentType.getId() + "'", e); 257 } 258 259 return childrenContent; 260 } 261 262 private Map<String, List<Content>> _handleMetadata(MetadataDefinitionHolder metadataDefHolder, CompositeMetadata metadataHolder, String metadataId, String absoluteMetadataName) 263 { 264 Map<String, List<Content>> childrenContent = new HashMap<>(); 265 266 String metadataName = StringUtils.substringBefore(metadataId, ContentConstants.METADATA_PATH_SEPARATOR); 267 268 MetadataDefinition subMetadataDef = metadataDefHolder.getMetadataDefinition(metadataName); 269 if (subMetadataDef == null) 270 { 271 throw new IllegalArgumentException("No metadata definition for " + metadataName); 272 } 273 274 275 if (metadataHolder.hasMetadata(metadataName)) 276 { 277 if (subMetadataDef instanceof RepeaterDefinition) 278 { 279 CompositeMetadata metadata = metadataHolder.getCompositeMetadata(subMetadataDef.getName()); 280 for (String entryName : metadata.getMetadataNames()) 281 { 282 CompositeMetadata entry = metadata.getCompositeMetadata(entryName); 283 String subMetadataId = StringUtils.substringAfter(metadataId, ContentConstants.METADATA_PATH_SEPARATOR); 284 285 Map<String, List<Content>> contents = _handleMetadata(subMetadataDef, entry, subMetadataId, absoluteMetadataName + metadataName + ContentConstants.METADATA_PATH_SEPARATOR + entryName + ContentConstants.METADATA_PATH_SEPARATOR); 286 _merge(childrenContent, contents); 287 } 288 } 289 else if (subMetadataDef.getType() == MetadataType.COMPOSITE) 290 { 291 CompositeMetadata metadata = metadataHolder.getCompositeMetadata(subMetadataDef.getName()); 292 String subMetadataId = StringUtils.substringAfter(metadataId, ContentConstants.METADATA_PATH_SEPARATOR); 293 294 Map<String, List<Content>> contents = _handleMetadata(subMetadataDef, metadata, subMetadataId, absoluteMetadataName + metadataName + ContentConstants.METADATA_PATH_SEPARATOR); 295 _merge(childrenContent, contents); 296 } 297 else if (subMetadataDef.getType() == MetadataType.CONTENT) 298 { 299 String[] contentIds = metadataHolder.getStringArray(metadataName); 300 for (String contentId : contentIds) 301 { 302 Content refContent = _ametysResolver.resolveById(contentId); 303 304 String key = absoluteMetadataName + metadataName; 305 if (!childrenContent.containsKey(key)) 306 { 307 childrenContent.put(key, new ArrayList<Content>()); 308 } 309 childrenContent.get(key).add(refContent); 310 } 311 } 312 else if (subMetadataDef.getType() == MetadataType.SUB_CONTENT) 313 { 314 for (AmetysObject ametysObject : metadataHolder.getObjectCollection(metadataName).getChildren()) 315 { 316 if (ametysObject instanceof Content) 317 { 318 Content subContent = (Content) ametysObject; 319 320 String key = absoluteMetadataName + metadataName; 321 if (!childrenContent.containsKey(key)) 322 { 323 childrenContent.put(key, new ArrayList<Content>()); 324 } 325 childrenContent.get(key).add(subContent); 326 } 327 } 328 } 329 else 330 { 331 throw new IllegalArgumentException("The metadata definition for " + absoluteMetadataName + metadataName + " is not a content"); 332 } 333 } 334 335 return childrenContent; 336 } 337}