View Javadoc
1   /**
2    * Copyright 2005-2016 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kns.web.struts.form;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.kuali.rice.core.api.CoreApiServiceLocator;
20  import org.kuali.rice.kns.authorization.AuthorizationConstants;
21  import org.kuali.rice.core.api.encryption.EncryptionService;
22  import org.kuali.rice.kns.datadictionary.BusinessObjectEntry;
23  import org.kuali.rice.kns.inquiry.Inquirable;
24  import org.kuali.rice.kns.service.BusinessObjectAuthorizationService;
25  import org.kuali.rice.kns.service.BusinessObjectMetaDataService;
26  import org.kuali.rice.kns.service.KNSServiceLocator;
27  import org.kuali.rice.krad.bo.Exporter;
28  import org.kuali.rice.krad.datadictionary.exception.UnknownBusinessClassAttributeException;
29  import org.kuali.rice.krad.service.DataDictionaryService;
30  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
31  import org.kuali.rice.krad.service.KualiModuleService;
32  import org.kuali.rice.krad.service.ModuleService;
33  import org.kuali.rice.krad.util.KRADConstants;
34  
35  import javax.servlet.http.HttpServletRequest;
36  import java.lang.reflect.Constructor;
37  import java.security.GeneralSecurityException;
38  import java.util.ArrayList;
39  import java.util.Enumeration;
40  import java.util.HashMap;
41  import java.util.List;
42  import java.util.Map;
43  
44  /**
45   * This class is the action form for inquiries.
46   *
47   * @deprecated Use {@link org.kuali.rice.krad.web.form.InquiryForm}.
48   */
49  @Deprecated
50  public class InquiryForm extends KualiForm {
51      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(InquiryForm.class);
52  
53      private static final long serialVersionUID = 1L;
54      private String fieldConversions;
55      private List sections;
56      private String businessObjectClassName;
57      private Map editingMode;
58      private String formKey;
59      private boolean canExport;
60  
61      @Override
62      public void addRequiredNonEditableProperties(){
63      	super.addRequiredNonEditableProperties();
64      	registerRequiredNonEditableProperty(KRADConstants.BUSINESS_OBJECT_CLASS_ATTRIBUTE);
65      	registerRequiredNonEditableProperty(KRADConstants.DISPATCH_REQUEST_PARAMETER);
66      	registerRequiredNonEditableProperty(KRADConstants.DOC_FORM_KEY);
67      	registerRequiredNonEditableProperty(KRADConstants.FORM_KEY);
68      	registerRequiredNonEditableProperty(KRADConstants.FIELDS_CONVERSION_PARAMETER);
69      	registerRequiredNonEditableProperty(KRADConstants.BACK_LOCATION);
70      }
71  
72      /**
73       * 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
74       * if the value was passed in as encrypted
75       */
76      private Map<String, String> inquiryPrimaryKeys;
77  
78      private Map<String, String> inquiryDecryptedPrimaryKeys;
79  
80      /**
81       * A map of collection name -> Boolean mappings.  Used to denote whether a collection name is configured to show inactive records.
82       */
83      private Map<String, Boolean> inactiveRecordDisplay;
84  
85      private Inquirable inquirable;
86  
87      public InquiryForm() {
88          super();
89          this.editingMode = new HashMap();
90          this.editingMode.put(AuthorizationConstants.EditMode.VIEW_ONLY, "TRUE");
91          this.inactiveRecordDisplay = null;
92      }
93  
94      @Override
95      public void populate(HttpServletRequest request) {
96      // set to null for security reasons (so POJO form base can't access it), then we'll make an new instance of it after
97      // POJO form base is done
98          this.inquirable = null;
99          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 }