001/** 002 * Copyright 2005-2018 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.maintenance; 017 018import java.io.Serializable; 019import java.security.GeneralSecurityException; 020import java.util.ArrayList; 021import java.util.Collection; 022import java.util.Iterator; 023import java.util.List; 024import java.util.Map; 025 026import org.apache.commons.collections.CollectionUtils; 027import org.apache.commons.collections.MapUtils; 028import org.apache.commons.lang.StringUtils; 029import org.kuali.rice.core.api.CoreApiServiceLocator; 030import org.kuali.rice.core.api.encryption.EncryptionService; 031import org.kuali.rice.kim.api.identity.Person; 032import org.kuali.rice.krad.bo.AdHocRoutePerson; 033import org.kuali.rice.krad.bo.AdHocRouteWorkgroup; 034import org.kuali.rice.krad.bo.BusinessObject; 035import org.kuali.rice.krad.bo.DocumentHeader; 036import org.kuali.rice.krad.bo.Note; 037import org.kuali.rice.krad.data.CompoundKey; 038import org.kuali.rice.krad.data.DataObjectService; 039import org.kuali.rice.krad.data.DataObjectWrapper; 040import org.kuali.rice.krad.exception.PessimisticLockingException; 041import org.kuali.rice.krad.rules.rule.event.AddCollectionLineEvent; 042import org.kuali.rice.krad.service.DataObjectAuthorizationService; 043import org.kuali.rice.krad.service.DocumentDictionaryService; 044import org.kuali.rice.krad.service.KRADServiceLocator; 045import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 046import org.kuali.rice.krad.service.KualiRuleService; 047import org.kuali.rice.krad.service.LegacyDataAdapter; 048import org.kuali.rice.krad.service.MaintenanceDocumentService; 049import org.kuali.rice.krad.uif.UifConstants; 050import org.kuali.rice.krad.uif.UifPropertyPaths; 051import org.kuali.rice.krad.uif.component.BindingInfo; 052import org.kuali.rice.krad.uif.container.CollectionGroup; 053import org.kuali.rice.krad.uif.field.DataField; 054import org.kuali.rice.krad.uif.field.InputField; 055import org.kuali.rice.krad.uif.lifecycle.ViewLifecycle; 056import org.kuali.rice.krad.uif.service.ViewHelperService; 057import org.kuali.rice.krad.uif.service.impl.ViewHelperServiceImpl; 058import org.kuali.rice.krad.uif.util.LifecycleElement; 059import org.kuali.rice.krad.uif.util.ObjectPropertyUtils; 060import org.kuali.rice.krad.uif.view.MaintenanceDocumentView; 061import org.kuali.rice.krad.uif.view.View; 062import org.kuali.rice.krad.uif.view.ViewModel; 063import org.kuali.rice.krad.util.KRADConstants; 064import org.kuali.rice.krad.util.KRADUtils; 065import org.kuali.rice.krad.web.form.MaintenanceDocumentForm; 066 067/** 068 * Default implementation of the <code>Maintainable</code> interface. 069 * 070 * @author Kuali Rice Team (rice.collab@kuali.org) 071 */ 072public class MaintainableImpl extends ViewHelperServiceImpl implements Maintainable { 073 private static final long serialVersionUID = 9125271369161634992L; 074 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(MaintainableImpl.class); 075 076 private String documentNumber; 077 private Object dataObject; 078 private Class<?> dataObjectClass; 079 private String maintenanceAction; 080 081 private transient LegacyDataAdapter legacyDataAdapter; 082 private transient DataObjectAuthorizationService dataObjectAuthorizationService; 083 private transient DocumentDictionaryService documentDictionaryService; 084 private transient EncryptionService encryptionService; 085 private transient DataObjectService dataObjectService; 086 private transient MaintenanceDocumentService maintenanceDocumentService; 087 private transient KualiRuleService kualiRuleService; 088 089 /** 090 * @see org.kuali.rice.krad.maintenance.Maintainable#retrieveObjectForEditOrCopy(MaintenanceDocument, java.util.Map) 091 */ 092 @Override 093 public Object retrieveObjectForEditOrCopy(MaintenanceDocument document, Map<String, String> dataObjectKeys) { 094 Object dataObject = null; 095 if ( getDataObjectService().supports(getDataObjectClass())) { 096 Map<String, Object> translatedValues = KRADUtils.coerceRequestParameterTypes(getDataObjectClass(), dataObjectKeys); 097 dataObject = getDataObjectService().find(getDataObjectClass(), new CompoundKey(translatedValues)); 098 } else { 099 try { 100 dataObject = getLegacyDataAdapter().findObjectBySearch(getDataObjectClass(), dataObjectKeys); 101 } catch (Exception ex) { 102 if ( ex.getClass().equals( LegacyDataAdapter.CLASS_NOT_PERSISTABLE_OJB_EXCEPTION_CLASS ) 103 && !document.getOldMaintainableObject().isExternalBusinessObject()) { 104 throw new RuntimeException("Data Object Class: " 105 + getDataObjectClass() 106 + " is not persistable and is not externalizable - configuration error"); 107 } 108 // otherwise, let fall through 109 } 110 } 111 112 return dataObject; 113 } 114 115 116 /** 117 * @see org.kuali.rice.krad.maintenance.Maintainable#setDocumentNumber 118 */ 119 @Override 120 public void setDocumentNumber(String documentNumber) { 121 this.documentNumber = documentNumber; 122 } 123 124 /** 125 * @see org.kuali.rice.krad.maintenance.Maintainable#getDocumentTitle 126 */ 127 @Override 128 public String getDocumentTitle(MaintenanceDocument document) { 129 // default implementation is to allow MaintenanceDocumentBase to 130 // generate the doc title 131 return ""; 132 } 133 134 /** 135 * @see org.kuali.rice.krad.maintenance.Maintainable#getDataObject 136 */ 137 @Override 138 public Object getDataObject() { 139 return dataObject; 140 } 141 142 /** 143 * @see org.kuali.rice.krad.maintenance.Maintainable#setDataObject 144 */ 145 @Override 146 public void setDataObject(Object object) { 147 this.dataObject = object; 148 } 149 150 /** 151 * @see org.kuali.rice.krad.maintenance.Maintainable#getDataObjectClass 152 */ 153 @Override 154 public Class<?> getDataObjectClass() { 155 return dataObjectClass; 156 } 157 158 /** 159 * @see org.kuali.rice.krad.maintenance.Maintainable#setDataObjectClass 160 */ 161 @Override 162 public void setDataObjectClass(Class<?> dataObjectClass) { 163 this.dataObjectClass = dataObjectClass; 164 } 165 166 /** 167 * Persistable business objects are lockable. 168 * 169 * @deprecated note used by Rice framework 170 */ 171 @Override 172 @Deprecated 173 public boolean isLockable() { 174 return KRADServiceLocatorWeb.getLegacyDataAdapter().isLockable(getDataObject()); 175 } 176 177// /** 178// * Returns the data object if its persistable, null otherwise. 179// * 180// * @deprecated this method has been left for compatibility reasons, use getDataObject instead. 181// */ 182// @Override 183// @Deprecated // Uses KNS Classes 184// public PersistableBusinessObject getPersistableBusinessObject() { 185// return KRADServiceLocatorWeb.getLegacyDataAdapter().toPersistableBusinessObject(getDataObject()); 186// } 187 188 /** 189 * @see org.kuali.rice.krad.maintenance.Maintainable#getMaintenanceAction 190 */ 191 @Override 192 public String getMaintenanceAction() { 193 return maintenanceAction; 194 } 195 196 /** 197 * @see org.kuali.rice.krad.maintenance.Maintainable#setMaintenanceAction 198 */ 199 @Override 200 public void setMaintenanceAction(String maintenanceAction) { 201 this.maintenanceAction = maintenanceAction; 202 } 203 204 /** 205 * Note: as currently implemented, every key field for a given 206 * data object class must have a visible getter. 207 * 208 * @see org.kuali.rice.krad.maintenance.Maintainable#generateMaintenanceLocks 209 */ 210 @Override 211 public List<MaintenanceLock> generateMaintenanceLocks() { 212 return generateMaintenanceLocks(getDocumentNumber(), getDocumentTypeName(), getDataObjectClass(), getDataObject()); 213 } 214 215 /** 216 * Allows locking of maintenance objects other than the one of the current maintenance object. 217 * 218 * @param documentNumber of the locking maintenance document 219 * @param documentTypeName of the maintenance document to be locked 220 * @param dataObjectClass of the maintenance document to be locked 221 * @param dataObject of the maintenance document to be locked 222 * @return 223 */ 224 protected List<MaintenanceLock> generateMaintenanceLocks(String documentNumber, String documentTypeName, Class<?> dataObjectClass, Object dataObject) { 225 List<MaintenanceLock> maintenanceLocks = new ArrayList<MaintenanceLock>(); 226 StringBuffer lockRepresentation = new StringBuffer(dataObjectClass.getName()); 227 lockRepresentation.append(KRADConstants.Maintenance.LOCK_AFTER_CLASS_DELIM); 228 229 DataObjectWrapper<Object> wrapper = getDataObjectService().wrap(dataObject); 230 231 List<String> keyFieldNames = getDocumentDictionaryService().getLockingKeys(documentTypeName); 232 233 for (Iterator<?> i = keyFieldNames.iterator(); i.hasNext(); ) { 234 String fieldName = (String) i.next(); 235 236 Object fieldValue = wrapper.getPropertyValueNullSafe(fieldName); 237 if (fieldValue == null) { 238 fieldValue = ""; 239 } 240 241 // check if field is a secure 242 if (getDataObjectAuthorizationService() 243 .attributeValueNeedsToBeEncryptedOnFormsAndLinks(dataObjectClass, fieldName)) { 244 try { 245 if(CoreApiServiceLocator.getEncryptionService().isEnabled()) { 246 fieldValue = getEncryptionService().encrypt(fieldValue); 247 } 248 } catch (GeneralSecurityException e) { 249 LOG.error("Unable to encrypt secure field for locking representation " + e.getMessage()); 250 throw new RuntimeException( 251 "Unable to encrypt secure field for locking representation " + e.getMessage()); 252 } 253 } 254 255 lockRepresentation.append(fieldName); 256 lockRepresentation.append(KRADConstants.Maintenance.LOCK_AFTER_FIELDNAME_DELIM); 257 lockRepresentation.append(String.valueOf(fieldValue)); 258 if (i.hasNext()) { 259 lockRepresentation.append(KRADConstants.Maintenance.LOCK_AFTER_VALUE_DELIM); 260 } 261 } 262 263 MaintenanceLock maintenanceLock = new MaintenanceLock(); 264 maintenanceLock.setDocumentNumber(documentNumber); 265 maintenanceLock.setLockingRepresentation(lockRepresentation.toString()); 266 maintenanceLocks.add(maintenanceLock); 267 268 return maintenanceLocks; 269 } 270 271 /** 272 * Retrieves the document type name from the data dictionary based on 273 * business object class 274 */ 275 protected String getDocumentTypeName() { 276 return getDocumentDictionaryService().getMaintenanceDocumentTypeName(dataObjectClass); 277 } 278 279 /** 280 * @see org.kuali.rice.krad.maintenance.Maintainable#saveDataObject 281 */ 282 @Override 283 public void saveDataObject() { 284 if ( dataObject == null ) { 285 LOG.warn( "dataObject in maintainable was null - this should not be the case. Skipping saveDataObject()"); 286 return; 287 } 288 dataObject = getLegacyDataAdapter().linkAndSave((Serializable)dataObject); 289 } 290 291 /** 292 * @see org.kuali.rice.krad.maintenance.Maintainable#deleteDataObject 293 */ 294 @Override 295 public void deleteDataObject() { 296 if (dataObject == null) { 297 return; 298 } 299 getLegacyDataAdapter().delete(dataObject); 300 } 301 302 /** 303 * @see org.kuali.rice.krad.maintenance.Maintainable#doRouteStatusChange 304 */ 305 @Override 306 public void doRouteStatusChange(DocumentHeader documentHeader) { 307 // no default implementation 308 } 309 310 /** 311 * @see org.kuali.rice.krad.maintenance.Maintainable#getLockingDocumentId 312 */ 313 @Override 314 public String getLockingDocumentId() { 315 return getMaintenanceDocumentService().getLockingDocumentId(this, documentNumber); 316 } 317 318 /** 319 * @see org.kuali.rice.krad.maintenance.Maintainable#getWorkflowEngineDocumentIdsToLock 320 */ 321 @Override 322 public List<String> getWorkflowEngineDocumentIdsToLock() { 323 return null; 324 } 325 326 /** 327 * Default implementation simply returns false to indicate that custom 328 * lock descriptors are not supported by MaintainableImpl. If custom 329 * lock descriptors are needed, the appropriate subclasses should override 330 * this method 331 * 332 * @see org.kuali.rice.krad.maintenance.Maintainable#useCustomLockDescriptors 333 */ 334 @Override 335 public boolean useCustomLockDescriptors() { 336 return false; 337 } 338 339 /** 340 * Default implementation just throws a PessimisticLockingException. 341 * Subclasses of MaintainableImpl that need support for custom lock 342 * descriptors should override this method 343 * 344 * @see org.kuali.rice.krad.maintenance.Maintainable#getCustomLockDescriptor 345 */ 346 @Override 347 public String getCustomLockDescriptor(Person user) { 348 throw new PessimisticLockingException("The Maintainable for document " + documentNumber + 349 " is using pessimistic locking with custom lock descriptors, but the Maintainable has not overridden the getCustomLockDescriptor method"); 350 } 351 352 /** 353 * @see org.kuali.rice.krad.maintenance.Maintainable#isNotesEnabled 354 */ 355 @Override 356 public boolean isNotesEnabled() { 357 return getLegacyDataAdapter().areNotesSupported(dataObjectClass); 358 } 359 360 /** 361 * @see org.kuali.rice.krad.maintenance.MaintainableImpl#isExternalBusinessObject 362 */ 363 @Override 364 public boolean isExternalBusinessObject() { 365 return false; 366 } 367 368 /** 369 * @see org.kuali.rice.krad.maintenance.MaintainableImpl#prepareExternalBusinessObject 370 */ 371 @Override 372 @Deprecated 373 public void prepareExternalBusinessObject(BusinessObject businessObject) { 374 // by default do nothing 375 } 376 377 /** 378 * Checks whether the data object is not null and has its primary key values populated. 379 * 380 * @see org.kuali.rice.krad.maintenance.MaintainableImpl#isOldDataObjectInDocument 381 */ 382 @Override 383 public boolean isOldDataObjectInDocument() { 384 boolean isOldDataObjectInExistence = true; 385 386 if (getDataObject() == null) { 387 isOldDataObjectInExistence = false; 388 } else { 389 Map<String, ?> keyFieldValues = getLegacyDataAdapter().getPrimaryKeyFieldValuesDOMDS(getDataObject()); 390 for (Object keyValue : keyFieldValues.values()) { 391 if (keyValue == null) { 392 isOldDataObjectInExistence = false; 393 } else if ((keyValue instanceof String) && StringUtils.isBlank((String) keyValue)) { 394 isOldDataObjectInExistence = false; 395 } 396 397 if (!isOldDataObjectInExistence) { 398 break; 399 } 400 } 401 } 402 403 return isOldDataObjectInExistence; 404 } 405 406 /** 407 * @see org.kuali.rice.krad.maintenance.Maintainable#prepareForSave 408 */ 409 @Override 410 public void prepareForSave() { 411 // by default do nothing 412 } 413 414 /** 415 * @see org.kuali.rice.krad.maintenance.Maintainable#processAfterRetrieve 416 */ 417 @Override 418 public void processAfterRetrieve() { 419 // by default do nothing 420 } 421 422 /** 423 * @see org.kuali.rice.krad.maintenance.MaintainableImpl#setupNewFromExisting 424 */ 425 @Override 426 public void setupNewFromExisting(MaintenanceDocument document, Map<String, String[]> parameters) { 427 // by default do nothing 428 } 429 430 /** 431 * @see org.kuali.rice.krad.maintenance.Maintainable#processAfterCopy 432 */ 433 @Override 434 public void processAfterCopy(MaintenanceDocument document, Map<String, String[]> requestParameters) { 435 // by default do nothing 436 } 437 438 /** 439 * @see org.kuali.rice.krad.maintenance.Maintainable#processAfterEdit 440 */ 441 @Override 442 public void processAfterEdit(MaintenanceDocument document, Map<String, String[]> requestParameters) { 443 // by default do nothing 444 } 445 446 /** 447 * @see org.kuali.rice.krad.maintenance.Maintainable#processAfterNew 448 */ 449 @Override 450 public void processAfterNew(MaintenanceDocument document, Map<String, String[]> requestParameters) { 451 // by default do nothing 452 } 453 454 /** 455 * @see org.kuali.rice.krad.maintenance.Maintainable#processAfterPost 456 */ 457 @Override 458 public void processAfterPost(MaintenanceDocument document, Map<String, String[]> requestParameters) { 459 // by default do nothing 460 } 461 462 /** 463 * In the case of edit maintenance adds a new blank line to the old side. 464 * 465 * TODO: should this write some sort of missing message on the old side 466 * instead? 467 * 468 */ 469 @Override 470 public void processAfterAddLine(ViewModel viewModel, Object addLine, String collectionId, String collectionPath, 471 boolean isValidLine) { 472 super.processAfterAddLine(viewModel, addLine, collectionId, collectionPath, isValidLine); 473 474 // Check for maintenance documents in edit but exclude notes and ad hoc recipients 475 if (viewModel instanceof MaintenanceDocumentForm 476 && KRADConstants.MAINTENANCE_EDIT_ACTION.equals( 477 ((MaintenanceDocumentForm) viewModel).getMaintenanceAction()) 478 && !(addLine instanceof Note) 479 && !(addLine instanceof AdHocRoutePerson) 480 && !(addLine instanceof AdHocRouteWorkgroup)) { 481 MaintenanceDocumentForm maintenanceForm = (MaintenanceDocumentForm) viewModel; 482 MaintenanceDocument document = maintenanceForm.getDocument(); 483 484 BindingInfo bindingInfo = (BindingInfo) viewModel.getViewPostMetadata().getComponentPostData(collectionId, 485 UifConstants.PostMetadata.BINDING_INFO); 486 487 // get the old object's collection 488 //KULRICE-7970 support multiple level objects 489 String bindingPrefix = bindingInfo.getBindByNamePrefix(); 490 String propertyPath = bindingInfo.getBindingName(); 491 if (bindingPrefix != "" && bindingPrefix != null) { 492 propertyPath = bindingPrefix + "." + propertyPath; 493 } 494 495 Collection<Object> oldCollection = ObjectPropertyUtils.getPropertyValue( 496 document.getOldMaintainableObject().getDataObject(), propertyPath); 497 498 Class<?> collectionObjectClass = (Class<?>) viewModel.getViewPostMetadata().getComponentPostData(collectionId, 499 UifConstants.PostMetadata.COLL_OBJECT_CLASS); 500 501 try { 502 Object blankLine = collectionObjectClass.newInstance(); 503 //Add a blank line to the top of the collection 504 if (oldCollection instanceof List) { 505 ((List<Object>) oldCollection).add(0, blankLine); 506 } else { 507 oldCollection.add(blankLine); 508 } 509 } catch (Exception e) { 510 throw new RuntimeException("Unable to create new line instance for old maintenance object", e); 511 } 512 } 513 } 514 515 /** 516 * In the case of edit maintenance deleted the item on the old side. 517 * 518 * @see org.kuali.rice.krad.uif.service.impl.ViewHelperServiceImpl#processAfterDeleteLine(org.kuali.rice.krad.uif.view.ViewModel, String, String, int) 519 */ 520 @Override 521 public void processAfterDeleteLine(ViewModel model, String collectionId, String collectionPath, int lineIndex) { 522 super.processAfterDeleteLine(model, collectionId, collectionPath, lineIndex); 523 524 Class<?> collectionObjectClass = (Class<?>) model.getViewPostMetadata().getComponentPostData(collectionId, 525 UifConstants.PostMetadata.COLL_OBJECT_CLASS); 526 527 // Check for maintenance documents in edit but exclude notes and ad hoc recipients 528 if (model instanceof MaintenanceDocumentForm 529 && KRADConstants.MAINTENANCE_EDIT_ACTION.equals(((MaintenanceDocumentForm)model).getMaintenanceAction()) 530 && !collectionObjectClass.getName().equals(Note.class.getName()) 531 && !collectionObjectClass.getName().equals(AdHocRoutePerson.class.getName()) 532 && !collectionObjectClass.getName().equals(AdHocRouteWorkgroup.class.getName())) { 533 MaintenanceDocumentForm maintenanceForm = (MaintenanceDocumentForm) model; 534 MaintenanceDocument document = maintenanceForm.getDocument(); 535 536 BindingInfo bindingInfo = (BindingInfo) model.getViewPostMetadata().getComponentPostData(collectionId, 537 UifConstants.PostMetadata.BINDING_INFO); 538 539 // get the old object's collection 540 //KULRICE-7970 support multiple level objects 541 String bindingPrefix = bindingInfo.getBindByNamePrefix(); 542 String propertyPath = bindingInfo.getBindingName(); 543 if (bindingPrefix != "" && bindingPrefix != null) { 544 propertyPath = bindingPrefix + "." + propertyPath; 545 } 546 547 Collection<Object> oldCollection = ObjectPropertyUtils.getPropertyValue( 548 document.getOldMaintainableObject().getDataObject(), propertyPath); 549 550 try { 551 // Remove the object at lineIndex from the collection 552 oldCollection.remove(oldCollection.toArray()[lineIndex]); 553 } catch (Exception e) { 554 throw new RuntimeException("Unable to delete line instance for old maintenance object", e); 555 } 556 } 557 } 558 559 @Override 560 protected boolean performAddLineValidation(ViewModel viewModel, Object newLine, String collectionId, 561 String collectionPath) { 562 boolean isValidLine = super.performAddLineValidation(viewModel, newLine, collectionId, collectionPath); 563 564 BindingInfo bindingInfo = (BindingInfo) viewModel.getViewPostMetadata().getComponentPostData(collectionId, 565 UifConstants.PostMetadata.BINDING_INFO); 566 567 if (viewModel instanceof MaintenanceDocumentForm) { 568 MaintenanceDocumentForm form = ((MaintenanceDocumentForm) viewModel); 569 isValidLine &= getKualiRuleService() 570 .applyRules(new AddCollectionLineEvent(form.getDocument(), bindingInfo.getBindingName(), newLine)); 571 } 572 573 return isValidLine; 574 } 575 576 /** 577 * Retrieves the document number configured on this maintainable. 578 * 579 * @return String document number 580 */ 581 protected String getDocumentNumber() { 582 return this.documentNumber; 583 } 584 585 586 587 /** 588 * Hook for service overrides to perform custom apply model logic on the component. 589 * 590 * @param element element instance to apply model to 591 * @param model Top level object containing the data (could be the model or a top level business 592 * object, dto) 593 */ 594 @Override 595 public void performCustomApplyModel(LifecycleElement element, Object model) { 596 597 MaintenanceDocumentForm form = (MaintenanceDocumentForm) model; 598 599 /** 600 * Primary keys should not be editable on maintenance edit action. 601 * 602 * Determines if the maintenance action matches MAINTENANCE_EDIT_ACTION, that the element is of type InputField and 603 * if the bindingPath includes a new maintainable path 604 */ 605 if (KRADConstants.MAINTENANCE_EDIT_ACTION.equals(form.getMaintenanceAction()) && element instanceof InputField 606 && StringUtils.contains(((InputField) element).getName(), KRADConstants.MAINTENANCE_NEW_MAINTAINABLE) 607 && !(StringUtils.contains(((InputField) element).getName(), UifPropertyPaths.NEW_COLLECTION_LINES))) { 608 setPrimaryKeyReadOnly(element); 609 610 } 611 } 612 613 /** 614 * sets primary keys to read-only 615 */ 616 private void setPrimaryKeyReadOnly(LifecycleElement element){ 617 618 String propertyName = ((InputField) element).getPropertyName(); 619 MaintenanceDocumentView maintenanceView = (MaintenanceDocumentView) ViewLifecycle.getView(); 620 621 /** 622 * get a list of primary keys from the maintenance view dataObject 623 */ 624 List<String> primaryKeys = KRADServiceLocatorWeb.getLegacyDataAdapter().listPrimaryKeyFieldNames(maintenanceView.getDataObjectClassName()); 625 626 /** 627 * loop thru primary keys, match to our component field name and set it to read-only 628 */ 629 for (String field : primaryKeys) { 630 if(propertyName.equals(field)){ 631 ((InputField) element).setReadOnly(true); 632 633 } 634 } 635 } 636 637 /** 638 * For the copy action, clears out primary key values, applies defaults to previously cleared fields, 639 * and replaces any new fields that the current user is unauthorized for with default values in the old record. 640 * 641 * {@inheritDoc} 642 */ 643 @Override 644 public void performCustomFinalize(LifecycleElement element, Object model, LifecycleElement parent) { 645 if (!(model instanceof MaintenanceDocumentForm)) { 646 return; 647 } 648 649 MaintenanceDocumentForm form = (MaintenanceDocumentForm) model; 650 651 if (form.getDocument().isFieldsClearedOnCopy()) { 652 return; 653 } 654 655 if (KRADConstants.MAINTENANCE_COPY_ACTION.equals(form.getMaintenanceAction())) { 656 View view = ViewLifecycle.getView(); 657 658 if (element instanceof DataField) { 659 DataField field = (DataField) element; 660 661 applyDefaultValuesForPreviouslyClearedFields(view, form, field); 662 663 clearUnauthorizedField(view, form, field); 664 } else if (element instanceof CollectionGroup) { 665 CollectionGroup group = (CollectionGroup) element; 666 667 clearUnauthorizedLine(view, form, group); 668 } 669 } 670 } 671 672 /** 673 * For the copy action, runs the custom processing after the copy and sets the indicator that fields have been 674 * copied as true. 675 * 676 * {@inheritDoc} 677 */ 678 @Override 679 public void performCustomViewFinalize(Object model) { 680 if (!(model instanceof MaintenanceDocumentForm)) { 681 return; 682 } 683 684 MaintenanceDocumentForm form = (MaintenanceDocumentForm) model; 685 686 if (KRADConstants.MAINTENANCE_COPY_ACTION.equals(form.getMaintenanceAction())) { 687 processAfterCopy(form.getDocument(), form.getInitialRequestParameters()); 688 689 form.getDocument().setFieldsClearedOnCopy(true); 690 } 691 } 692 693 /** 694 * Applies the default value of a field if it was a field that was previously cleared. 695 * 696 * @param view view instance that contains the fields being checked 697 * @param model model instance that contains the fields being checked 698 * @param field field being checked to see if it has been cleared 699 */ 700 private void applyDefaultValuesForPreviouslyClearedFields(View view, ViewModel model, DataField field) { 701 List<String> clearValueOnCopyPropertyNames = 702 KRADServiceLocatorWeb.getDocumentDictionaryService().getClearValueOnCopyPropertyNames( 703 ((MaintenanceDocumentView) view).getDataObjectClassName()); 704 705 for (String clearValueOnCopyPropertyName : clearValueOnCopyPropertyNames) { 706 if (field.getPropertyName().equalsIgnoreCase(clearValueOnCopyPropertyName)) { 707 String bindingPath = field.getBindingInfo().getBindingPath(); 708 709 view.getViewHelperService().populateDefaultValueForField(model, field, bindingPath); 710 } 711 } 712 } 713 714 /** 715 * Determines if the current field is restricted and replaces its value with a default value if so. 716 * This method should only be called on a copy operation as it checks for canCopyOnReadOnly 717 * 718 * @param view view instance that contains the fields being checked 719 * @param model model instance that contains the fields being checked 720 * @param field field being checked for restrictions 721 */ 722 private void clearUnauthorizedField(View view, ViewModel model, DataField field) { 723 ViewHelperService helper = ViewLifecycle.getHelper(); 724 String bindingPath = field.getBindingInfo().getBindingPath(); 725 726 if (StringUtils.contains(bindingPath, KRADConstants.MAINTENANCE_NEW_MAINTAINABLE)) { 727 // The field is restricted if it is hidden or read only or masked 728 boolean isRestricted = field.isHidden() || (Boolean.TRUE.equals(field.getReadOnly()) && !Boolean.TRUE.equals(field.getCanCopyOnReadOnly())) || field.isApplyMask(); 729 730 // If the default value is a sequence number set isRestricted to false since the new sequence number has 731 // already been retrieved. We don't want to set it to null and fetch it again. 732 Map<String, String> propertyExpressions = field.getPropertyExpressions(); 733 if (isRestricted && propertyExpressions.containsKey(UifConstants.ComponentProperties.DEFAULT_VALUE)) { 734 String propertyExpression = propertyExpressions.get(UifConstants.ComponentProperties.DEFAULT_VALUE); 735 if (StringUtils.contains(propertyExpression, UifConstants.SEQUENCE_PREFIX)) { 736 isRestricted = false; 737 } 738 } 739 740 // If just the field (not its containing line) is restricted, clear it out and apply default values 741 if (isRestricted && !isLineRestricted(field)) { 742 if (ObjectPropertyUtils.isWritableProperty(model, bindingPath)) { 743 ObjectPropertyUtils.setPropertyValue(model, bindingPath, null); 744 } 745 746 field.setReadOnlyDisplaySuffixPropertyName(null); 747 field.setReadOnlyDisplaySuffix(null); 748 749 helper.populateDefaultValueForField(model, field, bindingPath); 750 } 751 } 752 } 753 754 /** 755 * Returns whether a line that contains a field is restricted; that is, if the field is part of a group and that 756 * group has some unauthorized binding information. 757 * 758 * @param field field being checked for restrictions 759 * 760 * @return true if the field is in a line with restrictions, false otherwise 761 */ 762 private boolean isLineRestricted(DataField field) { 763 CollectionGroup group = (CollectionGroup) MapUtils.getObject(field.getContext(), 764 UifConstants.ContextVariableNames.COLLECTION_GROUP); 765 766 return group != null && CollectionUtils.isNotEmpty(group.getUnauthorizedLineBindingInfos()); 767 } 768 769 /** 770 * Determines if the current group contains restricted lines and clears them if so. 771 * This method should only be called on a copy operation as it checks for canCopyOnReadOnly 772 * 773 * @param view view instance that contains the group being checked 774 * @param model model instance that contains the group being checked 775 * @param group group being checked for restrictions 776 */ 777 private void clearUnauthorizedLine(View view, ViewModel model, CollectionGroup group) { 778 String bindingPath = group.getBindingInfo().getBindingPath(); 779 780 if (StringUtils.contains(bindingPath, KRADConstants.MAINTENANCE_NEW_MAINTAINABLE)) { 781 // A line is restricted if it is hidden or read only 782 if (group.getUnauthorizedLineBindingInfos() != null && !group.getCanCopyOnReadOnly()) { 783 Collection<Object> collection = ObjectPropertyUtils.getPropertyValue(model, bindingPath); 784 785 // If any lines are restricted, clear them out 786 for (BindingInfo bindingInfo : group.getUnauthorizedLineBindingInfos()) { 787 String lineBindingPath = bindingInfo.getBindingPath(); 788 Object line = ObjectPropertyUtils.getPropertyValue(model, lineBindingPath); 789 790 collection.remove(line); 791 } 792 } 793 } 794 } 795 796 @Override 797 @Deprecated // KNS Service 798 protected LegacyDataAdapter getLegacyDataAdapter() { 799 if (legacyDataAdapter == null) { 800 legacyDataAdapter = KRADServiceLocatorWeb.getLegacyDataAdapter(); 801 } 802 return this.legacyDataAdapter; 803 } 804 805 @Override 806 @Deprecated // KNS Service 807 public void setLegacyDataAdapter(LegacyDataAdapter legacyDataAdapter) { 808 this.legacyDataAdapter = legacyDataAdapter; 809 } 810 811 protected DataObjectAuthorizationService getDataObjectAuthorizationService() { 812 if (dataObjectAuthorizationService == null) { 813 this.dataObjectAuthorizationService = KRADServiceLocatorWeb.getDataObjectAuthorizationService(); 814 } 815 return dataObjectAuthorizationService; 816 } 817 818 public void setDataObjectAuthorizationService(DataObjectAuthorizationService dataObjectAuthorizationService) { 819 this.dataObjectAuthorizationService = dataObjectAuthorizationService; 820 } 821 822 public DocumentDictionaryService getDocumentDictionaryService() { 823 if (documentDictionaryService == null) { 824 this.documentDictionaryService = KRADServiceLocatorWeb.getDocumentDictionaryService(); 825 } 826 return documentDictionaryService; 827 } 828 829 public void setDocumentDictionaryService(DocumentDictionaryService documentDictionaryService) { 830 this.documentDictionaryService = documentDictionaryService; 831 } 832 833 protected EncryptionService getEncryptionService() { 834 if (encryptionService == null) { 835 encryptionService = CoreApiServiceLocator.getEncryptionService(); 836 } 837 return encryptionService; 838 } 839 840 public void setEncryptionService(EncryptionService encryptionService) { 841 this.encryptionService = encryptionService; 842 } 843 844 @Override 845 protected DataObjectService getDataObjectService() { 846 if (dataObjectService == null) { 847 dataObjectService = KRADServiceLocator.getDataObjectService(); 848 } 849 return dataObjectService; 850 } 851 852 protected MaintenanceDocumentService getMaintenanceDocumentService() { 853 if (maintenanceDocumentService == null) { 854 maintenanceDocumentService = KRADServiceLocatorWeb.getMaintenanceDocumentService(); 855 } 856 return maintenanceDocumentService; 857 } 858 859 public void setMaintenanceDocumentService(MaintenanceDocumentService maintenanceDocumentService) { 860 this.maintenanceDocumentService = maintenanceDocumentService; 861 } 862 863 public KualiRuleService getKualiRuleService() { 864 if (kualiRuleService == null) { 865 kualiRuleService = KRADServiceLocatorWeb.getKualiRuleService(); 866 } 867 return kualiRuleService; 868 } 869 870 public void setKualiRuleService(KualiRuleService kualiRuleService) { 871 this.kualiRuleService = kualiRuleService; 872 } 873 874 @Override 875 public Object getPersistableBusinessObject() { 876 return getDataObject(); 877 } 878}