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 Optional<String> queryType = Optional.of("queryType") 076 .map(jsParameters::get) 077 .filter(String.class::isInstance) 078 .map(String.class::cast); 079 080 if (!"read_access".equals(profileId) && !"write_access".equals(profileId) && !"right_access".equals(profileId)) 081 { 082 throw new IllegalArgumentException("Unexpected profile identifier : " + profileId); 083 } 084 085 QueryProfile profile = QueryProfile.valueOf(profileId.toUpperCase()); 086 QueryContainer parentContainer = _getParentContainer(jsParameters); 087 Boolean onlyContainers = (Boolean) jsParameters.getOrDefault("onlyContainers", Boolean.FALSE); 088 Boolean onlyQueries = onlyContainers ? false : (Boolean) jsParameters.getOrDefault("onlyQueries", Boolean.FALSE); 089 Boolean testIfEmptyContainer = (Boolean) jsParameters.getOrDefault("testIfEmptyContainer", Boolean.FALSE); 090 Boolean allDescendants = (Boolean) jsParameters.getOrDefault("allDescendants", Boolean.FALSE); 091 092 UserIdentity user = _userProvider.getUser(); 093 094 List<Map<String, Object>> nodes = new ArrayList<>(); 095 Stream<Query> childQueriesStream = onlyContainers 096 ? Stream.empty() 097 : _getChildQueries(parentContainer, !allDescendants, queryType, profile, user); 098 childQueriesStream 099 .map(_queryDAO::getQueryProperties) 100 .forEach(nodes::add); 101 102 Stream<Pair<QueryContainer, AdditionalInfoOnContainer>> childContainersStream = onlyQueries 103 ? Stream.empty() 104 : _getChildQueryContainers(parentContainer, testIfEmptyContainer, queryType, profile, user); 105 106 childContainersStream 107 .forEach(p -> 108 { 109 QueryContainer queryContainer = p.getLeft(); 110 Map<String, Object> properties = _queryDAO.getQueryContainerProperties(queryContainer ); 111 AdditionalInfoOnContainer additionalInfo = p.getRight(); 112 additionalInfo.fillAdditionalInfo(properties); 113 switch (profile) 114 { 115 case WRITE_ACCESS: 116 if ((boolean) properties.get(QueryDAO.WRITE_ACCESS_PROPERTY) || _queryDAO.hasAnyWritableDescendant(user, queryContainer)) 117 { 118 nodes.add(properties); 119 } 120 break; 121 case RIGHT_ACCESS: 122 if ((boolean) properties.get(QueryDAO.EDIT_RIGHTS_ACCESS_PROPERTY) || _queryDAO.hasAnyAssignableDescendant(user, queryContainer)) 123 { 124 nodes.add(properties); 125 } 126 break; 127 case READ_ACCESS: 128 default: 129 if ((boolean) properties.get(QueryDAO.READ_ACCESS_PROPERTY) || _queryDAO.hasAnyReadableDescendant(user, queryContainer)) 130 { 131 nodes.add(properties); 132 } 133 break; 134 } 135 }); 136 137 Map<String, Object> result = new HashMap<>(); 138 result.put("queries", nodes); 139 140 Request request = ObjectModelHelper.getRequest(objectModel); 141 request.setAttribute(JSonReader.OBJECT_TO_READ, result); 142 143 return EMPTY_MAP; 144 } 145 146 private QueryContainer _getParentContainer(Map jsParameters) 147 { 148 Predicate<String> isNotRoot = Predicates.not(Predicates.equalTo(QueryDAO.ROOT_QUERY_CONTAINER_ID)); 149 QueryContainer parent = Optional.of("node") 150 .map(jsParameters::get) 151 .filter(String.class::isInstance) 152 .map(String.class::cast) 153 .filter(isNotRoot) 154 .map(_resolver::<QueryContainer>resolveById) 155 .orElse(_queryDAO.getQueriesRootNode()/* root node must exist, so it must be created if it does not exist yet */); 156 return parent; 157 } 158 159 private Stream<Pair<QueryContainer, AdditionalInfoOnContainer>> _getChildQueryContainers(QueryContainer parentContainer, boolean testIfEmptyContainer, Optional<String> queryType, QueryProfile profile, UserIdentity user) 160 { 161 AmetysObjectIterable<QueryContainer> childQueryContainers = _queryDAO.getChildQueryContainers(parentContainer); 162 163 Stream<QueryContainer> childQueryContainersStream = childQueryContainers.stream(); 164 if (testIfEmptyContainer) 165 { 166 // indicates if container do not have (not necessarily direct) query children (i.e. is empty) 167 return childQueryContainersStream 168 .map(ct -> Pair.of(ct, new AdditionalInfoOnContainer(_hasNoDescendant(ct, queryType, profile, user)))); 169 } 170 else 171 { 172 return childQueryContainersStream 173 .map(ct -> Pair.of(ct, new AdditionalInfoOnContainer())); 174 } 175 } 176 177 private Boolean _hasNoDescendant(QueryContainer queryContainer, Optional<String> queryType, QueryProfile profile, UserIdentity user) 178 { 179 boolean onlyDirectChildren = false; // in order to test no only direct children, but all descendants 180 Stream<Query> childQueries = _getChildQueries(queryContainer, onlyDirectChildren, queryType, profile, user); 181 return !childQueries.findAny().isPresent(); 182 } 183 184 private Stream<Query> _getChildQueries(QueryContainer parentContainer, boolean onlyDirectChildren, Optional<String> queryType, QueryProfile profile, UserIdentity user) 185 { 186 Stream<Query> childQueries; 187 switch (profile) 188 { 189 case WRITE_ACCESS: 190 childQueries = _queryDAO.getChildQueriesInWriteAccess(parentContainer, onlyDirectChildren, user, queryType); 191 break; 192 case RIGHT_ACCESS: 193 childQueries = _queryDAO.getChildQueriesInRightAccess(parentContainer, onlyDirectChildren, user, queryType); 194 break; 195 case READ_ACCESS: 196 default: 197 childQueries = _queryDAO.getChildQueriesInReadAccess(parentContainer, onlyDirectChildren, user, queryType); 198 break; 199 } 200 201 return childQueries; 202 } 203 204 private static class AdditionalInfoOnContainer 205 { 206 private Optional<Boolean> _hasNoDescendant = Optional.empty(); 207 208 AdditionalInfoOnContainer() 209 { 210 } 211 212 AdditionalInfoOnContainer(boolean hasNoDescendant) 213 { 214 _hasNoDescendant = Optional.of(hasNoDescendant); 215 } 216 217 void fillAdditionalInfo(Map<String, Object> props) 218 { 219 _hasNoDescendant.ifPresent(b -> props.put("hasNoDescendantQuery", b)); 220 } 221 } 222 223}