001 /** 002 * Copyright 2004-2013 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.hr.time.base.web; 017 018 import java.security.GeneralSecurityException; 019 import java.sql.Date; 020 import java.text.SimpleDateFormat; 021 import java.util.ArrayList; 022 import java.util.Collections; 023 import java.util.HashMap; 024 import java.util.Iterator; 025 import java.util.List; 026 import java.util.Map; 027 import java.util.Properties; 028 029 import org.apache.commons.collections.BidiMap; 030 import org.apache.commons.collections.bidimap.DualHashBidiMap; 031 import org.apache.commons.lang.StringUtils; 032 import org.apache.log4j.Logger; 033 import org.kuali.rice.core.web.format.Formatter; 034 import org.kuali.rice.kns.inquiry.KualiInquirableImpl; 035 import org.kuali.rice.kns.lookup.HtmlData; 036 import org.kuali.rice.kns.lookup.HtmlData.AnchorHtmlData; 037 import org.kuali.rice.kns.lookup.LookupUtils; 038 import org.kuali.rice.kns.service.KNSServiceLocator; 039 import org.kuali.rice.krad.bo.BusinessObject; 040 import org.kuali.rice.krad.bo.DataObjectRelationship; 041 import org.kuali.rice.krad.bo.DocumentHeader; 042 import org.kuali.rice.krad.bo.ExternalizableBusinessObject; 043 import org.kuali.rice.krad.datadictionary.AttributeSecurity; 044 import org.kuali.rice.krad.util.ExternalizableBusinessObjectUtils; 045 import org.kuali.rice.krad.util.KRADConstants; 046 import org.kuali.rice.krad.util.ObjectUtils; 047 import org.kuali.rice.krad.util.UrlFactory; 048 049 public class TkInquirableImpl extends KualiInquirableImpl { 050 051 private static final Logger LOG = Logger.getLogger(TkInquirableImpl.class); 052 053 @Override 054 // copied the getInquiryUrl() from KualiInquirableImpl and added effectiveDate to parameters 055 public HtmlData getInquiryUrl(BusinessObject businessObject, String attributeName, boolean forceInquiry) { 056 Properties parameters = new Properties(); 057 AnchorHtmlData hRef = new AnchorHtmlData(KRADConstants.EMPTY_STRING, KRADConstants.EMPTY_STRING); 058 parameters.put(KRADConstants.DISPATCH_REQUEST_PARAMETER, "start"); 059 060 Class inquiryBusinessObjectClass = null; 061 String attributeRefName = ""; 062 boolean isPkReference = false; 063 064 boolean doesNestedReferenceHaveOwnPrimitiveReference = false; 065 BusinessObject nestedBusinessObject = null; 066 067 if (attributeName.equals(getBusinessObjectDictionaryService().getTitleAttribute(businessObject.getClass()))) { 068 inquiryBusinessObjectClass = businessObject.getClass(); 069 isPkReference = true; 070 } 071 else { 072 if (ObjectUtils.isNestedAttribute(attributeName)) { 073 // if we have a reference object, we should determine if we should either provide an inquiry link to 074 // the reference object itself, or some other nested primitive. 075 076 // for example, if the attribute is "referenceObject.someAttribute", and there is no primitive reference for 077 // "someAttribute", then an inquiry link is provided to the "referenceObject". If it does have a primitive reference, then 078 // the inquiry link is directed towards it instead 079 String nestedReferenceName = ObjectUtils.getNestedAttributePrefix(attributeName); 080 Object nestedReferenceObject = ObjectUtils.getNestedValue(businessObject, nestedReferenceName); 081 082 if (ObjectUtils.isNotNull(nestedReferenceObject) && nestedReferenceObject instanceof BusinessObject) { 083 nestedBusinessObject = (BusinessObject) nestedReferenceObject; 084 String nestedAttributePrimitive = ObjectUtils.getNestedAttributePrimitive(attributeName); 085 086 if (nestedAttributePrimitive.equals(getBusinessObjectDictionaryService().getTitleAttribute(nestedBusinessObject.getClass()))) { 087 // we are going to inquiry the record that contains the attribute we're rendering an inquiry URL for 088 inquiryBusinessObjectClass = nestedBusinessObject.getClass(); 089 // I know it's already set to false, just to show how this variable is set 090 doesNestedReferenceHaveOwnPrimitiveReference = false; 091 } 092 else { 093 Map primitiveReference = LookupUtils.getPrimitiveReference(nestedBusinessObject, nestedAttributePrimitive); 094 if (primitiveReference != null && !primitiveReference.isEmpty()) { 095 attributeRefName = (String) primitiveReference.keySet().iterator().next(); 096 inquiryBusinessObjectClass = (Class) primitiveReference.get(attributeRefName); 097 doesNestedReferenceHaveOwnPrimitiveReference = true; 098 } 099 else { 100 // we are going to inquiry the record that contains the attribute we're rendering an inquiry URL for 101 inquiryBusinessObjectClass = nestedBusinessObject.getClass(); 102 // I know it's already set to false, just to show how this variable is set 103 doesNestedReferenceHaveOwnPrimitiveReference = false; 104 } 105 } 106 } 107 } 108 else { 109 Map primitiveReference = LookupUtils.getPrimitiveReference(businessObject, attributeName); 110 if (primitiveReference != null && !primitiveReference.isEmpty()) { 111 attributeRefName = (String) primitiveReference.keySet().iterator().next(); 112 inquiryBusinessObjectClass = (Class) primitiveReference.get(attributeRefName); 113 } 114 } 115 } 116 117 if (inquiryBusinessObjectClass != null && DocumentHeader.class.isAssignableFrom(inquiryBusinessObjectClass)) { 118 String documentNumber = (String) ObjectUtils.getPropertyValue(businessObject, attributeName); 119 if (!StringUtils.isBlank(documentNumber)) { 120 // if NullPointerException on the following line, maybe the Spring bean wasn't injected w/ KualiConfigurationException, or if 121 // instances of a sub-class of this class are not Spring created, then override getKualiConfigurationService() in the subclass 122 // to return the configuration service from a Spring service locator (or set it). 123 hRef.setHref(getKualiConfigurationService().getPropertyValueAsString(KRADConstants.WORKFLOW_URL_KEY) + KRADConstants.DOCHANDLER_DO_URL + documentNumber + KRADConstants.DOCHANDLER_URL_CHUNK); 124 } 125 return hRef; 126 } 127 128 if (inquiryBusinessObjectClass == null || getBusinessObjectDictionaryService().isInquirable(inquiryBusinessObjectClass) == null || !getBusinessObjectDictionaryService().isInquirable(inquiryBusinessObjectClass).booleanValue()) { 129 return hRef; 130 } 131 132 synchronized (SUPER_CLASS_TRANSLATOR_LIST) { 133 for (Class clazz : SUPER_CLASS_TRANSLATOR_LIST) { 134 if (clazz.isAssignableFrom(inquiryBusinessObjectClass)) { 135 inquiryBusinessObjectClass = clazz; 136 break; 137 } 138 } 139 } 140 141 if (!inquiryBusinessObjectClass.isInterface() && ExternalizableBusinessObject.class.isAssignableFrom(inquiryBusinessObjectClass)) { 142 inquiryBusinessObjectClass = ExternalizableBusinessObjectUtils.determineExternalizableBusinessObjectSubInterface(inquiryBusinessObjectClass); 143 } 144 145 parameters.put(KRADConstants.BUSINESS_OBJECT_CLASS_ATTRIBUTE, inquiryBusinessObjectClass.getName()); 146 147 148 // listPrimaryKeyFieldNames returns an unmodifiable list. So a copy is necessary. 149 List<String> keys = new ArrayList<String>(getBusinessObjectMetaDataService().listPrimaryKeyFieldNames(inquiryBusinessObjectClass)); 150 151 if (keys == null) { 152 keys = Collections.emptyList(); 153 } 154 155 DataObjectRelationship businessObjectRelationship = null; 156 157 if(attributeRefName != null && !"".equals(attributeRefName)){ 158 businessObjectRelationship = 159 getBusinessObjectMetaDataService().getBusinessObjectRelationship( 160 businessObject, attributeRefName); 161 162 if (businessObjectRelationship != null && businessObjectRelationship.getParentToChildReferences() != null) { 163 for (String targetNamePrimaryKey : businessObjectRelationship.getParentToChildReferences().values()) { 164 keys.add(targetNamePrimaryKey); 165 } 166 } 167 } 168 // build key value url parameters used to retrieve the business object 169 String keyName = null; 170 String keyConversion = null; 171 Map<String, String> fieldList = new HashMap<String,String>(); 172 for (Iterator iter = keys.iterator(); iter.hasNext();) { 173 keyName = (String) iter.next(); 174 keyConversion = keyName; 175 if (ObjectUtils.isNestedAttribute(attributeName)) { 176 if (doesNestedReferenceHaveOwnPrimitiveReference) { 177 String nestedAttributePrefix = ObjectUtils.getNestedAttributePrefix(attributeName); 178 //String foreignKeyFieldName = getBusinessObjectMetaDataService().getForeignKeyFieldName( 179 // inquiryBusinessObjectClass.getClass(), attributeRefName, keyName); 180 181 String foreignKeyFieldName = getBusinessObjectMetaDataService().getForeignKeyFieldName( 182 nestedBusinessObject.getClass(), attributeRefName, keyName); 183 keyConversion = nestedAttributePrefix + "." + foreignKeyFieldName; 184 } 185 else { 186 keyConversion = ObjectUtils.getNestedAttributePrefix(attributeName) + "." + keyName; 187 } 188 } 189 else { 190 if (isPkReference) { 191 keyConversion = keyName; 192 } 193 else if (businessObjectRelationship != null) { 194 //Using BusinessObjectMetaDataService instead of PersistenceStructureService 195 //since otherwise, relationship information from datadictionary is not used at all 196 //Also, BOMDS.getBusinessObjectRelationship uses PersistenceStructureService, 197 //so both datadictionary and the persistance layer get covered 198 /* 199 BusinessObjectRelationship businessObjectRelationship = 200 getBusinessObjectMetaDataService().getBusinessObjectRelationship( 201 businessObject, attributeRefName); 202 */ 203 BidiMap bidiMap = new DualHashBidiMap(businessObjectRelationship.getParentToChildReferences()); 204 keyConversion = (String)bidiMap.getKey(keyName); 205 //keyConversion = getPersistenceStructureService().getForeignKeyFieldName(businessObject.getClass(), attributeRefName, keyName); 206 } 207 } 208 Object keyValue = null; 209 if (keyConversion != null) { 210 keyValue = ObjectUtils.getPropertyValue(businessObject, keyConversion); 211 } 212 213 if (keyValue == null) { 214 keyValue = ""; 215 } else if (keyValue instanceof java.sql.Date) { //format the date for passing in url 216 if (Formatter.findFormatter(keyValue.getClass()) != null) { 217 Formatter formatter = Formatter.getFormatter(keyValue.getClass()); 218 keyValue = (String) formatter.format(keyValue); 219 } 220 } else { 221 keyValue = keyValue.toString(); 222 } 223 224 // Encrypt value if it is a field that has restriction that prevents a value from being shown to user, 225 // because we don't want the browser history to store the restricted attribute's value in the URL 226 AttributeSecurity attributeSecurity = KNSServiceLocator.getDataDictionaryService().getAttributeSecurity(businessObject.getClass().getName(), keyName); 227 if(attributeSecurity != null && attributeSecurity.hasRestrictionThatRemovesValueFromUI()){ 228 try { 229 keyValue = getEncryptionService().encrypt(keyValue); 230 } 231 catch (GeneralSecurityException e) { 232 LOG.error("Exception while trying to encrypted value for inquiry framework.", e); 233 throw new RuntimeException(e); 234 } 235 } 236 237 parameters.put(keyName, keyValue); 238 fieldList.put(keyName, keyValue.toString()); 239 } 240 241 // pass in effective date of the businessObject 242 Date aDate = (Date) ObjectUtils.getPropertyValue(businessObject, "effectiveDate"); 243 if(aDate != null) { 244 parameters.put("effectiveDate", new SimpleDateFormat("MM/dd/yyyy").format(aDate)); 245 } 246 247 return getHyperLink(inquiryBusinessObjectClass, fieldList, UrlFactory.parameterizeUrl(KRADConstants.INQUIRY_ACTION, parameters)); 248 } 249 }