View Javadoc

1   /*
2    * Copyright 2007 The Kuali Foundation Licensed under the Educational Community
3    * License, Version 1.0 (the "License"); you may not use this file except in
4    * compliance with the License. You may obtain a copy of the License at
5    * http://www.opensource.org/licenses/ecl1.php Unless required by applicable law
6    * or agreed to in writing, software distributed under the License is
7    * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
8    * KIND, either express or implied. See the License for the specific language
9    * governing permissions and limitations under the License.
10   */
11  package org.kuali.rice.krad.web.controller;
12  
13  import org.apache.commons.lang.StringUtils;
14  import org.kuali.rice.core.api.config.property.ConfigContext;
15  import org.kuali.rice.core.api.exception.RiceRuntimeException;
16  import org.kuali.rice.core.web.format.BooleanFormatter;
17  import org.kuali.rice.kim.api.KimConstants;
18  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
19  import org.kuali.rice.krad.UserSession;
20  import org.kuali.rice.krad.bo.ExternalizableBusinessObject;
21  import org.kuali.rice.krad.exception.AuthorizationException;
22  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
23  import org.kuali.rice.krad.service.ModuleService;
24  import org.kuali.rice.krad.service.SessionDocumentService;
25  import org.kuali.rice.krad.uif.UifConstants;
26  import org.kuali.rice.krad.uif.UifParameters;
27  import org.kuali.rice.krad.uif.UifPropertyPaths;
28  import org.kuali.rice.krad.uif.component.Component;
29  import org.kuali.rice.krad.uif.container.CollectionGroup;
30  import org.kuali.rice.krad.uif.field.AttributeQueryResult;
31  import org.kuali.rice.krad.uif.service.ViewService;
32  import org.kuali.rice.krad.uif.util.ComponentFactory;
33  import org.kuali.rice.krad.uif.util.LookupInquiryUtils;
34  import org.kuali.rice.krad.uif.util.UifWebUtils;
35  import org.kuali.rice.krad.uif.view.History;
36  import org.kuali.rice.krad.uif.view.HistoryEntry;
37  import org.kuali.rice.krad.uif.view.View;
38  import org.kuali.rice.krad.util.GlobalVariables;
39  import org.kuali.rice.krad.util.KRADConstants;
40  import org.kuali.rice.krad.util.KRADUtils;
41  import org.kuali.rice.krad.util.UrlFactory;
42  import org.kuali.rice.krad.web.form.UifFormBase;
43  import org.springframework.validation.BindingResult;
44  import org.springframework.web.bind.annotation.ModelAttribute;
45  import org.springframework.web.bind.annotation.RequestMapping;
46  import org.springframework.web.bind.annotation.RequestMethod;
47  import org.springframework.web.bind.annotation.ResponseBody;
48  import org.springframework.web.servlet.ModelAndView;
49  
50  import javax.servlet.http.HttpServletRequest;
51  import javax.servlet.http.HttpServletResponse;
52  import java.util.Collections;
53  import java.util.HashMap;
54  import java.util.HashSet;
55  import java.util.List;
56  import java.util.Map;
57  import java.util.Map.Entry;
58  import java.util.Properties;
59  import java.util.Set;
60  
61  /**
62   * Base controller class for views within the KRAD User Interface Framework
63   *
64   * Provides common methods such as:
65   *
66   * <ul>
67   * <li>Authorization methods such as method to call check</li>
68   * <li>Preparing the View instance and setup in the returned
69   * <code>ModelAndView</code></li>
70   * </ul>
71   *
72   * All subclass controller methods after processing should call one of the
73   * #getUIFModelAndView methods to setup the <code>View</code> and return the
74   * <code>ModelAndView</code> instance.
75   *
76   * @author Kuali Rice Team (rice.collab@kuali.org)
77   */
78  public abstract class UifControllerBase {
79      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(UifControllerBase.class);
80  
81      protected static final String REDIRECT_PREFIX = "redirect:";
82  
83      /**
84       * Create/obtain the model(form) object before it is passed
85       * to the Binder/BeanWrapper. This method is not intended to be overridden
86       * by client applications as it handles framework setup and session
87       * maintenance. Clients should override createIntialForm() instead when they
88       * need custom form initialization.
89       */
90      @ModelAttribute(value = "KualiForm")
91      public UifFormBase initForm(HttpServletRequest request) {
92          UifFormBase form = null;
93          String formKeyParam = request.getParameter(UifParameters.FORM_KEY);
94          String documentNumber = request.getParameter(KRADConstants.DOCUMENT_DOCUMENT_NUMBER);
95  
96          if (StringUtils.isNotBlank(formKeyParam)) {
97              form = (UifFormBase) request.getSession().getAttribute(formKeyParam);
98  
99              // retrieve from db if form not in session
100             if (form == null) {
101                 UserSession userSession = (UserSession) request.getSession().getAttribute(
102                         KRADConstants.USER_SESSION_KEY);
103                 form = getSessionDocumentService().getDocumentForm(documentNumber, formKeyParam, userSession,
104                         request.getRemoteAddr());
105             }
106         } else {
107             form = createInitialForm();
108         }
109 
110         return form;
111     }
112 
113     /**
114      * Called to create a new model(form) object when
115      * necessary. This usually occurs on the initial request in a conversation
116      * (when the model is not present in the session). This method must be
117      * overridden when extending a controller and using a different form type
118      * than the superclass.
119      */
120     protected abstract Class<? extends UifFormBase> formType();
121 
122     /**
123      * Called to create a new model(form) object when
124      * necessary. This usually occurs on the initial request in a conversation
125      * (when the model is not present in the session).
126      */
127     protected UifFormBase createInitialForm() {
128         try {
129             return formType().newInstance();
130         } catch (InstantiationException e) {
131             throw new RiceRuntimeException(e);
132         } catch (IllegalAccessException e) {
133             throw new RiceRuntimeException(e);
134         }
135     }
136 
137     private Set<String> methodToCallsToNotCheckAuthorization = new HashSet<String>();
138 
139     {
140         methodToCallsToNotCheckAuthorization.add("performLookup");
141         methodToCallsToNotCheckAuthorization.add("performQuestion");
142         methodToCallsToNotCheckAuthorization.add("performQuestionWithInput");
143         methodToCallsToNotCheckAuthorization.add("performQuestionWithInputAgainBecauseOfErrors");
144         methodToCallsToNotCheckAuthorization.add("performQuestionWithoutInput");
145         methodToCallsToNotCheckAuthorization.add("performWorkgroupLookup");
146     }
147 
148     /**
149      * Use to add a methodToCall to the a list which will not have authorization
150      * checks. This assumes that the call will be redirected (as in the case of
151      * a lookup) that will perform the authorization.
152      */
153     protected final void addMethodToCallToUncheckedList(String methodToCall) {
154         methodToCallsToNotCheckAuthorization.add(methodToCall);
155     }
156 
157     /**
158      * Returns an immutable Set of methodToCall parameters that should not be
159      * checked for authorization.
160      */
161     public Set<String> getMethodToCallsToNotCheckAuthorization() {
162         return Collections.unmodifiableSet(methodToCallsToNotCheckAuthorization);
163     }
164 
165     /**
166      * Override this method to provide controller class-level access controls to
167      * the application.
168      */
169     public void checkAuthorization(UifFormBase form, String methodToCall) throws AuthorizationException {
170         String principalId = GlobalVariables.getUserSession().getPrincipalId();
171         Map<String, String> roleQualifier = new HashMap<String, String>(getRoleQualification(form, methodToCall));
172         Map<String, String> permissionDetails = KRADUtils.getNamespaceAndActionClass(this.getClass());
173 
174         if (!KimApiServiceLocator.getPermissionService().isAuthorizedByTemplateName(principalId,
175                 KRADConstants.KRAD_NAMESPACE, KimConstants.PermissionTemplateNames.USE_SCREEN, permissionDetails,
176                 roleQualifier)) {
177             throw new AuthorizationException(GlobalVariables.getUserSession().getPerson().getPrincipalName(),
178                     methodToCall, this.getClass().getSimpleName());
179         }
180     }
181 
182     /**
183      * Override this method to add data from the form for role qualification in
184      * the authorization check
185      */
186     protected Map<String, String> getRoleQualification(UifFormBase form, String methodToCall) {
187         return new HashMap<String, String>();
188     }
189 
190     /**
191      * Initial method called when requesting a new view instance which forwards
192      * the view for rendering
193      */
194     @RequestMapping(method = RequestMethod.GET, params = "methodToCall=start")
195     public ModelAndView start(@ModelAttribute("KualiForm") UifFormBase form, BindingResult result,
196             HttpServletRequest request, HttpServletResponse response) {
197 
198         return getUIFModelAndView(form);
199     }
200 
201     /**
202      * Called by the add line action for a new collection line. Method
203      * determines which collection the add action was selected for and invokes
204      * the view helper service to add the line
205      */
206     @RequestMapping(method = RequestMethod.POST, params = "methodToCall=addLine")
207     public ModelAndView addLine(@ModelAttribute("KualiForm") UifFormBase uifForm, BindingResult result,
208             HttpServletRequest request, HttpServletResponse response) {
209 
210         String selectedCollectionPath = uifForm.getActionParamaterValue(UifParameters.SELLECTED_COLLECTION_PATH);
211         if (StringUtils.isBlank(selectedCollectionPath)) {
212             throw new RuntimeException("Selected collection was not set for add line action, cannot add new line");
213         }
214 
215         View view = uifForm.getPreviousView();
216         view.getViewHelperService().processCollectionAddLine(view, uifForm, selectedCollectionPath);
217 
218         return updateComponent(uifForm, result, request, response);
219     }
220 
221     /**
222      * Called by the delete line action for a model collection. Method
223      * determines which collection the action was selected for and the line
224      * index that should be removed, then invokes the view helper service to
225      * process the action
226      */
227     @RequestMapping(method = RequestMethod.POST, params = "methodToCall=deleteLine")
228     public ModelAndView deleteLine(@ModelAttribute("KualiForm") UifFormBase uifForm, BindingResult result,
229             HttpServletRequest request, HttpServletResponse response) {
230 
231         String selectedCollectionPath = uifForm.getActionParamaterValue(UifParameters.SELLECTED_COLLECTION_PATH);
232         if (StringUtils.isBlank(selectedCollectionPath)) {
233             throw new RuntimeException("Selected collection was not set for delete line action, cannot delete line");
234         }
235 
236         int selectedLineIndex = -1;
237         String selectedLine = uifForm.getActionParamaterValue(UifParameters.SELECTED_LINE_INDEX);
238         if (StringUtils.isNotBlank(selectedLine)) {
239             selectedLineIndex = Integer.parseInt(selectedLine);
240         }
241 
242         if (selectedLineIndex == -1) {
243             throw new RuntimeException("Selected line index was not set for delete line action, cannot delete line");
244         }
245 
246         View view = uifForm.getPreviousView();
247         view.getViewHelperService().processCollectionDeleteLine(view, uifForm, selectedCollectionPath,
248                 selectedLineIndex);
249 
250         return updateComponent(uifForm, result, request, response);
251     }
252 
253     /**
254      * Invoked to toggle the show inactive indicator on the selected collection group and then
255      * rerun the component lifecycle and rendering based on the updated indicator and form data
256      *
257      * @param request - request object that should contain the request component id (for the collection group)
258      * and the show inactive indicator value
259      */
260     @RequestMapping(method = RequestMethod.POST, params = "methodToCall=toggleInactiveRecordDisplay")
261     public ModelAndView toggleInactiveRecordDisplay(@ModelAttribute("KualiForm") UifFormBase uifForm,
262             BindingResult result, HttpServletRequest request, HttpServletResponse response) {
263         String collectionGroupId = request.getParameter(UifParameters.REQUESTED_COMPONENT_ID);
264         if (StringUtils.isBlank(collectionGroupId)) {
265             throw new RuntimeException(
266                     "Collection group id to update for inactive record display not found in request");
267         }
268 
269         String showInactiveStr = request.getParameter(UifParameters.SHOW_INACTIVE_RECORDS);
270         Boolean showInactive = false;
271         if (StringUtils.isNotBlank(showInactiveStr)) {
272             // TODO: should use property editors once we have util class
273             showInactive = (Boolean) (new BooleanFormatter()).convertFromPresentationFormat(showInactiveStr);
274         } else {
275             throw new RuntimeException("Show inactive records flag not found in request");
276         }
277 
278         CollectionGroup collectionGroup = (CollectionGroup) ComponentFactory.getComponentById(uifForm,
279                 collectionGroupId);
280 
281         // update inactive flag on group
282         collectionGroup.setShowInactive(showInactive);
283 
284         // run lifecycle and update in view
285         uifForm.getView().getViewHelperService().performComponentLifecycle(uifForm, collectionGroup, collectionGroupId);
286         uifForm.getView().getViewIndex().indexComponent(collectionGroup);
287 
288         return UifWebUtils.getComponentModelAndView(collectionGroup, uifForm);
289     }
290 
291     /**
292      * Just returns as if return with no value was selected.
293      */
294     @RequestMapping(params = "methodToCall=cancel")
295     public ModelAndView cancel(@ModelAttribute("KualiForm") UifFormBase form, BindingResult result,
296             HttpServletRequest request, HttpServletResponse response) {
297         return close(form, result, request, response);
298     }
299 
300     /**
301      * Just returns as if return with no value was selected.
302      */
303     @RequestMapping(params = "methodToCall=close")
304     public ModelAndView close(@ModelAttribute("KualiForm") UifFormBase form, BindingResult result,
305             HttpServletRequest request, HttpServletResponse response) {
306         Properties props = new Properties();
307         props.put(UifParameters.METHOD_TO_CALL, UifConstants.MethodToCallNames.REFRESH);
308         if (StringUtils.isNotBlank(form.getReturnFormKey())) {
309             props.put(UifParameters.FORM_KEY, form.getReturnFormKey());
310         }
311 
312         // TODO this needs setup for lightbox and possible home location
313         // property
314         String returnUrl = form.getReturnLocation();
315         if (StringUtils.isBlank(returnUrl)) {
316             returnUrl = ConfigContext.getCurrentContextConfig().getProperty(KRADConstants.APPLICATION_URL_KEY);
317         }
318 
319         return performRedirect(form, returnUrl, props);
320     }
321 
322     /**
323      * Invoked to navigate back one page in history..
324      *
325      * @param form - form object that should contain the history object
326      */
327     @RequestMapping(params = "methodToCall=returnToPrevious")
328     public ModelAndView returnToPrevious(@ModelAttribute("KualiForm") UifFormBase form) {
329 
330         return returnToHistory(form, false);
331     }
332 
333     /**
334      * Invoked to navigate back to the first page in history.
335      *
336      * @param form - form object that should contain the history object
337      */
338     @RequestMapping(params = "methodToCall=returnToHub")
339     public ModelAndView returnToHub(@ModelAttribute("KualiForm") UifFormBase form) {
340 
341         return returnToHistory(form, true);
342     }
343 
344     /**
345      * Invoked to navigate back to a history entry. The homeFlag will determine whether navigation
346      * will be back to the first or last history entry.
347      *
348      * @param form - form object that should contain the history object
349      * @param homeFlag - if true will navigate back to first entry else will navigate to last entry
350      * in the history
351      */
352     public ModelAndView returnToHistory(UifFormBase form, boolean homeFlag) {
353         // Get the history from the form
354         History hist = form.getFormHistory();
355         List<HistoryEntry> histEntries = hist.getHistoryEntries();
356 
357         // Get the history page url. Default to the application url if there is no history.
358         String histUrl = null;
359         if (histEntries.isEmpty()) {
360             histUrl = ConfigContext.getCurrentContextConfig().getProperty(KRADConstants.APPLICATION_URL_KEY);
361         } else {
362             // For home get the first entry, for previous get the last entry.
363             // Remove history up to where page is opened
364             if (homeFlag) {
365                 histUrl = histEntries.get(0).getUrl();
366                 histEntries.clear();
367             } else {
368                 histUrl = histEntries.get(histEntries.size() - 1).getUrl();
369                 histEntries.remove(histEntries.size() - 1);
370                 hist.setCurrent(null);
371             }
372         }
373 
374         // Add the refresh call
375         Properties props = new Properties();
376         props.put(UifParameters.METHOD_TO_CALL, UifConstants.MethodToCallNames.REFRESH);
377 
378         return performRedirect(form, histUrl, props);
379     }
380 
381     /**
382      * Handles menu navigation between view pages
383      */
384     @RequestMapping(method = RequestMethod.POST, params = "methodToCall=navigate")
385     public ModelAndView navigate(@ModelAttribute("KualiForm") UifFormBase form, BindingResult result,
386             HttpServletRequest request, HttpServletResponse response) {
387         String pageId = form.getActionParamaterValue(UifParameters.NAVIGATE_TO_PAGE_ID);
388 
389         // only refreshing page
390         form.setRenderFullView(false);
391 
392         return getUIFModelAndView(form, pageId);
393     }
394 
395     @RequestMapping(params = "methodToCall=refresh")
396     public ModelAndView refresh(@ModelAttribute("KualiForm") UifFormBase form, BindingResult result,
397             HttpServletRequest request, HttpServletResponse response) throws Exception {
398         // TODO: this code still needs to handle reference refreshes
399         String refreshCallerType = "";
400         if (request.getParameterMap().containsKey(KRADConstants.REFRESH_CALLER_TYPE)) {
401             refreshCallerType = request.getParameter(KRADConstants.REFRESH_CALLER_TYPE);
402         }
403 
404         // process multi-value lookup returns
405         if (StringUtils.equals(refreshCallerType, UifConstants.RefreshCallerTypes.MULTI_VALUE_LOOKUP)) {
406             String lookupCollectionName = "";
407             if (request.getParameterMap().containsKey(UifParameters.LOOKUP_COLLECTION_NAME)) {
408                 lookupCollectionName = request.getParameter(UifParameters.LOOKUP_COLLECTION_NAME);
409             }
410 
411             if (StringUtils.isBlank(lookupCollectionName)) {
412                 throw new RuntimeException(
413                         "Lookup collection name is required for processing multi-value lookup results");
414             }
415 
416             String selectedLineValues = "";
417             if (request.getParameterMap().containsKey(UifParameters.SELECTED_LINE_VALUES)) {
418                 selectedLineValues = request.getParameter(UifParameters.SELECTED_LINE_VALUES);
419             }
420 
421             // invoked view helper to populate the collection from lookup results
422             form.getPreviousView().getViewHelperService().processMultipleValueLookupResults(form.getPreviousView(), form,
423                     lookupCollectionName, selectedLineValues);
424         }
425 
426         form.setRenderFullView(true);
427 
428         return getUIFModelAndView(form);
429     }
430 
431     /**
432      * Updates the current component by retrieving a fresh copy from the dictionary,
433      * running its component lifecycle, and returning it
434      *
435      * @param request - the request must contain reqComponentId that specifies the component to retrieve
436      */
437     @RequestMapping(method = RequestMethod.POST, params = "methodToCall=updateComponent")
438     public ModelAndView updateComponent(@ModelAttribute("KualiForm") UifFormBase form, BindingResult result,
439             HttpServletRequest request, HttpServletResponse response) {
440         String requestedComponentId = request.getParameter(UifParameters.REQUESTED_COMPONENT_ID);
441         if (StringUtils.isBlank(requestedComponentId)) {
442             throw new RuntimeException("Requested component id for update not found in request");
443         }
444 
445         Component comp = ComponentFactory.getComponentByIdWithLifecycle(form, requestedComponentId);
446 
447         return UifWebUtils.getComponentModelAndView(comp, form);
448     }
449 
450     /**
451      * Builds up a URL to the lookup view based on the given post action
452      * parameters and redirects
453      */
454     @RequestMapping(method = RequestMethod.POST, params = "methodToCall=performLookup")
455     public ModelAndView performLookup(@ModelAttribute("KualiForm") UifFormBase form, BindingResult result,
456             HttpServletRequest request, HttpServletResponse response) {
457         Properties lookupParameters = form.getActionParametersAsProperties();
458 
459         String lookupObjectClassName = (String) lookupParameters.get(UifParameters.DATA_OBJECT_CLASS_NAME);
460         Class<?> lookupObjectClass = null;
461         try {
462             lookupObjectClass = Class.forName(lookupObjectClassName);
463         } catch (ClassNotFoundException e) {
464             LOG.error("Unable to get class for name: " + lookupObjectClassName);
465             throw new RuntimeException("Unable to get class for name: " + lookupObjectClassName, e);
466         }
467 
468         // get form values for the lookup parameter fields
469         String lookupParameterString = (String) lookupParameters.get(UifParameters.LOOKUP_PARAMETERS);
470         if (lookupParameterString != null) {
471             Map<String, String> lookupParameterFields = KRADUtils.getMapFromParameterString(lookupParameterString);
472             for (Entry<String, String> lookupParameter : lookupParameterFields.entrySet()) {
473                 String lookupParameterValue = LookupInquiryUtils.retrieveLookupParameterValue(form, request,
474                         lookupObjectClass, lookupParameter.getValue(), lookupParameter.getKey());
475 
476                 if (StringUtils.isNotBlank(lookupParameterValue)) {
477                     lookupParameters.put(UifPropertyPaths.CRITERIA_FIELDS + "['" + lookupParameter.getValue() + "']",
478                             lookupParameterValue);
479                 }
480             }
481         }
482 
483         // TODO: lookup anchors and doc number?
484 
485         String baseLookupUrl = (String) lookupParameters.get(UifParameters.BASE_LOOKUP_URL);
486         lookupParameters.remove(UifParameters.BASE_LOOKUP_URL);
487 
488         // set lookup method to call
489         lookupParameters.put(UifParameters.METHOD_TO_CALL, UifConstants.MethodToCallNames.START);
490         String autoSearchString = (String) lookupParameters.get(UifParameters.AUTO_SEARCH);
491         if (Boolean.parseBoolean(autoSearchString)) {
492             lookupParameters.put(UifParameters.METHOD_TO_CALL, UifConstants.MethodToCallNames.SEARCH);
493         }
494 
495         lookupParameters.put(UifParameters.RETURN_LOCATION, form.getFormPostUrl());
496         lookupParameters.put(UifParameters.RETURN_FORM_KEY, form.getFormKey());
497 
498         // special check for external object classes
499         if (lookupObjectClass != null) {
500             ModuleService responsibleModuleService =
501                     KRADServiceLocatorWeb.getKualiModuleService().getResponsibleModuleService(lookupObjectClass);
502             if (responsibleModuleService != null && responsibleModuleService.isExternalizable(lookupObjectClass)) {
503                 Class<? extends ExternalizableBusinessObject> implLookupObjectClass =
504                         responsibleModuleService.getExternalizableBusinessObjectImplementation(
505                                 lookupObjectClass.asSubclass(ExternalizableBusinessObject.class));
506 
507                 if (implLookupObjectClass != null) {
508                     lookupParameters.put(UifParameters.DATA_OBJECT_CLASS_NAME, implLookupObjectClass.getName());
509                 } else {
510                     throw new RuntimeException(
511                             "Unable to find implementation class for EBO: " + lookupObjectClass.getName());
512                 }
513 
514                 // TODO: should call module service to get full URL, but right now it is coded to direct to the KNS lookups
515                 //                Map<String, String> parameterMap = new HashMap<String, String>();
516                 //                Enumeration<Object> e = lookupParameters.keys();
517                 //                while (e.hasMoreElements()) {
518                 //                    String paramName = (String) e.nextElement();
519                 //                    parameterMap.put(paramName, lookupParameters.getProperty(paramName));
520                 //                }
521                 //
522                 //                String lookupUrl = responsibleModuleService.getExternalizableBusinessObjectLookupUrl(lookupObjectClass,
523                 //                        parameterMap);
524                 //                return performRedirect(form, lookupUrl, new Properties());
525             }
526         }
527 
528         return performRedirect(form, baseLookupUrl, lookupParameters);
529     }
530 
531     /**
532      * Invoked to provide the options for a suggest widget. The valid options are retrieved by the associated
533      * <code>AttributeQuery</code> for the field containing the suggest widget. The controller method picks
534      * out the query parameters from the request and calls <code>AttributeQueryService</code> to perform the
535      * suggest query and prepare the result object that will be exposed with JSON
536      */
537     @RequestMapping(method = RequestMethod.GET, params = "methodToCall=performFieldSuggest")
538     public
539     @ResponseBody
540     AttributeQueryResult performFieldSuggest(@ModelAttribute("KualiForm") UifFormBase form, BindingResult result,
541             HttpServletRequest request, HttpServletResponse response) {
542 
543         // retrieve query fields from request
544         Map<String, String> queryParameters = new HashMap<String, String>();
545         for (Object parameterName : request.getParameterMap().keySet()) {
546             if (parameterName.toString().startsWith(UifParameters.QUERY_PARAMETER + ".")) {
547                 String fieldName = StringUtils.substringAfter(parameterName.toString(),
548                         UifParameters.QUERY_PARAMETER + ".");
549                 String fieldValue = request.getParameter(parameterName.toString());
550                 queryParameters.put(fieldName, fieldValue);
551             }
552         }
553 
554         // retrieve id for field to perform query for
555         String queryFieldId = request.getParameter(UifParameters.QUERY_FIELD_ID);
556         if (StringUtils.isBlank(queryFieldId)) {
557             throw new RuntimeException("Unable to find id for field to perform query on under request parameter name: "
558                     + UifParameters.QUERY_FIELD_ID);
559         }
560 
561         // get the field term to match
562         String queryTerm = request.getParameter(UifParameters.QUERY_TERM);
563         if (StringUtils.isBlank(queryTerm)) {
564             throw new RuntimeException(
565                     "Unable to find id for query term value for attribute query on under request parameter name: "
566                             + UifParameters.QUERY_TERM);
567         }
568 
569         // invoke attribute query service to perform the query
570         AttributeQueryResult queryResult = KRADServiceLocatorWeb.getAttributeQueryService().performFieldSuggestQuery(
571                 form.getView(), queryFieldId, queryTerm, queryParameters);
572 
573         return queryResult;
574     }
575 
576     /**
577      * Invoked to execute the <code>AttributeQuery</code> associated with a field given the query parameters
578      * found in the request. This controller method picks out the query parameters from the request and calls
579      * <code>AttributeQueryService</code> to perform the field query and prepare the result object
580      * that will be exposed with JSON. The result is then used to update field values in the UI with client
581      * script.
582      */
583     @RequestMapping(method = RequestMethod.GET, params = "methodToCall=performFieldQuery")
584     public
585     @ResponseBody
586     AttributeQueryResult performFieldQuery(@ModelAttribute("KualiForm") UifFormBase form, BindingResult result,
587             HttpServletRequest request, HttpServletResponse response) {
588 
589         // retrieve query fields from request
590         Map<String, String> queryParameters = new HashMap<String, String>();
591         for (Object parameterName : request.getParameterMap().keySet()) {
592             if (parameterName.toString().startsWith(UifParameters.QUERY_PARAMETER + ".")) {
593                 String fieldName = StringUtils.substringAfter(parameterName.toString(),
594                         UifParameters.QUERY_PARAMETER + ".");
595                 String fieldValue = request.getParameter(parameterName.toString());
596                 queryParameters.put(fieldName, fieldValue);
597             }
598         }
599 
600         // retrieve id for field to perform query for
601         String queryFieldId = request.getParameter(UifParameters.QUERY_FIELD_ID);
602         if (StringUtils.isBlank(queryFieldId)) {
603             throw new RuntimeException("Unable to find id for field to perform query on under request parameter name: "
604                     + UifParameters.QUERY_FIELD_ID);
605         }
606 
607         // invoke attribute query service to perform the query
608         AttributeQueryResult queryResult = KRADServiceLocatorWeb.getAttributeQueryService().performFieldQuery(
609                 form.getView(), queryFieldId, queryParameters);
610 
611         return queryResult;
612     }
613 
614     /**
615      * Builds a <code>ModelAndView</code> instance configured to redirect to the
616      * URL formed by joining the base URL with the given URL parameters
617      *
618      * @param form - current form instance
619      * @param baseUrl - base url to redirect to
620      * @param urlParameters - properties containing key/value pairs for the url parameters
621      * @return ModelAndView configured to redirect to the given URL
622      */
623     protected ModelAndView performRedirect(UifFormBase form, String baseUrl, Properties urlParameters) {
624         // On post redirects we need to make sure we are sending the history forward:
625         urlParameters.setProperty(UifConstants.UrlParams.HISTORY, form.getFormHistory().getHistoryParameterString());
626 
627         // If this is an Light Box call only return the redirectURL view with the URL
628         // set this is to avoid automatic redirect when using light boxes
629         // TODO: add constant to UifParameters
630         if (urlParameters.get("lightBoxCall") != null && urlParameters.get("lightBoxCall").equals("true")) {
631             urlParameters.remove("lightBoxCall");
632             String redirectUrl = UrlFactory.parameterizeUrl(baseUrl, urlParameters);
633 
634             ModelAndView modelAndView = new ModelAndView("redirectURL");
635             modelAndView.addObject("redirectUrl", redirectUrl);
636             return modelAndView;
637         }
638 
639         String redirectUrl = UrlFactory.parameterizeUrl(baseUrl, urlParameters);
640         ModelAndView modelAndView = new ModelAndView(REDIRECT_PREFIX + redirectUrl);
641 
642         return modelAndView;
643     }
644 
645     protected ModelAndView getUIFModelAndView(UifFormBase form) {
646         return getUIFModelAndView(form, form.getPageId());
647     }
648 
649     /**
650      * Configures the <code>ModelAndView</code> instance containing the form
651      * data and pointing to the UIF generic spring view
652      *
653      * @param form - Form instance containing the model data
654      * @param pageId - Id of the page within the view that should be rendered, can
655      * be left blank in which the current or default page is rendered
656      * @return ModelAndView object with the contained form
657      */
658     protected ModelAndView getUIFModelAndView(UifFormBase form, String pageId) {
659         return UifWebUtils.getUIFModelAndView(form, pageId);
660     }
661 
662     protected ViewService getViewService() {
663         return KRADServiceLocatorWeb.getViewService();
664     }
665 
666     public SessionDocumentService getSessionDocumentService() {
667         return KRADServiceLocatorWeb.getSessionDocumentService();
668     }
669 
670 }