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