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