001/** 002 * Copyright 2005-2015 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.kew.rule.bo; 017 018import org.apache.commons.lang.StringUtils; 019import org.kuali.rice.core.api.uif.RemotableAttributeError; 020import org.kuali.rice.core.api.util.ConcreteKeyValue; 021import org.kuali.rice.core.api.util.KeyValue; 022import org.kuali.rice.core.api.util.RiceKeyConstants; 023import org.kuali.rice.core.web.format.BooleanFormatter; 024import org.kuali.rice.core.web.format.CollectionFormatter; 025import org.kuali.rice.core.web.format.DateFormatter; 026import org.kuali.rice.core.web.format.Formatter; 027import org.kuali.rice.kew.api.KewApiServiceLocator; 028import org.kuali.rice.kew.api.rule.RuleTemplate; 029import org.kuali.rice.kew.api.rule.RuleTemplateAttribute; 030import org.kuali.rice.kew.lookupable.MyColumns; 031import org.kuali.rice.kew.rule.RuleBaseValues; 032import org.kuali.rice.kew.rule.WorkflowRuleAttributeRows; 033import org.kuali.rice.kew.rule.service.RuleServiceInternal; 034import org.kuali.rice.kew.service.KEWServiceLocator; 035import org.kuali.rice.kim.api.KimConstants; 036import org.kuali.rice.kim.api.group.Group; 037import org.kuali.rice.kim.api.identity.Person; 038import org.kuali.rice.kim.api.identity.principal.Principal; 039import org.kuali.rice.kim.api.services.KimApiServiceLocator; 040import org.kuali.rice.kns.document.authorization.BusinessObjectRestrictions; 041import org.kuali.rice.kns.lookup.HtmlData; 042import org.kuali.rice.kns.lookup.LookupableHelperService; 043import org.kuali.rice.kns.service.KNSServiceLocator; 044import org.kuali.rice.kns.web.comparator.CellComparatorHelper; 045import org.kuali.rice.kns.web.struts.form.LookupForm; 046import org.kuali.rice.kns.web.ui.Column; 047import org.kuali.rice.kns.web.ui.Field; 048import org.kuali.rice.kns.web.ui.ResultRow; 049import org.kuali.rice.kns.web.ui.Row; 050import org.kuali.rice.krad.bo.BusinessObject; 051import org.kuali.rice.krad.bo.PersistableBusinessObject; 052import org.kuali.rice.krad.data.KradDataServiceLocator; 053import org.kuali.rice.krad.exception.ValidationException; 054import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 055import org.kuali.rice.krad.util.GlobalVariables; 056import org.kuali.rice.krad.util.KRADConstants; 057import org.kuali.rice.krad.util.KRADUtils; 058 059import java.sql.Date; 060import java.util.ArrayList; 061import java.util.Collection; 062import java.util.HashMap; 063import java.util.Iterator; 064import java.util.List; 065import java.util.Map; 066 067/** 068 * This is a description of what this class does - jjhanso don't forget to fill this in. 069 * 070 * @author Kuali Rice Team (rice.collab@kuali.org) 071 * 072 */ 073public class RuleBaseValuesLookupableHelperServiceImpl extends AbstractRuleLookupableHelperServiceImpl { 074 075 private LookupableHelperService ruleDelegationLookupableHelperService; 076 private List<?> delegationPkNames; 077 078 private static final String RULE_TEMPLATE_PROPERTY_NAME = "ruleTemplate.name"; 079 private static final String RULE_ID_PROPERTY_NAME = "id"; 080 private static final String RULE_TEMPLATE_ID_PROPERTY_NAME = "ruleTemplateId"; 081 private static final String ACTIVE_IND_PROPERTY_NAME = "active"; 082 private static final String DELEGATE_RULE_PROPERTY_NAME = "delegateRule"; 083 private static final String DOC_TYP_NAME_PROPERTY_NAME = "documentType.name"; 084 private static final String RULE_DESC_PROPERTY_NAME = "description"; 085 086 @Override 087 public boolean checkForAdditionalFields(Map<String, String> fieldValues) { 088 String ruleTemplateNameParam = fieldValues.get(RULE_TEMPLATE_PROPERTY_NAME); 089 090 return super.checkForAdditionalFields(fieldValues, ruleTemplateNameParam); 091 } 092 093 @Override 094 public List<? extends BusinessObject> getSearchResults(Map<String, String> fieldValues) { 095 String docTypeNameParam = fieldValues.get(DOC_TYP_NAME_PROPERTY_NAME); 096 String ruleTemplateIdParam = fieldValues.get(RULE_TEMPLATE_ID_PROPERTY_NAME); 097 String ruleTemplateNameParam = fieldValues.get(RULE_TEMPLATE_PROPERTY_NAME); 098 String groupIdParam = fieldValues.get(GROUP_REVIEWER_PROPERTY_NAME); 099 String groupNameParam = fieldValues.get(GROUP_REVIEWER_NAME_PROPERTY_NAME); 100 String groupNamespaceParam = fieldValues.get(GROUP_REVIEWER_NAMESPACE_PROPERTY_NAME); 101 String networkIdParam = fieldValues.get(PERSON_REVIEWER_PROPERTY_NAME); 102 String userDirectiveParam = fieldValues.get(PERSON_REVIEWER_TYPE_PROPERTY_NAME); 103 String activeParam = fieldValues.get(ACTIVE_IND_PROPERTY_NAME); 104 String ruleIdParam = fieldValues.get(RULE_ID_PROPERTY_NAME); 105 String ruleDescription = fieldValues.get(RULE_DESC_PROPERTY_NAME); 106 String deleteSelection = fieldValues.get(DELEGATE_RULE_PROPERTY_NAME); 107 108 String docTypeSearchName = null; 109 String workflowId = null; 110 String workgroupId = null; 111 String ruleTemplateId = null; 112 Boolean isDelegateRule = null; 113 Boolean isActive = null; 114 String ruleId = null; 115 116 117 //for KULRICE-3678 118 if(StringUtils.isNotBlank(deleteSelection)) 119 { 120 if(deleteSelection.equalsIgnoreCase("Y")) { 121 isDelegateRule = Boolean.TRUE; 122 } else { 123 isDelegateRule = Boolean.FALSE; 124 } 125 } 126 127 if (StringUtils.isNotBlank(ruleIdParam)) { 128 ruleId = ruleIdParam.trim(); 129 } 130 131 if (StringUtils.isNotBlank(activeParam)) { 132 if (activeParam.equals("Y")) { 133 isActive = Boolean.TRUE; 134 } else { 135 isActive = Boolean.FALSE; 136 } 137 } 138 139 if (StringUtils.isNotBlank(docTypeNameParam)) { 140 docTypeSearchName = docTypeNameParam.replace('*', '%'); 141 docTypeSearchName = "%" + docTypeSearchName.trim() + "%"; 142 } 143 144 if (StringUtils.isNotBlank(networkIdParam)) { 145 Principal principal = KimApiServiceLocator.getIdentityService().getPrincipalByPrincipalName(networkIdParam); 146 if (principal != null) { 147 workflowId = principal.getPrincipalId(); 148 } 149 } 150 151 if (!StringUtils.isEmpty(groupIdParam) || !StringUtils.isEmpty(groupNameParam)) { 152 Group group = null; 153 if (groupIdParam != null && !"".equals(groupIdParam)) { 154 group = getGroupService().getGroup(groupIdParam.trim()); 155 } else { 156 if (groupNamespaceParam == null) { 157 groupNamespaceParam = KimConstants.KIM_GROUP_DEFAULT_NAMESPACE_CODE; 158 } 159 group = getGroupService().getGroupByNamespaceCodeAndName(groupNamespaceParam, groupNameParam.trim()); 160 if (group == null) { 161 GlobalVariables.getMessageMap().putError(GROUP_REVIEWER_NAMESPACE_PROPERTY_NAME, RiceKeyConstants.ERROR_CUSTOM, INVALID_WORKGROUP_ERROR); 162 } else { 163 workgroupId = group.getId(); 164 } 165 } 166 } 167 168 Map<String, String> attributes = null; 169 MyColumns myColumns = new MyColumns(); 170 if (StringUtils.isNotBlank(ruleTemplateNameParam) || StringUtils.isNotBlank(ruleTemplateIdParam) && !"null".equals(ruleTemplateIdParam)) { 171 RuleTemplate ruleTemplate = null; 172 if (StringUtils.isNotBlank(ruleTemplateIdParam)) { 173 ruleTemplate = KewApiServiceLocator.getRuleService().getRuleTemplate(ruleTemplateIdParam); 174 } else { 175 ruleTemplate = KewApiServiceLocator.getRuleService().getRuleTemplateByName(ruleTemplateNameParam.trim()); 176 ruleTemplateId = ruleTemplate.getId(); 177 } 178 179 attributes = new HashMap<String, String>(); 180 for (RuleTemplateAttribute ruleTemplateAttribute : ruleTemplate.getActiveRuleTemplateAttributes()) { 181 if (!RuleAttribute.isWorkflowAttribute(ruleTemplateAttribute.getRuleAttribute().getType())) { 182 continue; 183 } 184 WorkflowRuleAttributeRows workflowRuleAttributeRows = 185 KEWServiceLocator.getWorkflowRuleAttributeMediator().getSearchRows(fieldValues, ruleTemplateAttribute, false); 186 187 for (RemotableAttributeError error : workflowRuleAttributeRows.getValidationErrors()) { 188 GlobalVariables.getMessageMap().putError(error.getAttributeName(), RiceKeyConstants.ERROR_CUSTOM, error.getMessage()); 189 } 190 191 for (Row row : workflowRuleAttributeRows.getRows()) { 192 for (Field field : row.getFields()) { 193 if (fieldValues.get(field.getPropertyName()) != null) { 194 String attributeParam = fieldValues.get(field.getPropertyName()); 195 if (!attributeParam.equals("")) { 196 attributes.put(field.getPropertyName(), attributeParam.trim()); 197 } 198 } 199 if (field.getFieldType().equals(Field.TEXT) || field.getFieldType().equals(Field.DROPDOWN) || field.getFieldType().equals(Field.DROPDOWN_REFRESH) || field.getFieldType().equals(Field.RADIO)) { 200 myColumns.getColumns().add(new ConcreteKeyValue(field.getPropertyName(), ruleTemplateAttribute.getId())); 201 } 202 } 203 } 204 } 205 } 206 207 if (!StringUtils.isEmpty(ruleDescription)) { 208 ruleDescription = ruleDescription.replace('*', '%'); 209 ruleDescription = "%" + ruleDescription.trim() + "%"; 210 } 211 212 if (!GlobalVariables.getMessageMap().hasNoErrors()) { 213 throw new ValidationException("errors in search criteria"); 214 } 215 216 // TODO: replace this with new API find method ?? 217 List<RuleBaseValues> rules = getRuleService().search(docTypeSearchName, ruleId, ruleTemplateId, ruleDescription, workgroupId, workflowId, isDelegateRule, isActive, attributes, userDirectiveParam); 218 List<RuleBaseValues> displayList = new ArrayList<RuleBaseValues>(); 219 220 for (RuleBaseValues record : rules) { 221 if (StringUtils.isEmpty(record.getDescription())) { 222 record.setDescription(""); 223 } 224 225 if (ruleTemplateNameParam != null && !ruleTemplateNameParam.trim().equals("") || ruleTemplateIdParam != null && !"".equals(ruleTemplateIdParam) && !"null".equals(ruleTemplateIdParam)) { 226 MyColumns myNewColumns = new MyColumns(); 227 for (KeyValue pair : myColumns.getColumns()) { 228 final KeyValue newPair; 229 if (record.getRuleExtensionValue(pair.getValue(), pair.getKey().toString()) != null) { 230 newPair = new ConcreteKeyValue(pair.getKey(), record.getRuleExtensionValue(pair.getValue(), pair.getKey().toString()).getValue()); 231 } else { 232 newPair = new ConcreteKeyValue(pair.getKey(), ""); 233 } 234 myNewColumns.getColumns().add(newPair); 235 record.getFieldValues().put(newPair.getKey(), newPair.getValue()); 236 } 237 record.setMyColumns(myNewColumns); 238 } 239 240 StringBuffer returnUrl = new StringBuffer("<a href=\""); 241 returnUrl.append(fieldValues.get(BACK_LOCATION)).append("?methodToCall=refresh&docFormKey=").append(fieldValues.get(DOC_FORM_KEY)).append("&"); 242 243 returnUrl.append(RULE_ID_PROPERTY_NAME); 244 returnUrl.append("=").append(record.getId()).append("\">return value</a>"); 245 record.setReturnUrl(returnUrl.toString()); 246 247 String destinationUrl = "<a href=\"Rule.do?methodToCall=report¤tRuleId=" + record.getId() + "\">report</a>"; 248 249 record.setDestinationUrl(destinationUrl); 250 251 displayList.add(record); 252 } 253 return displayList; 254 255 } 256 257 private RuleServiceInternal getRuleService() { 258 return (RuleServiceInternal) KEWServiceLocator.getService(KEWServiceLocator.RULE_SERVICE); 259 } 260 261 @Override 262 public Collection performLookup(LookupForm lookupForm, 263 Collection resultTable, boolean bounded) { 264 //return super.performLookup(lookupForm, resultTable, bounded); 265 setBackLocation((String) lookupForm.getFieldsForLookup().get(KRADConstants.BACK_LOCATION)); 266 setDocFormKey((String) lookupForm.getFieldsForLookup().get(KRADConstants.DOC_FORM_KEY)); 267 Collection displayList; 268 269 // call search method to get results 270 if (bounded) { 271 displayList = getSearchResults(lookupForm.getFieldsForLookup()); 272 } 273 else { 274 displayList = getSearchResultsUnbounded(lookupForm.getFieldsForLookup()); 275 } 276 277 HashMap<String,Class> propertyTypes = new HashMap<String, Class>(); 278 279 boolean hasReturnableRow = false; 280 281 List returnKeys = getReturnKeys(); 282 283 List pkNames = KRADServiceLocatorWeb.getLegacyDataAdapter().listPrimaryKeyFieldNames(getBusinessObjectClass()); 284 Person user = GlobalVariables.getUserSession().getPerson(); 285 286 // iterate through result list and wrap rows with return url and action urls 287 for (Iterator iter = displayList.iterator(); iter.hasNext();) { 288 BusinessObject element = (BusinessObject) iter.next(); 289 if(element instanceof PersistableBusinessObject){ 290 lookupForm.setLookupObjectId(((PersistableBusinessObject)element).getObjectId()); 291 } 292 293 BusinessObjectRestrictions businessObjectRestrictions = getBusinessObjectAuthorizationService().getLookupResultRestrictions(element, user); 294 295 HtmlData returnUrl = getReturnUrl(element, lookupForm, returnKeys, businessObjectRestrictions); 296 297 String actionUrls = getActionUrls(element, pkNames, businessObjectRestrictions); 298 //Fix for JIRA - KFSMI-2417 299 if("".equals(actionUrls)){ 300 actionUrls = ACTION_URLS_EMPTY; 301 } 302 303 // Determine whether or not this rule is a delegate rule. 304 boolean isRuleDelegation = (element instanceof RuleBaseValues && ((RuleBaseValues) element).getDelegateRule().booleanValue()); 305 306 List<Column> columns = getColumns(); 307 for (Object element2 : columns) { 308 309 Column col = (Column) element2; 310 Formatter formatter = col.getFormatter(); 311 312 // pick off result column from result list, do formatting 313 String propValue = KRADConstants.EMPTY_STRING; 314 Object prop = null; 315 boolean skipPropTypeCheck = false; 316 //try to get value elsewhere 317 if (element instanceof RuleBaseValues) { 318 prop = ((RuleBaseValues)element).getFieldValues().get(col.getPropertyName()); 319 skipPropTypeCheck = true; 320 } 321 if (prop == null) { 322 prop = KradDataServiceLocator.getDataObjectService().wrap(element).getPropertyValueNullSafe(col.getPropertyName()); 323 } 324 325 // set comparator and formatter based on property type 326 Class propClass = propertyTypes.get(col.getPropertyName()); 327 if ( propClass == null && !skipPropTypeCheck) { 328 try { 329 propClass = KRADServiceLocatorWeb.getLegacyDataAdapter().getPropertyType(element, col.getPropertyName()); 330 propertyTypes.put( col.getPropertyName(), propClass ); 331 } catch (Exception e) { 332 throw new RuntimeException("Cannot access PropertyType for property " + "'" + col.getPropertyName() + "' " + " on an instance of '" + element.getClass().getName() + "'.", e); 333 } 334 } 335 336 // formatters 337 if (prop != null) { 338 // for Booleans, always use BooleanFormatter 339 if (prop instanceof Boolean) { 340 formatter = new BooleanFormatter(); 341 } 342 343 // for Dates, always use DateFormatter 344 if (prop instanceof Date) { 345 formatter = new DateFormatter(); 346 } 347 348 // for collection, use the list formatter if a formatter hasn't been defined yet 349 if (prop instanceof Collection && formatter == null) { 350 formatter = new CollectionFormatter(); 351 } 352 353 if (formatter != null) { 354 propValue = (String) formatter.format(prop); 355 } 356 else { 357 propValue = prop.toString(); 358 } 359 } 360 361 // comparator 362 col.setComparator(CellComparatorHelper.getAppropriateComparatorForPropertyClass(propClass)); 363 col.setValueComparator(CellComparatorHelper.getAppropriateValueComparatorForPropertyClass(propClass)); 364 365 propValue = maskValueIfNecessary(element.getClass(), col.getPropertyName(), propValue, businessObjectRestrictions); 366 367 col.setPropertyValue(propValue); 368 369 if (StringUtils.isNotBlank(propValue)) { 370 if (RULE_ID_PROPERTY_NAME.equals(col.getPropertyName()) && isRuleDelegation) { 371 // If the row represents a delegate rule, make the ID column's inquiry link lead to the corresponding delegate rule instead. 372 List<?> delegationList = KEWServiceLocator.getRuleDelegationService().findByDelegateRuleId( 373 ((RuleBaseValues) element).getId()); 374 if (KRADUtils.isNotNull(delegationList) && !delegationList.isEmpty()) { 375 BusinessObject ruleDelegation = (BusinessObject) delegationList.get(0); 376 col.setColumnAnchor(getInquiryUrl(ruleDelegation, "ruleDelegationId")); 377 } else { 378 col.setColumnAnchor(getInquiryUrl(element, col.getPropertyName())); 379 } 380 }else { 381 col.setColumnAnchor(getInquiryUrl(element, col.getPropertyName())); 382 } 383 384 } 385 } 386 387 ResultRow row = new ResultRow(columns, returnUrl.constructCompleteHtmlTag(), actionUrls); 388 row.setRowId(returnUrl.getName()); 389 row.setReturnUrlHtmlData(returnUrl); 390 // because of concerns of the BO being cached in session on the ResultRow, 391 // let's only attach it when needed (currently in the case of export) 392 if (getBusinessObjectDictionaryService().isExportable(getBusinessObjectClass())) { 393 row.setBusinessObject(element); 394 } 395 if(element instanceof PersistableBusinessObject){ 396 row.setObjectId((((PersistableBusinessObject)element).getObjectId())); 397 } 398 399 400 boolean rowReturnable = isResultReturnable(element); 401 row.setRowReturnable(rowReturnable); 402 if (rowReturnable) { 403 hasReturnableRow = true; 404 } 405 resultTable.add(row); 406 } 407 408 lookupForm.setHasReturnableRow(hasReturnableRow); 409 410 return displayList; 411 } 412 413 @Override 414 public List<HtmlData> getCustomActionUrls(BusinessObject businessObject, 415 List pkNames) { 416 RuleBaseValues ruleBaseValues = (RuleBaseValues)businessObject; 417 List<HtmlData> htmlDataList = new ArrayList<HtmlData>(); 418 if (StringUtils.isNotBlank(ruleBaseValues.getRuleTemplateName()) && StringUtils.isNotBlank(getMaintenanceDocumentTypeName())) { 419 if (ruleBaseValues.getDelegateRule().booleanValue()) { 420 // If the rule is a delegate rule, have the edit/copy links open the rule delegation maintenance document screen instead. 421 List<?> delegationList = KEWServiceLocator.getRuleDelegationService().findByDelegateRuleId(ruleBaseValues.getId()); 422 if (KRADUtils.isNotNull(delegationList) && !delegationList.isEmpty()) { 423 BusinessObject ruleDelegation = (BusinessObject) delegationList.get(0); 424 // Retrieve the rule delegation lookupable helper service and the primary key names, if they have not been obtained yet. 425 if (ruleDelegationLookupableHelperService == null) { 426 ruleDelegationLookupableHelperService = KNSServiceLocator.getLookupable( 427 KNSServiceLocator.getBusinessObjectDictionaryService() 428 .getLookupableID(ruleDelegation.getClass())).getLookupableHelperService(); 429 if (ruleDelegationLookupableHelperService.getBusinessObjectClass() == null) { 430 ruleDelegationLookupableHelperService.setBusinessObjectClass(ruleDelegation.getClass()); 431 } 432 delegationPkNames = KRADServiceLocatorWeb.getLegacyDataAdapter().listPrimaryKeyFieldNames(ruleDelegation.getClass()); 433 } 434 // Allow the rule delegation's lookupable helper service to handle the custom action URL generation instead. 435 htmlDataList = ruleDelegationLookupableHelperService.getCustomActionUrls(ruleDelegation, delegationPkNames); 436 } 437 } else { 438 // Otherwise, have the links open the regular routing rule maintenance document screen. 439 if (allowsMaintenanceEditAction(businessObject)) { 440 htmlDataList.add(getUrlData(businessObject, KRADConstants.MAINTENANCE_EDIT_METHOD_TO_CALL, pkNames)); 441 } 442 if (allowsMaintenanceNewOrCopyAction()) { 443 htmlDataList.add(getUrlData(businessObject, KRADConstants.MAINTENANCE_COPY_METHOD_TO_CALL, pkNames)); 444 } 445 } 446 } 447 448 return htmlDataList; 449 } 450 451}