001 /** 002 * Copyright 2005-2014 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.rice.kew.rule.web; 017 018 import org.apache.commons.beanutils.PropertyUtils; 019 import org.apache.struts.action.ActionErrors; 020 import org.apache.struts.action.ActionMessage; 021 import org.kuali.rice.core.api.util.RiceConstants; 022 import org.kuali.rice.core.framework.persistence.jdbc.sql.SQLUtils; 023 import org.kuali.rice.kew.api.KewApiConstants; 024 import org.kuali.rice.kew.api.rule.RoleName; 025 import org.kuali.rice.kew.api.util.CodeTranslator; 026 import org.kuali.rice.kew.exception.WorkflowServiceError; 027 import org.kuali.rice.kew.rule.KeyValueId; 028 import org.kuali.rice.kew.rule.RuleBaseValues; 029 import org.kuali.rice.kew.rule.RuleDelegationBo; 030 import org.kuali.rice.kew.rule.RuleExtensionValue; 031 import org.kuali.rice.kew.rule.RuleResponsibilityBo; 032 import org.kuali.rice.kew.rule.WorkflowRuleAttributeRows; 033 import org.kuali.rice.kew.rule.bo.RuleTemplateAttributeBo; 034 import org.kuali.rice.kew.rule.bo.RuleTemplateBo; 035 import org.kuali.rice.kew.rule.service.RuleDelegationService; 036 import org.kuali.rice.kew.rule.service.RuleTemplateService; 037 import org.kuali.rice.kew.service.KEWServiceLocator; 038 import org.kuali.rice.kns.web.ui.Field; 039 import org.kuali.rice.kns.web.ui.Row; 040 041 import java.sql.Timestamp; 042 import java.text.ParseException; 043 import java.text.SimpleDateFormat; 044 import java.util.ArrayList; 045 import java.util.Calendar; 046 import java.util.Collection; 047 import java.util.Collections; 048 import java.util.Date; 049 import java.util.HashMap; 050 import java.util.Iterator; 051 import java.util.List; 052 import java.util.Map; 053 054 055 /** 056 * A decorator around a {@link RuleBaseValues} object which provides some 057 * convienance functions for interacting with the bean from the web-tier. 058 * This helps to alleviate some of the weaknesses of JSTL. 059 * 060 * @author Kuali Rice Team (rice.collab@kuali.org) 061 */ 062 public class WebRuleBaseValues extends RuleBaseValues { 063 064 private static final long serialVersionUID = 5938997470219200474L; 065 private static final int TO_DATE_UPPER_LIMIT = 2100; 066 private List rows = new ArrayList(); 067 private List fields = new ArrayList(); 068 private List roles = new ArrayList(); 069 private String fromDateString; 070 private String toDateString; 071 private String ruleTemplateName; 072 private boolean hasExtensionValueErrors = false; 073 074 public WebRuleBaseValues() { 075 } 076 077 public WebRuleBaseValues(RuleBaseValues rule) throws Exception { 078 edit(rule); 079 } 080 081 private void loadFields() { 082 fields.clear(); 083 if (getRuleTemplateId() != null) { 084 RuleTemplateBo ruleTemplate = getRuleTemplateService().findByRuleTemplateId(getRuleTemplateId()); 085 if (ruleTemplate != null) { 086 List ruleTemplateAttributes = ruleTemplate.getActiveRuleTemplateAttributes(); 087 Collections.sort(ruleTemplateAttributes); 088 for (Iterator iter = ruleTemplateAttributes.iterator(); iter.hasNext();) { 089 RuleTemplateAttributeBo ruleTemplateAttribute = (RuleTemplateAttributeBo) iter.next(); 090 if (!ruleTemplateAttribute.isWorkflowAttribute()) { 091 continue; 092 } 093 WorkflowRuleAttributeRows workflowRuleAttributeRows = 094 KEWServiceLocator.getWorkflowRuleAttributeMediator().getRuleRows(null, ruleTemplateAttribute); 095 for (Row row : workflowRuleAttributeRows.getRows()) { 096 for (Field field : row.getFields()) { 097 String fieldValue = ""; 098 RuleExtensionValue extensionValue = getRuleExtensionValue(ruleTemplateAttribute.getId(), field.getPropertyName()); 099 fieldValue = (extensionValue != null) ? extensionValue.getValue() : field.getPropertyValue(); 100 fields.add(new KeyValueId(field.getPropertyName(), fieldValue, ruleTemplateAttribute.getId())); 101 } 102 } 103 } 104 } 105 } 106 } 107 108 private void loadWebValues() { 109 loadRows(); 110 loadDates(); 111 loadRuleTemplateName(); 112 } 113 114 private void loadRows() { 115 getRoles().clear(); 116 if (getRuleTemplateId() != null) { 117 RuleTemplateBo ruleTemplate = getRuleTemplateService().findByRuleTemplateId(getRuleTemplateId()); 118 if (ruleTemplate != null) { 119 setRuleTemplateName(ruleTemplate.getName()); 120 List<RuleTemplateAttributeBo> ruleTemplateAttributes = ruleTemplate.getActiveRuleTemplateAttributes(); 121 Collections.sort(ruleTemplateAttributes); 122 List<Row> rows = new ArrayList<Row>(); 123 for (RuleTemplateAttributeBo ruleTemplateAttribute : ruleTemplateAttributes) { 124 if (!ruleTemplateAttribute.isWorkflowAttribute()) { 125 continue; 126 } 127 WorkflowRuleAttributeRows workflowRuleAttributeRows = 128 KEWServiceLocator.getWorkflowRuleAttributeMediator().getRuleRows(getFieldMap(ruleTemplateAttribute.getId()), ruleTemplateAttribute); 129 rows.addAll(workflowRuleAttributeRows.getRows()); 130 getRoles().addAll(KEWServiceLocator.getWorkflowRuleAttributeMediator().getRoleNames(ruleTemplateAttribute)); 131 } 132 setRows(rows); 133 } 134 } 135 } 136 137 private void loadDates() { 138 if (getFromDateString() != null) { 139 setFromDateString(RiceConstants.getDefaultDateFormat().format(getFromDateValue())); 140 } 141 if (getToDateString() != null) { 142 setToDateString(RiceConstants.getDefaultDateFormat().format(getToDateValue())); 143 } 144 } 145 146 private void loadRuleTemplateName() { 147 if (org.apache.commons.lang.StringUtils.isEmpty(getRuleTemplateName()) && getRuleTemplateId() != null) { 148 RuleTemplateBo ruleTemplate = getRuleTemplateService().findByRuleTemplateId(getRuleTemplateId()); 149 if (ruleTemplate != null) { 150 setRuleTemplateName(ruleTemplate.getName()); 151 } 152 } 153 } 154 155 public List getFields() { 156 return fields; 157 } 158 159 public void setFields(List fields) { 160 this.fields = fields; 161 } 162 163 public KeyValueId getField(int index) { 164 while (getFields().size() <= index) { 165 KeyValueId field = new KeyValueId(); 166 getFields().add(field); 167 } 168 return (KeyValueId) getFields().get(index); 169 } 170 171 public String getFromDateString() { 172 return fromDateString; 173 } 174 175 public void setFromDateString(String fromDateString) { 176 this.fromDateString = fromDateString; 177 } 178 179 public List<RoleName> getRoles() { 180 return roles; 181 } 182 183 public void setRoles(List<RoleName> roles) { 184 this.roles = roles; 185 } 186 187 public List<RoleName> getRows() { 188 return rows; 189 } 190 191 public void setRows(List ruleTemplateAttributes) { 192 this.rows = ruleTemplateAttributes; 193 } 194 195 @Override 196 public String getToDateString() { 197 return this.toDateString; 198 } 199 200 public void setToDateString(String toDateString) { 201 this.toDateString = toDateString; 202 } 203 204 @Override 205 public String getRuleTemplateName() { 206 return ruleTemplateName; 207 } 208 209 public void setRuleTemplateName(String ruleTemplateName) { 210 this.ruleTemplateName = ruleTemplateName; 211 } 212 213 public boolean isHasExtensionValueErrors() { 214 return hasExtensionValueErrors; 215 } 216 217 public void setHasExtensionValueErrors(boolean hasRuleExtensionValueErrors) { 218 this.hasExtensionValueErrors = hasRuleExtensionValueErrors; 219 } 220 221 /** Web Logic * */ 222 223 /** 224 * Populates this WebRuleBaseValues object for editing the given rule. 225 */ 226 public void edit(RuleBaseValues rule) throws Exception { 227 load(rule); 228 initialize(); 229 } 230 231 /** 232 * Loads the given rule into this WebRuleBaseValues. 233 */ 234 public void load(RuleBaseValues rule) throws Exception { 235 PropertyUtils.copyProperties(this, rule); 236 injectWebMembers(); 237 } 238 239 public void initialize() throws Exception { 240 loadFields(); 241 // setPreviousRuleId(getId()); 242 for (Object element : getRuleResponsibilities()) { 243 WebRuleResponsibility responsibility = (WebRuleResponsibility) element; 244 responsibility.initialize(); 245 } 246 establishRequiredState(); 247 } 248 249 private void injectWebMembers() throws Exception { 250 List currentResponsibilities = getRuleResponsibilities(); 251 setRuleResponsibilities(new ArrayList()); 252 for (Iterator iterator = currentResponsibilities.iterator(); iterator.hasNext();) { 253 RuleResponsibilityBo responsibility = (RuleResponsibilityBo) iterator.next(); 254 WebRuleResponsibility webResponsibility = createNewRuleResponsibility(); 255 webResponsibility.load(responsibility); 256 } 257 } 258 259 /** 260 * Establishes any missing and required state in the WebRuleBaseValues. 261 */ 262 public void establishRequiredState() throws Exception { 263 loadWebValues(); 264 if (getRuleResponsibilities().isEmpty()) { 265 createNewRuleResponsibility(); 266 } 267 for (Object element : getRuleResponsibilities()) { 268 WebRuleResponsibility responsibility = (WebRuleResponsibility) element; 269 responsibility.establishRequiredState(); 270 } 271 } 272 273 @Override 274 public RuleResponsibilityBo getResponsibility(int index) { 275 while (getRuleResponsibilities().size() <= index) { 276 createNewRuleResponsibility(); 277 } 278 return getRuleResponsibilities().get(index); 279 } 280 281 public int getResponsibilitiesSize() { 282 return getRuleResponsibilities().size(); 283 } 284 285 public WebRuleResponsibility createNewRuleResponsibility() { 286 WebRuleResponsibility responsibility = new WebRuleResponsibility(); 287 responsibility.setRuleBaseValues(this); 288 addRuleResponsibility(responsibility); 289 return responsibility; 290 } 291 292 public Map getFieldMap(String ruleTemplateAttributeId) { 293 Map fieldMap = new HashMap(); 294 for (Iterator iterator = getFields().iterator(); iterator.hasNext();) { 295 KeyValueId field = (KeyValueId) iterator.next(); 296 if (ruleTemplateAttributeId.equals(field.getId())) { 297 fieldMap.put(field.getKey(), field.getValue()); 298 } 299 } 300 return fieldMap; 301 } 302 303 public void populatePreviousRuleIds() { 304 if (getPreviousRuleId() == null) { 305 setPreviousRuleId(getId()); 306 } 307 for (Object element : getRuleResponsibilities()) { 308 WebRuleResponsibility responsibility = (WebRuleResponsibility) element; 309 responsibility.populatePreviousRuleIds(); 310 } 311 } 312 313 /** 314 * This method is used to "materialize" the web rule before it gets saved, if we don't do this then certain fields will be saved as NULL. For example, ruleTemplate. 315 */ 316 public void materialize() { 317 if (getRuleTemplate() == null && getRuleTemplateId() != null) { 318 setRuleTemplate(getRuleTemplateService().findByRuleTemplateId(getRuleTemplateId())); 319 } 320 } 321 322 /** 323 * This will ensure that the toDate is never larger than 2100, currently 324 * doesn't do any throttling on the from date 325 */ 326 private void throttleDates() { 327 if (getToDateValue() != null) { 328 Calendar calendar = Calendar.getInstance(); 329 calendar.setTime(getToDateValue()); 330 if (calendar.get(Calendar.YEAR) > TO_DATE_UPPER_LIMIT) { 331 calendar.set(Calendar.YEAR, TO_DATE_UPPER_LIMIT); 332 setToDateValue(new Timestamp(calendar.getTimeInMillis())); 333 setToDateString(new SimpleDateFormat("MM/dd/yyyy").format(getToDateValue())); 334 } 335 } 336 } 337 338 private void saveServiceErrors(String errorKey, Collection srvErrors, ActionErrors errors) { 339 for (Iterator iterator = srvErrors.iterator(); iterator.hasNext();) { 340 WorkflowServiceError error = (WorkflowServiceError) iterator.next(); 341 if (error.getArg1() == null && error.getArg2() == null) { 342 errors.add(errorKey, new ActionMessage(error.getKey())); 343 } else if (error.getArg1() != null && error.getArg2() == null) { 344 errors.add(errorKey, new ActionMessage(error.getKey(), error.getArg1())); 345 } else { 346 errors.add(errorKey, new ActionMessage(error.getKey(), error.getArg1(), error.getArg2())); 347 } 348 } 349 } 350 351 private Timestamp decodeTimestamp(String dateValue) throws ParseException { 352 if (org.apache.commons.lang.StringUtils.isEmpty(dateValue)) { 353 return null; 354 } 355 /* Not the best solution below but does allow our forcing of the 4 digit year 356 * until KEW and use the KNS for it's date entry/validation 357 */ 358 String convertedDate = SQLUtils.getEntryFormattedDate(dateValue); 359 if (convertedDate == null) { 360 throw new ParseException("Date entered as '" + dateValue + "' is in invalid format", 0); 361 } 362 Date date = RiceConstants.getDefaultDateFormat().parse(convertedDate); 363 return new Timestamp(date.getTime()); 364 } 365 366 private RuleTemplateService getRuleTemplateService() { 367 return (RuleTemplateService) KEWServiceLocator.getService(KEWServiceLocator.RULE_TEMPLATE_SERVICE); 368 } 369 370 /** 371 * @return Returns the actionRequestCodes. 372 */ 373 public Map getActionRequestCodes() { 374 Map actionRequestCodes = new HashMap(); 375 actionRequestCodes.putAll(CodeTranslator.arLabels); 376 if (getRuleTemplateId() != null) { 377 RuleTemplateBo ruleTemplate = getRuleTemplateService().findByRuleTemplateId(getRuleTemplateId()); 378 if (ruleTemplate != null) { 379 if (ruleTemplate.getAcknowledge() != null && "false".equals(ruleTemplate.getAcknowledge().getValue())) { 380 actionRequestCodes.remove(KewApiConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ); 381 } 382 if (ruleTemplate.getComplete() != null && "false".equals(ruleTemplate.getComplete().getValue())) { 383 actionRequestCodes.remove(KewApiConstants.ACTION_REQUEST_COMPLETE_REQ); 384 } 385 if (ruleTemplate.getApprove() != null && "false".equals(ruleTemplate.getApprove().getValue())) { 386 actionRequestCodes.remove(KewApiConstants.ACTION_REQUEST_APPROVE_REQ); 387 } 388 if (ruleTemplate.getFyi() != null && "false".equals(ruleTemplate.getFyi().getValue())) { 389 actionRequestCodes.remove(KewApiConstants.ACTION_REQUEST_FYI_REQ); 390 } 391 } 392 } 393 return actionRequestCodes; 394 } 395 396 public RuleDelegationBo getRuleDelegation() { 397 if (getDelegateRule().booleanValue()) { 398 List ruleDelegations = getRuleDelegationService().findByDelegateRuleId(getId()); 399 RuleDelegationBo currentRuleDelegation = (RuleDelegationBo) ruleDelegations.get(0); 400 RuleBaseValues mostRecentRule = currentRuleDelegation.getRuleResponsibility().getRuleBaseValues(); 401 402 for (Iterator iter = ruleDelegations.iterator(); iter.hasNext();) { 403 RuleDelegationBo ruleDelegation = (RuleDelegationBo) iter.next(); 404 RuleBaseValues parentRule = ruleDelegation.getRuleResponsibility().getRuleBaseValues(); 405 406 if (parentRule.getActivationDate().after(mostRecentRule.getActivationDate())) { 407 mostRecentRule = ruleDelegation.getRuleResponsibility().getRuleBaseValues(); 408 currentRuleDelegation = ruleDelegation; 409 } 410 } 411 return currentRuleDelegation; 412 } 413 return null; 414 } 415 416 public String getParentRuleId() { 417 if (getDelegateRule().booleanValue()) { 418 List ruleDelegations = getRuleDelegationService().findByDelegateRuleId(getId()); 419 RuleDelegationBo currentRuleDelegation = (RuleDelegationBo) ruleDelegations.get(0); 420 RuleBaseValues mostRecentRule = currentRuleDelegation.getRuleResponsibility().getRuleBaseValues(); 421 422 for (Iterator iter = ruleDelegations.iterator(); iter.hasNext();) { 423 RuleDelegationBo ruleDelegation = (RuleDelegationBo) iter.next(); 424 RuleBaseValues parentRule = ruleDelegation.getRuleResponsibility().getRuleBaseValues(); 425 426 if (parentRule.getActivationDate().after(mostRecentRule.getActivationDate())) { 427 mostRecentRule = ruleDelegation.getRuleResponsibility().getRuleBaseValues(); 428 } 429 } 430 return mostRecentRule.getId(); 431 } 432 return null; 433 } 434 435 private RuleDelegationService getRuleDelegationService() { 436 return (RuleDelegationService) KEWServiceLocator.getService(KEWServiceLocator.RULE_DELEGATION_SERVICE); 437 } 438 }