001/* 002 * Copyright 2021 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.forms.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.forms.dao.FormDAO; 041import org.ametys.plugins.forms.dao.FormDirectoryDAO; 042import org.ametys.plugins.forms.repository.Form; 043import org.ametys.plugins.forms.repository.Form.FormProfile; 044import org.ametys.plugins.forms.repository.FormDirectory; 045import org.ametys.plugins.repository.AmetysObjectIterable; 046import org.ametys.plugins.repository.AmetysObjectResolver; 047 048import com.google.common.base.Predicates; 049 050 051/** 052 * Get forms action 053 */ 054public class GetFormsAction extends ServiceableAction 055{ 056 /** The current user provider */ 057 protected CurrentUserProvider _userProvider; 058 059 /** The Ametys object resolver */ 060 protected AmetysObjectResolver _resolver; 061 062 /** DAO for manipulating form directory */ 063 protected FormDirectoryDAO _formDirectoryDAO; 064 065 /** DAO for manipulating form */ 066 protected FormDAO _formDAO; 067 068 @Override 069 public void service(ServiceManager serviceManager) throws ServiceException 070 { 071 super.service(serviceManager); 072 _userProvider = (CurrentUserProvider) serviceManager.lookup(CurrentUserProvider.ROLE); 073 _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE); 074 _formDirectoryDAO = (FormDirectoryDAO) serviceManager.lookup(FormDirectoryDAO.ROLE); 075 _formDAO = (FormDAO) serviceManager.lookup(FormDAO.ROLE); 076 } 077 078 public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception 079 { 080 @SuppressWarnings("unchecked") 081 Map<String, Object> jsParameters = (Map<String, Object>) objectModel.get(ObjectModelHelper.PARENT_CONTEXT); 082 String profileId = StringUtils.defaultIfEmpty((String) jsParameters.get("profile"), "read_access"); 083 084 if (!"read_access".equals(profileId) && !"write_access".equals(profileId) && !"right_access".equals(profileId)) 085 { 086 throw new IllegalArgumentException("Unexpected profile identifier : " + profileId); 087 } 088 089 FormProfile profile = FormProfile.valueOf(profileId.toUpperCase()); 090 FormDirectory parentDirectory = _getParentDirectory(jsParameters); 091 Boolean onlyDirectories = (Boolean) jsParameters.getOrDefault("onlyDirectories", Boolean.FALSE); 092 Boolean onlyConfiguredForm = (Boolean) jsParameters.getOrDefault("onlyConfiguredForm", Boolean.FALSE); 093 Boolean onlyForms = onlyDirectories ? false : (Boolean) jsParameters.getOrDefault("onlyForms", Boolean.FALSE); 094 Boolean testIfEmptyDirectory = (Boolean) jsParameters.getOrDefault("testIfEmptyDirectory", Boolean.FALSE); 095 Boolean allDescendants = (Boolean) jsParameters.getOrDefault("allDescendants", Boolean.FALSE); 096 097 UserIdentity user = _userProvider.getUser(); 098 099 List<Map<String, Object>> nodes = new ArrayList<>(); 100 Stream<Form> childFormsStream = onlyDirectories 101 ? Stream.empty() 102 : _getChildForms(parentDirectory, !allDescendants, profile, user) 103 .filter(f -> !onlyConfiguredForm || _formDAO.isFormConfigured(f)); 104 childFormsStream 105 .map(f -> _formDAO.getFormProperties(f, false, false)) 106 .forEach(nodes::add); 107 108 Stream<Pair<FormDirectory, AdditionalInfoOnDirectory>> childDirectoriesStream = onlyForms 109 ? Stream.empty() 110 : _getChildFormDirectories(parentDirectory, testIfEmptyDirectory, profile, user); 111 childDirectoriesStream 112 .forEach(p -> 113 { 114 Map<String, Object> props = _formDirectoryDAO.getFormDirectoryProperties(p.getLeft(), false); 115 AdditionalInfoOnDirectory additionalInfo = p.getRight(); 116 additionalInfo.fillAdditionalInfo(props); 117 switch (profile) 118 { 119 case WRITE_ACCESS: 120 if ((boolean) props.get("displayForWrite")) 121 { 122 nodes.add(props); 123 } 124 break; 125 case RIGHT_ACCESS: 126 if ((boolean) props.get("displayForRights")) 127 { 128 nodes.add(props); 129 } 130 break; 131 case READ_ACCESS: 132 default: 133 if ((boolean) props.get("displayForRead")) 134 { 135 nodes.add(props); 136 } 137 break; 138 } 139 }); 140 141 Map<String, Object> result = new HashMap<>(); 142 result.put("forms", nodes); 143 144 Request request = ObjectModelHelper.getRequest(objectModel); 145 request.setAttribute(JSonReader.OBJECT_TO_READ, result); 146 147 return EMPTY_MAP; 148 } 149 150 private FormDirectory _getParentDirectory(Map jsParameters) 151 { 152 Predicate<String> isNotRoot = Predicates.not(Predicates.equalTo(FormDirectoryDAO.ROOT_FORM_DIRECTORY_ID)); 153 FormDirectory parent = Optional.of("node") 154 .map(jsParameters::get) 155 .filter(String.class::isInstance) 156 .map(String.class::cast) 157 .filter(isNotRoot) 158 .map(_resolver::<FormDirectory>resolveById) 159 .orElse(_formDirectoryDAO.getFormDirectoriesRootNode((String) jsParameters.get("siteName"))/* root node must exist, so it must be created if it does not exist yet */); 160 return parent; 161 } 162 163 private Stream<Pair<FormDirectory, AdditionalInfoOnDirectory>> _getChildFormDirectories(FormDirectory parentDirectory, boolean testIfEmptyDirectory, FormProfile profile, UserIdentity user) 164 { 165 AmetysObjectIterable<FormDirectory> childFormDirectories = _formDirectoryDAO.getChildFormDirectories(parentDirectory); 166 167 Stream<FormDirectory> childFormDirectoriesStream = childFormDirectories.stream(); 168 if (testIfEmptyDirectory) 169 { 170 // indicates if directory do not have (not necessarily direct) form children (i.e. is empty) 171 return childFormDirectoriesStream 172 .map(ct -> Pair.of(ct, new AdditionalInfoOnDirectory(_hasNoDescendant(ct, profile, user)))); 173 } 174 else 175 { 176 return childFormDirectoriesStream 177 .map(ct -> Pair.of(ct, new AdditionalInfoOnDirectory())); 178 } 179 } 180 181 private Boolean _hasNoDescendant(FormDirectory formDirectory, FormProfile profile, UserIdentity user) 182 { 183 boolean onlyDirectChildren = false; // in order to test no only direct children, but all descendants 184 Stream<Form> childForms = _getChildForms(formDirectory, onlyDirectChildren, profile, user); 185 return !childForms.findAny().isPresent(); 186 } 187 188 private Stream<Form> _getChildForms(FormDirectory parentDirectory, boolean onlyDirectChildren, FormProfile profile, UserIdentity user) 189 { 190 Stream<Form> childForms; 191 switch (profile) 192 { 193 case WRITE_ACCESS: 194 childForms = _formDirectoryDAO.getChildFormsInWriteAccess(parentDirectory, onlyDirectChildren, user); 195 break; 196 case RIGHT_ACCESS: 197 childForms = _formDirectoryDAO.getChildFormsInRightAccess(parentDirectory, onlyDirectChildren, user); 198 break; 199 case READ_ACCESS: 200 default: 201 childForms = _formDirectoryDAO.getChildFormsInReadAccess(parentDirectory, onlyDirectChildren, user); 202 break; 203 } 204 205 return childForms; 206 } 207 208 private static class AdditionalInfoOnDirectory 209 { 210 private Optional<Boolean> _hasNoDescendant = Optional.empty(); 211 212 AdditionalInfoOnDirectory() 213 { 214 } 215 216 AdditionalInfoOnDirectory(boolean hasNoDescendant) 217 { 218 _hasNoDescendant = Optional.of(hasNoDescendant); 219 } 220 221 void fillAdditionalInfo(Map<String, Object> props) 222 { 223 _hasNoDescendant.ifPresent(b -> props.put("hasNoDescendantForm", b)); 224 } 225 } 226}