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.lookup; 017 018import java.util.HashMap; 019import java.util.Map; 020 021import org.apache.commons.lang.StringUtils; 022import org.kuali.rice.core.api.util.ConcreteKeyValue; 023import org.kuali.rice.krad.datadictionary.AttributeDefinition; 024import org.kuali.rice.krad.datadictionary.parse.BeanTag; 025import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute; 026import org.kuali.rice.krad.datadictionary.validation.constraint.ValidCharactersConstraint; 027import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 028import org.kuali.rice.krad.uif.UifConstants; 029import org.kuali.rice.krad.uif.UifPropertyPaths; 030import org.kuali.rice.krad.uif.control.CheckboxControl; 031import org.kuali.rice.krad.uif.control.Control; 032import org.kuali.rice.krad.uif.control.FilterableLookupCriteriaControl; 033import org.kuali.rice.krad.uif.control.MultiValueControl; 034import org.kuali.rice.krad.uif.control.RadioGroupControl; 035import org.kuali.rice.krad.uif.control.TextAreaControl; 036import org.kuali.rice.krad.uif.element.Message; 037import org.kuali.rice.krad.uif.field.InputField; 038import org.kuali.rice.krad.uif.field.InputFieldBase; 039import org.kuali.rice.krad.uif.lifecycle.ViewLifecycle; 040import org.kuali.rice.krad.uif.util.ComponentFactory; 041import org.kuali.rice.krad.uif.util.ComponentUtils; 042import org.kuali.rice.krad.uif.util.KeyMessage; 043import org.kuali.rice.krad.uif.util.LifecycleElement; 044import org.kuali.rice.krad.util.KRADConstants; 045import org.kuali.rice.krad.util.KRADPropertyConstants; 046 047/** 048 * Custom {@link InputField} for criteria fields within a lookup view that adds criteria specific options. 049 * 050 * @author Kuali Rice Team (rice.collab@kuali.org) 051 */ 052@BeanTag(name = "lookupCriteria", parent = "Uif-LookupCriteriaInputField") 053public class LookupInputField extends InputFieldBase { 054 private static final long serialVersionUID = -8294275596836322699L; 055 056 private boolean disableWildcardsAndOperators; 057 private boolean addControlSelectAllOption; 058 private boolean ranged; 059 060 public LookupInputField() { 061 super(); 062 } 063 064 /** 065 * The following actions are performed: 066 * 067 * <ul> 068 * <li>Add all option if enabled and control is multi-value</li> 069 * </ul> 070 * 071 * {@inheritDoc} 072 */ 073 @Override 074 public void performFinalize(Object model, LifecycleElement parent) { 075 super.performFinalize(model, parent); 076 077 // if enabled add option to select all values 078 if (addControlSelectAllOption && (getControl() != null) && getControl() instanceof MultiValueControl) { 079 String allOptionText = KRADServiceLocatorWeb.getMessageService().getMessageText( 080 UifConstants.MessageKeys.OPTION_ALL); 081 082 MultiValueControl multiValueControl = (MultiValueControl) getControl(); 083 if (multiValueControl.getOptions() != null) { 084 multiValueControl.getOptions().add(0, new ConcreteKeyValue("", allOptionText)); 085 } 086 087 if (multiValueControl.getRichOptions() != null) { 088 Message message = ComponentFactory.getMessage(); 089 message.setMessageText(allOptionText); 090 message.setRenderWrapperTag(false); 091 092 multiValueControl.getRichOptions().add(0, new KeyMessage("", allOptionText, message)); 093 } 094 } 095 } 096 097 /** 098 * Invoked during the finalize phase to capture state of the component needs to support post operations. 099 */ 100 @Override 101 protected void addComponentPostMetadata() { 102 super.addComponentPostMetadata(); 103 104 Map<String, Map<String, Object>> lookupCriteriaFields = ViewLifecycle.getViewPostMetadata().getLookupCriteria(); 105 106 Map<String, Object> criteriaAttributes = new HashMap<String, Object>(); 107 criteriaAttributes.put(UifConstants.LookupCriteriaPostMetadata.COMPONENT_ID, this.getId()); 108 109 if (this.isDisableWildcardsAndOperators()) { 110 criteriaAttributes.put(UifConstants.LookupCriteriaPostMetadata.DISABLE_WILDCARDS_AND_OPERATORS, true); 111 } 112 113 if (this.getRequired()) { 114 criteriaAttributes.put(UifConstants.LookupCriteriaPostMetadata.REQUIRED, true); 115 } 116 117 if (this.hasSecureValue()) { 118 criteriaAttributes.put(UifConstants.LookupCriteriaPostMetadata.SECURE_VALUE, true); 119 } 120 121 ValidCharactersConstraint validCharactersConstraint = this.getValidCharactersConstraint(); 122 if (validCharactersConstraint != null) { 123 criteriaAttributes.put(UifConstants.LookupCriteriaPostMetadata.VALID_CHARACTERS_CONSTRAINT, 124 validCharactersConstraint); 125 } 126 127 lookupCriteriaFields.put(this.getPropertyName(), criteriaAttributes); 128 129 addHiddenComponentPostMetadata(lookupCriteriaFields); 130 } 131 132 /** 133 * Add hidden search criteria components. 134 * 135 * @param lookupCriteriaFields 136 */ 137 protected void addHiddenComponentPostMetadata(Map<String, Map<String, Object>> lookupCriteriaFields) { 138 for (String hiddenPropertyName: this.getAdditionalHiddenPropertyNames()) { 139 hiddenPropertyName = StringUtils.substringBetween(hiddenPropertyName, UifPropertyPaths.LOOKUP_CRITERIA + "[", "]"); 140 141 // Prevent overwriting of visible components. Note, hidden components are allowed to be overwritten. 142 if (!lookupCriteriaFields.containsKey(hiddenPropertyName)) { 143 Map<String, Object> criteriaAttributes = new HashMap<String, Object>(); 144 criteriaAttributes.put(UifConstants.LookupCriteriaPostMetadata.HIDDEN, true); 145 lookupCriteriaFields.put(hiddenPropertyName, criteriaAttributes); 146 } 147 } 148 } 149 150 /** 151 * Override of InputField copy to setup properties necessary to make the field usable for inputting 152 * search criteria. 153 * 154 * <p>Note super is not being called because we don't want to add restirctions that can cause problems 155 * with the use of wildcard</p> 156 * 157 * {@inheritDoc} 158 */ 159 @Override 160 public void copyFromAttributeDefinition(AttributeDefinition attributeDefinition) { 161 // label 162 if (StringUtils.isEmpty(getLabel())) { 163 setLabel(attributeDefinition.getLabel()); 164 } 165 166 // short label 167 if (StringUtils.isEmpty(getShortLabel())) { 168 setShortLabel(attributeDefinition.getShortLabel()); 169 } 170 171 // security 172 if ((attributeDefinition.getAttributeSecurity() != null) && ((getDataFieldSecurity() == null) || ( 173 getDataFieldSecurity().getAttributeSecurity() == null))) { 174 initializeComponentSecurity(); 175 176 getDataFieldSecurity().setAttributeSecurity(attributeDefinition.getAttributeSecurity()); 177 } 178 179 // options 180 if (getOptionsFinder() == null) { 181 setOptionsFinder(attributeDefinition.getOptionsFinder()); 182 } 183 184 // use control from dictionary if not specified and convert for searching 185 if (getControl() == null) { 186 Control control = convertControlToLookupControl(attributeDefinition); 187 setControl(control); 188 } 189 190 // overwrite maxLength to allow for wildcards and ranges; set a minimum max length unless it is greater than 100 191 setMaxLength(100); 192 if ( attributeDefinition.getMaxLength()!=null && (attributeDefinition.getMaxLength() > 100)) { 193 setMaxLength(attributeDefinition.getMaxLength()); 194 } 195 196 // set default value for active field to true 197 if (getDefaultValue() == null || (getDefaultValue() instanceof String && StringUtils.isEmpty((String)getDefaultValue()))) { 198 if ((StringUtils.equals(getPropertyName(), KRADPropertyConstants.ACTIVE))) { 199 setDefaultValue(KRADConstants.YES_INDICATOR_VALUE); 200 } 201 } 202 } 203 204 /** 205 * If control definition is defined on the given attribute definition, converts to an appropriate control for 206 * searching (if necessary) and returns a copy for setting on the field. 207 * 208 * @param attributeDefinition attribute definition instance to retrieve control from 209 * @return Control instance or null if not found 210 */ 211 protected static Control convertControlToLookupControl(AttributeDefinition attributeDefinition) { 212 if (attributeDefinition.getControlField() == null) { 213 return null; 214 } 215 216 Control newControl = null; 217 218 // convert checkbox to radio with yes/no/both options 219 if (CheckboxControl.class.isAssignableFrom(attributeDefinition.getControlField().getClass())) { 220 newControl = (RadioGroupControl) ComponentFactory.getNewComponentInstance( 221 ComponentFactory.CHECKBOX_CONVERTED_RADIO_CONTROL); 222 } 223 // text areas get converted to simple text inputs 224 else if (TextAreaControl.class.isAssignableFrom(attributeDefinition.getControlField().getClass())) { 225 newControl = ComponentFactory.getTextControl(); 226 } else { 227 newControl = ComponentUtils.copy(attributeDefinition.getControlField(), ""); 228 } 229 230 return newControl; 231 } 232 233 /** 234 * Invoked before search is carried out to perform any necessary filtering of the criteria. 235 * 236 * @param searchCriteria the search criteria to be filtered 237 * @return map of filtered search criteria 238 */ 239 public Map<String, String> filterSearchCriteria(Map<String, String> searchCriteria) { 240 return searchCriteria; 241 } 242 243 /** 244 * Indicates whether wildcard and other search operators should be disabled (treated as literals) for 245 * the input field. 246 * 247 * @return boolean true if wildcards and search operators should be disabled, false if enabled 248 */ 249 @BeanTagAttribute(name = "disableWildcardsAndOperators") 250 public boolean isDisableWildcardsAndOperators() { 251 return this.disableWildcardsAndOperators; 252 } 253 254 /** 255 * @see LookupInputField#isDisableWildcardsAndOperators() 256 */ 257 public void setDisableWildcardsAndOperators(boolean disableWildcardsAndOperators) { 258 this.disableWildcardsAndOperators = disableWildcardsAndOperators; 259 } 260 261 /** 262 * Indicates whether the option for all values (blank key, 'All' label) should be added to the lookup 263 * field, note this is only supported for {@link org.kuali.rice.krad.uif.control.MultiValueControl} instance. 264 * 265 * @return boolean true if all option should be added, false if not 266 */ 267 @BeanTagAttribute(name = "addControlSelectAllOption") 268 public boolean isAddControlSelectAllOption() { 269 return addControlSelectAllOption; 270 } 271 272 /** 273 * @see LookupInputField#isAddControlSelectAllOption() 274 */ 275 public void setAddControlSelectAllOption(boolean addControlSelectAllOption) { 276 this.addControlSelectAllOption = addControlSelectAllOption; 277 } 278 279 /** 280 * Indicates a field group should be created containing a from and to input field for date search 281 * ranges. 282 * 283 * <p> 284 * When this is set to true, the input field will be replaced by a field group that is created by 285 * copying the prototype {@link org.kuali.rice.krad.lookup.LookupView#getRangeFieldGroupPrototype()}. Within the 286 * field group, an lookup input field will be created for the from field, and this input will be used 287 * as the to date field. Between the two fields a message will be rendered that can be specified using 288 * {@link LookupView#getRangedToMessage()} 289 * </p> 290 * 291 * @return boolean true if ranged field group should be created, false if not 292 */ 293 public boolean isRanged() { 294 return ranged; 295 } 296 297 /** 298 * @see LookupInputField#isRanged() 299 */ 300 public void setRanged(boolean ranged) { 301 this.ranged = ranged; 302 } 303}