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.kns.web.ui; 017 018import org.apache.commons.beanutils.PropertyUtils; 019import org.apache.commons.lang.StringUtils; 020import org.kuali.rice.core.api.mo.common.active.Inactivatable; 021import org.kuali.rice.kns.datadictionary.CollectionDefinitionI; 022import org.kuali.rice.kns.datadictionary.FieldDefinition; 023import org.kuali.rice.kns.datadictionary.FieldDefinitionI; 024import org.kuali.rice.kns.datadictionary.InquiryCollectionDefinition; 025import org.kuali.rice.kns.datadictionary.InquirySectionDefinition; 026import org.kuali.rice.kns.datadictionary.InquirySubSectionHeaderDefinition; 027import org.kuali.rice.kns.datadictionary.MaintainableCollectionDefinition; 028import org.kuali.rice.kns.datadictionary.MaintainableFieldDefinition; 029import org.kuali.rice.kns.datadictionary.MaintainableItemDefinition; 030import org.kuali.rice.kns.datadictionary.MaintainableSectionDefinition; 031import org.kuali.rice.kns.datadictionary.MaintainableSubSectionHeaderDefinition; 032import org.kuali.rice.kns.datadictionary.SubSectionHeaderDefinitionI; 033import org.kuali.rice.kns.document.authorization.FieldRestriction; 034import org.kuali.rice.kns.inquiry.Inquirable; 035import org.kuali.rice.kns.inquiry.InquiryRestrictions; 036import org.kuali.rice.kns.lookup.LookupUtils; 037import org.kuali.rice.kns.maintenance.Maintainable; 038import org.kuali.rice.kns.service.BusinessObjectAuthorizationService; 039import org.kuali.rice.kns.service.KNSServiceLocator; 040import org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService; 041import org.kuali.rice.kns.util.FieldUtils; 042import org.kuali.rice.kns.util.KNSConstants; 043import org.kuali.rice.kns.util.MaintenanceUtils; 044import org.kuali.rice.kns.util.WebUtils; 045import org.kuali.rice.krad.bo.BusinessObject; 046import org.kuali.rice.krad.bo.PersistableBusinessObject; 047import org.kuali.rice.krad.datadictionary.mask.MaskFormatter; 048import org.kuali.rice.krad.exception.ClassNotPersistableException; 049import org.kuali.rice.krad.service.DataDictionaryService; 050import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 051import org.kuali.rice.krad.util.KRADConstants; 052import org.kuali.rice.krad.util.ObjectUtils; 053 054import java.util.ArrayList; 055import java.util.Collection; 056import java.util.HashMap; 057import java.util.HashSet; 058import java.util.Iterator; 059import java.util.List; 060import java.util.Map; 061import java.util.Set; 062 063@Deprecated 064public class SectionBridge { 065 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(SectionBridge.class); 066 private static BusinessObjectAuthorizationService businessObjectAuthorizationService; 067 private static BusinessObjectAuthorizationService getBusinessObjectAuthorizationService() { 068 if (businessObjectAuthorizationService == null) { 069 businessObjectAuthorizationService = KNSServiceLocator.getBusinessObjectAuthorizationService(); 070 } 071 return businessObjectAuthorizationService; 072 } 073 private static DataDictionaryService dataDictionaryService; 074 private static DataDictionaryService getDataDictionaryService() { 075 if (dataDictionaryService == null) { 076 dataDictionaryService = KRADServiceLocatorWeb.getDataDictionaryService(); 077 } 078 return dataDictionaryService; 079 } 080 private static MaintenanceDocumentDictionaryService maintenanceDocumentDictionaryService; 081 082 /** 083 * This method creates a Section for display on an Inquiry Screen. 084 * 085 * @param sd The DD definition from which to construct the Section. 086 * @param o The BusinessObject from which to populate the Section values. 087 * @return A populated Section. 088 */ 089 public static final Section toSection(Inquirable inquirable, InquirySectionDefinition sd, BusinessObject o, InquiryRestrictions auths) { 090 Section section = new Section(); 091 section.setSectionId( sd.getId() ); 092 section.setSectionTitle(sd.getTitle()); 093 section.setRows(new ArrayList()); 094 section.setDefaultOpen(sd.isDefaultOpen()); 095 096 if (sd.getNumberOfColumns() != null) { 097 section.setNumberOfColumns(sd.getNumberOfColumns()); 098 } 099 else { 100 section.setNumberOfColumns(KRADConstants.DEFAULT_NUM_OF_COLUMNS); 101 } 102 103 List<Field> sectionFields = new ArrayList(); 104 for (FieldDefinition fieldDefinition : sd.getInquiryFields()) { 105 List row = new ArrayList(); 106 107 Field f = null; 108 if (fieldDefinition instanceof InquiryCollectionDefinition) { 109 InquiryCollectionDefinition inquiryCollectionDefinition = (InquiryCollectionDefinition) fieldDefinition; 110 111 List<Row> sectionRows = new ArrayList(); 112 sectionRows = getContainerRows(section, inquiryCollectionDefinition, o, null, null, new ArrayList(), new HashSet<String>(), new StringBuffer(section.getErrorKey()), inquiryCollectionDefinition.getNumberOfColumns(), inquirable); 113 section.setRows(sectionRows); 114 } 115 else if (fieldDefinition instanceof InquirySubSectionHeaderDefinition) { 116 f = createMaintainableSubSectionHeader((InquirySubSectionHeaderDefinition) fieldDefinition); 117 } 118 else { 119 f = FieldBridge.toField(fieldDefinition, o, section); 120 } 121 122 if (null != f) { 123 sectionFields.add(f); 124 } 125 126 } 127 128 if (!sectionFields.isEmpty()) { 129 section.setRows(FieldUtils.wrapFields(sectionFields, section.getNumberOfColumns())); 130 } 131 132 applyInquirySectionAuthorizations(section, auths); 133 134 section.setRows(reArrangeRows(section.getRows(), section.getNumberOfColumns())); 135 136 return section; 137 } 138 139 140 private static final void applyInquirySectionAuthorizations(Section section, InquiryRestrictions inquiryRestrictions) { 141 applyInquiryRowsAuthorizations(section.getRows(), inquiryRestrictions); 142 } 143 144 private static final void applyInquiryRowsAuthorizations(List<Row> rows, InquiryRestrictions inquiryRestrictions) { 145 for (Row row : rows) { 146 List<Field> rowFields = row.getFields(); 147 for (Field field : rowFields) { 148 applyInquiryFieldAuthorizations(field, inquiryRestrictions); 149 } 150 } 151 } 152 153 protected static final void applyInquiryFieldAuthorizations(Field field, InquiryRestrictions inquiryRestrictions) { 154 if (Field.CONTAINER.equals(field.getFieldType())) { 155 applyInquiryRowsAuthorizations(field.getContainerRows(), inquiryRestrictions); 156 field.setContainerRows(reArrangeRows(field.getContainerRows(), field.getNumberOfColumnsForCollection())); 157 } 158 else if (!Field.IMAGE_SUBMIT.equals(field.getFieldType())) { 159 FieldRestriction fieldRestriction = inquiryRestrictions.getFieldRestriction(field.getPropertyName()); 160 if (fieldRestriction.isHidden()) { 161 field.setFieldType(Field.HIDDEN); 162 field.setPropertyValue(null); 163 } 164 // KULRICE-8271: partially masked field can't be masked properly 165 else if (fieldRestriction.isMasked() || fieldRestriction.isPartiallyMasked()) { 166 field.setSecure(true); 167 MaskFormatter maskFormatter = fieldRestriction.getMaskFormatter(); 168 String displayMaskValue = maskFormatter.maskValue(field.getPropertyValue()); 169 field.setDisplayMaskValue(displayMaskValue); 170 // since it's an inquiry, let's wipe out the encrypted field value since we don't need to post it back 171 field.setEncryptedValue(""); 172 } 173 } 174 } 175 176 //This method is used to remove hidden fields (To fix JIRA KFSMI-2449) 177 private static final List<Row> reArrangeRows(List<Row> rows, int numberOfColumns){ 178 List<Row> rearrangedRows = new ArrayList<Row>(); 179 180 for (Row row : rows) { 181 List<Field> fields = new ArrayList<Field>(); 182 List<Field> rowFields = row.getFields(); 183 for (Field field : rowFields) { 184 if(!Field.HIDDEN.equals(field.getFieldType()) && !Field.BLANK_SPACE.equals(field.getFieldType())){ 185 fields.add(field); 186 } 187 } 188 List<Row> rewrappedFieldRows = FieldUtils.wrapFields(fields, numberOfColumns); 189 if (row.isHidden()) { 190 for (Row rewrappedRow : rewrappedFieldRows) { 191 rewrappedRow.setHidden(true); 192 } 193 } 194 rearrangedRows.addAll(rewrappedFieldRows); 195 } 196 197 return rearrangedRows; 198 } 199 200 201 /** 202 * This method creates a Section for a MaintenanceDocument. 203 * 204 * @param sd The DD definition of the Section. 205 * @param o The BusinessObject from which the Section will be populated. 206 * @param maintainable 207 * @param maintenanceAction The action (new, newwithexisting, copy, edit, etc) requested from the UI. 208 * @param autoFillDefaultValues Should default values be auto-filled? 209 * @param autoFillBlankRequiredValues Should required values left blank on the UI be auto-filled? 210 * @param displayedFieldNames What fields are displayed on the UI? 211 * @return A populated Section. 212 * @throws InstantiationException 213 * @throws IllegalAccessException 214 */ 215 public static final Section toSection(MaintainableSectionDefinition sd, BusinessObject o, Maintainable maintainable, Maintainable oldMaintainable, String maintenanceAction, List<String> displayedFieldNames, Set<String> conditionallyRequiredMaintenanceFields) throws InstantiationException, IllegalAccessException { 216 Section section = new Section(); 217 218 section.setSectionId( sd.getId() ); 219 section.setSectionTitle(sd.getTitle()); 220 section.setSectionClass(o.getClass()); 221 section.setHidden( sd.isHidden() ); 222 section.setDefaultOpen(sd.isDefaultOpen()); 223 section.setHelpUrl(sd.getHelpUrl()); 224 225 // iterate through section maint items and contruct Field UI objects 226 Collection<MaintainableItemDefinition> maintItems = sd.getMaintainableItems(); 227 List<Row> sectionRows = new ArrayList<Row>(); 228 List<Field> sectionFields = new ArrayList<Field>(); 229 230 for (MaintainableItemDefinition maintItem : maintItems) { 231 Field field = FieldBridge.toField(maintItem, sd, o, maintainable, section, displayedFieldNames, conditionallyRequiredMaintenanceFields); 232 boolean skipAdd = false; 233 234 // if CollectionDefiniton, then have a many section 235 if (maintItem instanceof MaintainableCollectionDefinition) { 236 MaintainableCollectionDefinition definition = (MaintainableCollectionDefinition) maintItem; 237 section.getContainedCollectionNames().add(maintItem.getName()); 238 239 StringBuffer containerRowErrorKey = new StringBuffer(); 240 sectionRows = getContainerRows(section, definition, o, maintainable, oldMaintainable, displayedFieldNames, conditionallyRequiredMaintenanceFields, containerRowErrorKey, KRADConstants.DEFAULT_NUM_OF_COLUMNS, null); 241 } else if (maintItem instanceof MaintainableSubSectionHeaderDefinition) { 242 MaintainableSubSectionHeaderDefinition definition = (MaintainableSubSectionHeaderDefinition) maintItem; 243 field = createMaintainableSubSectionHeader(definition); 244 } 245 246 if (!skipAdd) { 247 sectionFields.add(field); 248 } 249 } 250 251 // populate field values from business object 252 //if (o != null && !autoFillDefaultValues) { 253 if (o != null) { 254 sectionFields = FieldUtils.populateFieldsFromBusinessObject(sectionFields, o); 255 256 /* if maintenance action is copy, clear out secure fields */ 257 if (KRADConstants.MAINTENANCE_COPY_ACTION.equals(maintenanceAction)) { 258 for (Iterator iterator = sectionFields.iterator(); iterator.hasNext();) { 259 Field element = (Field) iterator.next(); 260 if (element.isSecure()) { 261 element.setPropertyValue(""); 262 } 263 } 264 } 265 } 266 267 sectionRows.addAll(FieldUtils.wrapFields(sectionFields)); 268 section.setRows(sectionRows); 269 270 return section; 271 272 } 273 274 275 /** 276 * @see #getContainerRows(Section, CollectionDefinitionI, BusinessObject, Maintainable, List<String>, StringBuffer, String, 277 * boolean, int) 278 */ 279 public static final List<Row> getContainerRows(Section s, CollectionDefinitionI collectionDefinition, BusinessObject o, Maintainable m, Maintainable oldMaintainable, List<String> displayedFieldNames, Set<String> conditionallyRequiredMaintenanceFields, StringBuffer containerRowErrorKey, int numberOfColumns, Inquirable inquirable) { 280 return getContainerRows(s, collectionDefinition, o, m, oldMaintainable, displayedFieldNames, conditionallyRequiredMaintenanceFields, containerRowErrorKey, "", false, numberOfColumns, inquirable); 281 } 282 283 /** 284 * Builds a list of Rows with Fields of type containers for a many section. 285 * 286 * @param s The Section containing the Collection/Container. 287 * @param collectionDefinition The DD definition of the Collection. 288 * @param o The BusinessObject from which the Container/Collection will be populated. 289 * @param m The Maintainable for the BO (needed by some methods called on FieldBridge, FieldUtils etc.) 290 * @param displayedFieldNames 291 * @param containerRowErrorKey The error key for the Container/Collection. 292 * @param parents 293 * @param hideAdd Should the add line be added to the Container/Collection? 294 * @param numberOfColumns In how many columns in the UI will the fields in the Container/Collection be shown? 295 * @return 296 */ 297 public static final List<Row> getContainerRows(Section s, CollectionDefinitionI collectionDefinition, BusinessObject o, Maintainable m, Maintainable oldMaintainable, List<String> displayedFieldNames, Set<String> conditionallyRequiredMaintenanceFields, StringBuffer containerRowErrorKey, String parents, boolean hideAdd, int numberOfColumns, Inquirable inquirable) { 298 List<Row> containerRows = new ArrayList<Row>(); 299 List<Field> collFields = new ArrayList<Field>(); 300 301 String collectionName = collectionDefinition.getName(); 302 303 // add the toggle inactive record display button for the collection 304 if (m != null && Inactivatable.class.isAssignableFrom(collectionDefinition.getBusinessObjectClass()) && StringUtils.isBlank(parents)) { 305 addShowInactiveButtonField(s, collectionName, !m.getShowInactiveRecords(collectionName)); 306 } 307 if (inquirable != null && Inactivatable.class.isAssignableFrom(collectionDefinition.getBusinessObjectClass()) && StringUtils.isBlank(parents)) { 308 addShowInactiveButtonField(s, collectionName, !inquirable.getShowInactiveRecords(collectionName)); 309 } 310 311 // first need to populate the containerRows with the "new" form if available 312 if (!hideAdd) { 313 List<Field> newFormFields = new ArrayList<Field>(); 314 if (collectionDefinition.getIncludeAddLine()) { 315 316 317 newFormFields = FieldBridge.getNewFormFields(collectionDefinition, o, m, displayedFieldNames, conditionallyRequiredMaintenanceFields, containerRowErrorKey, parents, hideAdd, numberOfColumns); 318 319 320 } else if(collectionDefinition instanceof MaintainableCollectionDefinition) { 321 MaintainableCollectionDefinition mcd = (MaintainableCollectionDefinition)collectionDefinition; 322 if(FieldUtils.isCollectionMultipleLookupEnabled(mcd)) { 323 //do just the top line for collection if add is not allowed 324 newFormFields = FieldBridge.constructContainerField(collectionDefinition, parents, o, hideAdd, numberOfColumns, mcd.getName(), new ArrayList<Field>()); 325 } 326 } 327 if (null != newFormFields) { 328 containerRows.add(new Row(newFormFields)); 329 } 330 } 331 332 Collection<? extends CollectionDefinitionI> collections = collectionDefinition.getCollections(); 333 for (CollectionDefinitionI collection : collections) { 334 int subCollectionNumberOfColumn = numberOfColumns; 335 if (collectionDefinition instanceof InquiryCollectionDefinition) { 336 InquiryCollectionDefinition icd = (InquiryCollectionDefinition) collection; 337 if (icd.getNumberOfColumns() != null) { 338 subCollectionNumberOfColumn = icd.getNumberOfColumns(); 339 } 340 } 341 // no colNum for add rows 342 containerRows.addAll(getContainerRows(s, collection, o, m, oldMaintainable, displayedFieldNames, conditionallyRequiredMaintenanceFields, containerRowErrorKey, parents + collectionDefinition.getName() + ".", true, subCollectionNumberOfColumn, inquirable)); 343 } 344 345 // then we need to loop through the existing collection and add those fields 346 Collection<? extends FieldDefinitionI> collectionFields = collectionDefinition.getFields(); 347 // get label for collection 348 String collectionLabel = getDataDictionaryService().getCollectionLabel(o.getClass(), collectionDefinition.getName()); 349 350 // retrieve the summary label either from the override or from the DD 351 String collectionElementLabel = collectionDefinition.getSummaryTitle(); 352 if (StringUtils.isEmpty(collectionElementLabel)) { 353 collectionElementLabel = getDataDictionaryService().getCollectionElementLabel(o.getClass().getName(), collectionDefinition.getName(), collectionDefinition.getBusinessObjectClass()); 354 } 355 356 boolean translateCodes = getMaintenanceDocumentDictionaryService().translateCodes(o.getClass()); 357 358 if (o != null) { 359 if (PropertyUtils.isWriteable(o, collectionDefinition.getName()) && ObjectUtils.getPropertyValue(o, collectionDefinition.getName()) != null) { 360 Object obj = ObjectUtils.getPropertyValue(o, collectionName); 361 362 Object oldObj = null; 363 if (oldMaintainable != null && oldMaintainable.getBusinessObject() != null) { 364 oldObj = ObjectUtils.getPropertyValue(oldMaintainable.getBusinessObject(), collectionName); 365 } 366 367 if (obj instanceof List) { 368 Map summaryFields = new HashMap(); 369 boolean hidableRowsPresent = false; 370 for (int i = 0; i < ((List) obj).size(); i++) { 371 BusinessObject lineBusinessObject = (BusinessObject) ((List) obj).get(i); 372 373 if (lineBusinessObject instanceof PersistableBusinessObject) { 374 ((PersistableBusinessObject) lineBusinessObject).refreshNonUpdateableReferences(); 375 } 376 377 /* 378 * Handle display of inactive records. The old maintainable is used to compare the old side (if it exists). If the row should not be displayed, it is set as 379 * hidden and will be handled in the maintenance rowDisplay.tag. 380 */ 381 boolean setRowHidden = false; 382 BusinessObject oldLineBusinessObject = null; 383 if (oldObj != null && ((List) oldObj).size() > i) { 384 oldLineBusinessObject = (BusinessObject) ((List) oldObj).get(i); 385 } 386 387 if (lineBusinessObject instanceof Inactivatable && !((Inactivatable) lineBusinessObject).isActive()) { 388 if (m != null) { 389 // rendering a maint doc 390 if (!hidableRowsPresent) { 391 hidableRowsPresent = isRowHideableForMaintenanceDocument(lineBusinessObject, oldLineBusinessObject); 392 } 393 setRowHidden = isRowHiddenForMaintenanceDocument(lineBusinessObject, oldLineBusinessObject, m, collectionName); 394 } 395 if (inquirable != null) { 396 // rendering an inquiry screen 397 if (!hidableRowsPresent) { 398 hidableRowsPresent = isRowHideableForInquiry(lineBusinessObject); 399 } 400 setRowHidden = isRowHiddenForInquiry(lineBusinessObject, inquirable, collectionName); 401 } 402 } 403 404 collFields = new ArrayList<Field>(); 405 List<String> duplicateIdentificationFieldNames = new ArrayList<String>(); 406 //We only need to do this if the collection definition is a maintainable collection definition, 407 //don't need it for inquiry collection definition. 408 if (collectionDefinition instanceof MaintainableCollectionDefinition) { 409 Collection<MaintainableFieldDefinition> duplicateFieldDefs = ((MaintainableCollectionDefinition)collectionDefinition).getDuplicateIdentificationFields(); 410 for (MaintainableFieldDefinition eachFieldDef : duplicateFieldDefs) { 411 duplicateIdentificationFieldNames.add(eachFieldDef.getName()); 412 } 413 } 414 415 for (FieldDefinitionI collectionField : collectionFields) { 416 417 // construct Field UI object from definition 418 Field collField = FieldUtils.getPropertyField(collectionDefinition.getBusinessObjectClass(), collectionField.getName(), false); 419 420 if (translateCodes) { 421 FieldUtils.setAdditionalDisplayPropertyForCodes(lineBusinessObject.getClass(), collField.getPropertyName(), collField); 422 } 423 424 FieldBridge.setupField(collField, collectionField, conditionallyRequiredMaintenanceFields); 425 setPrimaryKeyFieldsReadOnly(collectionDefinition.getBusinessObjectClass(), collField); 426 427 //If the duplicateIdentificationFields were specified in the maint. doc. DD, we'll need 428 //to set the fields to be read only as well, in addition to the primary key fields. 429 if (duplicateIdentificationFieldNames.size() > 0) { 430 setDuplicateIdentificationFieldsReadOnly(collField, duplicateIdentificationFieldNames); 431 } 432 433 FieldUtils.setInquiryURL(collField, lineBusinessObject, collectionField.getName()); 434 // save the simple property name 435 String name = collField.getPropertyName(); 436 437 // prefix name for multi line (indexed) 438 collField.setPropertyName(collectionDefinition.getName() + "[" + (new Integer(i)).toString() + "]." + collField.getPropertyName()); 439 440 // commenting out codes for sub-collections show/hide for now 441 // subCollField.setContainerName(collectionDefinition.getName() + "["+i+"]" +"." + 442 // subCollectionDefinition.getName() + "[" + j + "]"); 443 444 if (collectionField instanceof MaintainableFieldDefinition) { 445 MaintenanceUtils.setFieldQuickfinder(lineBusinessObject, collectionDefinition.getName(), false, i, name, collField, displayedFieldNames, m, (MaintainableFieldDefinition) collectionField); 446 MaintenanceUtils.setFieldDirectInquiry(lineBusinessObject, name, (MaintainableFieldDefinition) collectionField, collField, displayedFieldNames); 447 } else { 448 LookupUtils 449 .setFieldQuickfinder(lineBusinessObject, collectionDefinition.getName(), false, 450 i, name, collField, displayedFieldNames, m); 451 LookupUtils.setFieldDirectInquiry(lineBusinessObject, name, collField); 452 } 453 454 String propertyValue = ObjectUtils.getFormattedPropertyValueUsingDataDictionary(lineBusinessObject, collectionField.getName()); 455 // For files the FormFile is not being persisted instead the file data is stored in 456 // individual fields as defined by PersistableAttachment. However, newly added rows contain all data 457 // in the FormFile, so check if it's empty. 458 if (StringUtils.isBlank(propertyValue) && Field.FILE.equals(collField.getFieldType())) { 459 Object fileName = ObjectUtils.getNestedValue(lineBusinessObject, KRADConstants.BO_ATTACHMENT_FILE_NAME); 460 collField.setPropertyValue(fileName); 461 } else { 462 collField.setPropertyValue(propertyValue); 463 464 } 465 466 if (Field.FILE.equals(collField.getFieldType())) { 467 Object fileType = ObjectUtils.getNestedValue(lineBusinessObject, KRADConstants.BO_ATTACHMENT_FILE_CONTENT_TYPE); 468 if (fileType == null 469 && collField.getPropertyName().contains(".")) { 470 // fileType not found on bo, so check in the attachment field on bo 471 String tempName = collField.getPropertyName().substring(collField.getPropertyName().lastIndexOf('.')+1); 472 fileType = ObjectUtils.getNestedValue(lineBusinessObject, (tempName + "." + KRADConstants.BO_ATTACHMENT_FILE_CONTENT_TYPE)); 473 } 474 collField.setImageSrc(WebUtils.getAttachmentImageForUrl((String) fileType)); 475 } 476 477 if (StringUtils.isNotBlank(collField.getAlternateDisplayPropertyName())) { 478 String alternateDisplayPropertyValue = ObjectUtils.getFormattedPropertyValueUsingDataDictionary(lineBusinessObject, 479 collField.getAlternateDisplayPropertyName()); 480 collField.setAlternateDisplayPropertyValue(alternateDisplayPropertyValue); 481 } 482 483 if (StringUtils.isNotBlank(collField.getAdditionalDisplayPropertyName())) { 484 String additionalDisplayPropertyValue = ObjectUtils.getFormattedPropertyValueUsingDataDictionary(lineBusinessObject, 485 collField.getAdditionalDisplayPropertyName()); 486 collField.setAdditionalDisplayPropertyValue(additionalDisplayPropertyValue); 487 } 488 489 //update user fields with universal id and/or name 490 updateUserFields(collField, lineBusinessObject); 491 492 // the the field as read only (if appropriate) 493 if (collectionField.isReadOnlyAfterAdd()) { 494 collField.setReadOnly(true); 495 } 496 497 // check if this is a summary field 498 if (collectionDefinition.hasSummaryField(collectionField.getName())) { 499 summaryFields.put(collectionField.getName(), collField); 500 } 501 502 collFields.add(collField); 503 } 504 505 Field containerField; 506 containerField = FieldUtils.constructContainerField( 507 KRADConstants.EDIT_PREFIX + "[" + (new Integer(i)).toString() + "]", collectionLabel + " " + (i + 1), collFields, numberOfColumns); 508 // why is this only on collections and not subcollections any significance or just oversight? 509 containerField.setContainerName(collectionDefinition.getName() + "[" + (new Integer(i)).toString() + "]."); 510 511 /* If the collection line is pending (meaning added by this document) the isNewCollectionRecord will be set to true. In this 512 case we give an option to delete the line. The parameters for the delete action method are embedded into the button name. */ 513 if (lineBusinessObject instanceof PersistableBusinessObject && 514 (((PersistableBusinessObject) lineBusinessObject).isNewCollectionRecord() 515 || collectionDefinition.isAlwaysAllowCollectionDeletion())) { 516 containerField.getContainerRows().add(new Row(getDeleteRowButtonField(parents + collectionDefinition.getName(), (new Integer(i)).toString()))); 517 } 518 519 if (StringUtils.isNotEmpty(collectionElementLabel)) { 520 //We don't want to associate any indexes to the containerElementName anymore so that 521 //when the element is deleted, the currentTabIndex won't be associated with the 522 //wrong tab for the remaining tab. 523 //containerField.setContainerElementName(collectionElementLabel + " " + (i + 1)); 524 containerField.setContainerElementName(collectionElementLabel); 525 // reorder summaryFields to make sure they are in the order specified in the summary section 526 List orderedSummaryFields = getSummaryFields(summaryFields, collectionDefinition); 527 containerField.setContainerDisplayFields(orderedSummaryFields); 528 } 529 530 Row containerRow = new Row(containerField); 531 if (setRowHidden) { 532 containerRow.setHidden(true); 533 } 534 containerRows.add(containerRow); 535 536 537 538 Collection<? extends CollectionDefinitionI> subCollections = collectionDefinition.getCollections(); 539 List<Field> subCollFields = new ArrayList<Field>(); 540 541 summaryFields = new HashMap(); 542 // iterate over the subCollections directly on this collection 543 for (CollectionDefinitionI subCollection : subCollections) { 544 Collection<? extends FieldDefinitionI> subCollectionFields = subCollection.getFields(); 545 int subCollectionNumberOfColumns = numberOfColumns; 546 547 if (!s.getContainedCollectionNames().contains(collectionDefinition.getName() + "." + subCollection.getName())) { 548 s.getContainedCollectionNames().add(collectionDefinition.getName() + "." + subCollection.getName()); 549 } 550 551 if (subCollection instanceof InquiryCollectionDefinition) { 552 InquiryCollectionDefinition icd = (InquiryCollectionDefinition) subCollection; 553 if (icd.getNumberOfColumns() != null) { 554 subCollectionNumberOfColumns = icd.getNumberOfColumns(); 555 } 556 } 557 // get label for collection 558 String subCollectionLabel = getDataDictionaryService().getCollectionLabel(o.getClass(), subCollection.getName()); 559 560 // retrieve the summary label either from the override or from the DD 561 String subCollectionElementLabel = subCollection.getSummaryTitle(); 562 if (StringUtils.isEmpty(subCollectionElementLabel)) { 563 subCollectionElementLabel = getDataDictionaryService().getCollectionElementLabel(o.getClass().getName(), subCollection.getName(), subCollection.getBusinessObjectClass()); 564 } 565 // make sure it's really a collection (i.e. list) 566 567 String subCollectionName = subCollection.getName(); 568 Object subObj = ObjectUtils.getPropertyValue(lineBusinessObject, subCollectionName); 569 570 Object oldSubObj = null; 571 if (oldLineBusinessObject != null) { 572 oldSubObj = ObjectUtils.getPropertyValue(oldLineBusinessObject, subCollectionName); 573 } 574 575 if (subObj instanceof List) { 576 /* recursively call this method to get the add row and exisiting members of the subCollections subcollections containerRows.addAll(getContainerRows(subCollectionDefinition, 577 displayedFieldNames,containerRowErrorKey, parents+collectionDefinition.getName()+"["+i+"]"+".","[0]",false, subCollectionNumberOfColumn)); */ 578 containerField.getContainerRows().addAll(getContainerRows(s, subCollection, o, m, oldMaintainable, displayedFieldNames, conditionallyRequiredMaintenanceFields, containerRowErrorKey, parents + collectionDefinition.getName() + "[" + i + "]" + ".", false, subCollectionNumberOfColumns, inquirable)); 579 580 // iterate over the fields 581 for (int j = 0; j < ((List) subObj).size(); j++) { 582 BusinessObject lineSubBusinessObject = (BusinessObject) ((List) subObj).get(j); 583 584 if (lineSubBusinessObject instanceof PersistableBusinessObject) { 585 ((PersistableBusinessObject) lineSubBusinessObject).refreshNonUpdateableReferences(); 586 } 587 588 // determine if sub collection line is inactive and should be hidden 589 boolean setSubRowHidden = false; 590 if (lineSubBusinessObject instanceof Inactivatable && !((Inactivatable) lineSubBusinessObject).isActive()) { 591 if (oldSubObj != null) { 592 // get corresponding elements in both the new list and the old list 593 BusinessObject oldLineSubBusinessObject = (BusinessObject) ((List) oldSubObj).get(j); 594 if (m != null) { 595 if (!hidableRowsPresent) { 596 hidableRowsPresent = isRowHideableForMaintenanceDocument(lineSubBusinessObject, oldLineSubBusinessObject); 597 } 598 setSubRowHidden = isRowHiddenForMaintenanceDocument(lineSubBusinessObject, oldLineSubBusinessObject, m, collectionName); 599 } 600 } 601 if (inquirable != null) { 602 if (!hidableRowsPresent) { 603 hidableRowsPresent = isRowHideableForInquiry(lineSubBusinessObject); 604 } 605 setSubRowHidden = isRowHiddenForInquiry(lineSubBusinessObject, inquirable, collectionName); 606 } 607 } 608 609 610 subCollFields = new ArrayList<Field>(); 611 // construct field objects based on fields 612 for (FieldDefinitionI subCollectionField : subCollectionFields) { 613 614 // construct Field UI object from definition 615 Field subCollField = FieldUtils.getPropertyField(subCollection.getBusinessObjectClass(), subCollectionField.getName(), false); 616 617 String subCollectionFullName = collectionDefinition.getName() + "[" + i + "]" + "." + subCollection.getName(); 618 619 if (translateCodes) { 620 FieldUtils.setAdditionalDisplayPropertyForCodes(lineSubBusinessObject.getClass(), subCollField.getPropertyName(), subCollField); 621 } 622 623 FieldBridge.setupField(subCollField, subCollectionField, conditionallyRequiredMaintenanceFields); 624 setPrimaryKeyFieldsReadOnly(subCollection.getBusinessObjectClass(), subCollField); 625 626 // save the simple property name 627 String name = subCollField.getPropertyName(); 628 629 // prefix name for multi line (indexed) 630 subCollField.setPropertyName(subCollectionFullName + "[" + j + "]." + subCollField.getPropertyName()); 631 632 // commenting out codes for sub-collections show/hide for now 633 if (subCollectionField instanceof MaintainableFieldDefinition) { 634 MaintenanceUtils.setFieldQuickfinder(lineSubBusinessObject, subCollectionFullName, false, j, name, subCollField, displayedFieldNames, m, (MaintainableFieldDefinition) subCollectionField); 635 MaintenanceUtils 636 .setFieldDirectInquiry(lineSubBusinessObject, subCollectionFullName, 637 false, j, name, subCollField, displayedFieldNames, m, 638 (MaintainableFieldDefinition) subCollectionField); 639 } else { 640 LookupUtils.setFieldQuickfinder(lineSubBusinessObject, subCollectionFullName, false, j, name, subCollField, displayedFieldNames); 641 LookupUtils.setFieldDirectInquiry(lineBusinessObject, name, subCollField); 642 } 643 644 String propertyValue = ObjectUtils.getFormattedPropertyValueUsingDataDictionary(lineSubBusinessObject, subCollectionField.getName()); 645 subCollField.setPropertyValue(propertyValue); 646 647 if (StringUtils.isNotBlank(subCollField.getAlternateDisplayPropertyName())) { 648 String alternateDisplayPropertyValue = ObjectUtils.getFormattedPropertyValueUsingDataDictionary(lineSubBusinessObject, 649 subCollField.getAlternateDisplayPropertyName()); 650 subCollField.setAlternateDisplayPropertyValue(alternateDisplayPropertyValue); 651 } 652 653 if (StringUtils.isNotBlank(subCollField.getAdditionalDisplayPropertyName())) { 654 String additionalDisplayPropertyValue = ObjectUtils.getFormattedPropertyValueUsingDataDictionary(lineSubBusinessObject, 655 subCollField.getAdditionalDisplayPropertyName()); 656 subCollField.setAdditionalDisplayPropertyValue(additionalDisplayPropertyValue); 657 } 658 659 // check if this is a summary field 660 if (subCollection.hasSummaryField(subCollectionField.getName())) { 661 summaryFields.put(subCollectionField.getName(), subCollField); 662 } 663 664 if (subCollectionField.isReadOnlyAfterAdd()) { 665 subCollField.setReadOnly(true); 666 } 667 668 subCollFields.add(subCollField); 669 } 670 671 Field subContainerField = FieldUtils.constructContainerField( 672 KRADConstants.EDIT_PREFIX + "[" + (new Integer(j)).toString() + "]", subCollectionLabel, subCollFields); 673 if (lineSubBusinessObject instanceof PersistableBusinessObject && (((PersistableBusinessObject) lineSubBusinessObject).isNewCollectionRecord() || subCollection.isAlwaysAllowCollectionDeletion())) { 674 subContainerField.getContainerRows().add(new Row(getDeleteRowButtonField(parents + collectionDefinition.getName() + "[" + i + "]" + "." + subCollectionName, (new Integer(j)).toString()))); 675 } 676 677 // summary line code 678 if (StringUtils.isNotEmpty(subCollectionElementLabel)) { 679 //We don't want to associate any indexes to the containerElementName anymore so that 680 //when the element is deleted, the currentTabIndex won't be associated with the 681 //wrong tab for the remaining tab. 682 //subContainerField.setContainerElementName(subCollectionElementLabel + " " + (j + 1)); 683 subContainerField.setContainerElementName(collectionElementLabel + "-" + subCollectionElementLabel); 684 } 685 subContainerField.setContainerName(collectionDefinition.getName() + "." + subCollectionName); 686 if (!summaryFields.isEmpty()) { 687 // reorder summaryFields to make sure they are in the order specified in the summary section 688 List orderedSummaryFields = getSummaryFields(summaryFields, subCollection); 689 subContainerField.setContainerDisplayFields(orderedSummaryFields); 690 } 691 692 Row subContainerRow = new Row(subContainerField); 693 if (setRowHidden || setSubRowHidden) { 694 subContainerRow.setHidden(true); 695 } 696 containerField.getContainerRows().add(subContainerRow); 697 } 698 } 699 } 700 } 701 if ( !hidableRowsPresent ) { 702 s.setExtraButtonSource( "" ); 703 } 704 } 705 } 706 } 707 708 return containerRows; 709 } 710 711 /** 712 * Updates fields of type kualiuser sets the universal user id and/or name if required. 713 * 714 * @param field 715 * @param businessObject 716 */ 717 private static final void updateUserFields(Field field, BusinessObject businessObject){ 718 // for user fields, attempt to pull the principal ID and person's name from the source object 719 if ( field.getFieldType().equals(Field.KUALIUSER) ) { 720 // this is supplemental, so catch and log any errors 721 try { 722 if ( StringUtils.isNotBlank(field.getUniversalIdAttributeName()) ) { 723 Object principalId = ObjectUtils.getNestedValue(businessObject, field.getUniversalIdAttributeName()); 724 if ( principalId != null ) { 725 field.setUniversalIdValue(principalId.toString()); 726 } 727 } 728 if ( StringUtils.isNotBlank(field.getPersonNameAttributeName()) ) { 729 Object personName = ObjectUtils.getNestedValue(businessObject, field.getPersonNameAttributeName()); 730 if ( personName != null ) { 731 field.setPersonNameValue( personName.toString() ); 732 } 733 } 734 } catch ( Exception ex ) { 735 LOG.warn( "Unable to get principal ID or person name property in SectionBridge.", ex ); 736 } 737 } 738 } 739 740 /** 741 * Helper method to build up a Field containing a delete button mapped up to remove the collection record identified by the 742 * given collection name and index. 743 * 744 * @param collectionName - name of the collection 745 * @param rowIndex - index of the record to associate delete button 746 * @return Field - of type IMAGE_SUBMIT 747 */ 748 private static final Field getDeleteRowButtonField(String collectionName, String rowIndex) { 749 Field deleteButtonField = new Field(); 750 751 String deleteButtonName = KRADConstants.DISPATCH_REQUEST_PARAMETER + "." + KRADConstants.DELETE_LINE_METHOD + "." + collectionName + "." + KRADConstants.METHOD_TO_CALL_BOPARM_LEFT_DEL + ".line" + rowIndex; 752 deleteButtonField.setPropertyName(deleteButtonName); 753 deleteButtonField.setFieldType(Field.IMAGE_SUBMIT); 754 deleteButtonField.setPropertyValue("images/tinybutton-delete1.gif"); 755 756 return deleteButtonField; 757 } 758 759 /** 760 * Helper method to build up the show inactive button source and place in the section. 761 * 762 * @param section - section that will display the button 763 * @param collectionName - name of the collection to toggle setting 764 * @param showInactive - boolean indicating whether inactive rows should be displayed 765 * @return Field - of type IMAGE_SUBMIT 766 */ 767 private static final void addShowInactiveButtonField(Section section, String collectionName, boolean showInactive) { 768 String methodName = KRADConstants.DISPATCH_REQUEST_PARAMETER + "." + KRADConstants.TOGGLE_INACTIVE_METHOD + "." + collectionName.replace( '.', '_' ); 769 methodName += "." + KRADConstants.METHOD_TO_CALL_BOPARM_LEFT_DEL + showInactive + ".anchorshowInactive." + collectionName + KRADConstants.METHOD_TO_CALL_BOPARM_RIGHT_DEL; 770 771 String imageSource = showInactive ? "tinybutton-showinact.gif" : "tinybutton-hideinact.gif"; 772 773 String showInactiveButton = "property=" + methodName + ";src=" + imageSource + ";alt=show(hide) inactive" + ";title=show(hide) inactive"; 774 775 section.setExtraButtonSource(showInactiveButton); 776 } 777 778 /** 779 * Retrieves the primary key property names for the given class. If the field's property is one of those keys, makes the field 780 * read-only. This is called for collection lines. Since deletion is not allowed for existing lines, the pk fields must be 781 * read-only, otherwise a user could change the pk value which would be equivalent to deleting the line and adding a new line. 782 */ 783 private static final void setPrimaryKeyFieldsReadOnly(Class businessObjectClass, Field field) { 784 try{ 785 //TODO: Revisit this. Changing since getPrimaryKeys and listPrimaryKeyFieldNames are apparently same. 786 //May be we might want to replace listPrimaryKeyFieldNames with getPrimaryKeys... Not sure. 787 List primaryKeyPropertyNames = 788 KNSServiceLocator.getBusinessObjectMetaDataService().listPrimaryKeyFieldNames(businessObjectClass); 789 if (primaryKeyPropertyNames.contains(field.getPropertyName())) { 790 field.setReadOnly(true); 791 } 792 } catch(ClassNotPersistableException ex){ 793 //Not all classes will be persistable in a collection. For e.g. externalizable business objects. 794 LOG.info("Not persistable dataObjectClass: "+businessObjectClass+", field: "+field); 795 } 796 } 797 798 private static void setDuplicateIdentificationFieldsReadOnly(Field field, List<String>duplicateIdentificationFieldNames) { 799 if (duplicateIdentificationFieldNames.contains(field.getPropertyName())) { 800 field.setReadOnly(true); 801 } 802 } 803 804 /** 805 * This method returns an ordered list of fields. 806 * 807 * @param collSummaryFields 808 * @param collectionDefinition 809 * @return 810 */ 811 private static final List<Field> getSummaryFields(Map collSummaryFields, CollectionDefinitionI collectionDefinition) { 812 List<Field> orderedSummaryFields = new ArrayList<Field>(); 813 for (FieldDefinitionI summaryField : collectionDefinition.getSummaryFields()) { 814 String name = summaryField.getName(); 815 boolean found = false; 816 Field addField = (Field) collSummaryFields.get(name); 817 if (!(addField == null)) { 818 orderedSummaryFields.add(addField); 819 found = true; 820 } 821 822 if (!found) { 823 // should we throw a real error here? 824 LOG.error("summaryField " + summaryField + " not present in the list"); 825 } 826 827 } 828 return orderedSummaryFields; 829 } 830 831 /** 832 * This is a helper method to create a sub section header 833 * 834 * @param definition the MaintainableSubSectionHeaderDefinition that we'll use to create the sub section header 835 * @return the Field, which is the sub section header 836 */ 837 private static final Field createMaintainableSubSectionHeader(SubSectionHeaderDefinitionI definition) { 838 Field separatorField = new Field(); 839 separatorField.setFieldLabel(definition.getName()); 840 separatorField.setFieldType(Field.SUB_SECTION_SEPARATOR); 841 separatorField.setReadOnly(true); 842 843 return separatorField; 844 } 845 846 /** 847 * Determines whether a business object is hidable on a maintenance document. Hidable means that if the user chose to hide the inactive 848 * elements in the collection in which the passed in BOs reside, then the BOs would be hidden 849 * 850 * @param lineBusinessObject the BO in the new maintainable, should be of type {@link PersistableBusinessObject} and {@link Inquirable} 851 * @param oldLineBusinessObject the corresponding BO in the old maintainable, should be of type {@link PersistableBusinessObject} and 852 * {@link Inquirable} 853 * @return whether the BOs are eligible to be hidden if the user decides to hide them 854 */ 855 protected static boolean isRowHideableForMaintenanceDocument(BusinessObject lineBusinessObject, BusinessObject oldLineBusinessObject) { 856 if (oldLineBusinessObject != null) { 857 if (((PersistableBusinessObject) lineBusinessObject).isNewCollectionRecord()) { 858 // new records are never hidden, regardless of active status 859 return false; 860} 861 if (!((Inactivatable) lineBusinessObject).isActive() && !((Inactivatable) oldLineBusinessObject).isActive()) { 862 // records with an old and new collection elements of NOT active are eligible to be hidden 863 return true; 864 } 865 } 866 return false; 867 } 868 /** 869 * Determines whether a business object is hidden on a maintenance document. 870 * 871 * @param lineBusinessObject the BO in the new maintainable, should be of type {@link PersistableBusinessObject} 872 * @param oldLineBusinessObject the corresponding BO in the old maintainable 873 * @param newMaintainable the new maintainable from the maintenace document 874 * @param collectionName the name of the collection from which these BOs come 875 * @return 876 */ 877 protected static boolean isRowHiddenForMaintenanceDocument(BusinessObject lineBusinessObject, BusinessObject oldLineBusinessObject, 878 Maintainable newMaintainable, String collectionName) { 879 return isRowHideableForMaintenanceDocument(lineBusinessObject, oldLineBusinessObject) && !newMaintainable.getShowInactiveRecords(collectionName); 880 } 881 882 /** 883 * Determines whether a business object is hidable on an inquiry screen. Hidable means that if the user chose to hide the inactive 884 * elements in the collection in which the passed in BO resides, then the BO would be hidden 885 * 886 * @param lineBusinessObject the collection element BO, should be of type {@link PersistableBusinessObject} and {@link Inquirable} 887 * @return whether the BO is eligible to be hidden if the user decides to hide them 888 */ 889 protected static boolean isRowHideableForInquiry(BusinessObject lineBusinessObject) { 890 return !((Inactivatable) lineBusinessObject).isActive(); 891 } 892 893 /** 894 * Determines whether a business object is hidden on an inquiry screen. 895 * 896 * @param lineBusinessObject the BO in the collection, should be of type {@link PersistableBusinessObject} and {@link Inquirable} 897 * @param inquirable the inquirable 898 * @param collectionName the name of the collection from which the BO comes 899 * @return true if the business object is to be hidden; false otherwise 900 */ 901 protected static boolean isRowHiddenForInquiry(BusinessObject lineBusinessObject, Inquirable inquirable, String collectionName) { 902 return isRowHideableForInquiry(lineBusinessObject) && !inquirable.getShowInactiveRecords(collectionName); 903 } 904 905 public static MaintenanceDocumentDictionaryService getMaintenanceDocumentDictionaryService() { 906 if (maintenanceDocumentDictionaryService == null) { 907 maintenanceDocumentDictionaryService = KNSServiceLocator.getMaintenanceDocumentDictionaryService(); 908 } 909 return maintenanceDocumentDictionaryService; 910 } 911} 912