001 /**
002 * Copyright 2005-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.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 }