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