View Javadoc
1   /**
2    * Copyright 2005-2016 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.KewApiConstants;
28  import org.kuali.rice.kew.api.KewApiServiceLocator;
29  import org.kuali.rice.kew.api.rule.RuleTemplate;
30  import org.kuali.rice.kew.api.rule.RuleTemplateAttribute;
31  import org.kuali.rice.kew.lookupable.MyColumns;
32  import org.kuali.rice.kew.rule.RuleBaseValues;
33  import org.kuali.rice.kew.rule.RuleDelegationBo;
34  import org.kuali.rice.kew.rule.RuleExtensionBo;
35  import org.kuali.rice.kew.rule.WorkflowRuleAttributeRows;
36  import org.kuali.rice.kew.rule.service.RuleDelegationService;
37  import org.kuali.rice.kew.service.KEWServiceLocator;
38  import org.kuali.rice.kim.api.KimConstants;
39  import org.kuali.rice.kim.api.group.Group;
40  import org.kuali.rice.kim.api.identity.Person;
41  import org.kuali.rice.kim.api.identity.principal.Principal;
42  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
43  import org.kuali.rice.kns.document.authorization.BusinessObjectRestrictions;
44  import org.kuali.rice.kns.lookup.HtmlData;
45  import org.kuali.rice.kns.web.comparator.CellComparatorHelper;
46  import org.kuali.rice.kns.web.struts.form.LookupForm;
47  import org.kuali.rice.kns.web.ui.Column;
48  import org.kuali.rice.kns.web.ui.Field;
49  import org.kuali.rice.kns.web.ui.ResultRow;
50  import org.kuali.rice.kns.web.ui.Row;
51  import org.kuali.rice.krad.bo.BusinessObject;
52  import org.kuali.rice.krad.bo.PersistableBusinessObject;
53  import org.kuali.rice.krad.data.KradDataServiceLocator;
54  import org.kuali.rice.krad.exception.ValidationException;
55  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
56  import org.kuali.rice.krad.util.GlobalVariables;
57  import org.kuali.rice.krad.util.KRADConstants;
58  
59  import java.sql.Date;
60  import java.util.ArrayList;
61  import java.util.Collection;
62  import java.util.Collections;
63  import java.util.HashMap;
64  import java.util.Iterator;
65  import java.util.List;
66  import java.util.Map;
67  
68  /**
69   * This is a description of what this class does - jjhanso don't forget to fill this in.
70   *
71   * @author Kuali Rice Team (rice.collab@kuali.org)
72   *
73   */
74  public class RuleDelegationLookupableHelperServiceImpl extends AbstractRuleLookupableHelperServiceImpl {
75  
76      private static final String PARENT_RESPONSIBILITY_ID_PROPERTY_NAME = "responsibilityId";
77      private static final String PARENT_RULE_ID_PROPERTY_NAME = "ruleResponsibility.ruleBaseValuesId";
78      private static final String RULE_TEMPLATE_PROPERTY_NAME = "delegationRule.ruleTemplate.name";
79      private static final String RULE_ID_PROPERTY_NAME = "delegationRule.id";
80      private static final String ACTIVE_IND_PROPERTY_NAME = "delegationRule.active";
81      private static final String DELEGATION_PROPERTY_NAME = "delegationType";
82      private static final String DOC_TYP_NAME_PROPERTY_NAME = "delegationRule.documentType.name";
83      private static final String RULE_DESC_PROPERTY_NAME = "delegationRule.description";
84  
85      @Override
86      public boolean checkForAdditionalFields(Map<String, String> fieldValues) {
87          String ruleTemplateNameParam = fieldValues.get(RULE_TEMPLATE_PROPERTY_NAME);
88  
89          return super.checkForAdditionalFields(fieldValues, ruleTemplateNameParam);
90      }
91  
92      @Override
93      public List<? extends BusinessObject> getSearchResults(Map<String, String> fieldValues) {
94          String parentRuleBaseValueId = fieldValues.get(PARENT_RULE_ID_PROPERTY_NAME);
95          String parentResponsibilityId = fieldValues.get(PARENT_RESPONSIBILITY_ID_PROPERTY_NAME);
96          String docTypeNameParam = fieldValues.get(DOC_TYP_NAME_PROPERTY_NAME);
97          String ruleTemplateIdParam = null;
98          String ruleTemplateNameParam = fieldValues.get(RULE_TEMPLATE_PROPERTY_NAME);
99          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&currentRuleId=" + 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 }