View Javadoc

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