001/** 002 * Copyright 2005-2018 The Kuali Foundation 003 * 004 * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php 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.kuali.rice.krad.lookup; 017 018import org.apache.commons.lang.StringUtils; 019import org.kuali.rice.core.api.exception.RiceRuntimeException; 020import org.kuali.rice.core.api.util.RiceConstants; 021import org.kuali.rice.core.api.util.RiceKeyConstants; 022import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 023import org.kuali.rice.krad.service.ModuleService; 024import org.kuali.rice.krad.uif.UifConstants; 025import org.kuali.rice.krad.uif.UifParameters; 026import org.kuali.rice.krad.uif.UifPropertyPaths; 027import org.kuali.rice.krad.util.GlobalVariables; 028import org.kuali.rice.krad.util.KRADConstants; 029import org.kuali.rice.krad.util.KRADUtils; 030import org.kuali.rice.krad.util.UrlFactory; 031import org.kuali.rice.krad.web.form.UifFormBase; 032import org.kuali.rice.krad.web.service.ModelAndViewService; 033import org.kuali.rice.krad.web.service.impl.ControllerServiceImpl; 034import org.springframework.web.servlet.ModelAndView; 035import org.springframework.web.servlet.mvc.support.RedirectAttributes; 036 037import javax.servlet.http.HttpServletRequest; 038import java.util.ArrayList; 039import java.util.Collection; 040import java.util.Collections; 041import java.util.HashSet; 042import java.util.List; 043import java.util.Map; 044import java.util.Properties; 045import java.util.Set; 046 047/** 048 * Default implementation of the lookup controller service. 049 * 050 * @author Kuali Rice Team (rice.collab@kuali.org) 051 */ 052public class LookupControllerServiceImpl extends ControllerServiceImpl implements LookupControllerService { 053 054 private ModelAndViewService modelAndViewService; 055 056 /** 057 * {@inheritDoc} 058 */ 059 @Override 060 public ModelAndView start(UifFormBase form) { 061 LookupForm lookupForm = (LookupForm) form; 062 063 Lookupable lookupable = lookupForm.getLookupable(); 064 if (lookupable == null) { 065 throw new RuntimeException("Lookupable is null"); 066 } 067 068 HttpServletRequest request = form.getRequest(); 069 if (request.getParameter(UifParameters.MESSAGE_TO_DISPLAY) != null) { 070 GlobalVariables.getMessageMap().putErrorForSectionId(UifConstants.MessageKeys.LOOKUP_RESULT_MESSAGES, 071 request.getParameter(UifParameters.MESSAGE_TO_DISPLAY)); 072 } 073 074 if (!lookupForm.isRedirectedLookup()) { 075 ModelAndView redirectModelAndView = checkForModuleLookupRedirect(lookupForm, request); 076 if (redirectModelAndView != null) { 077 return redirectModelAndView; 078 } 079 } 080 081 String dialogId = request.getParameter(UifParameters.DIALOG_ID); 082 if (dialogId != null) { 083 lookupForm.setShowDialogId(dialogId); 084 } 085 086 return super.start(lookupForm); 087 } 088 089 /** 090 * Checks for a module service that claims the lookup class as an EBO, and if found redirects to the URL 091 * given by the module service. 092 * 093 * @param lookupForm form instance containing the lookup data 094 * @param request http request being handled 095 * @return ModelAndView instance for redirecting to the lookup, or null if a redirect is not needed 096 */ 097 protected ModelAndView checkForModuleLookupRedirect(LookupForm lookupForm, HttpServletRequest request) { 098 Class<?> lookupObjectClass; 099 try { 100 lookupObjectClass = Class.forName(lookupForm.getDataObjectClassName()); 101 } catch (ClassNotFoundException e) { 102 throw new RiceRuntimeException("Unable to get class for name: " + lookupForm.getDataObjectClassName(), 103 e); 104 } 105 106 ModuleService responsibleModuleService = 107 KRADServiceLocatorWeb.getKualiModuleService().getResponsibleModuleService(lookupObjectClass); 108 if (responsibleModuleService != null && responsibleModuleService.isExternalizable(lookupObjectClass)) { 109 String lookupUrl = responsibleModuleService.getExternalizableDataObjectLookupUrl(lookupObjectClass, 110 KRADUtils.convertRequestMapToProperties(request.getParameterMap())); 111 112 Properties redirectUrlProps = new Properties(); 113 redirectUrlProps.setProperty(UifParameters.REDIRECTED_LOOKUP, "true"); 114 115 // clear current form from session 116 GlobalVariables.getUifFormManager().removeSessionForm(lookupForm); 117 118 return getModelAndViewService().performRedirect(lookupForm, lookupUrl, redirectUrlProps); 119 } 120 121 return null; 122 } 123 124 /** 125 * Carries out the search action by invoking the {@link Lookupable#performSearch)} method on the 126 * configured lookupable (view helper) and then setting the results onto the given form. 127 * 128 * {@inheritDoc} 129 */ 130 @Override 131 public ModelAndView search(LookupForm lookupForm) { 132 Lookupable lookupable = lookupForm.getLookupable(); 133 if (lookupable == null) { 134 throw new RuntimeException("Lookupable is null."); 135 } 136 137 Collection<?> displayList = lookupable.performSearch(lookupForm, lookupForm.getLookupCriteria(), true); 138 139 lookupForm.setLookupResults(displayList); 140 141 return getModelAndViewService().getModelAndView(lookupForm); 142 } 143 144 /** 145 * Carries out the clear values action by invoking the {@link Lookupable#performClear)} method on the 146 * configured lookupable (view helper) and then setting the cleared criteria onto the given form. 147 * 148 * {@inheritDoc} 149 */ 150 @Override 151 public ModelAndView clearValues(LookupForm lookupForm) { 152 Lookupable lookupable = lookupForm.getLookupable(); 153 if (lookupable == null) { 154 throw new RuntimeException("Lookupable is null."); 155 } 156 157 Map<String, String> resetLookupCriteria = lookupable.performClear(lookupForm, lookupForm.getLookupCriteria()); 158 159 lookupForm.setLookupCriteria(resetLookupCriteria); 160 161 return getModelAndViewService().getModelAndView(lookupForm); 162 } 163 164 /** 165 * Loops through all the lookup results generating the line identifier for each and adding the 166 * resulting set of identifies to the form property 167 * {@link org.kuali.rice.krad.web.form.UifFormBase#getSelectedLookupResultsCache()}. 168 * 169 * {@inheritDoc} 170 */ 171 @Override 172 public ModelAndView selectAllPages(LookupForm lookupForm) { 173 List<? extends Object> lookupResults = (List<? extends Object>) lookupForm.getLookupResults(); 174 175 List<String> fromFieldNames = new ArrayList<String>(lookupForm.getFieldConversions().keySet()); 176 177 // loop through the lookup results and store identifiers for all items in the set 178 Set<String> selectedValues = new HashSet<String>(); 179 for (Object lineItem : lookupResults) { 180 String lineIdentifier = LookupUtils.generateMultiValueKey(lineItem, fromFieldNames); 181 182 selectedValues.add(lineIdentifier); 183 } 184 185 lookupForm.setSelectedLookupResultsCache(selectedValues); 186 187 return getModelAndViewService().getModelAndView(lookupForm); 188 } 189 190 /** 191 * Clears the form property {@link org.kuali.rice.krad.web.form.UifFormBase#getSelectedLookupResultsCache()} 192 * and the selected lines property. 193 * 194 * {@inheritDoc} 195 */ 196 @Override 197 public ModelAndView deselectAllPages(LookupForm lookupForm) { 198 lookupForm.getSelectedLookupResultsCache().clear(); 199 200 Set<String> selectedLines = lookupForm.getSelectedCollectionLines().get(UifPropertyPaths.LOOKUP_RESULTS); 201 if (selectedLines != null) { 202 selectedLines.clear(); 203 } 204 205 return getModelAndViewService().getModelAndView(lookupForm); 206 } 207 208 /** 209 * Builds the URL for returning back to the calling view and passing the selected line values. 210 * 211 * <p>We attempt to pass back all the selected line identifiers as a request parameter on the return URL. 212 * However, this could result in an URL longer than the max length supported by browsers (the most restrictive 213 * is used). If this happens, for local lookups we use Spring flash attributes. In the case of a remote 214 * lookup, there is nothing we can do and return an error message.</p> 215 * 216 * {@inheritDoc} 217 */ 218 @Override 219 public String returnSelected(LookupForm lookupForm, RedirectAttributes redirectAttributes) { 220 LookupUtils.refreshLookupResultSelections(lookupForm); 221 222 Properties urlParams = buildReturnSelectedParameters(lookupForm); 223 String returnUrl = UrlFactory.parameterizeUrl(lookupForm.getReturnLocation(), urlParams); 224 225 boolean lookupCameFromDifferentServer = KRADUtils.areDifferentDomains(lookupForm.getReturnLocation(), 226 lookupForm.getRequestUrl()); 227 228 boolean urlGreaterThanMaxLength = returnUrl.length() > RiceConstants.MAXIMUM_URL_LENGTH; 229 if (urlGreaterThanMaxLength) { 230 // removed selected values parameter from the return url 231 urlParams.remove(UifParameters.SELECTED_LINE_VALUES); 232 233 // if lookup was on a different server, we can't return the selected lines and instead 234 // will return an error message 235 if (lookupCameFromDifferentServer) { 236 urlParams.setProperty(UifParameters.REFRESH_STATUS, UifConstants.RefreshStatus.ERROR); 237 urlParams.setProperty(UifParameters.MESSAGE_TO_DISPLAY, 238 RiceKeyConstants.INFO_LOOKUP_RESULTS_MV_RETURN_EXCEEDS_LIMIT); 239 } else { 240 // otherwise use flash attributes instead of the URL to return the selected line identifiers 241 String selectedLineValues = getSelectedLineValues(lookupForm); 242 redirectAttributes.addFlashAttribute(UifParameters.SELECTED_LINE_VALUES, selectedLineValues); 243 } 244 } 245 246 GlobalVariables.getUifFormManager().removeSessionForm(lookupForm); 247 248 // rebuild url based on updated parameters 249 returnUrl = UrlFactory.parameterizeUrl(lookupForm.getReturnLocation(), urlParams); 250 251 return UifConstants.REDIRECT_PREFIX + returnUrl; 252 } 253 254 /** 255 * Builds all the request parameters for the return URL. 256 * 257 * @param lookupForm form instance containing the lookup data 258 * @return Properties contains the request parameters key/value pairs 259 */ 260 protected Properties buildReturnSelectedParameters(LookupForm lookupForm) { 261 Properties parameters = new Properties(); 262 263 parameters.setProperty(KRADConstants.DISPATCH_REQUEST_PARAMETER, KRADConstants.RETURN_METHOD_TO_CALL); 264 parameters.setProperty(KRADConstants.REFRESH_CALLER_TYPE, UifConstants.RefreshCallerTypes.MULTI_VALUE_LOOKUP); 265 266 if (StringUtils.isNotBlank(lookupForm.getView().getId())) { 267 parameters.setProperty(KRADConstants.REFRESH_CALLER, lookupForm.getView().getId()); 268 } 269 270 if (StringUtils.isNotBlank(lookupForm.getDataObjectClassName())) { 271 parameters.setProperty(KRADConstants.REFRESH_DATA_OBJECT_CLASS, lookupForm.getDataObjectClassName()); 272 } 273 274 if (StringUtils.isNotBlank(lookupForm.getReturnFormKey())) { 275 parameters.setProperty(UifParameters.FORM_KEY, lookupForm.getReturnFormKey()); 276 } 277 278 String multiValueReturnFieldsParam = getMultiValueReturnFields(lookupForm); 279 parameters.setProperty(UifParameters.MULIT_VALUE_RETURN_FILEDS, multiValueReturnFieldsParam); 280 281 String selectedLineValues = getSelectedLineValues(lookupForm); 282 parameters.setProperty(UifParameters.SELECTED_LINE_VALUES, selectedLineValues); 283 284 if (StringUtils.isNotBlank(lookupForm.getQuickfinderId())) { 285 parameters.setProperty(UifParameters.QUICKFINDER_ID, lookupForm.getQuickfinderId()); 286 } 287 288 if (StringUtils.isNotBlank(lookupForm.getLookupCollectionName())) { 289 parameters.setProperty(UifParameters.LOOKUP_COLLECTION_NAME, lookupForm.getLookupCollectionName()); 290 } 291 292 if (StringUtils.isNotBlank(lookupForm.getLookupCollectionId())) { 293 parameters.setProperty(UifParameters.LOOKUP_COLLECTION_ID, lookupForm.getLookupCollectionId()); 294 } 295 296 if (StringUtils.isNotBlank(lookupForm.getReferencesToRefresh())) { 297 parameters.setProperty(KRADConstants.REFERENCES_TO_REFRESH, lookupForm.getReferencesToRefresh()); 298 } 299 300 if (StringUtils.isNotBlank(lookupForm.getShowDialogId())) { 301 parameters.setProperty(UifParameters.DIALOG_ID, lookupForm.getShowDialogId()); 302 } 303 304 return parameters; 305 } 306 307 /** 308 * Builds a string containing the names of the fields being returned separated by a comma. 309 * 310 * @param lookupForm form instance containing the lookup data 311 * @return String names of return fields separated by a comma 312 */ 313 protected String getMultiValueReturnFields(LookupForm lookupForm) { 314 String multiValueReturnFieldsParam = ""; 315 316 List<String> multiValueReturnFields = lookupForm.getMultiValueReturnFields(); 317 Collections.sort(multiValueReturnFields); 318 if (multiValueReturnFields != null && !multiValueReturnFields.isEmpty()) { 319 for (String field : multiValueReturnFields) { 320 multiValueReturnFieldsParam += field + ","; 321 } 322 323 multiValueReturnFieldsParam = StringUtils.removeEnd(multiValueReturnFieldsParam, ","); 324 } 325 326 return multiValueReturnFieldsParam; 327 } 328 329 /** 330 * Builds a string containing the selected line identifiers separated by a comma. 331 * 332 * @param lookupForm form instance containing the lookup data 333 * @return String selected line identifiers separated by a comma 334 */ 335 protected String getSelectedLineValues(LookupForm lookupForm) { 336 String selectedLineValues = ""; 337 338 Set<String> selectedLines = lookupForm.getSelectedCollectionLines().get(UifPropertyPaths.LOOKUP_RESULTS); 339 if (selectedLines != null) { 340 for (String selectedLine : selectedLines) { 341 selectedLineValues += selectedLine + ","; 342 } 343 344 selectedLineValues = StringUtils.removeEnd(selectedLineValues, ","); 345 } 346 347 return selectedLineValues; 348 } 349 350 /** 351 * Instance of model and view service to use within the collection service. 352 * 353 * @return ModelAndViewService instance 354 */ 355 protected ModelAndViewService getModelAndViewService() { 356 return modelAndViewService; 357 } 358 359 /** 360 * @see LookupControllerServiceImpl#getModelAndViewService() 361 */ 362 public void setModelAndViewService(ModelAndViewService modelAndViewService) { 363 this.modelAndViewService = modelAndViewService; 364 } 365}