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