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.contenttype; 017 018import java.util.ArrayList; 019import java.util.Arrays; 020import java.util.Collection; 021import java.util.Collections; 022import java.util.HashMap; 023import java.util.HashSet; 024import java.util.List; 025import java.util.Map; 026import java.util.Set; 027 028import org.apache.avalon.framework.component.Component; 029import org.apache.avalon.framework.logger.AbstractLogEnabled; 030import org.apache.avalon.framework.service.ServiceException; 031import org.apache.avalon.framework.service.ServiceManager; 032import org.apache.avalon.framework.service.Serviceable; 033 034import org.ametys.core.ui.Callable; 035import org.ametys.runtime.i18n.I18nizableText; 036 037/** 038 * This component get content types according to a lot of parameters (hide mixin content type, hide non reference content type...) 039 */ 040public class ContentTypesTreeComponent extends AbstractLogEnabled implements Component, Serviceable 041{ 042 /** Avalon Role. */ 043 public static final String ROLE = ContentTypesTreeComponent.class.getName(); 044 045 private ContentTypeExtensionPoint _contentTypeEP; 046 047 public void service(ServiceManager smanager) throws ServiceException 048 { 049 _contentTypeEP = (ContentTypeExtensionPoint) smanager.lookup(ContentTypeExtensionPoint.ROLE); 050 } 051 052 enum ExcludeMode 053 { 054 DISABLED, 055 HIDE 056 } 057 058 /** 059 * Get content types to hierarchical or flat representation according to given parameters 060 * @param params The parameters :<ul> 061 * <li>{Boolean} excludeReferenceTable : to exclude reference table. Defaults to true.</li> 062 * <li>{Boolean} excludePrivate : to exclude private content types. Defaults to false.</li> 063 * <li>{Boolean} excludeMixin : to exclude mixins. Defaults to false.</li> 064 * <li>{Boolean} excludeAbstract : to exclude asbtract content types. Defaults to false.</li> 065 * <li>{String} excludeMode : 'hide' or 'disabled'. The mode of exclusion. Defaults to 'disabled'.</li> 066 * <li>{Boolean} hierarchicalView: true to get a hierarchical representation of content types, or false to get a flat representation. Defaults to true.</li> 067 * <li>{List<String>} contentTypes: the ids of content types to restrict the list. The child content types will be also returned</li> 068 * <li>{List<String>} strictContentTypes: the ids of content types to restrict the list. Only these content types will be returned</li></ul> 069 * @return The content types according to received parameters 070 */ 071 @SuppressWarnings("unchecked") 072 @Callable 073 public Map<String, Object> getContentTypes(Map<String, Object> params) 074 { 075 Map<String, Object> result = new HashMap<>(); 076 077 boolean excludeReferenceTable = params.containsKey("excludeReferenceTable") ? (boolean) params.get("excludeReferenceTable") : true; 078 boolean excludePrivate = params.containsKey("excludePrivate") ? (boolean) params.get("excludePrivate") : false; 079 boolean excludeMixin = params.containsKey("excludeMixin") ? (boolean) params.get("excludeMixin") : false; 080 boolean excludeAbstract = params.containsKey("excludeAbstract") ? (boolean) params.get("excludeAbstract") : false; 081 boolean includeMixinOnly = params.containsKey("includeMixinOnly") ? (boolean) params.get("includeMixinOnly") : false; 082 083 String excludeModeAsStr = params.containsKey("excludeMode") ? (String) params.get("excludeMode") : "disabled"; 084 ExcludeMode excludeMode = ExcludeMode.valueOf(excludeModeAsStr.toUpperCase()); 085 086 boolean hierarchicalView = params.containsKey("hierarchicalView") ? (boolean) params.get("hierarchicalView") : true; 087 List<String> strictContentTypeIds = params.containsKey("strictContentTypes") ? (List<String>) params.get("strictContentTypes") : null; 088 List<String> contentTypeIds = params.containsKey("contentTypes") ? (List<String>) params.get("contentTypes") : null; 089 090 if ((strictContentTypeIds == null || strictContentTypeIds.isEmpty()) && contentTypeIds != null && !contentTypeIds.isEmpty()) 091 { 092 strictContentTypeIds = _getRestrictedContentTypes(contentTypeIds); 093 } 094 095 List<Map<String, Object>> cTypes = _getContentTypes(excludeReferenceTable, excludePrivate, excludeMixin, excludeAbstract, includeMixinOnly, excludeMode, hierarchicalView, strictContentTypeIds); 096 097 result.put("children", cTypes); 098 return result; 099 } 100 101 private List<Map<String, Object>> _getContentTypes(boolean excludeReferenceTable, boolean excludePrivate, boolean excludeMixin, boolean excludeAbstract, boolean includeMixinOnly, ExcludeMode excludeMode, boolean hierarchicalView, List<String> strictContentTypeIds) 102 { 103 List<Map<String, Object>> cTypes = new ArrayList<>(); 104 105 if (hierarchicalView) 106 { 107 Set<ContentType> rootCTypes = _getRootContentTypes(); 108 for (ContentType rootCType : rootCTypes) 109 { 110 cTypes.addAll(_getHierarchicalContentType(rootCType, strictContentTypeIds, excludeReferenceTable, excludePrivate, excludeMixin, excludeAbstract, includeMixinOnly, excludeMode)); 111 } 112 } 113 else 114 { 115 Set<ContentType> rootCTypes = _getContentTypes(); 116 for (ContentType rootCType : rootCTypes) 117 { 118 cTypes.addAll(_getNonHierarchicalContentType(rootCType, strictContentTypeIds, excludeReferenceTable, excludePrivate, excludeMixin, excludeAbstract, includeMixinOnly)); 119 } 120 } 121 122 return cTypes; 123 } 124 125 private List<String> _getRestrictedContentTypes(Collection<String> contentTypeIds) 126 { 127 Set<String> restrictedContentTypeIds = new HashSet<>(); 128 restrictedContentTypeIds.addAll(contentTypeIds); 129 130 for (String contentTypeId : contentTypeIds) 131 { 132 restrictedContentTypeIds.addAll(_contentTypeEP.getSubTypes(contentTypeId)); 133 } 134 135 return new ArrayList<>(restrictedContentTypeIds); 136 } 137 138 private List<Map<String, Object>> _getHierarchicalContentType(ContentType contentType, Collection<String> restrictedContentTypes, boolean excludeReferenceTable, boolean excludePrivate, boolean excludeMixin, boolean excludeAbstract, boolean mixinOnly, ExcludeMode excludeMode) 139 { 140 List<Map<String, Object>> cTypes = new ArrayList<>(); 141 142 boolean matchCondition = _matchCondition(contentType, restrictedContentTypes, excludeReferenceTable, excludePrivate, excludeMixin, excludeAbstract, mixinOnly); 143 if (matchCondition || excludeMode == ExcludeMode.DISABLED) 144 { 145 Map<String, Object> params = _getContentTypeParams(contentType, !matchCondition); 146 147 List<Map<String, Object>> children = new ArrayList<>(); 148 Collection<String> childContentTypes = _contentTypeEP.getDirectSubTypes(contentType.getId()); 149 for (String id : childContentTypes) 150 { 151 ContentType childContentType = _contentTypeEP.getExtension(id); 152 children.addAll(_getHierarchicalContentType(childContentType, restrictedContentTypes, excludeReferenceTable, excludePrivate, excludeMixin, excludeAbstract, mixinOnly, excludeMode)); 153 } 154 155 if (!children.isEmpty() || matchCondition) 156 { 157 params.put("leaf", children.isEmpty()); 158 cTypes.add(params); 159 160 if (!children.isEmpty()) 161 { 162 params.put("children", children); 163 } 164 } 165 166 } 167 168 return cTypes; 169 } 170 171 private List<Map<String, Object>> _getNonHierarchicalContentType(ContentType contentType, Collection<String> restrictedContentTypes, boolean excludeReferenceTable, boolean excludePrivate, boolean excludeMixin, boolean excludeAbstract, boolean mixinOnly) 172 { 173 List<Map<String, Object>> cTypes = new ArrayList<>(); 174 boolean matchCondition = _matchCondition(contentType, restrictedContentTypes, excludeReferenceTable, excludePrivate, excludeMixin, excludeAbstract, mixinOnly); 175 if (matchCondition) 176 { 177 Map<String, Object> params = _getContentTypeParams(contentType, !matchCondition); 178 params.put("leaf", true); 179 cTypes.add(params); 180 } 181 return cTypes; 182 } 183 184 private Set<ContentType> _getRootContentTypes() 185 { 186 Set<ContentType> rootContentTypes = new HashSet<>(); 187 188 Set<String> ids = _contentTypeEP.getExtensionsIds(); 189 for (String id : ids) 190 { 191 ContentType contentType = _contentTypeEP.getExtension(id); 192 if (contentType.getSupertypeIds().length == 0) 193 { 194 rootContentTypes.add(contentType); 195 } 196 } 197 return rootContentTypes; 198 } 199 200 private Set<ContentType> _getContentTypes() 201 { 202 Set<ContentType> contentTypes = new HashSet<>(); 203 204 Set<String> ids = _contentTypeEP.getExtensionsIds(); 205 for (String id : ids) 206 { 207 ContentType contentType = _contentTypeEP.getExtension(id); 208 contentTypes.add(contentType); 209 } 210 return contentTypes; 211 } 212 213 private boolean _matchCondition(ContentType cType, Collection<String> restrictedContentTypes, boolean excludeReferenceTable, boolean excludePrivate, boolean excludeMixin, boolean excludeAbstract, boolean mixinOnly) 214 { 215 if (cType.isReferenceTable() && excludeReferenceTable 216 || cType.isPrivate() && excludePrivate 217 || cType.isMixin() && excludeMixin 218 || cType.isAbstract() && excludeAbstract 219 || !cType.isMixin() && mixinOnly 220 || (restrictedContentTypes != null && !restrictedContentTypes.isEmpty() && !restrictedContentTypes.contains(cType.getId()))) 221 { 222 return false; 223 } 224 225 return true; 226 } 227 228 /** 229 * Get the matching content types 230 * @param excludeReferenceTable true to exclude reference table 231 * @param excludePrivate true to exclude the private content types 232 * @param excludeAbstract true to exclude the abstract content types 233 * @param excludeMixin true to exclude the mixins 234 * @param mixinOnly true to include the mixins only 235 * @return matching content types 236 */ 237 public Set<ContentType> getMatchingContentTypes(boolean excludeReferenceTable, boolean excludePrivate, boolean excludeAbstract, boolean excludeMixin, boolean mixinOnly) 238 { 239 return getMatchingContentTypes(null, true, excludeReferenceTable, excludePrivate, excludeAbstract, excludeMixin, mixinOnly); 240 } 241 242 /** 243 * Get the matching content types among the given list 244 * @param contentTypesIds The list of content types to browse. Can be null to browse all available content types 245 * @param browseChildren to browse the child content types 246 * @param excludeReferenceTable true to exclude reference table 247 * @param excludePrivate true to exclude the private content types 248 * @param excludeAbstract true to exclude the abstract content types 249 * @param excludeMixin true to exclude the mixins 250 * @param mixinOnly true to include the mixins only 251 * @return matching content types 252 */ 253 public Set<ContentType> getMatchingContentTypes(String[] contentTypesIds, boolean browseChildren, boolean excludeReferenceTable, boolean excludePrivate, boolean excludeAbstract, boolean excludeMixin, boolean mixinOnly) 254 { 255 Set<ContentType> matchingContentTypes = new HashSet<>(); 256 Set<String> ids; 257 if (contentTypesIds != null && contentTypesIds.length != 0) 258 { 259 ids = new HashSet<>(Arrays.asList(contentTypesIds)); 260 } 261 else 262 { 263 ids = _contentTypeEP.getExtensionsIds(); 264 } 265 266 for (String id : ids) 267 { 268 ContentType contentType = _contentTypeEP.getExtension(id); 269 270 if (_matchCondition(contentType, null, excludeReferenceTable, excludePrivate, excludeMixin, excludeAbstract, mixinOnly)) 271 { 272 matchingContentTypes.add(contentType); 273 } 274 275 if (browseChildren) 276 { 277 Collection<String> childIds = _contentTypeEP.getSubTypes(id); 278 for (String childId : childIds) 279 { 280 matchingContentTypes.addAll(getMatchingContentTypes(new String[]{childId}, true, excludeReferenceTable, excludePrivate, excludeAbstract, excludeMixin, mixinOnly)); 281 } 282 } 283 } 284 285 return matchingContentTypes; 286 } 287 288 private Map<String, Object> _getContentTypeParams(ContentType contentType, boolean disabled) 289 { 290 Map<String, Object> params = new HashMap<>(); 291 params.put("contentTypeId", contentType.getId()); 292 params.put("text", contentType.getLabel()); 293 params.put("label", contentType.getLabel()); 294 params.put("description", contentType.getDescription()); 295 params.put("abstract", contentType.isAbstract()); 296 params.put("private", contentType.isPrivate()); 297 params.put("reference-table", contentType.isReferenceTable()); 298 params.put("mixin", contentType.isMixin()); 299 params.put("simple", contentType.isSimple()); 300 params.put("multilingual", contentType.isMultilingual()); 301 params.put("iconGlyph", contentType.getIconGlyph()); 302 params.put("iconDecorator", contentType.getIconDecorator()); 303 params.put("iconSmall", contentType.getSmallIcon()); 304 params.put("iconMedium", contentType.getMediumIcon()); 305 params.put("iconLarge", contentType.getLargeIcon()); 306 if (contentType instanceof AutomaticContentType) 307 { 308 params.put("origin", new I18nizableText("plugin.cms", "PLUGINS_CMS_CONTENTTYPE_TREE_CONF_ORIGIN")); 309 params.put("glyphOrigin", "ametysicon-xml6"); 310 } 311 else 312 { 313 Map<String, I18nizableText> pluginName = Collections.singletonMap("pluginName", new I18nizableText(contentType.getPluginName())); 314 I18nizableText origin = new I18nizableText("plugin.cms", "PLUGINS_CMS_CONTENTTYPE_TREE_PLUGIN_ORIGIN", pluginName); 315 params.put("origin", origin); 316 params.put("glyphOrigin", "ametysicon-puzzle-piece1"); 317 } 318 if (disabled) 319 { 320 params.put("disabled", true); 321 } 322 323 return params; 324 } 325}