001/* 002 * Copyright 2013 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.queriesdirectory.actions; 017 018import java.util.ArrayList; 019import java.util.HashMap; 020import java.util.List; 021import java.util.Map; 022import java.util.Optional; 023import java.util.function.Predicate; 024import java.util.stream.Stream; 025 026import org.apache.avalon.framework.parameters.Parameters; 027import org.apache.avalon.framework.service.ServiceException; 028import org.apache.avalon.framework.service.ServiceManager; 029import org.apache.cocoon.acting.ServiceableAction; 030import org.apache.cocoon.environment.ObjectModelHelper; 031import org.apache.cocoon.environment.Redirector; 032import org.apache.cocoon.environment.Request; 033import org.apache.cocoon.environment.SourceResolver; 034import org.apache.commons.lang3.StringUtils; 035import org.apache.commons.lang3.tuple.Pair; 036 037import org.ametys.core.cocoon.JSonReader; 038import org.ametys.core.user.CurrentUserProvider; 039import org.ametys.core.user.UserIdentity; 040import org.ametys.plugins.queriesdirectory.Query; 041import org.ametys.plugins.queriesdirectory.Query.QueryProfile; 042import org.ametys.plugins.queriesdirectory.QueryContainer; 043import org.ametys.plugins.queriesdirectory.QueryDAO; 044import org.ametys.plugins.repository.AmetysObjectIterable; 045import org.ametys.plugins.repository.AmetysObjectResolver; 046 047import com.google.common.base.Predicates; 048 049/** 050 * SAX queries 051 * 052 */ 053public class GetQueriesAction extends ServiceableAction 054{ 055 private CurrentUserProvider _userProvider; 056 private AmetysObjectResolver _resolver; 057 private QueryDAO _queryDAO; 058 059 060 @Override 061 public void service(ServiceManager serviceManager) throws ServiceException 062 { 063 super.service(serviceManager); 064 _userProvider = (CurrentUserProvider) serviceManager.lookup(CurrentUserProvider.ROLE); 065 _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE); 066 _queryDAO = (QueryDAO) serviceManager.lookup(QueryDAO.ROLE); 067 } 068 069 public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception 070 { 071 @SuppressWarnings("unchecked") 072 Map<String, Object> jsParameters = (Map<String, Object>) objectModel.get(ObjectModelHelper.PARENT_CONTEXT); 073 074 String profileId = StringUtils.defaultIfEmpty((String) jsParameters.get("profile"), "read_access"); 075 076 @SuppressWarnings("unchecked") 077 List<String> acceptedQueryTypes = (List<String>) jsParameters.get("acceptedQueryTypes"); 078 079 if (!"read_access".equals(profileId) && !"write_access".equals(profileId) && !"right_access".equals(profileId)) 080 { 081 throw new IllegalArgumentException("Unexpected profile identifier : " + profileId); 082 } 083 084 QueryProfile profile = QueryProfile.valueOf(profileId.toUpperCase()); 085 QueryContainer parentContainer = _getParentContainer(jsParameters); 086 Boolean onlyContainers = (Boolean) jsParameters.getOrDefault("onlyContainers", Boolean.FALSE); 087 Boolean onlyQueries = onlyContainers ? false : (Boolean) jsParameters.getOrDefault("onlyQueries", Boolean.FALSE); 088 Boolean testIfEmptyContainer = (Boolean) jsParameters.getOrDefault("testIfEmptyContainer", Boolean.FALSE); 089 Boolean allDescendants = (Boolean) jsParameters.getOrDefault("allDescendants", Boolean.FALSE); 090 091 UserIdentity user = _userProvider.getUser(); 092 093 List<Map<String, Object>> nodes = new ArrayList<>(); 094 Stream<Query> childQueriesStream = onlyContainers 095 ? Stream.empty() 096 : _getChildQueries(parentContainer, !allDescendants, acceptedQueryTypes, profile, user); 097 childQueriesStream 098 .map(_queryDAO::getQueryProperties) 099 .forEach(nodes::add); 100 101 Stream<Pair<QueryContainer, AdditionalInfoOnContainer>> childContainersStream = onlyQueries 102 ? Stream.empty() 103 : _getChildQueryContainers(parentContainer, testIfEmptyContainer, acceptedQueryTypes, profile, user); 104 105 childContainersStream 106 .forEach(p -> 107 { 108 QueryContainer queryContainer = p.getLeft(); 109 Map<String, Object> properties = _queryDAO.getQueryContainerProperties(queryContainer); 110 AdditionalInfoOnContainer additionalInfo = p.getRight(); 111 additionalInfo.fillAdditionalInfo(properties); 112 switch (profile) 113 { 114 case WRITE_ACCESS: 115 if ((boolean) properties.get(QueryDAO.WRITE_ACCESS_PROPERTY) || _queryDAO.hasAnyWritableDescendant(user, queryContainer)) 116 { 117 nodes.add(properties); 118 } 119 break; 120 case RIGHT_ACCESS: 121 if ((boolean) properties.get(QueryDAO.EDIT_RIGHTS_ACCESS_PROPERTY) || _queryDAO.hasAnyAssignableDescendant(user, queryContainer)) 122 { 123 nodes.add(properties); 124 } 125 break; 126 case READ_ACCESS: 127 default: 128 if ((boolean) properties.get(QueryDAO.READ_ACCESS_PROPERTY) || _queryDAO.hasAnyReadableDescendant(user, queryContainer)) 129 { 130 nodes.add(properties); 131 } 132 break; 133 } 134 }); 135 136 Map<String, Object> result = new HashMap<>(); 137 result.put("queries", nodes); 138 139 Request request = ObjectModelHelper.getRequest(objectModel); 140 request.setAttribute(JSonReader.OBJECT_TO_READ, result); 141 142 return EMPTY_MAP; 143 } 144 145 private QueryContainer _getParentContainer(Map jsParameters) 146 { 147 Predicate<String> isNotRoot = Predicates.not(Predicates.equalTo(QueryDAO.ROOT_QUERY_CONTAINER_ID)); 148 QueryContainer parent = Optional.of("node") 149 .map(jsParameters::get) 150 .filter(String.class::isInstance) 151 .map(String.class::cast) 152 .filter(isNotRoot) 153 .map(_resolver::<QueryContainer>resolveById) 154 .orElse(_queryDAO.getQueriesRootNode()/* root node must exist, so it must be created if it does not exist yet */); 155 return parent; 156 } 157 158 private Stream<Pair<QueryContainer, AdditionalInfoOnContainer>> _getChildQueryContainers(QueryContainer parentContainer, boolean testIfEmptyContainer, List<String> acceptedQueryTypes, QueryProfile profile, UserIdentity user) 159 { 160 AmetysObjectIterable<QueryContainer> childQueryContainers = _queryDAO.getChildQueryContainers(parentContainer); 161 162 Stream<QueryContainer> childQueryContainersStream = childQueryContainers.stream(); 163 if (testIfEmptyContainer) 164 { 165 // indicates if container do not have (not necessarily direct) query children (i.e. is empty) 166 return childQueryContainersStream 167 .map(ct -> Pair.of(ct, new AdditionalInfoOnContainer(_hasNoDescendant(ct, acceptedQueryTypes, profile, user)))); 168 } 169 else 170 { 171 return childQueryContainersStream 172 .map(ct -> Pair.of(ct, new AdditionalInfoOnContainer())); 173 } 174 } 175 176 private Boolean _hasNoDescendant(QueryContainer queryContainer, List<String> acceptedQueryTypes, QueryProfile profile, UserIdentity user) 177 { 178 boolean onlyDirectChildren = false; // in order to test no only direct children, but all descendants 179 Stream<Query> childQueries = _getChildQueries(queryContainer, onlyDirectChildren, acceptedQueryTypes, profile, user); 180 return !childQueries.findAny().isPresent(); 181 } 182 183 private Stream<Query> _getChildQueries(QueryContainer parentContainer, boolean onlyDirectChildren, List<String> acceptedQueryTypes, QueryProfile profile, UserIdentity user) 184 { 185 Stream<Query> childQueries; 186 switch (profile) 187 { 188 case WRITE_ACCESS: 189 childQueries = _queryDAO.getChildQueriesInWriteAccess(parentContainer, onlyDirectChildren, user, acceptedQueryTypes); 190 break; 191 case RIGHT_ACCESS: 192 childQueries = _queryDAO.getChildQueriesInRightAccess(parentContainer, onlyDirectChildren, user, acceptedQueryTypes); 193 break; 194 case READ_ACCESS: 195 default: 196 childQueries = _queryDAO.getChildQueriesInReadAccess(parentContainer, onlyDirectChildren, user, acceptedQueryTypes); 197 break; 198 } 199 200 return childQueries; 201 } 202 203 private static class AdditionalInfoOnContainer 204 { 205 private Optional<Boolean> _hasNoDescendant = Optional.empty(); 206 207 AdditionalInfoOnContainer() 208 { 209 } 210 211 AdditionalInfoOnContainer(boolean hasNoDescendant) 212 { 213 _hasNoDescendant = Optional.of(hasNoDescendant); 214 } 215 216 void fillAdditionalInfo(Map<String, Object> props) 217 { 218 _hasNoDescendant.ifPresent(b -> props.put("hasNoDescendantQuery", b)); 219 } 220 } 221 222}