View Javadoc
1   /**
2    * Copyright 2005-2014 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kew.rule.bo;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.kuali.rice.core.api.uif.RemotableAttributeError;
20  import org.kuali.rice.core.api.util.ConcreteKeyValue;
21  import org.kuali.rice.core.api.util.KeyValue;
22  import org.kuali.rice.core.api.util.RiceKeyConstants;
23  import org.kuali.rice.core.web.format.BooleanFormatter;
24  import org.kuali.rice.core.web.format.CollectionFormatter;
25  import org.kuali.rice.core.web.format.DateFormatter;
26  import org.kuali.rice.core.web.format.Formatter;
27  import org.kuali.rice.kew.api.KewApiServiceLocator;
28  import org.kuali.rice.kew.api.rule.RuleTemplate;
29  import org.kuali.rice.kew.api.rule.RuleTemplateAttribute;
30  import org.kuali.rice.kew.lookupable.MyColumns;
31  import org.kuali.rice.kew.rule.RuleBaseValues;
32  import org.kuali.rice.kew.rule.WorkflowRuleAttributeRows;
33  import org.kuali.rice.kew.rule.service.RuleServiceInternal;
34  import org.kuali.rice.kew.service.KEWServiceLocator;
35  import org.kuali.rice.kim.api.KimConstants;
36  import org.kuali.rice.kim.api.group.Group;
37  import org.kuali.rice.kim.api.identity.Person;
38  import org.kuali.rice.kim.api.identity.principal.Principal;
39  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
40  import org.kuali.rice.kns.document.authorization.BusinessObjectRestrictions;
41  import org.kuali.rice.kns.lookup.HtmlData;
42  import org.kuali.rice.kns.lookup.LookupableHelperService;
43  import org.kuali.rice.kns.service.KNSServiceLocator;
44  import org.kuali.rice.kns.web.comparator.CellComparatorHelper;
45  import org.kuali.rice.kns.web.struts.form.LookupForm;
46  import org.kuali.rice.kns.web.ui.Column;
47  import org.kuali.rice.kns.web.ui.Field;
48  import org.kuali.rice.kns.web.ui.ResultRow;
49  import org.kuali.rice.kns.web.ui.Row;
50  import org.kuali.rice.krad.bo.BusinessObject;
51  import org.kuali.rice.krad.bo.PersistableBusinessObject;
52  import org.kuali.rice.krad.data.KradDataServiceLocator;
53  import org.kuali.rice.krad.exception.ValidationException;
54  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
55  import org.kuali.rice.krad.util.GlobalVariables;
56  import org.kuali.rice.krad.util.KRADConstants;
57  import org.kuali.rice.krad.util.KRADUtils;
58  
59  import java.sql.Date;
60  import java.util.ArrayList;
61  import java.util.Collection;
62  import java.util.HashMap;
63  import java.util.Iterator;
64  import java.util.List;
65  import java.util.Map;
66  
67  /**
68   * This is a description of what this class does - jjhanso don't forget to fill this in.
69   *
70   * @author Kuali Rice Team (rice.collab@kuali.org)
71   *
72   */
73  public class RuleBaseValuesLookupableHelperServiceImpl extends AbstractRuleLookupableHelperServiceImpl {
74  
75      private LookupableHelperService ruleDelegationLookupableHelperService;
76      private List<?> delegationPkNames;
77  
78      private static final String RULE_TEMPLATE_PROPERTY_NAME = "ruleTemplate.name";
79      private static final String RULE_ID_PROPERTY_NAME = "id";
80      private static final String RULE_TEMPLATE_ID_PROPERTY_NAME = "ruleTemplateId";
81      private static final String ACTIVE_IND_PROPERTY_NAME = "active";
82      private static final String DELEGATE_RULE_PROPERTY_NAME = "delegateRule";
83      private static final String DOC_TYP_NAME_PROPERTY_NAME = "documentType.name";
84      private static final String RULE_DESC_PROPERTY_NAME = "description";
85  
86      @Override
87      public boolean checkForAdditionalFields(Map<String, String> fieldValues) {
88          String ruleTemplateNameParam = fieldValues.get(RULE_TEMPLATE_PROPERTY_NAME);
89  
90          return super.checkForAdditionalFields(fieldValues, ruleTemplateNameParam);
91      }
92  
93      @Override
94      public List<? extends BusinessObject> getSearchResults(Map<String, String> fieldValues) {
95          String docTypeNameParam = fieldValues.get(DOC_TYP_NAME_PROPERTY_NAME);
96          String ruleTemplateIdParam = fieldValues.get(RULE_TEMPLATE_ID_PROPERTY_NAME);
97          String ruleTemplateNameParam = fieldValues.get(RULE_TEMPLATE_PROPERTY_NAME);
98          String groupIdParam = fieldValues.get(GROUP_REVIEWER_PROPERTY_NAME);
99          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&currentRuleId=" + 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 }