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.kns.web.struts.form; 017 018import org.apache.commons.lang.StringUtils; 019import org.kuali.rice.core.api.CoreApiServiceLocator; 020import org.kuali.rice.kns.authorization.AuthorizationConstants; 021import org.kuali.rice.core.api.encryption.EncryptionService; 022import org.kuali.rice.kns.datadictionary.BusinessObjectEntry; 023import org.kuali.rice.kns.inquiry.Inquirable; 024import org.kuali.rice.kns.service.BusinessObjectAuthorizationService; 025import org.kuali.rice.kns.service.BusinessObjectMetaDataService; 026import org.kuali.rice.kns.service.KNSServiceLocator; 027import org.kuali.rice.krad.bo.Exporter; 028import org.kuali.rice.krad.datadictionary.exception.UnknownBusinessClassAttributeException; 029import org.kuali.rice.krad.service.DataDictionaryService; 030import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 031import org.kuali.rice.krad.service.KualiModuleService; 032import org.kuali.rice.krad.service.ModuleService; 033import org.kuali.rice.krad.util.KRADConstants; 034 035import javax.servlet.http.HttpServletRequest; 036import java.lang.reflect.Constructor; 037import java.security.GeneralSecurityException; 038import java.util.ArrayList; 039import java.util.Enumeration; 040import java.util.HashMap; 041import java.util.List; 042import java.util.Map; 043 044/** 045 * This class is the action form for inquiries. 046 * 047 * @deprecated Use {@link org.kuali.rice.krad.web.form.InquiryForm}. 048 */ 049@Deprecated 050public class InquiryForm extends KualiForm { 051 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(InquiryForm.class); 052 053 private static final long serialVersionUID = 1L; 054 private String fieldConversions; 055 private List sections; 056 private String businessObjectClassName; 057 private Map editingMode; 058 private String formKey; 059 private boolean canExport; 060 061 @Override 062 public void addRequiredNonEditableProperties(){ 063 super.addRequiredNonEditableProperties(); 064 registerRequiredNonEditableProperty(KRADConstants.BUSINESS_OBJECT_CLASS_ATTRIBUTE); 065 registerRequiredNonEditableProperty(KRADConstants.DISPATCH_REQUEST_PARAMETER); 066 registerRequiredNonEditableProperty(KRADConstants.DOC_FORM_KEY); 067 registerRequiredNonEditableProperty(KRADConstants.FORM_KEY); 068 registerRequiredNonEditableProperty(KRADConstants.FIELDS_CONVERSION_PARAMETER); 069 registerRequiredNonEditableProperty(KRADConstants.BACK_LOCATION); 070 } 071 072 /** 073 * The following map is used to pass primary key values between invocations of the inquiry screens after the start method has been called. Values in this map will remain encrypted 074 * if the value was passed in as encrypted 075 */ 076 private Map<String, String> inquiryPrimaryKeys; 077 078 private Map<String, String> inquiryDecryptedPrimaryKeys; 079 080 /** 081 * A map of collection name -> Boolean mappings. Used to denote whether a collection name is configured to show inactive records. 082 */ 083 private Map<String, Boolean> inactiveRecordDisplay; 084 085 private Inquirable inquirable; 086 087 public InquiryForm() { 088 super(); 089 this.editingMode = new HashMap(); 090 this.editingMode.put(AuthorizationConstants.EditMode.VIEW_ONLY, "TRUE"); 091 this.inactiveRecordDisplay = null; 092 } 093 094 @Override 095 public void populate(HttpServletRequest request) { 096 // set to null for security reasons (so POJO form base can't access it), then we'll make an new instance of it after 097 // POJO form base is done 098 this.inquirable = null; 099 super.populate(request); 100 if (request.getParameter("returnLocation") != null) { 101 setBackLocation(request.getParameter("returnLocation")); 102 } 103 if (request.getParameter(KRADConstants.DOC_FORM_KEY) != null) { 104 setFormKey(request.getParameter(KRADConstants.DOC_FORM_KEY)); 105 } 106 //if the action is download attachment then skip the following populate logic 107 if(!KRADConstants.DOWNLOAD_BO_ATTACHMENT_METHOD.equals(getMethodToCall())){ 108 inquirable = getInquirable(getBusinessObjectClassName()); 109 110 // the following variable is true if the method to call is not start, meaning that we already called start 111 boolean passedFromPreviousInquiry = !KRADConstants.START_METHOD.equals(getMethodToCall()) && !KRADConstants.CONTINUE_WITH_INQUIRY_METHOD_TO_CALL.equals(getMethodToCall()) && !KRADConstants 112 .DOWNLOAD_CUSTOM_BO_ATTACHMENT_METHOD.equals(getMethodToCall()); 113 114 // There is no requirement that an inquiry screen must display the primary key values. However, when clicking on hide/show (without javascript) and 115 // hide/show inactive, the PK values are needed to allow the server to properly render results after the user clicks on a hide/show button that results 116 // in server processing. This line will populate the form with the PK values so that they may be used in subsequent requests. Note that encrypted 117 // values will remain encrypted in this map. 118 this.inquiryPrimaryKeys = new HashMap<String, String>(); 119 this.inquiryDecryptedPrimaryKeys = new HashMap<String, String>(); 120 121 populatePKFieldValues(request, getBusinessObjectClassName(), passedFromPreviousInquiry); 122 123 populateInactiveRecordsInIntoInquirable(inquirable, request); 124 populateExportCapabilities(request, getBusinessObjectClassName()); 125 } 126 } 127 128 protected Inquirable getInquirable(String boClassName) { 129 try { 130 Class customInquirableClass = null; 131 132 try { 133 BusinessObjectEntry entry = (BusinessObjectEntry) KRADServiceLocatorWeb.getDataDictionaryService().getDataDictionary().getBusinessObjectEntry(boClassName); 134 customInquirableClass = entry.getInquiryDefinition().getInquirableClass(); 135 } 136 catch (Exception e) { 137 LOG.error("Unable to correlate business object class with maintenance document entry"); 138 } 139 140 Inquirable kualiInquirable = KNSServiceLocator.getKualiInquirable(); // get inquirable impl from Spring 141 142 if (customInquirableClass != null) { 143 Class[] defaultConstructor = new Class[] {}; 144 Constructor cons = customInquirableClass.getConstructor(defaultConstructor); 145 kualiInquirable = (Inquirable) cons.newInstance(); 146 } 147 148 kualiInquirable.setBusinessObjectClass(Class.forName(boClassName)); 149 150 return kualiInquirable; 151 } 152 catch (Exception e) { 153 LOG.error("Error attempting to retrieve inquirable.", e); 154 throw new RuntimeException("Error attempting to retrieve inquirable."); 155 } 156 } 157 158 /** 159 * Gets the alt keys for a class. Will not return null but and empty list if no keys exist. 160 * 161 * @param clazz the class. 162 * @return the alt keys 163 */ 164 private List<List<String>> getAltkeys(Class<?> clazz) { 165 final KualiModuleService kualiModuleService = KRADServiceLocatorWeb.getKualiModuleService(); 166 final ModuleService moduleService = kualiModuleService.getResponsibleModuleService(clazz); 167 168 List<List<String>> altKeys = null; 169 if (moduleService != null) { 170 altKeys = moduleService.listAlternatePrimaryKeyFieldNames(clazz); 171 } 172 173 return altKeys != null ? altKeys : new ArrayList<List<String>>(); 174 } 175 176 protected void populatePKFieldValues(HttpServletRequest request, String boClassName, boolean passedFromPreviousInquiry) { 177 try { 178 EncryptionService encryptionService = CoreApiServiceLocator.getEncryptionService(); 179 DataDictionaryService dataDictionaryService = KRADServiceLocatorWeb.getDataDictionaryService(); 180 BusinessObjectAuthorizationService businessObjectAuthorizationService = KNSServiceLocator 181 .getBusinessObjectAuthorizationService(); 182 BusinessObjectMetaDataService businessObjectMetaDataService = KNSServiceLocator 183 .getBusinessObjectMetaDataService(); 184 185 Class businessObjectClass = Class.forName(boClassName); 186 187 // build list of key values from request, if all keys not given throw error 188 List<String> boPKeys = KRADServiceLocatorWeb.getLegacyDataAdapter().listPrimaryKeyFieldNames(businessObjectClass); 189 final List<List<String>> altKeys = this.getAltkeys(businessObjectClass); 190 191 altKeys.add(boPKeys); 192 boolean bFound = false; 193 for(List<String> boKeys : altKeys ){ 194 if(bFound) 195 break; 196 int keyCount = boKeys.size(); 197 int foundCount = 0; 198 for (String boKey : boKeys) { 199 String pkParamName = boKey; 200 if (passedFromPreviousInquiry) { 201 pkParamName = KRADConstants.INQUIRY_PK_VALUE_PASSED_FROM_PREVIOUS_REQUEST_PREFIX + pkParamName; 202 } 203 204 if (request.getParameter(pkParamName) != null) { 205 foundCount++; 206 String parameter = request.getParameter(pkParamName); 207 208 Boolean forceUppercase = Boolean.FALSE; 209 try { 210 forceUppercase = dataDictionaryService.getAttributeForceUppercase(businessObjectClass, boKey); 211 } catch (UnknownBusinessClassAttributeException ex) { 212 // swallowing exception because this check for ForceUppercase would 213 // require a DD entry for the attribute. it is only checking keys 214 // so most likely there should be an entry. 215 LOG.warn("BO class " + businessObjectClassName + " property " + boKey + " should probably have a DD definition.", ex); 216 } 217 String parameterCopy = parameter; 218 if (forceUppercase) { 219 parameter = parameter.toUpperCase(); 220 } 221 222 inquiryPrimaryKeys.put(boKey, parameter); 223 if (businessObjectAuthorizationService.attributeValueNeedsToBeEncryptedOnFormsAndLinks(businessObjectClass, boKey)) { 224 try { 225 if(CoreApiServiceLocator.getEncryptionService().isEnabled()) { 226 inquiryDecryptedPrimaryKeys.put(boKey, encryptionService.decrypt(parameterCopy)); 227 } 228 } catch (GeneralSecurityException e) { 229 LOG.error("BO class " + businessObjectClassName + " property " + boKey + " should have been encrypted, but there was a problem decrypting it."); 230 throw e; 231 } 232 } 233 else { 234 inquiryDecryptedPrimaryKeys.put(boKey, parameter); 235 } 236 } 237 } 238 if (foundCount == keyCount) { 239 bFound = true; 240 } 241 } 242 if(!bFound){ 243 LOG.error("All keys not given to lookup for bo class name " + businessObjectClass.getName()); 244 throw new RuntimeException("All keys not given to lookup for bo class name " + businessObjectClass.getName()); 245 } 246 } 247 catch (ClassNotFoundException e) { 248 LOG.error("Can't instantiate class: " + boClassName, e); 249 throw new RuntimeException("Can't instantiate class: " + boClassName); 250 } 251 catch (GeneralSecurityException e) { 252 LOG.error("Can't decrypt value", e); 253 throw new RuntimeException("Can't decrypt value"); 254 } 255 } 256 257 /** 258 * Examines the BusinessObject's data dictionary entry to determine if it supports 259 * XML export or not and set's canExport appropriately. 260 */ 261 protected void populateExportCapabilities(HttpServletRequest request, String boClassName) { 262 setCanExport(false); 263 BusinessObjectEntry businessObjectEntry = (BusinessObjectEntry) KRADServiceLocatorWeb.getDataDictionaryService().getDataDictionary().getBusinessObjectEntry(boClassName); 264 Class<? extends Exporter> exporterClass = businessObjectEntry.getExporterClass(); 265 if (exporterClass != null) { 266 try { 267 Exporter exporter = exporterClass.newInstance(); 268 if (exporter.getSupportedFormats(businessObjectEntry.getBusinessObjectClass()).contains(KRADConstants.XML_FORMAT)) { 269 setCanExport(true); 270 } 271 } catch (Exception e) { 272 LOG.error("Failed to locate or create exporter class: " + exporterClass, e); 273 throw new RuntimeException("Failed to locate or create exporter class: " + exporterClass); 274 } 275 } 276 } 277 278 279 /** 280 * @return Returns the fieldConversions. 281 */ 282 public String getFieldConversions() { 283 return fieldConversions; 284 } 285 286 287 /** 288 * @param fieldConversions The fieldConversions to set. 289 */ 290 public void setFieldConversions(String fieldConversions) { 291 this.fieldConversions = fieldConversions; 292 } 293 294 295 /** 296 * @return Returns the inquiry sections. 297 */ 298 public List getSections() { 299 return sections; 300 } 301 302 303 /** 304 * @param sections The sections to set. 305 */ 306 public void setSections(List sections) { 307 this.sections = sections; 308 } 309 310 /** 311 * @return Returns the businessObjectClassName. 312 */ 313 public String getBusinessObjectClassName() { 314 return businessObjectClassName; 315 } 316 317 /** 318 * @param businessObjectClassName The businessObjectClassName to set. 319 */ 320 public void setBusinessObjectClassName(String businessObjectClassName) { 321 this.businessObjectClassName = businessObjectClassName; 322 } 323 324 public Map getEditingMode() { 325 return editingMode; 326 } 327 328 /** 329 * Gets the map used to pass primary key values between invocations of the inquiry screens after the start method has been called. All field values that were passed in encrypted will 330 * be encrypted in this map 331 * 332 * @return 333 */ 334 public Map<String, String> getInquiryPrimaryKeys() { 335 return this.inquiryPrimaryKeys; 336 } 337 338 /** 339 * Gets the map used to pass primary key values between invocations of the inquiry screens after the start method has been called. All fields will be decrypted 340 * 341 * Purposely not named as a getter, to make it harder for POJOFormBase to access it 342 * 343 * @return 344 */ 345 public Map<String, String> retrieveInquiryDecryptedPrimaryKeys() { 346 return this.inquiryDecryptedPrimaryKeys; 347 } 348 349 /** 350 * Sets the map used to pass primary key values between invocations of the inquiry screens after the start method has been called. 351 * 352 * @param inquiryPrimaryKeys 353 */ 354 public void setInquiryPrimaryKeys(Map<String, String> inquiryPrimaryKeys) { 355 this.inquiryPrimaryKeys = inquiryPrimaryKeys; 356 } 357 358 /** 359 * Gets map of collection name -> Boolean mappings. Used to denote whether a collection name is configured to show inactive records. 360 * 361 * @return 362 */ 363 public Map<String, Boolean> getInactiveRecordDisplay() { 364 return getInquirable().getInactiveRecordDisplay(); 365 } 366 367 public Inquirable getInquirable() { 368 return inquirable; 369 } 370 371 protected void populateInactiveRecordsInIntoInquirable(Inquirable inquirable, HttpServletRequest request) { 372 for (Enumeration e = request.getParameterNames(); e.hasMoreElements();) { 373 String paramName = (String) e.nextElement(); 374 if (paramName.startsWith(KRADConstants.INACTIVE_RECORD_DISPLAY_PARAM_PREFIX)) { 375 String collectionName = StringUtils.substringAfter(paramName, KRADConstants.INACTIVE_RECORD_DISPLAY_PARAM_PREFIX); 376 Boolean showInactive = Boolean.parseBoolean(request.getParameter(paramName)); 377 inquirable.setShowInactiveRecords(collectionName, showInactive); 378 } 379 } 380 } 381 382 public String getFormKey() { 383 return this.formKey; 384 } 385 386 public void setFormKey(String formKey) { 387 this.formKey = formKey; 388 } 389 390 /** 391 * Returns true if this Inquiry supports XML export of the BusinessObject. 392 */ 393 public boolean isCanExport() { 394 return this.canExport; 395 } 396 397 /** 398 * Sets whether or not this Inquiry supports XML export of it's BusinessObject. 399 */ 400 public void setCanExport(boolean canExport) { 401 this.canExport = canExport; 402 } 403 404 405}