View Javadoc

1   /**
2    * Copyright 2005-2015 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.apache.log4j.Logger;
20  import org.codehaus.jackson.map.ObjectMapper;
21  import org.kuali.rice.core.api.CoreApiServiceLocator;
22  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
23  import org.kuali.rice.krad.uif.UifConstants;
24  import org.kuali.rice.krad.uif.UifParameters;
25  import org.kuali.rice.krad.uif.container.Container;
26  import org.kuali.rice.krad.uif.layout.LayoutManager;
27  import org.kuali.rice.krad.uif.util.ComponentFactory;
28  import org.kuali.rice.krad.uif.util.ComponentUtils;
29  import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
30  import org.kuali.rice.krad.uif.view.View;
31  import org.kuali.rice.krad.uif.component.Component;
32  import org.kuali.rice.krad.uif.service.ViewService;
33  import org.kuali.rice.krad.util.KRADConstants;
34  import org.kuali.rice.krad.util.KRADUtils;
35  import org.kuali.rice.krad.web.form.UifFormBase;
36  import org.kuali.rice.krad.web.form.UifFormManager;
37  import org.springframework.web.servlet.ModelAndView;
38  
39  import javax.servlet.http.HttpServletRequest;
40  import javax.servlet.http.HttpServletResponse;
41  import java.util.HashMap;
42  import java.util.List;
43  import java.util.Map;
44  
45  /**
46   * Provides helper methods that will be used during the request lifecycle
47   *
48   * <p>
49   * Created to avoid duplication of the methods used by the UifHandlerExceptionResolver
50   * </p>
51   *
52   * @author Kuali Rice Team (rice.collab@kuali.org)
53   */
54  public class UifControllerHelper {
55      private static final Logger LOG = Logger.getLogger(UifControllerHelper.class);
56  
57      /**
58       * Attempts to resolve a view id from the given request
59       *
60       * <p>
61       * First an attempt will be made to find the view id as a request parameter. If no such request parameter
62       * is found, the request will be looked at for view type information and a call will be made to the
63       * view service to find the view id by type
64       * </p>
65       *
66       * <p>
67       * If a view id is found it is stuck in the request as an attribute (under the key
68       * {@link org.kuali.rice.krad.uif.UifParameters#VIEW_ID}) for subsequent retrieval
69       * </p>
70       *
71       * @param request instance to resolve view id for
72       * @return view id if one is found, null if not found
73       */
74      public static String getViewIdFromRequest(HttpServletRequest request) {
75          String viewId = request.getParameter(UifParameters.VIEW_ID);
76  
77          if (StringUtils.isBlank(viewId)) {
78              String viewTypeName = request.getParameter(UifParameters.VIEW_TYPE_NAME);
79  
80              UifConstants.ViewType viewType = null;
81              if (StringUtils.isNotBlank(viewTypeName)) {
82                  viewType = UifConstants.ViewType.valueOf(viewTypeName);
83              }
84  
85              if (viewType != null) {
86                  Map<String, String> parameterMap = KRADUtils.translateRequestParameterMap(request.getParameterMap());
87                  viewId = getViewService().getViewIdForViewType(viewType, parameterMap);
88              }
89          }
90  
91          if (StringUtils.isNotBlank(viewId)) {
92             request.setAttribute(UifParameters.VIEW_ID, viewId);
93          }
94  
95          return viewId;
96      }
97  
98      /**
99       * Configures the <code>ModelAndView</code> instance containing the form
100      * data and pointing to the UIF generic spring view
101      *
102      * @param form - Form instance containing the model data
103      * @param pageId - Id of the page within the view that should be rendered, can
104      * be left blank in which the current or default page is rendered
105      * @return ModelAndView object with the contained form
106      */
107     public static ModelAndView getUIFModelAndView(UifFormBase form, String pageId) {
108         if (StringUtils.isNotBlank(pageId)) {
109             form.setPageId(pageId);
110         }
111 
112         // create the spring return object pointing to View.jsp
113         ModelAndView modelAndView = new ModelAndView();
114         modelAndView.addObject(UifConstants.DEFAULT_MODEL_NAME, form);
115         modelAndView.setViewName(UifConstants.SPRING_VIEW_ID);
116 
117         return modelAndView;
118     }
119 
120     /**
121      * After the controller logic is executed, the form is placed into session
122      * and the corresponding view is prepared for rendering
123      */
124     public static void postControllerHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
125             ModelAndView modelAndView) throws Exception {
126         if (modelAndView == null) {
127             return;
128         }
129 
130         Object model = modelAndView.getModelMap().get(UifConstants.DEFAULT_MODEL_NAME);
131         if (!(model instanceof UifFormBase)) {
132             return;
133         }
134 
135         UifFormBase form = (UifFormBase) model;
136 
137         // handle view building if not a redirect
138         if (!form.isRequestRedirected()) {
139             if (!form.isJsonRequest() && !form.isOriginalComponentRequest()) {
140                 prepareViewForRendering(request, form);
141             }
142 
143             // export the component to the request model if an update id has been set
144             Component component = null;
145             if (StringUtils.isNotBlank(form.getUpdateComponentId())) {
146                 component = form.getPostedView().getViewIndex().getComponentById(form.getUpdateComponentId());
147             } else if (form.isUpdatePageRequest()) {
148                 component = form.getView().getCurrentPage();
149             }
150 
151             if (form.isOriginalComponentRequest()) {
152                 // This needs to be done because scenarios where the templates are not present
153                 updateViewTemplates(component, form);
154             }
155 
156             String changeProperties = request.getParameter(UifParameters.CHANGE_PROPERTIES);
157 
158             // Change properties on the the final component
159             if (StringUtils.isNotBlank(changeProperties) && component != null){
160                 HashMap<String,Object> changePropertiesMap = new ObjectMapper().readValue(changeProperties,
161                         HashMap.class);
162 
163                 for (String changePropertyPath : changePropertiesMap.keySet()){
164                     Object value = changePropertiesMap.get(changePropertyPath);
165                     ObjectPropertyUtils.setPropertyValue(component, changePropertyPath, value);
166                 }
167             }
168 
169             if (component != null) {
170                 modelAndView.addObject(UifConstants.COMPONENT_MODEL_NAME, component);
171             }
172         }
173 
174         // expose additional objects to the templates
175         modelAndView.addObject(UifParameters.REQUEST, request);
176         modelAndView.addObject(KRADConstants.USER_SESSION_KEY, request.getSession().getAttribute(
177                 KRADConstants.USER_SESSION_KEY));
178 
179         Map<String, String> properties = CoreApiServiceLocator.getKualiConfigurationService().getAllProperties();
180         modelAndView.addObject(UifParameters.CONFIG_PROPERTIES, properties);
181 
182         //modelAndView.addObject(UifParameters.STRING_RENDER_CONTEXT, new StringRenderContext());
183     }
184 
185     /**
186      * Update the view templates with the ones necessary to render the currentComponent passed in
187      *
188      * @param currentComponent the component to use to update the templates
189      * @param form the current form
190      */
191     protected static void updateViewTemplates(Component currentComponent, UifFormBase form){
192         List<Component> components = ComponentUtils.getAllNestedComponents(currentComponent);
193         components.add(currentComponent);
194         for (Component component : components){
195             // add the components template to the views list of components
196             if (!component.isSelfRendered() && StringUtils.isNotBlank(component.getTemplate()) &&
197                     !form.getPostedView().getViewTemplates().contains(component.getTemplate())) {
198                 form.getPostedView().getViewTemplates().add(component.getTemplate());
199             }
200 
201             if (component instanceof Container) {
202                 LayoutManager layoutManager = ((Container) component).getLayoutManager();
203 
204                 if ((layoutManager != null)
205                         && !form.getPostedView().getViewTemplates().contains(layoutManager.getTemplate())) {
206                     form.getPostedView().getViewTemplates().add(layoutManager.getTemplate());
207                 }
208             }
209         }
210     }
211 
212     /**
213      * Prepares the <code>View</code> instance contained on the form for rendering
214      *
215      * @param request - request object
216      * @param form - form instance containing the data and view instance
217      */
218     public static void prepareViewForRendering(HttpServletRequest request, UifFormBase form) {
219         // for component refreshes only lifecycle for component is performed
220         if (form.isUpdateComponentRequest() || form.isUpdateDialogRequest()) {
221             String refreshComponentId = form.getUpdateComponentId();
222 
223             View postedView = form.getPostedView();
224 
225             // check if the component is nested in a box layout in order to reapply the layout item style
226             boolean boxLayoutHorizontalItem = false;
227             boolean boxLayoutVerticalItem = false;
228 
229             if (form.isUpdateComponentRequest()) {
230                 Component postedComponent = ComponentUtils.findNestedComponentById(postedView, refreshComponentId);
231                 if (postedComponent != null && postedComponent.getCssClasses() != null &&
232                         postedComponent.getCssClasses().contains("uif-boxLayoutHorizontalItem")) {
233                     boxLayoutHorizontalItem = true;
234                 } else if (postedComponent != null && postedComponent.getCssClasses() != null &&
235                         postedComponent.getCssClasses().contains("uif-boxLayoutVerticalItem")) {
236                     boxLayoutVerticalItem = true;
237                 }
238             }
239 
240             // get a new instance of the component
241             Component comp = ComponentFactory.getNewInstanceForRefresh(form.getPostedView(), refreshComponentId);
242 
243             // run lifecycle and update in view
244             postedView.getViewHelperService().performComponentLifecycle(postedView, form, comp, refreshComponentId);
245 
246             // TODO: this should be in ViewHelperServiceImpl#performComponentLifecycle where other
247             // adjustments are made, and it should use constants
248 
249             // add the layout item style that should happen in the parent BoxLayoutManager
250             // and is skipped in a child component refresh
251             if (boxLayoutHorizontalItem) {
252                 comp.addStyleClass("uif-boxLayoutHorizontalItem");
253             } else if (boxLayoutVerticalItem) {
254                 comp.addStyleClass("uif-boxLayoutVerticalItem");
255             }
256 
257             // regenerate server message content for page
258             postedView.getCurrentPage().getValidationMessages().generateMessages(false, postedView, form,
259                     postedView.getCurrentPage());
260         } else {
261             // full view build
262             View view = form.getView();
263 
264             // set view page to page requested on form
265             if (StringUtils.isNotBlank(form.getPageId())) {
266                 view.setCurrentPageId(form.getPageId());
267             }
268 
269             Map<String, String> parameterMap = KRADUtils.translateRequestParameterMap(request.getParameterMap());
270             parameterMap.putAll(form.getViewRequestParameters());
271 
272             // build view which will prepare for rendering
273             getViewService().buildView(view, form, parameterMap);
274         }
275     }
276 
277     /**
278      * Remove unused forms from breadcrumb history
279      *
280      * <p>
281      * When going back in the breadcrumb history some forms become unused in the breadcrumb history.  Here the unused
282      * forms are being determine and removed from the server to free memory.
283      * </p>
284      *
285      * @param uifFormManager
286      * @param formKey of the current form
287      * @param lastFormKey of the last form
288      */
289     public static void removeUnusedBreadcrumbs(UifFormManager uifFormManager, String formKey, String lastFormKey) {
290         if (StringUtils.isBlank(formKey) || StringUtils.isBlank(lastFormKey) || StringUtils.equals(formKey,
291                 lastFormKey)) {
292             return;
293         }
294 
295         UifFormBase previousForm = uifFormManager.getSessionForm(lastFormKey);
296         if (previousForm == null) {
297             return;
298         }
299 
300         uifFormManager.removeSessionFormByKey(lastFormKey);
301     }
302 
303     protected static ViewService getViewService() {
304         return KRADServiceLocatorWeb.getViewService();
305     }
306 }