View Javadoc
1   /**
2    * Copyright 2005-2016 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krad.lookup;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.kuali.rice.core.api.exception.RiceRuntimeException;
20  import org.kuali.rice.core.api.util.RiceConstants;
21  import org.kuali.rice.core.api.util.RiceKeyConstants;
22  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
23  import org.kuali.rice.krad.service.ModuleService;
24  import org.kuali.rice.krad.uif.UifConstants;
25  import org.kuali.rice.krad.uif.UifParameters;
26  import org.kuali.rice.krad.uif.UifPropertyPaths;
27  import org.kuali.rice.krad.util.GlobalVariables;
28  import org.kuali.rice.krad.util.KRADConstants;
29  import org.kuali.rice.krad.util.KRADUtils;
30  import org.kuali.rice.krad.util.UrlFactory;
31  import org.kuali.rice.krad.web.form.UifFormBase;
32  import org.kuali.rice.krad.web.service.ModelAndViewService;
33  import org.kuali.rice.krad.web.service.impl.ControllerServiceImpl;
34  import org.springframework.web.servlet.ModelAndView;
35  import org.springframework.web.servlet.mvc.support.RedirectAttributes;
36  
37  import javax.servlet.http.HttpServletRequest;
38  import java.util.ArrayList;
39  import java.util.Collection;
40  import java.util.Collections;
41  import java.util.HashSet;
42  import java.util.List;
43  import java.util.Map;
44  import java.util.Properties;
45  import java.util.Set;
46  
47  /**
48   * Default implementation of the lookup controller service.
49   *
50   * @author Kuali Rice Team (rice.collab@kuali.org)
51   */
52  public class LookupControllerServiceImpl extends ControllerServiceImpl implements LookupControllerService {
53  
54      private ModelAndViewService modelAndViewService;
55  
56      /**
57       * {@inheritDoc}
58       */
59      @Override
60      public ModelAndView start(UifFormBase form) {
61          LookupForm lookupForm = (LookupForm) form;
62  
63          Lookupable lookupable = lookupForm.getLookupable();
64          if (lookupable == null) {
65              throw new RuntimeException("Lookupable is null");
66          }
67  
68          HttpServletRequest request = form.getRequest();
69          if (request.getParameter(UifParameters.MESSAGE_TO_DISPLAY) != null) {
70              GlobalVariables.getMessageMap().putErrorForSectionId(UifConstants.MessageKeys.LOOKUP_RESULT_MESSAGES,
71                      request.getParameter(UifParameters.MESSAGE_TO_DISPLAY));
72          }
73  
74          if (!lookupForm.isRedirectedLookup()) {
75              ModelAndView redirectModelAndView = checkForModuleLookupRedirect(lookupForm, request);
76              if (redirectModelAndView != null) {
77                  return redirectModelAndView;
78              }
79          }
80  
81          String dialogId = request.getParameter(UifParameters.DIALOG_ID);
82          if (dialogId != null) {
83              lookupForm.setShowDialogId(dialogId);
84          }
85  
86          return super.start(lookupForm);
87      }
88  
89      /**
90       * Checks for a module service that claims the lookup class as an EBO, and if found redirects to the URL
91       * given by the module service.
92       *
93       * @param lookupForm form instance containing the lookup data
94       * @param request http request being handled
95       * @return ModelAndView instance for redirecting to the lookup, or null if a redirect is not needed
96       */
97      protected ModelAndView checkForModuleLookupRedirect(LookupForm lookupForm, HttpServletRequest request) {
98          Class<?> lookupObjectClass;
99          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 }