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