001/** 002 * Copyright 2005-2018 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.krad.uif.field; 017 018import java.util.ArrayList; 019import java.util.List; 020import java.util.Map; 021 022import org.apache.commons.collections.CollectionUtils; 023import org.apache.commons.lang.StringUtils; 024import org.kuali.rice.core.api.data.DataType; 025import org.kuali.rice.core.api.util.ConcreteKeyValue; 026import org.kuali.rice.core.api.util.KeyValue; 027import org.kuali.rice.core.api.util.type.TypeUtils; 028import org.kuali.rice.krad.bo.DataObjectRelationship; 029import org.kuali.rice.krad.datadictionary.AttributeDefinition; 030import org.kuali.rice.krad.datadictionary.parse.BeanTag; 031import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute; 032import org.kuali.rice.krad.datadictionary.parse.BeanTags; 033import org.kuali.rice.krad.datadictionary.state.StateMapping; 034import org.kuali.rice.krad.datadictionary.validation.constraint.CaseConstraint; 035import org.kuali.rice.krad.datadictionary.validation.constraint.MustOccurConstraint; 036import org.kuali.rice.krad.datadictionary.validation.constraint.PrerequisiteConstraint; 037import org.kuali.rice.krad.datadictionary.validation.constraint.SimpleConstraint; 038import org.kuali.rice.krad.datadictionary.validation.constraint.ValidCharactersConstraint; 039import org.kuali.rice.krad.datadictionary.validator.ValidationTrace; 040import org.kuali.rice.krad.datadictionary.validator.Validator; 041import org.kuali.rice.krad.keyvalues.KeyValuesFinder; 042import org.kuali.rice.krad.service.DataDictionaryService; 043import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 044import org.kuali.rice.krad.uif.CssConstants; 045import org.kuali.rice.krad.uif.UifConstants; 046import org.kuali.rice.krad.uif.UifPropertyPaths; 047import org.kuali.rice.krad.uif.component.Component; 048import org.kuali.rice.krad.uif.component.DelayedCopy; 049import org.kuali.rice.krad.uif.control.Control; 050import org.kuali.rice.krad.uif.control.MultiValueControlBase; 051import org.kuali.rice.krad.uif.control.SelectControl; 052import org.kuali.rice.krad.uif.control.TextAreaControl; 053import org.kuali.rice.krad.uif.control.TextControl; 054import org.kuali.rice.krad.uif.control.UifKeyValuesFinder; 055import org.kuali.rice.krad.uif.element.Action; 056import org.kuali.rice.krad.uif.element.FieldValidationMessages; 057import org.kuali.rice.krad.uif.element.Label; 058import org.kuali.rice.krad.uif.element.Link; 059import org.kuali.rice.krad.uif.element.Message; 060import org.kuali.rice.krad.uif.element.ValidationMessages; 061import org.kuali.rice.krad.uif.lifecycle.ViewLifecycle; 062import org.kuali.rice.krad.uif.lifecycle.ViewLifecycleRestriction; 063import org.kuali.rice.krad.uif.lifecycle.ViewPostMetadata; 064import org.kuali.rice.krad.uif.util.ClientValidationUtils; 065import org.kuali.rice.krad.uif.util.ComponentFactory; 066import org.kuali.rice.krad.uif.util.ComponentUtils; 067import org.kuali.rice.krad.uif.util.ConstraintStateUtils; 068import org.kuali.rice.krad.uif.util.ContextUtils; 069import org.kuali.rice.krad.uif.util.LifecycleElement; 070import org.kuali.rice.krad.uif.util.ObjectPropertyUtils; 071import org.kuali.rice.krad.uif.util.ViewModelUtils; 072import org.kuali.rice.krad.uif.view.View; 073import org.kuali.rice.krad.uif.view.ViewModel; 074import org.kuali.rice.krad.uif.widget.QuickFinder; 075import org.kuali.rice.krad.uif.widget.Suggest; 076import org.kuali.rice.krad.util.KRADConstants; 077import org.kuali.rice.krad.util.KRADUtils; 078import org.kuali.rice.krad.web.form.MaintenanceDocumentForm; 079import org.kuali.rice.krad.web.form.UifFormBase; 080 081/** 082 * Field that encapsulates data input/output captured by an attribute within the 083 * application 084 * 085 * <p> 086 * The {@code InputField} provides the majority of the data input/output 087 * for the screen. Through these fields the model can be displayed and updated. 088 * For data input, the field contains a {@link Control} instance will 089 * render an HTML control element(s). The input field also contains a 090 * {@link Label}, summary, and widgets such as a quickfinder (for 091 * looking up values) and inquiry (for getting more information on the value). 092 * {@code InputField} instances can have associated messages (errors) 093 * due to invalid input or business rule failures. Security can also be 094 * configured to restrict who may view the fields valnue. 095 * </p> 096 * 097 * @author Kuali Rice Team (rice.collab@kuali.org) 098 */ 099@BeanTags({@BeanTag(name = "input", parent = "Uif-InputField"), 100 @BeanTag(name = "inputLabelTop", parent = "Uif-InputField-LabelTop"), 101 @BeanTag(name = "inputLabelRight", parent = "Uif-InputField-LabelRight"), 102 @BeanTag(name = "checkboxInput", parent = "Uif-CheckboxInputField")}) 103public class InputFieldBase extends DataFieldBase implements InputField { 104 private static final long serialVersionUID = -3703656713706343840L; 105 106 // constraint variables 107 private ValidCharactersConstraint validCharactersConstraint; 108 private CaseConstraint caseConstraint; 109 private List<PrerequisiteConstraint> dependencyConstraints; 110 private List<MustOccurConstraint> mustOccurConstraints; 111 private SimpleConstraint simpleConstraint; 112 private DataType dataType; 113 114 // display props 115 private Control control; 116 117 private boolean inlineEdit; 118 private boolean ajaxInlineEdit; 119 120 private KeyValuesFinder optionsFinder; 121 122 private boolean uppercaseValue; 123 private boolean disableNativeAutocomplete; 124 125 @DelayedCopy 126 private FieldValidationMessages validationMessages; 127 128 // messages 129 private String constraintText; 130 private String instructionalText; 131 132 private Message constraintMessage; 133 private Message instructionalMessage; 134 135 private String helperText; 136 137 private AttributeQuery attributeQuery; 138 139 // widgets 140 private boolean enableAutoDirectInquiry; 141 142 private QuickFinder quickfinder; 143 private boolean enableAutoQuickfinder; 144 145 private Suggest suggest; 146 147 private boolean widgetInputOnly; 148 149 private boolean renderInputAddonGroup; 150 private List<String> postInputCssClasses; 151 private List<Component> postInputAddons; 152 153 public InputFieldBase() { 154 super(); 155 156 simpleConstraint = new SimpleConstraint(); 157 158 enableAutoDirectInquiry = true; 159 enableAutoQuickfinder = true; 160 } 161 162 /** 163 * {@inheritDoc} 164 */ 165 @Override 166 public void performInitialization(Object model) { 167 super.performInitialization(model); 168 169 if ((StringUtils.isNotBlank(constraintText) || (getPropertyExpression("constraintText") != null)) && ( 170 constraintMessage 171 == null)) { 172 constraintMessage = ComponentFactory.getConstraintMessage(); 173 } 174 175 if ((StringUtils.isNotBlank(instructionalText) || (getPropertyExpression("instructionalText") != null)) && ( 176 instructionalMessage 177 == null)) { 178 instructionalMessage = ComponentFactory.getInstructionalMessage(); 179 } 180 } 181 182 /** 183 * {@inheritDoc} 184 */ 185 @Override 186 public void afterEvaluateExpression() { 187 // populate readOnly from parent before calling super, to prevent DataField 188 // from forcing to true. 189 if (getReadOnly() == null) { 190 Component parent = ViewLifecycle.getPhase().getParent(); 191 setReadOnly(parent == null ? null : parent.getReadOnly()); 192 } 193 194 super.afterEvaluateExpression(); 195 } 196 197 /** 198 * {@inheritDoc} 199 */ 200 @Override 201 public void performApplyModel(Object model, LifecycleElement parent) { 202 super.performApplyModel(model, parent); 203 204 // Done in apply model so we have the message text for additional rich message processing in Message 205 // Sets message 206 if (StringUtils.isNotBlank(instructionalText) && instructionalMessage != null && StringUtils.isBlank( 207 instructionalMessage.getMessageText())) { 208 instructionalMessage.setMessageText(instructionalText); 209 } 210 211 // Sets constraints 212 if (StringUtils.isNotBlank(constraintText) && constraintMessage != null && StringUtils.isBlank( 213 constraintMessage.getMessageText())) { 214 constraintMessage.setMessageText(constraintText); 215 } 216 217 // invoke options finder if options not configured on the control 218 List<KeyValue> fieldOptions = new ArrayList<KeyValue>(); 219 220 // use options directly configured on the control first 221 if ((control != null) && control instanceof MultiValueControlBase) { 222 MultiValueControlBase multiValueControl = (MultiValueControlBase) control; 223 if ((multiValueControl.getOptions() != null) && !multiValueControl.getOptions().isEmpty()) { 224 fieldOptions = multiValueControl.getOptions(); 225 } 226 } 227 228 // set multiLineReadOnlyDisplay to true to preserve text formatting 229 if (control instanceof TextAreaControl) { 230 setMultiLineReadOnlyDisplay(true); 231 } 232 233 // if options not configured on the control, invoke configured options finder 234 if (fieldOptions.isEmpty() && (optionsFinder != null)) { 235 if (optionsFinder instanceof UifKeyValuesFinder) { 236 fieldOptions = ((UifKeyValuesFinder) optionsFinder).getKeyValues((ViewModel) model, this); 237 238 // check if blank option should be added 239 if (((UifKeyValuesFinder) optionsFinder).isAddBlankOption()) { 240 fieldOptions.add(0, new ConcreteKeyValue("", "")); 241 } 242 } else { 243 fieldOptions = optionsFinder.getKeyValues(); 244 } 245 246 if ((control != null) && control instanceof MultiValueControlBase) { 247 ((MultiValueControlBase) control).setOptions(fieldOptions); 248 } 249 } 250 251 if (enableAutoDirectInquiry && this.getInquiry() == null && hasAutoInquiryRelationship()) { 252 setInquiry(ComponentFactory.getInquiry()); 253 } 254 255 if (enableAutoQuickfinder && this.getQuickfinder() == null && hasAutoQuickfinderRelationship()) { 256 setQuickfinder(ComponentFactory.getQuickFinder()); 257 ContextUtils.pushAllToContextDeep(quickfinder, this.getContext()); 258 } 259 260 // if read only do key/value translation if necessary (if alternative and additional properties not set) 261 if (Boolean.TRUE.equals(getReadOnly()) 262 && !fieldOptions.isEmpty() 263 && StringUtils.isBlank(getReadOnlyDisplayReplacement()) 264 && StringUtils.isBlank(getReadOnlyDisplaySuffix()) 265 && StringUtils.isBlank(getReadOnlyDisplayReplacementPropertyName()) 266 && StringUtils.isBlank(getReadOnlyDisplaySuffixPropertyName())) { 267 268 Object fieldValue = ObjectPropertyUtils.getPropertyValue(model, getBindingInfo().getBindingPath()); 269 270 // TODO: can we translate Collections? (possibly combining output with delimiter 271 if ((fieldValue != null) && (TypeUtils.isSimpleType(fieldValue.getClass()))) { 272 for (KeyValue keyValue : fieldOptions) { 273 if (StringUtils.equals(fieldValue.toString(), keyValue.getKey())) { 274 setReadOnlyDisplayReplacement(keyValue.getValue()); 275 break; 276 } 277 } 278 } 279 } 280 281 if(control != null && quickfinder != null && quickfinder.getQuickfinderAction() != null) { 282 String disabledExpression = control.getPropertyExpression("disabled"); 283 if(StringUtils.isNotBlank(disabledExpression)) { 284 quickfinder.getQuickfinderAction().getPropertyExpressions().put("disabled", disabledExpression); 285 } else { 286 quickfinder.getQuickfinderAction().setDisabled(control.isDisabled()); 287 } 288 } 289 290 } 291 292 /** 293 * {@inheritDoc} 294 */ 295 @Override 296 public void performFinalize(Object model, LifecycleElement parent) { 297 super.performFinalize(model, parent); 298 299 setupIds(); 300 301 this.addDataAttribute(UifConstants.DataAttributes.ROLE, UifConstants.RoleTypes.INPUT_FIELD); 302 303 boolean ajaxInlineEditRefresh = ajaxInlineEdit && ((UifFormBase)model).getUpdateComponentId() != null && 304 ((UifFormBase)model).getUpdateComponentId().equals(this.getId()); 305 306 // if read only or the control is null no input can be given so no need to setup validation 307 if ((Boolean.TRUE.equals(getReadOnly()) && !inlineEdit && !ajaxInlineEditRefresh) || getControl() == null) { 308 return; 309 } 310 311 if (StringUtils.isNotBlank(helperText) && (getControl() != null)) { 312 getControl().getCssClasses().add(CssConstants.Classes.HAS_HELPER); 313 } 314 315 DataDictionaryService dataDictionaryService = KRADServiceLocatorWeb.getDataDictionaryService(); 316 if (this.getDictionaryObjectEntry() != null && this.getDictionaryAttributeName() != null) { 317 AttributeDefinition ad = dataDictionaryService.getAttributeDefinition(this.getDictionaryObjectEntry(), 318 this.getDictionaryAttributeName()); 319 320 Map<String, String> propertyExpressions = this.getPropertyExpressions(); 321 322 if (propertyExpressions.containsKey(UifPropertyPaths.DICTIONARY_ATTR_NAME)) { 323 // call copyFromAttributeDefinition since the dictionaryAttributeName expression had not yet 324 // been evaluated when this field was initialized. 325 copyFromAttributeDefinition(ad); 326 } 327 328 if (ad.getForceUppercase() || uppercaseValue) { 329 Object currentPropertyValue = ObjectPropertyUtils.getPropertyValue(model, 330 getBindingInfo().getBindingPath()); 331 if (currentPropertyValue instanceof String) { 332 String uppercasedValue = ((String) currentPropertyValue).toUpperCase(); 333 ObjectPropertyUtils.setPropertyValue(model, getBindingInfo().getBindingPath(), uppercasedValue); 334 } 335 } 336 } 337 338 // browser's native autocomplete causes issues with the suggest plugin 339 if ((suggest != null) && suggest.isSuggestConfigured()) { 340 setDisableNativeAutocomplete(true); 341 } 342 343 // adjust paths on PrerequisiteConstraint property names 344 adjustPrerequisiteConstraintBinding(dependencyConstraints); 345 346 // adjust paths on MustOccurConstraints property names 347 adjustMustOccurConstraintBinding(mustOccurConstraints); 348 349 // adjust paths on CaseConstraint property names 350 if (caseConstraint != null) { 351 String propertyName = getBindingInfo().getPropertyAdjustedBindingPath(caseConstraint.getPropertyName()); 352 caseConstraint.setPropertyName(propertyName); 353 } 354 355 View view = ViewLifecycle.getView(); 356 357 setupFieldQuery(view); 358 359 // special requiredness indicator handling, if this was previously not required reset its required 360 // message to be ** for indicating required in the next state 361 String path = view.getStateObjectBindingPath(); 362 Object stateObject; 363 364 if (StringUtils.isNotBlank(path)) { 365 stateObject = ObjectPropertyUtils.getPropertyValue(model, path); 366 } else { 367 stateObject = model; 368 } 369 StateMapping stateMapping = view.getStateMapping(); 370 String nextStateReqIndicator = (String) KRADServiceLocatorWeb.getDataDictionaryService().getDictionaryBean( 371 UifConstants.REQUIRED_NEXT_STATE_INDICATOR_ID); 372 373 if (stateMapping != null) { 374 String validationState = ConstraintStateUtils.getClientViewValidationState(model, view); 375 SimpleConstraint appliedSimpleConstraint = ConstraintStateUtils.getApplicableConstraint( 376 this.getSimpleConstraint(), validationState, stateMapping); 377 378 if (appliedSimpleConstraint != null 379 && appliedSimpleConstraint.getRequired() != null 380 && appliedSimpleConstraint.getRequired()) { 381 SimpleConstraint prevConstraint = ConstraintStateUtils.getApplicableConstraint( 382 this.getSimpleConstraint(), stateMapping.getCurrentState(stateObject), stateMapping); 383 if (prevConstraint == null || prevConstraint.getRequired() == null || !prevConstraint.getRequired()) { 384 this.getFieldLabel().setRequiredIndicator(nextStateReqIndicator); 385 } 386 } 387 } 388 389 ClientValidationUtils.processAndApplyConstraints(this, view, model); 390 391 if (inlineEdit || ajaxInlineEdit) { 392 this.addDataAttribute(UifConstants.DataAttributes.INLINE_EDIT, "true"); 393 } 394 395 // Generate validation messages 396 if (validationMessages != null) { 397 // Messages will not use tooltip for inline edit cases 398 if (inlineEdit || ajaxInlineEdit) { 399 validationMessages.setUseTooltip(false); 400 } 401 402 validationMessages.generateMessages(view, model, this); 403 } 404 405 addComponentPostMetadata(); 406 407 if (this.getHelp() != null && StringUtils.isNotBlank(this.getHelp().getExternalHelpUrl())) { 408 this.setRenderInputAddonGroup(true); 409 } 410 } 411 412 /** 413 * Invoked during the finalize phase to capture state of the component needs to support post operations. 414 */ 415 protected void addComponentPostMetadata() { 416 ViewPostMetadata viewPostMetadata = ViewLifecycle.getViewPostMetadata(); 417 418 viewPostMetadata.getInputFieldIds().add(this.getId()); 419 420 viewPostMetadata.addComponentPostData(this, UifConstants.PostMetadata.LABEL, this.getLabel()); 421 422 viewPostMetadata.addComponentPostData(this, UifConstants.PostMetadata.PATH, this.getName()); 423 424 viewPostMetadata.addComponentPostData(this, UifConstants.PostMetadata.SIMPLE_CONSTRAINT, 425 this.getSimpleConstraint()); 426 427 viewPostMetadata.addComponentPostData(this, UifConstants.PostMetadata.VALID_CHARACTER_CONSTRAINT, 428 this.getValidCharactersConstraint()); 429 430 viewPostMetadata.addComponentPostData(this, UifConstants.PostMetadata.CASE_CONSTRAINT, this.getCaseConstraint()); 431 432 viewPostMetadata.addComponentPostData(this, UifConstants.PostMetadata.MUST_OCCUR_CONSTRAINTS, 433 this.getMustOccurConstraints()); 434 435 viewPostMetadata.addComponentPostData(this, UifConstants.PostMetadata.PREREQ_CONSTSTRAINTS, 436 this.getPrerequisiteConstraints()); 437 438 viewPostMetadata.addComponentPostData(this, UifConstants.PostMetadata.INPUT_FIELD_ATTRIBUTE_QUERY, 439 attributeQuery); 440 441 if (this.suggest != null) { 442 viewPostMetadata.addComponentPostData(this, UifConstants.PostMetadata.SUGGEST, this.suggest.getPostData()); 443 } 444 445 viewPostMetadata.addComponentPostData(this, UifConstants.PostMetadata.INPUT_FIELD_IS_UPPERCASE, 446 isUppercaseValue()); 447 448 if ((isRender() || StringUtils.isNotBlank(getProgressiveRender())) && !isHidden() && (!Boolean.TRUE.equals( 449 getReadOnly()) || inlineEdit || ajaxInlineEdit)) { 450 viewPostMetadata.addAccessibleBindingPath(getBindingInfo().getBindingPath()); 451 } 452 } 453 454 /** 455 * Overrides processReadOnlyListDisplay to handle MultiValueControls by creating the list of values from values 456 * instead of the keys of the options selected (makes the list "human-readable"). Otherwise it just passes the 457 * list ahead as normal if this InputField does not use a MultiValueControl. 458 * 459 * @param model the model 460 * @param originalList originalList of values 461 */ 462 @Override 463 protected void processReadOnlyListDisplay(Object model, List<?> originalList) { 464 //Special handling for option based fields 465 if ((control != null) && control instanceof MultiValueControlBase) { 466 List<String> newList = new ArrayList<String>(); 467 List<KeyValue> fieldOptions = ((MultiValueControlBase) control).getOptions(); 468 469 if (fieldOptions == null || fieldOptions.isEmpty()) { 470 return; 471 } 472 473 for (Object fieldValue : originalList) { 474 for (KeyValue keyValue : fieldOptions) { 475 if (fieldValue != null && StringUtils.equals(fieldValue.toString(), keyValue.getKey())) { 476 newList.add(keyValue.getValue()); 477 break; 478 } 479 } 480 } 481 this.setReadOnlyDisplayReplacement(super.generateReadOnlyListDisplayReplacement(newList)); 482 } else { 483 this.setReadOnlyDisplayReplacement(super.generateReadOnlyListDisplayReplacement(originalList)); 484 } 485 } 486 487 /** 488 * Overriding to check quickfinder when masked is being applied. If quickfinder is configured set the component 489 * to widgetInputOnly, else set to readOnly 490 * 491 * {@inheritDoc} 492 */ 493 @Override 494 protected void setAlternateAndAdditionalDisplayValue(View view, Object model) { 495 // if alternate or additional display values set don't override 496 if (StringUtils.isNotBlank(getReadOnlyDisplayReplacement()) || StringUtils.isNotBlank( 497 getReadOnlyDisplaySuffix())) { 498 return; 499 } 500 501 if (isApplyMask()) { 502 if ((this.quickfinder != null) && StringUtils.isNotBlank(this.quickfinder.getDataObjectClassName())) { 503 setWidgetInputOnly(true); 504 } else { 505 String maintenanceAction = null; 506 if (view.getViewTypeName().equals(UifConstants.ViewType.MAINTENANCE)) { 507 maintenanceAction =((MaintenanceDocumentForm) model).getMaintenanceAction(); 508 } 509 510 if ((!view.getViewTypeName().equals(UifConstants.ViewType.LOOKUP)) && 511 (!KRADConstants.MAINTENANCE_COPY_ACTION.equals(maintenanceAction))) { 512 setReadOnly(true); 513 } 514 } 515 } 516 517 super.setAlternateAndAdditionalDisplayValue(view, model); 518 } 519 520 /** 521 * Adjust paths on the must occur constrain bindings 522 * 523 * @param mustOccurConstraints 524 */ 525 protected void adjustMustOccurConstraintBinding(List<MustOccurConstraint> mustOccurConstraints) { 526 if (mustOccurConstraints != null) { 527 for (MustOccurConstraint mustOccurConstraint : mustOccurConstraints) { 528 adjustPrerequisiteConstraintBinding(mustOccurConstraint.getPrerequisiteConstraints()); 529 adjustMustOccurConstraintBinding(mustOccurConstraint.getMustOccurConstraints()); 530 } 531 } 532 } 533 534 /** 535 * Adjust paths on the prerequisite constraint bindings 536 * 537 * @param prerequisiteConstraints 538 */ 539 protected void adjustPrerequisiteConstraintBinding(List<PrerequisiteConstraint> prerequisiteConstraints) { 540 if (prerequisiteConstraints != null) { 541 for (PrerequisiteConstraint prerequisiteConstraint : prerequisiteConstraints) { 542 String propertyName = getBindingInfo().getPropertyAdjustedBindingPath( 543 prerequisiteConstraint.getPropertyName()); 544 prerequisiteConstraint.setPropertyName(propertyName); 545 } 546 } 547 } 548 549 /** 550 * Performs setup of the field attribute query and informational display properties. 551 * 552 * <p>Paths are adjusted to match the binding for the this field, and the necessary onblur script for 553 * triggering the query client side is constructed</p> 554 * 555 * @param view view instance the input field is associated with 556 */ 557 protected void setupFieldQuery(View view) { 558 if (getAttributeQuery() != null) { 559 getAttributeQuery().defaultQueryTarget(view.getViewHelperService()); 560 561 // adjust paths on query mappings 562 getAttributeQuery().updateQueryFieldMapping(getBindingInfo()); 563 getAttributeQuery().updateReturnFieldMapping(getBindingInfo()); 564 getAttributeQuery().updateQueryMethodArgumentFieldList(getBindingInfo()); 565 566 // build onblur script for field query 567 String script = "executeFieldQuery('" + getControl().getId() + "',"; 568 script += "'" + getId() + "'," + getAttributeQuery().getQueryFieldMappingJsString() + ","; 569 script += getAttributeQuery().getQueryMethodArgumentFieldsJsString() + ","; 570 script += getAttributeQuery().getReturnFieldMappingJsString() + ");"; 571 572 // show the span wich will contain the info 573 this.setRenderInfoMessageSpan(true); 574 575 if (StringUtils.isNotBlank(getControl().getOnBlurScript())) { 576 script = getControl().getOnBlurScript() + script; 577 } 578 getControl().setOnBlurScript(script); 579 } 580 } 581 582 /** 583 * Sets the ids on all components the input field uses so they will all 584 * contain this input field's id in their ids. This is useful for jQuery 585 * manipulation. 586 */ 587 protected void setupIds() { 588 // update ids so they all match the attribute 589 590 setNestedComponentIdAndSuffix(getControl(), UifConstants.IdSuffixes.CONTROL); 591 setNestedComponentIdAndSuffix(getFieldLabel(), UifConstants.IdSuffixes.LABEL); 592 setNestedComponentIdAndSuffix(getInstructionalMessage(), UifConstants.IdSuffixes.INSTRUCTIONAL); 593 setNestedComponentIdAndSuffix(getConstraintMessage(), UifConstants.IdSuffixes.CONSTRAINT); 594 setNestedComponentIdAndSuffix(getQuickfinder(), UifConstants.IdSuffixes.QUICK_FINDER); 595 setNestedComponentIdAndSuffix(getSuggest(), UifConstants.IdSuffixes.SUGGEST); 596 597 if (this.getControl() != null) { 598 this.getControl().addDataAttribute(UifConstants.DataAttributes.CONTROL_FOR, this.getId()); 599 } 600 } 601 602 /** 603 * {@inheritDoc} 604 */ 605 @Override 606 public void copyFromAttributeDefinition(AttributeDefinition attributeDefinition) { 607 super.copyFromAttributeDefinition(attributeDefinition); 608 609 // max length 610 if (getMaxLength() == null) { 611 setMaxLength(attributeDefinition.getMaxLength()); 612 } 613 614 // min length 615 if (getMinLength() == null) { 616 setMinLength(attributeDefinition.getMinLength()); 617 } 618 619 // valid characters 620 if (getValidCharactersConstraint() == null) { 621 setValidCharactersConstraint(attributeDefinition.getValidCharactersConstraint()); 622 } 623 624 if (getCaseConstraint() == null) { 625 setCaseConstraint(attributeDefinition.getCaseConstraint()); 626 } 627 628 if (getDependencyConstraints() == null) { 629 setDependencyConstraints(attributeDefinition.getPrerequisiteConstraints()); 630 } 631 632 if (getMustOccurConstraints() == null) { 633 setMustOccurConstraints(attributeDefinition.getMustOccurConstraints()); 634 } 635 636 // required 637 if (getRequired() == null) { 638 setRequired(attributeDefinition.isRequired()); 639 640 //if still null, default to false 641 if (getRequired() == null) { 642 setRequired(Boolean.FALSE); 643 } 644 } 645 646 if (getDataType() == null) { 647 setDataType(attributeDefinition.getDataType()); 648 //Assume date if dataType is still null and using a DatePicker 649 if (getDataType() == null 650 && control instanceof TextControl 651 && ((TextControl) control).getDatePicker() != null) { 652 setDataType(DataType.DATE); 653 } 654 } 655 656 // control 657 if ((getControl() == null) && (attributeDefinition.getControlField() != null)) { 658 Control control = ComponentUtils.copy(attributeDefinition.getControlField()); 659 setControl(control); 660 } 661 662 // constraint 663 if (StringUtils.isEmpty(getConstraintText())) { 664 setConstraintText(attributeDefinition.getConstraintText()); 665 666 if (constraintMessage == null) { 667 constraintMessage = ComponentFactory.getConstraintMessage(); 668 } 669 670 getConstraintMessage().setMessageText(attributeDefinition.getConstraintText()); 671 } 672 673 // options 674 if (getOptionsFinder() == null) { 675 setOptionsFinder(attributeDefinition.getOptionsFinder()); 676 } 677 678 // copy over simple constraint information because we cannot directly use simpleConstraint from 679 // attributeDefinition because the settings in InputField take precedence 680 if (this.getSimpleConstraint().getConstraintStateOverrides() == null) { 681 this.getSimpleConstraint().setConstraintStateOverrides( 682 attributeDefinition.getSimpleConstraint().getConstraintStateOverrides()); 683 } 684 685 if (this.getSimpleConstraint().getStates().isEmpty()) { 686 this.getSimpleConstraint().setStates(attributeDefinition.getSimpleConstraint().getStates()); 687 } 688 689 if (this.getSimpleConstraint().getMessageKey() == null) { 690 this.getSimpleConstraint().setMessageKey(attributeDefinition.getSimpleConstraint().getMessageKey()); 691 } 692 693 if (this.getSimpleConstraint().getApplyClientSide() == null) { 694 this.getSimpleConstraint().setApplyClientSide( 695 attributeDefinition.getSimpleConstraint().getApplyClientSide()); 696 } 697 698 if (this.getSimpleConstraint().getValidationMessageParams() == null) { 699 this.getSimpleConstraint().setValidationMessageParams( 700 attributeDefinition.getSimpleConstraint().getValidationMessageParams()); 701 } 702 } 703 704 /** 705 * {@inheritDoc} 706 */ 707 @Override 708 public boolean isInputAllowed() { 709 return true; 710 } 711 712 /** 713 * {@inheritDoc} 714 */ 715 @Override 716 @BeanTagAttribute(type = BeanTagAttribute.AttributeType.BYTYPE) 717 public Control getControl() { 718 return this.control; 719 } 720 721 /** 722 * {@inheritDoc} 723 */ 724 @Override 725 public void setControl(Control control) { 726 this.control = control; 727 } 728 729 /** 730 * {@inheritDoc} 731 */ 732 @BeanTagAttribute 733 public boolean isInlineEdit() { 734 return inlineEdit; 735 } 736 737 /** 738 * {@inheritDoc} 739 */ 740 public void setInlineEdit(boolean inlineEdit) { 741 this.inlineEdit = inlineEdit; 742 } 743 744 /** 745 * {@inheritDoc} 746 */ 747 @BeanTagAttribute 748 public boolean isAjaxInlineEdit() { 749 return ajaxInlineEdit; 750 } 751 752 /** 753 * {@inheritDoc} 754 */ 755 public void setAjaxInlineEdit(boolean ajaxInlineEdit) { 756 this.ajaxInlineEdit = ajaxInlineEdit; 757 } 758 759 /** 760 * {@inheritDoc} 761 */ 762 @Override 763 @ViewLifecycleRestriction 764 @BeanTagAttribute 765 public FieldValidationMessages getValidationMessages() { 766 return this.validationMessages; 767 } 768 769 /** 770 * {@inheritDoc} 771 */ 772 @Override 773 public void setValidationMessages(FieldValidationMessages validationMessages) { 774 this.validationMessages = validationMessages; 775 } 776 777 /** 778 * {@inheritDoc} 779 */ 780 @Override 781 @BeanTagAttribute 782 public KeyValuesFinder getOptionsFinder() { 783 return this.optionsFinder; 784 } 785 786 /** 787 * {@inheritDoc} 788 */ 789 @Override 790 public void setOptionsFinder(KeyValuesFinder optionsFinder) { 791 this.optionsFinder = optionsFinder; 792 } 793 794 /** 795 * {@inheritDoc} 796 */ 797 @Override 798 @BeanTagAttribute 799 public Class<? extends KeyValuesFinder> getOptionsFinderClass() { 800 if (this.optionsFinder != null) { 801 return this.optionsFinder.getClass(); 802 } else { 803 return null; 804 } 805 } 806 807 /** 808 * {@inheritDoc} 809 */ 810 @Override 811 public void setOptionsFinderClass(Class<? extends KeyValuesFinder> optionsFinderClass) { 812 this.optionsFinder = KRADUtils.createNewObjectFromClass(optionsFinderClass); 813 } 814 815 /** 816 * {@inheritDoc} 817 */ 818 @Override 819 @BeanTagAttribute 820 public boolean isEnableAutoDirectInquiry() { 821 return enableAutoDirectInquiry; 822 } 823 824 /** 825 * {@inheritDoc} 826 */ 827 @Override 828 public void setEnableAutoDirectInquiry(boolean enableAutoDirectInquiry) { 829 this.enableAutoDirectInquiry = enableAutoDirectInquiry; 830 } 831 832 /** 833 * {@inheritDoc} 834 */ 835 @Override 836 @BeanTagAttribute(type = BeanTagAttribute.AttributeType.DIRECTORBYTYPE) 837 public QuickFinder getQuickfinder() { 838 return this.quickfinder; 839 } 840 841 /** 842 * {@inheritDoc} 843 */ 844 @Override 845 public void setQuickfinder(QuickFinder quickfinder) { 846 this.quickfinder = quickfinder; 847 } 848 849 /** 850 * {@inheritDoc} 851 */ 852 @Override 853 @BeanTagAttribute 854 public boolean isEnableAutoQuickfinder() { 855 return enableAutoQuickfinder; 856 } 857 858 /** 859 * {@inheritDoc} 860 */ 861 @Override 862 public void setEnableAutoQuickfinder(boolean enableAutoQuickfinder) { 863 this.enableAutoQuickfinder = enableAutoQuickfinder; 864 } 865 866 /** 867 * {@inheritDoc} 868 */ 869 @Override 870 @BeanTagAttribute(type = BeanTagAttribute.AttributeType.DIRECTORBYTYPE) 871 public Suggest getSuggest() { 872 return suggest; 873 } 874 875 /** 876 * {@inheritDoc} 877 */ 878 @Override 879 public void setSuggest(Suggest suggest) { 880 this.suggest = suggest; 881 } 882 883 /** 884 * {@inheritDoc} 885 */ 886 @Override 887 @BeanTagAttribute 888 public boolean isWidgetInputOnly() { 889 return this.widgetInputOnly; 890 } 891 892 /** 893 * {@inheritDoc} 894 */ 895 @Override 896 public void setWidgetInputOnly(boolean widgetInputOnly) { 897 this.widgetInputOnly = widgetInputOnly; 898 } 899 900 /** 901 * {@inheritDoc} 902 */ 903 @Override 904 @BeanTagAttribute 905 public boolean isRenderInputAddonGroup() { 906 return renderInputAddonGroup; 907 } 908 909 /** 910 * {@inheritDoc} 911 */ 912 @Override 913 public void setRenderInputAddonGroup(boolean renderInputAddonGroup) { 914 this.renderInputAddonGroup = renderInputAddonGroup; 915 } 916 917 /** 918 * {@inheritDoc} 919 */ 920 @Override 921 @BeanTagAttribute 922 public List<String> getPostInputCssClasses() { 923 return postInputCssClasses; 924 } 925 926 /** 927 * {@inheritDoc} 928 */ 929 @Override 930 public String getPostInputCssClassesAsString() { 931 if (postInputCssClasses != null) { 932 return StringUtils.join(postInputCssClasses, " "); 933 } 934 935 return ""; 936 } 937 938 /** 939 * {@inheritDoc} 940 */ 941 @Override 942 public void setPostInputCssClasses(List<String> postInputCssClasses) { 943 this.postInputCssClasses = postInputCssClasses; 944 } 945 946 /** 947 * {@inheritDoc} 948 */ 949 @Override 950 @BeanTagAttribute 951 public List<Component> getPostInputAddons() { 952 return postInputAddons; 953 } 954 955 /** 956 * {@inheritDoc} 957 */ 958 @Override 959 public void setPostInputAddons(List<Component> postInputAddons) { 960 this.postInputAddons = postInputAddons; 961 } 962 963 /** 964 * {@inheritDoc} 965 */ 966 @Override 967 public void addPostInputAddon(Component addOn) { 968 if (postInputAddons == null) { 969 postInputAddons = new ArrayList<Component>(); 970 } 971 972 postInputAddons.add(addOn); 973 } 974 975 /** 976 * {@inheritDoc} 977 */ 978 @Override 979 @BeanTagAttribute 980 public String getInstructionalText() { 981 return this.instructionalText; 982 } 983 984 /** 985 * {@inheritDoc} 986 */ 987 @Override 988 public void setInstructionalText(String instructionalText) { 989 this.instructionalText = instructionalText; 990 } 991 992 /** 993 * {@inheritDoc} 994 */ 995 @Override 996 @BeanTagAttribute 997 public Message getInstructionalMessage() { 998 return this.instructionalMessage; 999 } 1000 1001 /** 1002 * {@inheritDoc} 1003 */ 1004 @Override 1005 public void setInstructionalMessage(Message instructionalMessage) { 1006 this.instructionalMessage = instructionalMessage; 1007 } 1008 1009 /** 1010 * {@inheritDoc} 1011 */ 1012 @Override 1013 @BeanTagAttribute 1014 public String getHelperText() { 1015 return helperText; 1016 } 1017 1018 /** 1019 * {@inheritDoc} 1020 */ 1021 @Override 1022 public void setHelperText(String helperText) { 1023 this.helperText = helperText; 1024 } 1025 1026 /** 1027 * {@inheritDoc} 1028 */ 1029 @Override 1030 @BeanTagAttribute 1031 public String getConstraintText() { 1032 return this.constraintText; 1033 } 1034 1035 /** 1036 * {@inheritDoc} 1037 */ 1038 @Override 1039 public void setConstraintText(String constraintText) { 1040 this.constraintText = constraintText; 1041 } 1042 1043 /** 1044 * {@inheritDoc} 1045 */ 1046 @Override 1047 @BeanTagAttribute 1048 public Message getConstraintMessage() { 1049 return this.constraintMessage; 1050 } 1051 1052 /** 1053 * {@inheritDoc} 1054 */ 1055 @Override 1056 public void setConstraintMessage(Message constraintMessage) { 1057 this.constraintMessage = constraintMessage; 1058 } 1059 1060 /** 1061 * {@inheritDoc} 1062 */ 1063 @Override 1064 @BeanTagAttribute 1065 public ValidCharactersConstraint getValidCharactersConstraint() { 1066 return this.validCharactersConstraint; 1067 } 1068 1069 /** 1070 * {@inheritDoc} 1071 */ 1072 @Override 1073 public void setValidCharactersConstraint(ValidCharactersConstraint validCharactersConstraint) { 1074 this.validCharactersConstraint = validCharactersConstraint; 1075 } 1076 1077 /** 1078 * {@inheritDoc} 1079 */ 1080 @Override 1081 @BeanTagAttribute 1082 public CaseConstraint getCaseConstraint() { 1083 return this.caseConstraint; 1084 } 1085 1086 /** 1087 * {@inheritDoc} 1088 */ 1089 @Override 1090 public void setCaseConstraint(CaseConstraint caseConstraint) { 1091 this.caseConstraint = caseConstraint; 1092 } 1093 1094 /** 1095 * {@inheritDoc} 1096 */ 1097 @Override 1098 @BeanTagAttribute 1099 public List<PrerequisiteConstraint> getDependencyConstraints() { 1100 return this.dependencyConstraints; 1101 } 1102 1103 /** 1104 * {@inheritDoc} 1105 */ 1106 @Override 1107 public void setDependencyConstraints(List<PrerequisiteConstraint> dependencyConstraints) { 1108 this.dependencyConstraints = dependencyConstraints; 1109 } 1110 1111 /** 1112 * {@inheritDoc} 1113 */ 1114 @Override 1115 public List<PrerequisiteConstraint> getPrerequisiteConstraints() { 1116 return dependencyConstraints; 1117 } 1118 1119 /** 1120 * {@inheritDoc} 1121 */ 1122 @Override 1123 @BeanTagAttribute 1124 public List<MustOccurConstraint> getMustOccurConstraints() { 1125 return this.mustOccurConstraints; 1126 } 1127 1128 /** 1129 * {@inheritDoc} 1130 */ 1131 @Override 1132 public void setMustOccurConstraints(List<MustOccurConstraint> mustOccurConstraints) { 1133 this.mustOccurConstraints = mustOccurConstraints; 1134 } 1135 1136 /** 1137 * {@inheritDoc} 1138 */ 1139 @Override 1140 @BeanTagAttribute 1141 public SimpleConstraint getSimpleConstraint() { 1142 return this.simpleConstraint; 1143 } 1144 1145 /** 1146 * {@inheritDoc} 1147 */ 1148 @Override 1149 public void setSimpleConstraint(SimpleConstraint simpleConstraint) { 1150 this.simpleConstraint = simpleConstraint; 1151 } 1152 1153 /** 1154 * {@inheritDoc} 1155 */ 1156 @Override 1157 @BeanTagAttribute(type = BeanTagAttribute.AttributeType.SINGLEBEAN) 1158 public DataType getDataType() { 1159 return this.simpleConstraint.getDataType(); 1160 } 1161 1162 /** 1163 * {@inheritDoc} 1164 */ 1165 @Override 1166 public void setDataType(DataType dataType) { 1167 this.simpleConstraint.setDataType(dataType); 1168 } 1169 1170 /** 1171 * {@inheritDoc} 1172 */ 1173 @Override 1174 public void setDataType(String dataType) { 1175 this.simpleConstraint.setDataType(DataType.valueOf(dataType)); 1176 } 1177 1178 /** 1179 * {@inheritDoc} 1180 */ 1181 @Override 1182 @BeanTagAttribute 1183 public Integer getMaxLength() { 1184 return simpleConstraint.getMaxLength(); 1185 } 1186 1187 /** 1188 * {@inheritDoc} 1189 */ 1190 @Override 1191 public void setMaxLength(Integer maxLength) { 1192 simpleConstraint.setMaxLength(maxLength); 1193 } 1194 1195 /** 1196 * {@inheritDoc} 1197 */ 1198 @Override 1199 @BeanTagAttribute 1200 public Integer getMinLength() { 1201 return simpleConstraint.getMinLength(); 1202 } 1203 1204 /** 1205 * {@inheritDoc} 1206 */ 1207 @Override 1208 public void setMinLength(Integer minLength) { 1209 simpleConstraint.setMinLength(minLength); 1210 } 1211 1212 /** 1213 * {@inheritDoc} 1214 */ 1215 @Override 1216 @BeanTagAttribute 1217 public Boolean getRequired() { 1218 return this.simpleConstraint.getRequired(); 1219 } 1220 1221 /** 1222 * {@inheritDoc} 1223 */ 1224 @Override 1225 public void setRequired(Boolean required) { 1226 this.simpleConstraint.setRequired(required); 1227 } 1228 1229 /** 1230 * {@inheritDoc} 1231 */ 1232 @Override 1233 @BeanTagAttribute 1234 public String getExclusiveMin() { 1235 return simpleConstraint.getExclusiveMin(); 1236 } 1237 1238 /** 1239 * {@inheritDoc} 1240 */ 1241 @Override 1242 public void setExclusiveMin(String exclusiveMin) { 1243 simpleConstraint.setExclusiveMin(exclusiveMin); 1244 } 1245 1246 /** 1247 * {@inheritDoc} 1248 */ 1249 @Override 1250 @BeanTagAttribute 1251 public String getInclusiveMax() { 1252 return simpleConstraint.getInclusiveMax(); 1253 } 1254 1255 /** 1256 * {@inheritDoc} 1257 */ 1258 @Override 1259 public void setInclusiveMax(String inclusiveMax) { 1260 simpleConstraint.setInclusiveMax(inclusiveMax); 1261 } 1262 1263 /** 1264 * {@inheritDoc} 1265 */ 1266 @Override 1267 @BeanTagAttribute(type = BeanTagAttribute.AttributeType.DIRECTORBYTYPE) 1268 public AttributeQuery getAttributeQuery() { 1269 return attributeQuery; 1270 } 1271 1272 /** 1273 * {@inheritDoc} 1274 */ 1275 @Override 1276 public void setAttributeQuery(AttributeQuery attributeQuery) { 1277 this.attributeQuery = attributeQuery; 1278 } 1279 1280 /** 1281 * {@inheritDoc} 1282 */ 1283 @Override 1284 @BeanTagAttribute 1285 public boolean isUppercaseValue() { 1286 return uppercaseValue; 1287 } 1288 1289 /** 1290 * {@inheritDoc} 1291 */ 1292 @Override 1293 public void setUppercaseValue(boolean uppercaseValue) { 1294 this.uppercaseValue = uppercaseValue; 1295 } 1296 1297 /** 1298 * {@inheritDoc} 1299 */ 1300 @Override 1301 @BeanTagAttribute 1302 public boolean isDisableNativeAutocomplete() { 1303 return disableNativeAutocomplete; 1304 } 1305 1306 /** 1307 * {@inheritDoc} 1308 */ 1309 @Override 1310 public void setDisableNativeAutocomplete(boolean disableNativeAutocomplete) { 1311 this.disableNativeAutocomplete = disableNativeAutocomplete; 1312 } 1313 1314 /** 1315 * {@inheritDoc} 1316 */ 1317 @Override 1318 public boolean isRenderFieldset() { 1319 return super.isRenderFieldset() || (quickfinder != null 1320 && quickfinder.isRender() 1321 && quickfinder.getQuickfinderAction() != null 1322 && quickfinder.getQuickfinderAction().isRender()); 1323 } 1324 1325 /** 1326 * {@inheritDoc} 1327 */ 1328 @Override 1329 public void completeValidation(ValidationTrace tracer) { 1330 tracer.addBean(this); 1331 1332 // Checks that the control is set 1333 if (getControl() == null) { 1334 if (Validator.checkExpressions(this, "control")) { 1335 String currentValues[] = {"propertyName =" + getPropertyName()}; 1336 tracer.createWarning("Control should be set", currentValues); 1337 } 1338 } 1339 1340 1341 if (getControl() != null && !(getControl() instanceof TextControl 1342 || getControl() instanceof TextAreaControl 1343 || getControl() instanceof SelectControl)){ 1344 1345 if (CollectionUtils.isNotEmpty(this.getPostInputAddons())) { 1346 String currentValues[] = {"propertyName =" + getPropertyName()}; 1347 tracer.createWarning("Inputs which are not text or select should not use post input addons for " 1348 + "user experience reasons", currentValues); 1349 } 1350 } 1351 1352 super.completeValidation(tracer.getCopy()); 1353 } 1354 1355 1356 /** 1357 * Determines wheter or not to create an automatic quickfinder widget for this field within the current lifecycle. 1358 * 1359 * @return True if an automatic quickfinder widget should be created for this field on the current lifecycle. 1360 */ 1361 protected boolean hasAutoQuickfinderRelationship() { 1362 String propertyName = getBindingInfo().getBindingName(); 1363 1364 // get object instance and class for parent 1365 View view = ViewLifecycle.getView(); 1366 Object model = ViewLifecycle.getModel(); 1367 Object parentObject = ViewModelUtils.getParentObjectForMetadata(view, model, this); 1368 Class<?> parentObjectClass = null; 1369 if (parentObject != null) { 1370 parentObjectClass = parentObject.getClass(); 1371 } 1372 1373 // get relationship from metadata service 1374 @SuppressWarnings("deprecation") 1375 DataObjectRelationship relationship = null; 1376 if (parentObject != null) { 1377 relationship = KRADServiceLocatorWeb.getLegacyDataAdapter().getDataObjectRelationship(parentObject, 1378 parentObjectClass, propertyName, "", true, true, false); 1379 } 1380 1381 return relationship != null; 1382 } 1383 1384}