001/** 002 * Copyright 2005-2016 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.krad.web.form; 017 018import org.apache.commons.lang.StringUtils; 019import org.apache.commons.logging.Log; 020import org.apache.commons.logging.LogFactory; 021import org.codehaus.jackson.map.ObjectMapper; 022import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 023import org.kuali.rice.krad.uif.UifConstants; 024import org.kuali.rice.krad.uif.UifParameters; 025import org.kuali.rice.krad.uif.view.History; 026import org.kuali.rice.krad.uif.view.HistoryEntry; 027import org.kuali.rice.krad.uif.view.View; 028import org.kuali.rice.krad.uif.service.ViewService; 029import org.kuali.rice.krad.uif.view.ViewModel; 030import org.kuali.rice.krad.util.KRADUtils; 031import org.springframework.web.multipart.MultipartFile; 032import org.kuali.rice.krad.uif.UifConstants.ViewType; 033 034import javax.servlet.http.HttpServletRequest; 035import java.io.IOException; 036import java.util.ArrayList; 037import java.util.HashMap; 038import java.util.List; 039import java.util.Map; 040import java.util.Properties; 041import java.util.Set; 042import java.util.UUID; 043 044/** 045 * Base form class for views within the KRAD User Interface Framework 046 * 047 * <p> 048 * Holds properties necessary to determine the <code>View</code> instance that 049 * will be used to render the UI 050 * </p> 051 * 052 * @author Kuali Rice Team (rice.collab@kuali.org) 053 */ 054public class UifFormBase implements ViewModel { 055 private static final long serialVersionUID = 8432543267099454434L; 056 057 // logger 058 private static final Log LOG = LogFactory.getLog(UifFormBase.class); 059 060 // current view 061 protected String viewId; 062 protected String viewName; 063 protected ViewType viewTypeName; 064 protected String pageId; 065 protected String methodToCall; 066 protected String formKey; 067 protected String jumpToId; 068 protected String jumpToName; 069 protected String focusId; 070 protected String formPostUrl; 071 072 protected boolean defaultsApplied; 073 protected boolean skipViewInit; 074 075 protected View view; 076 protected View postedView; 077 078 protected Map<String, String> viewRequestParameters; 079 protected List<String> readOnlyFieldsList; 080 081 protected Map<String, Object> newCollectionLines; 082 protected Map<String, String> actionParameters; 083 protected Map<String, Object> clientStateForSyncing; 084 protected Map<String, Set<String>> selectedCollectionLines; 085 086 protected MultipartFile attachmentFile; 087 088 // navigation 089 protected String returnLocation; 090 protected String returnFormKey; 091 092 protected History formHistory; 093 094 protected boolean renderFullView; 095 protected boolean validateDirty; 096 097 public UifFormBase() { 098 formKey = generateFormKey(); 099 renderFullView = true; 100 defaultsApplied = false; 101 skipViewInit = false; 102 103 formHistory = new History(); 104 105 readOnlyFieldsList = new ArrayList<String>(); 106 viewRequestParameters = new HashMap<String, String>(); 107 newCollectionLines = new HashMap<String, Object>(); 108 actionParameters = new HashMap<String, String>(); 109 clientStateForSyncing = new HashMap<String, Object>(); 110 selectedCollectionLines = new HashMap<String, Set<String>>(); 111 } 112 113 /** 114 * Creates the unique id used to store this "conversation" in the session. 115 * The default method generates a java UUID. 116 * 117 * @return 118 */ 119 protected String generateFormKey() { 120 return UUID.randomUUID().toString(); 121 } 122 123 /** 124 * Called after Spring binds the request to the form and before the 125 * controller method is invoked. 126 * 127 * @param request - request object containing the query parameters 128 */ 129 public void postBind(HttpServletRequest request) { 130 // default form post URL to request URL 131 formPostUrl = request.getRequestURL().toString(); 132 133 // get any sent client view state and parse into map 134 if (request.getParameterMap().containsKey(UifParameters.CLIENT_VIEW_STATE)) { 135 String clientStateJSON = request.getParameter(UifParameters.CLIENT_VIEW_STATE); 136 if (StringUtils.isNotBlank(clientStateJSON)) { 137 // change single quotes to double quotes (necessary because the reverse was done for sending) 138 clientStateJSON = StringUtils.replace(clientStateJSON, "'", "\""); 139 140 ObjectMapper mapper = new ObjectMapper(); 141 try { 142 clientStateForSyncing = mapper.readValue(clientStateJSON, Map.class); 143 } catch (IOException e) { 144 throw new RuntimeException("Unable to decode client side state JSON", e); 145 } 146 } 147 } 148 149 // populate read only fields list 150 if (request.getParameter(UifParameters.READ_ONLY_FIELDS) != null) { 151 String readOnlyFields = request.getParameter(UifParameters.READ_ONLY_FIELDS); 152 setReadOnlyFieldsList(KRADUtils.convertStringParameterToList(readOnlyFields)); 153 } 154 155 // reset skip view init parameter if not passed 156 if (!request.getParameterMap().containsKey(UifParameters.SKIP_VIEW_INIT)) { 157 skipViewInit = false; 158 } 159 } 160 161 /** 162 * @see org.kuali.rice.krad.uif.view.ViewModel#getViewId() 163 */ 164 public String getViewId() { 165 return this.viewId; 166 } 167 168 /** 169 * @see org.kuali.rice.krad.uif.view.ViewModel#setViewId(java.lang.String) 170 */ 171 public void setViewId(String viewId) { 172 this.viewId = viewId; 173 } 174 175 /** 176 * @see org.kuali.rice.krad.uif.view.ViewModel#getViewName() 177 */ 178 public String getViewName() { 179 return this.viewName; 180 } 181 182 /** 183 * @see org.kuali.rice.krad.uif.view.ViewModel#setViewName(java.lang.String) 184 */ 185 public void setViewName(String viewName) { 186 this.viewName = viewName; 187 } 188 189 /** 190 * @see org.kuali.rice.krad.uif.view.ViewModel#getViewTypeName() 191 */ 192 public ViewType getViewTypeName() { 193 return this.viewTypeName; 194 } 195 196 /** 197 * @see org.kuali.rice.krad.uif.view.ViewModel#setViewTypeName(org.kuali.rice.krad.uif.UifConstants.ViewType) 198 */ 199 public void setViewTypeName(ViewType viewTypeName) { 200 this.viewTypeName = viewTypeName; 201 } 202 203 /** 204 * @see org.kuali.rice.krad.uif.view.ViewModel#getPageId() 205 */ 206 public String getPageId() { 207 return this.pageId; 208 } 209 210 /** 211 * @see org.kuali.rice.krad.uif.view.ViewModel#setPageId(java.lang.String) 212 */ 213 public void setPageId(String pageId) { 214 this.pageId = pageId; 215 } 216 217 /** 218 * @see org.kuali.rice.krad.uif.view.ViewModel#getFormPostUrl() 219 */ 220 public String getFormPostUrl() { 221 return this.formPostUrl; 222 } 223 224 /** 225 * @see org.kuali.rice.krad.uif.view.ViewModel#setFormPostUrl(java.lang.String) 226 */ 227 public void setFormPostUrl(String formPostUrl) { 228 this.formPostUrl = formPostUrl; 229 } 230 231 public String getReturnLocation() { 232 return this.returnLocation; 233 } 234 235 public void setReturnLocation(String returnLocation) { 236 this.returnLocation = returnLocation; 237 } 238 239 public String getReturnFormKey() { 240 return this.returnFormKey; 241 } 242 243 public void setReturnFormKey(String returnFormKey) { 244 this.returnFormKey = returnFormKey; 245 } 246 247 /** 248 * Identifies the controller method that should be invoked to fulfill a 249 * request. The value will be matched up against the 'params' setting on the 250 * <code>RequestMapping</code> annotation for the controller method 251 * 252 * @return String method to call 253 */ 254 public String getMethodToCall() { 255 return this.methodToCall; 256 } 257 258 /** 259 * Setter for the method to call 260 * 261 * @param methodToCall 262 */ 263 public void setMethodToCall(String methodToCall) { 264 this.methodToCall = methodToCall; 265 } 266 267 /** 268 * @see org.kuali.rice.krad.uif.view.ViewModel#getViewRequestParameters() 269 */ 270 public Map<String, String> getViewRequestParameters() { 271 return this.viewRequestParameters; 272 } 273 274 /** 275 * @see org.kuali.rice.krad.uif.view.ViewModel#setViewRequestParameters(java.util.Map<java.lang.String,java.lang.String>) 276 */ 277 public void setViewRequestParameters(Map<String, String> viewRequestParameters) { 278 this.viewRequestParameters = viewRequestParameters; 279 } 280 281 /** 282 * @see org.kuali.rice.krad.uif.view.ViewModel#getReadOnlyFieldsList() 283 */ 284 public List<String> getReadOnlyFieldsList() { 285 return readOnlyFieldsList; 286 } 287 288 /** 289 * @see org.kuali.rice.krad.uif.view.ViewModel#setReadOnlyFieldsList(java.util.List<java.lang.String>) 290 */ 291 public void setReadOnlyFieldsList(List<String> readOnlyFieldsList) { 292 this.readOnlyFieldsList = readOnlyFieldsList; 293 } 294 295 /** 296 * @see org.kuali.rice.krad.uif.view.ViewModel#getNewCollectionLines() 297 */ 298 public Map<String, Object> getNewCollectionLines() { 299 return this.newCollectionLines; 300 } 301 302 /** 303 * @see org.kuali.rice.krad.uif.view.ViewModel#setNewCollectionLines(java.util.Map<java.lang.String,java.lang.Object>) 304 */ 305 public void setNewCollectionLines(Map<String, Object> newCollectionLines) { 306 this.newCollectionLines = newCollectionLines; 307 } 308 309 /** 310 * @see org.kuali.rice.krad.uif.view.ViewModel#getActionParameters() 311 */ 312 public Map<String, String> getActionParameters() { 313 return this.actionParameters; 314 } 315 316 /** 317 * Returns the action parameters map as a <code>Properties</code> instance 318 * 319 * @return Properties action parameters 320 */ 321 public Properties getActionParametersAsProperties() { 322 return KRADUtils.convertMapToProperties(actionParameters); 323 } 324 325 /** 326 * @see org.kuali.rice.krad.uif.view.ViewModel#setActionParameters(java.util.Map<java.lang.String,java.lang.String>) 327 */ 328 public void setActionParameters(Map<String, String> actionParameters) { 329 this.actionParameters = actionParameters; 330 } 331 332 /** 333 * Retrieves the value for the given action parameter, or empty string if 334 * not found 335 * 336 * @param actionParameterName - name of the action parameter to retrieve value for 337 * @return String parameter value or empty string 338 */ 339 public String getActionParamaterValue(String actionParameterName) { 340 if ((actionParameters != null) && actionParameters.containsKey(actionParameterName)) { 341 return actionParameters.get(actionParameterName); 342 } 343 344 return ""; 345 } 346 347 /** 348 * Returns the action event that was sent in the action parameters (if any) 349 * 350 * <p> 351 * The action event is a special action parameter that can be sent to indicate a type of action being taken. This 352 * can be looked at by the view or components to render differently 353 * </p> 354 * 355 * TODO: make sure action parameters are getting reinitialized on each request 356 * 357 * @return String action event name or blank if action event was not sent 358 */ 359 public String getActionEvent() { 360 if ((actionParameters != null) && actionParameters.containsKey(UifConstants.UrlParams.ACTION_EVENT)) { 361 return actionParameters.get(UifConstants.UrlParams.ACTION_EVENT); 362 } 363 364 return ""; 365 } 366 367 /** 368 * @see org.kuali.rice.krad.uif.view.ViewModel#getClientStateForSyncing() 369 */ 370 public Map<String, Object> getClientStateForSyncing() { 371 return clientStateForSyncing; 372 } 373 374 /** 375 * @see org.kuali.rice.krad.uif.view.ViewModel#getSelectedCollectionLines() 376 */ 377 public Map<String, Set<String>> getSelectedCollectionLines() { 378 return selectedCollectionLines; 379 } 380 381 /** 382 * @see org.kuali.rice.krad.uif.view.ViewModel#setSelectedCollectionLines(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>) 383 */ 384 public void setSelectedCollectionLines(Map<String, Set<String>> selectedCollectionLines) { 385 this.selectedCollectionLines = selectedCollectionLines; 386 } 387 388 /** 389 * Key string that identifies the form instance in session storage 390 * 391 * <p> 392 * When the view is posted, the previous form instance is retrieved and then 393 * populated from the request parameters. This key string is retrieve the 394 * session form from the session service 395 * </p> 396 * 397 * @return String form session key 398 */ 399 public String getFormKey() { 400 return this.formKey; 401 } 402 403 /** 404 * Setter for the form's session key 405 * 406 * @param formKey 407 */ 408 public void setFormKey(String formKey) { 409 this.formKey = formKey; 410 } 411 412 /** 413 * @see org.kuali.rice.krad.uif.view.ViewModel#isDefaultsApplied() 414 */ 415 public boolean isDefaultsApplied() { 416 return this.defaultsApplied; 417 } 418 419 /** 420 * @see org.kuali.rice.krad.uif.view.ViewModel#setDefaultsApplied(boolean) 421 */ 422 public void setDefaultsApplied(boolean defaultsApplied) { 423 this.defaultsApplied = defaultsApplied; 424 } 425 426 /** 427 * Indicates whether a new view is being initialized or the call is refresh (or query) call 428 * 429 * @return boolean true if view initialization was skipped, false if new view is being created 430 */ 431 public boolean isSkipViewInit() { 432 return skipViewInit; 433 } 434 435 /** 436 * Setter for the skip view initialization flag 437 * 438 * @param skipViewInit 439 */ 440 public void setSkipViewInit(boolean skipViewInit) { 441 this.skipViewInit = skipViewInit; 442 } 443 444 /** 445 * Holder for files that are attached through the view 446 * 447 * @return MultipartFile representing the attachment 448 */ 449 public MultipartFile getAttachmentFile() { 450 return this.attachmentFile; 451 } 452 453 /** 454 * Setter for the form's attachment file 455 * 456 * @param attachmentFile 457 */ 458 public void setAttachmentFile(MultipartFile attachmentFile) { 459 this.attachmentFile = attachmentFile; 460 } 461 462 /** 463 * @return the renderFullView 464 */ 465 public boolean isRenderFullView() { 466 return this.renderFullView; 467 } 468 469 /** 470 * @param renderFullView 471 */ 472 public void setRenderFullView(boolean renderFullView) { 473 this.renderFullView = renderFullView; 474 } 475 476 /** 477 * @see org.kuali.rice.krad.uif.view.ViewModel#getView() 478 */ 479 public View getView() { 480 return this.view; 481 } 482 483 /** 484 * @see org.kuali.rice.krad.uif.view.ViewModel#setView(org.kuali.rice.krad.uif.view.View) 485 */ 486 public void setView(View view) { 487 this.view = view; 488 initHomewardPathList(); 489 } 490 491 /** 492 * Set the "Home" url of the homewardPathList (ie. breadcrumbs history) 493 */ 494 private void initHomewardPathList() { 495 if (getReturnLocation() == null) { 496 LOG.warn("Could not init homewardPathList. returnLocation is null."); 497 return; 498 } 499 500 List<HistoryEntry> homewardPathList = new ArrayList<HistoryEntry>(); 501 if ((view != null) && (view.getBreadcrumbs() != null) && (view.getBreadcrumbs().getHomewardPathList() != null)) { 502 homewardPathList = view.getBreadcrumbs().getHomewardPathList(); 503 } 504 505 HistoryEntry historyEntry = new HistoryEntry("","","Home",getReturnLocation(),""); 506 if (homewardPathList.isEmpty()) { 507 homewardPathList.add(historyEntry); 508 } else if (StringUtils.equals(homewardPathList.get(0).getTitle(), "Home")) { 509 homewardPathList.set(0, historyEntry); 510 } else { 511 homewardPathList.add(0, historyEntry); 512 } 513 } 514 515 /** 516 * @see org.kuali.rice.krad.uif.view.ViewModel#getPostedView() 517 */ 518 public View getPostedView() { 519 return this.postedView; 520 } 521 522 /** 523 * @see org.kuali.rice.krad.uif.view.ViewModel#setPostedView(org.kuali.rice.krad.uif.view.View) 524 */ 525 public void setPostedView(View postedView) { 526 this.postedView = postedView; 527 } 528 529 /** 530 * Instance of the <code>ViewService</code> that can be used to retrieve 531 * <code>View</code> instances 532 * 533 * @return ViewService implementation 534 */ 535 protected ViewService getViewService() { 536 return KRADServiceLocatorWeb.getViewService(); 537 } 538 539 /** 540 * The jumpToId for this form, the element with this id will be jumped to automatically 541 * when the form is loaded in the view. 542 * Using "TOP" or "BOTTOM" will jump to the top or the bottom of the resulting page. 543 * jumpToId always takes precedence over jumpToName, if set. 544 * 545 * @return the jumpToId 546 */ 547 public String getJumpToId() { 548 return this.jumpToId; 549 } 550 551 /** 552 * @param jumpToId the jumpToId to set 553 */ 554 public void setJumpToId(String jumpToId) { 555 this.jumpToId = jumpToId; 556 } 557 558 /** 559 * The jumpToName for this form, the element with this name will be jumped to automatically 560 * when the form is loaded in the view. 561 * WARNING: jumpToId always takes precedence over jumpToName, if set. 562 * 563 * @return the jumpToName 564 */ 565 public String getJumpToName() { 566 return this.jumpToName; 567 } 568 569 /** 570 * @param jumpToName the jumpToName to set 571 */ 572 public void setJumpToName(String jumpToName) { 573 this.jumpToName = jumpToName; 574 } 575 576 /** 577 * Field to place focus on when the page loads 578 * An empty focusId will result in focusing on the first visible input element by default. 579 * 580 * @return the focusId 581 */ 582 public String getFocusId() { 583 return this.focusId; 584 } 585 586 /** 587 * @param focusId the focusId to set 588 */ 589 public void setFocusId(String focusId) { 590 this.focusId = focusId; 591 } 592 593 /** 594 * History parameter representing the History of views that have come before the 595 * viewing of the current view 596 * 597 * <p> 598 * Used for breadcrumb widget generation on the view and also for navigating back 599 * to previous or hub locations 600 * </p> 601 * 602 * @return History instance giving current history 603 */ 604 public History getFormHistory() { 605 return formHistory; 606 } 607 608 /** 609 * Setter for the current History object 610 * 611 * @param history the history to set 612 */ 613 public void setFormHistory(History history) { 614 this.formHistory = history; 615 } 616 617 /** 618 * Indicates whether the form should be validated for dirtyness 619 * 620 * <p> 621 * For FormView, it's necessary to validate when the user tries to navigate out of the form. If set, all the 622 * InputFields will be validated on refresh, navigate, cancel or close Action or on form 623 * unload and if dirty, displays a message and user can decide whether to continue with 624 * the action or stay on the form 625 * </p> 626 * 627 * @return boolean true if dirty validation should be enabled 628 */ 629 public boolean isValidateDirty() { 630 return this.validateDirty; 631 } 632 633 /** 634 * Setter for dirty validation indicator 635 * 636 * @param validateDirty 637 */ 638 public void setValidateDirty(boolean validateDirty) { 639 this.validateDirty = validateDirty; 640 } 641 642}