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.cms.contenttype; 017 018import java.util.ArrayList; 019import java.util.Collection; 020import java.util.Collections; 021import java.util.HashMap; 022import java.util.HashSet; 023import java.util.List; 024import java.util.Map; 025import java.util.Set; 026import java.util.stream.Collectors; 027import java.util.stream.Stream; 028 029import org.apache.avalon.framework.parameters.Parameters; 030import org.apache.avalon.framework.service.ServiceException; 031import org.apache.avalon.framework.service.ServiceManager; 032import org.apache.cocoon.acting.ServiceableAction; 033import org.apache.cocoon.environment.ObjectModelHelper; 034import org.apache.cocoon.environment.Redirector; 035import org.apache.cocoon.environment.Request; 036import org.apache.cocoon.environment.SourceResolver; 037import org.apache.commons.lang.BooleanUtils; 038import org.apache.commons.lang3.StringUtils; 039 040import org.ametys.cms.repository.Content; 041import org.ametys.core.cocoon.JSonReader; 042import org.ametys.plugins.repository.AmetysObjectResolver; 043 044/** 045 * Get the common metadata set between given content types and/or among given contents 046 */ 047public class GetCommonMetadataSetAction extends ServiceableAction 048{ 049 /** The content type EP */ 050 protected ContentTypeExtensionPoint _cTypeEP; 051 052 /** The content types Helper */ 053 protected ContentTypesHelper _contentTypesHelper; 054 055 /** The ametys object resolver */ 056 protected AmetysObjectResolver _resolver; 057 058 @Override 059 public void service(ServiceManager smanager) throws ServiceException 060 { 061 super.service(smanager); 062 _cTypeEP = (ContentTypeExtensionPoint) smanager.lookup(ContentTypeExtensionPoint.ROLE); 063 _contentTypesHelper = (ContentTypesHelper) smanager.lookup(ContentTypesHelper.ROLE); 064 _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE); 065 } 066 067 @Override 068 public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception 069 { 070 Request request = ObjectModelHelper.getRequest(objectModel); 071 072 boolean isEdition = BooleanUtils.toBoolean(request.getParameter("isEdition")); // false by default 073 boolean includeInternal = BooleanUtils.toBoolean(request.getParameter("includeInternal")); // false by default 074 075 Set<String> requestedCTypeIds = getContentTypes(request); 076 Map<String, Set<String>> cTypesByContent = getContentTypesFromContents(request); 077 if (requestedCTypeIds.isEmpty() && cTypesByContent.keySet().isEmpty()) 078 { 079 requestedCTypeIds = getAllAvailablesContentTypes(request, true); 080 } 081 082 Map<String, Object> result = new HashMap<>(); 083 084 Collection<Map<String, Object>> commonMetadataSetsInfo = getCommonMetadataSetsInfo(cTypesByContent, requestedCTypeIds, isEdition, includeInternal); 085 result.put("metadataSets", commonMetadataSetsInfo); 086 087 request.setAttribute(JSonReader.OBJECT_TO_READ, result); 088 return EMPTY_MAP; 089 } 090 091 /** 092 * Retrieves the common metadata labels by name 093 * @param cTypesByContent the content type ids by content 094 * @param cTypeIds the content type ids (not coming from contents) 095 * @param isEdition Is the metadata set for edition ? 096 * @param includeInternal include internal metadata sets ? 097 * @return The map of metadata set 098 */ 099 protected Collection<Map<String, Object>> getCommonMetadataSetsInfo(Map<String, Set<String>> cTypesByContent, Collection<String> cTypeIds, boolean isEdition, boolean includeInternal) 100 { 101 Map<String, Map<String, Object>> commonMetadataSetsInfo = null; 102 103 for (Set<String> cTypeIdsForCurrentContent : cTypesByContent.values()) 104 { 105 List<Map<String, Object>> metadataSetsInfoForCurrentContent = _metadataSetUnion(cTypeIdsForCurrentContent, includeInternal); 106 commonMetadataSetsInfo = _metadataSetIntersection(commonMetadataSetsInfo, metadataSetsInfoForCurrentContent); 107 } 108 109 for (String cTypeId : cTypeIds) 110 { 111 List<Map<String, Object>> metadataSetsInfoForCurrentCType = _contentTypesHelper.getMetadataSetsInfo(cTypeId, false, includeInternal); 112 commonMetadataSetsInfo = _metadataSetIntersection(commonMetadataSetsInfo, metadataSetsInfoForCurrentCType); 113 } 114 115 return commonMetadataSetsInfo != null ? commonMetadataSetsInfo.values() : Collections.EMPTY_LIST; 116 } 117 118 private List<Map<String, Object>> _metadataSetUnion(Set<String> cTypes, boolean includeInternal) 119 { 120 List<Map<String, Object>> metadataSetsInfo = new ArrayList<>(); 121 for (String ctype : cTypes) 122 { 123 metadataSetsInfo.addAll(_contentTypesHelper.getMetadataSetsInfo(ctype, false, includeInternal)); 124 } 125 return metadataSetsInfo; 126 } 127 128 private Map<String, Map<String, Object>> _metadataSetIntersection(Map<String, Map<String, Object>> commonMetadataSetsInfo, List<Map<String, Object>> metadataSetsInfo) 129 { 130 Map<String, Map<String, Object>> result = commonMetadataSetsInfo; 131 if (result == null) 132 { 133 result = new HashMap<>(); 134 for (Map<String, Object> entry : metadataSetsInfo) 135 { 136 String metadataSetName = (String) entry.get("name"); 137 result.put(metadataSetName, entry); 138 } 139 } 140 else 141 { 142 Set<String> metadataSetNames = new HashSet<>(); 143 for (Map<String, Object> entry : metadataSetsInfo) 144 { 145 String metadataSetName = (String) entry.get("name"); 146 metadataSetNames.add(metadataSetName); 147 } 148 149 // only retains common metadata (performs a set intersection) 150 result.keySet().retainAll(metadataSetNames); 151 } 152 153 return result; 154 } 155 156 /** 157 * Get the content types id to search for 158 * @param request the request 159 * @return the content types 160 */ 161 protected Set<String> getContentTypes(Request request) 162 { 163 Set<String> cTypeIds = new HashSet<>(); 164 String[] ids = request.getParameterValues("ids"); 165 166 if (ids != null) 167 { 168 for (String id : ids) 169 { 170 if (StringUtils.isNotEmpty(id)) 171 { 172 // id can contains comma (when allOption is selected for example). 173 for (String idPart : StringUtils.split(id, ',')) 174 { 175 cTypeIds.add(idPart); 176 } 177 } 178 } 179 } 180 181 return cTypeIds; 182 } 183 184 /** 185 * Get the content types id to search for (by content id) 186 * @param request the request 187 * @return the content types by content 188 */ 189 protected Map<String, Set<String>> getContentTypesFromContents(Request request) 190 { 191 Map<String, Set<String>> result = new HashMap<>(); 192 193 String[] contentIds = request.getParameterValues("contentIds"); 194 195 if (contentIds != null) 196 { 197 for (String contentId : contentIds) 198 { 199 if (StringUtils.isNotEmpty(contentId)) 200 { 201 Content content = _resolver.resolveById(contentId); 202 203 Set<String> allContentTypes = Stream.concat(Stream.of(content.getTypes()), Stream.of(content.getMixinTypes())) 204 .collect(Collectors.toSet()); 205 result.put(contentId, allContentTypes); 206 } 207 } 208 } 209 210 return result; 211 } 212 213 /** 214 * Get all the available content types 215 * @param request the request 216 * @param publicOnly Only the non private content types will be returned 217 * @return all the available content types 218 */ 219 protected Set<String> getAllAvailablesContentTypes (Request request, boolean publicOnly) 220 { 221 Set<String> types = new HashSet<>(); 222 223 for (String id : _cTypeEP.getExtensionsIds()) 224 { 225 if (!publicOnly || !_cTypeEP.getExtension(id).isPrivate()) 226 { 227 types.add(id); 228 } 229 } 230 231 return types; 232 } 233}