001/**
002 * Copyright 2005-2016 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.actionlist.service.impl;
017
018import java.sql.Timestamp;
019import java.util.ArrayList;
020import java.util.Collection;
021import java.util.Collections;
022import java.util.Date;
023import java.util.Iterator;
024import java.util.List;
025
026import org.apache.commons.lang.StringUtils;
027import org.kuali.rice.core.api.config.property.ConfigContext;
028import org.kuali.rice.kew.actionitem.ActionItem;
029import org.kuali.rice.kew.actionitem.ActionItemActionListExtension;
030import org.kuali.rice.kew.actionitem.OutboxItemActionListExtension;
031import org.kuali.rice.kew.actionitem.dao.ActionItemDAO;
032import org.kuali.rice.kew.actionlist.ActionListFilter;
033import org.kuali.rice.kew.actionlist.dao.ActionListDAO;
034import org.kuali.rice.kew.actionlist.service.ActionListService;
035import org.kuali.rice.kew.actionrequest.ActionRequestValue;
036import org.kuali.rice.kew.actionrequest.Recipient;
037import org.kuali.rice.kew.actionrequest.service.ActionRequestService;
038import org.kuali.rice.kew.actiontaken.ActionTakenValue;
039import org.kuali.rice.kew.doctype.bo.DocumentType;
040import org.kuali.rice.kew.exception.WorkflowServiceErrorException;
041import org.kuali.rice.kew.exception.WorkflowServiceErrorImpl;
042import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
043import org.kuali.rice.kew.service.KEWServiceLocator;
044import org.kuali.rice.kew.useroptions.UserOptions;
045import org.kuali.rice.kew.useroptions.UserOptionsService;
046import org.kuali.rice.kew.api.KewApiConstants;
047import org.kuali.rice.kim.api.group.GroupService;
048import org.kuali.rice.kim.api.identity.principal.Principal;
049import org.kuali.rice.kim.api.services.KimApiServiceLocator;
050
051/**
052 * Default implementation of the {@link ActionListService}.
053 *
054 * @author Kuali Rice Team (rice.collab@kuali.org)
055 */
056public class ActionListServiceImpl implements ActionListService {
057
058    protected final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(getClass());
059
060    private ActionListDAO actionListDAO;
061
062    private ActionItemDAO actionItemDAO;
063
064    public Collection<Recipient> findUserSecondaryDelegators(String principalId) {
065        return getActionItemDAO().findSecondaryDelegators(principalId);
066    }
067
068    public Collection<Recipient> findUserPrimaryDelegations(String principalId) {
069        return getActionItemDAO().findPrimaryDelegationRecipients(principalId);
070    }
071
072    public Collection<ActionItemActionListExtension> getActionList(String principalId, ActionListFilter filter) {
073        return getActionListDAO().getActionList(principalId, filter);
074    }
075
076    public Collection<ActionItemActionListExtension> getActionListForSingleDocument(String documentId) {
077        return getActionListDAO().getActionListForSingleDocument(documentId);
078    }
079
080    public void setActionListDAO(ActionListDAO actionListDAO) {
081        this.actionListDAO = actionListDAO;
082    }
083
084    public ActionListDAO getActionListDAO() {
085        return actionListDAO;
086    }
087
088    public void deleteActionItem(ActionItem actionItem) {
089        deleteActionItem(actionItem, false);
090    }
091
092    public void deleteActionItem(ActionItem actionItem, boolean forceIntoOutbox) {
093        getActionItemDAO().deleteActionItem(actionItem);
094        // remove notification from KCB
095        KEWServiceLocator.getNotificationService().removeNotification(Collections.singletonList(ActionItem.to(actionItem)));
096        this.saveOutboxItem(actionItem, forceIntoOutbox);
097    }
098
099    public void deleteByDocumentId(String documentId) {
100        getActionItemDAO().deleteByDocumentId(documentId);
101    }
102
103    public Collection<ActionItem> findByDocumentId(String documentId) {
104        return getActionItemDAO().findByDocumentId(documentId);
105    }
106
107    public Collection<ActionItem> findByActionRequestId(String actionRequestId) {
108        return getActionItemDAO().findByActionRequestId(actionRequestId);
109    }
110
111    public Collection<ActionItem> findByWorkflowUserDocumentId(String workflowUserId, String documentId) {
112        return getActionItemDAO().findByWorkflowUserDocumentId(workflowUserId, documentId);
113    }
114
115    public Collection<ActionItem> findByDocumentTypeName(String documentTypeName) {
116        return getActionItemDAO().findByDocumentTypeName(documentTypeName);
117    }
118
119    public ActionItem createActionItemForActionRequest(ActionRequestValue actionRequest) {
120        ActionItem actionItem = new ActionItem();
121
122        DocumentRouteHeaderValue routeHeader = actionRequest.getRouteHeader();
123        DocumentType docType = routeHeader.getDocumentType();
124
125        actionItem.setActionRequestCd(actionRequest.getActionRequested());
126        actionItem.setActionRequestId(actionRequest.getActionRequestId());
127        actionItem.setDocName(docType.getName());
128        actionItem.setRoleName(actionRequest.getQualifiedRoleName());
129        actionItem.setPrincipalId(actionRequest.getPrincipalId());
130        actionItem.setDocumentId(actionRequest.getDocumentId());
131        actionItem.setDateAssigned(new Timestamp(new Date().getTime()));
132        actionItem.setDocHandlerURL(docType.getResolvedDocumentHandlerUrl());
133        actionItem.setDocLabel(docType.getLabel());
134        actionItem.setDocTitle(routeHeader.getDocTitle());
135        actionItem.setGroupId(actionRequest.getGroupId());
136        actionItem.setResponsibilityId(actionRequest.getResponsibilityId());
137        actionItem.setDelegationType(actionRequest.getDelegationType());
138        actionItem.setRequestLabel(actionRequest.getRequestLabel());
139
140        ActionRequestValue delegatorActionRequest = getActionRequestService().findDelegatorRequest(actionRequest);
141        if (delegatorActionRequest != null) {
142            actionItem.setDelegatorPrincipalId(delegatorActionRequest.getPrincipalId());
143            actionItem.setDelegatorGroupId(delegatorActionRequest.getGroupId());
144        }
145
146        return actionItem;
147    }
148
149
150    public void updateActionItemsForTitleChange(String documentId, String newTitle) {
151        Collection<ActionItem> items = getActionItemDAO().findByDocumentId(documentId);
152        for (Iterator<ActionItem> iterator = items.iterator(); iterator.hasNext();) {
153            ActionItem item = iterator.next();
154            item.setDocTitle(newTitle);
155            saveActionItem(item);
156        }
157    }
158
159    public void saveActionItem(ActionItem actionItem) {
160        getActionItemDAO().saveActionItem(actionItem);
161    }
162
163    public ActionItemDAO getActionItemDAO() {
164        return actionItemDAO;
165    }
166
167    public ActionRequestService getActionRequestService() {
168        return (ActionRequestService) KEWServiceLocator.getActionRequestService();
169    }
170
171    public GroupService getGroupService(){
172        return KimApiServiceLocator.getGroupService();
173    }
174
175    public void setActionItemDAO(ActionItemDAO actionItemDAO) {
176        this.actionItemDAO = actionItemDAO;
177    }
178
179
180    public void validateActionItem(ActionItem actionItem) {
181        List<WorkflowServiceErrorImpl> errors = new ArrayList<WorkflowServiceErrorImpl>();
182        String principalId = actionItem.getPrincipalId();
183        if (principalId == null || principalId.trim().equals("")) {
184            errors.add(new WorkflowServiceErrorImpl("ActionItem person null.", "actionitem.personid.empty", actionItem
185                    .getId().toString()));
186        } else {
187            Principal principal = KimApiServiceLocator.getIdentityService().getPrincipal(principalId);
188            if (principal == null) {
189                errors.add(new WorkflowServiceErrorImpl("ActionItem person invalid.", "actionitem.personid.invalid",
190                        actionItem.getId().toString()));
191            }
192        }
193
194        if (actionItem.getDateAssigned() == null) {
195            errors.add(new WorkflowServiceErrorImpl("ActionItem date assigned empty.", "actionitem.dateassigned.empty",
196                    actionItem.getId().toString()));
197        }
198
199        String actionRequestCd = actionItem.getActionRequestCd();
200        if (actionRequestCd == null || actionRequestCd.trim().equals("")) {
201            errors.add(new WorkflowServiceErrorImpl("ActionItem action request cd empty.",
202                    "actionitem.actionrequestcd.empty", actionItem.getId().toString()));
203        } else if (!KewApiConstants.ACTION_REQUEST_CD.containsKey(actionRequestCd)) {
204            errors.add(new WorkflowServiceErrorImpl("ActionItem action request cd invalid.",
205                    "actionitem.actionrequestcd.invalid", actionItem.getId().toString()));
206        }
207
208        if (actionItem.getActionRequestId() == null) {
209            errors.add(new WorkflowServiceErrorImpl("ActionItem action request id empty.",
210                    "actionitem.actionrequestid.empty", actionItem.getId().toString()));
211        }
212
213        if (actionItem.getDocumentId() == null) {
214            errors.add(new WorkflowServiceErrorImpl("ActionItem Document id empty.", "actionitem.documentid.empty",
215                    actionItem.getId().toString()));
216        } else if (KEWServiceLocator.getRouteHeaderService().getRouteHeader(actionItem.getDocumentId()) == null) {
217            errors.add(new WorkflowServiceErrorImpl("ActionItem Document id invalid.", "actionitem.documentid.invalid",
218                    actionItem.getId().toString()));
219        }
220
221        String docTypeName = actionItem.getDocName();
222        DocumentType docType = null;
223        if (docTypeName == null || docTypeName.trim().equals("")) {
224            errors.add(new WorkflowServiceErrorImpl("ActionItem doctypename empty.", "actionitem.doctypename.empty",
225                    actionItem.getId().toString()));
226        } else {
227            docType = KEWServiceLocator.getDocumentTypeService().findByName(actionItem.getDocName());
228            if (docType == null) {
229                errors.add(new WorkflowServiceErrorImpl("ActionItem doctypename invalid.", "actionitem.doctypename.invalid",
230                        actionItem.getId().toString()));
231            }
232        }
233
234        if (actionItem.getDocLabel() == null || actionItem.getDocLabel().trim().equals("")) {
235            errors.add(new WorkflowServiceErrorImpl("ActionItem doctypelabel empty.", "actionitem.doctypelabel.empty",
236                    actionItem.getId().toString()));
237        } else if (docType != null && !docType.getLabel().equals(actionItem.getDocLabel())) {
238            errors.add(new WorkflowServiceErrorImpl("ActionItem doctypelabel no match.", "actionitem.doctypelabel.nomatch",
239                    actionItem.getId().toString()));
240        }
241
242        // first check to see if the document type has an empty document handler url
243        if (StringUtils.isNotBlank(docType.getResolvedDocumentHandlerUrl())) {
244            if (actionItem.getDocHandlerURL() == null || actionItem.getDocHandlerURL().trim().equals("")) {
245                errors.add(new WorkflowServiceErrorImpl("ActionItem doc handler url empty.", "actionitem.dochdrurl.empty",
246                        actionItem.getId().toString()));
247            } else if (docType != null && !docType.getResolvedDocumentHandlerUrl().equals(actionItem.getDocHandlerURL())) {
248                errors.add(new WorkflowServiceErrorImpl("ActionItem doc handler url no match.", "actionitem.dochdrurl.nomatch",
249                        actionItem.getId().toString()));
250            }
251        } else {
252            // if the doc type doc handler url is blank, verify that the action item doc handler url is also blank
253            if (StringUtils.isNotBlank(actionItem.getDocHandlerURL())) {
254                errors.add(new WorkflowServiceErrorImpl("ActionItem doc handler url not empty.", "actionitem.dochdrurl.not.empty",
255                        actionItem.getId().toString()));
256            }
257        }
258
259        if (!errors.isEmpty()) {
260            throw new WorkflowServiceErrorException("ActionItem Validation Error", errors);
261        }
262    }
263
264    public ActionItem findByActionItemId(String actionItemId) {
265        return getActionItemDAO().findByActionItemId(actionItemId);
266    }
267
268    public int getCount(String principalId) {
269        return getActionListDAO().getCount(principalId);
270    }
271
272    public List<Object> getMaxActionItemDateAssignedAndCountForUser(String principalId) {
273        return getActionListDAO().getMaxActionItemDateAssignedAndCountForUser(principalId);
274    }
275
276    /**
277     *
278     * This overridden method ...
279     *
280     * @see org.kuali.rice.kew.actionlist.service.ActionListService#getOutbox(java.lang.String, org.kuali.rice.kew.actionlist.ActionListFilter)
281     */
282    public Collection<OutboxItemActionListExtension> getOutbox(String principalId, ActionListFilter filter) {
283        return this.getActionListDAO().getOutbox(principalId, filter);
284    }
285
286    public Collection<OutboxItemActionListExtension> getOutboxItemsByDocumentType(String documentTypeName) {
287        return this.getActionItemDAO().getOutboxItemsByDocumentType(documentTypeName);
288    }
289
290    /**
291     * This overridden method ...
292     *
293     * @see org.kuali.rice.kew.actionlist.service.ActionListService#removeOutboxItems(String, java.util.List)
294     */
295    public void removeOutboxItems(String principalId, List<String> outboxItems) {
296        this.getActionListDAO().removeOutboxItems(principalId, outboxItems);
297    }
298
299    public void saveOutboxItem(ActionItem actionItem) {
300        saveOutboxItem(actionItem, false);
301    }
302
303    /**
304     *
305     * save the ouboxitem unless the document is saved or the user already has the item in their outbox.
306     *
307     * @see org.kuali.rice.kew.actionlist.service.ActionListService#saveOutboxItem(org.kuali.rice.kew.actionitem.ActionItem, boolean)
308     */
309    public void saveOutboxItem(ActionItem actionItem, boolean forceIntoOutbox) {
310        UserOptionsService userOptionsService = KEWServiceLocator.getUserOptionsService();
311        Boolean isUsingOutBox = true;
312        List<UserOptions> options = userOptionsService.findByUserQualified(actionItem.getPrincipalId(), KewApiConstants.USE_OUT_BOX);
313        if (options == null || options.isEmpty()){
314            isUsingOutBox = true;
315        } else {
316            for (Iterator iter = options.iterator(); iter.hasNext();) {
317                UserOptions u = (UserOptions) iter.next();
318                if (u.getOptionVal() == null || !(u.getOptionVal().equals("yes"))){
319                    isUsingOutBox = false;
320                }
321            }
322        }
323
324        if (isUsingOutBox
325                && ConfigContext.getCurrentContextConfig().getOutBoxOn()
326                && getActionListDAO().getOutboxByDocumentIdUserId(actionItem.getDocumentId(), actionItem.getPrincipalId()) == null
327                && !KEWServiceLocator.getRouteHeaderService().getRouteHeader(actionItem.getDocumentId()).getDocRouteStatus().equals(
328                KewApiConstants.ROUTE_HEADER_SAVED_CD)) {
329
330            // only create an outbox item if this user has taken action on the document
331            ActionRequestValue actionRequest = KEWServiceLocator.getActionRequestService().findByActionRequestId(
332                    actionItem.getActionRequestId());
333            ActionTakenValue actionTaken = actionRequest.getActionTaken();
334            // if an action was taken...
335            if (forceIntoOutbox || (actionTaken != null && actionTaken.getPrincipalId().equals(actionItem.getPrincipalId()))) {
336                this.getActionListDAO().saveOutboxItem(new OutboxItemActionListExtension(actionItem));
337            }
338
339        }
340    }
341
342    public Collection<ActionItem> findByPrincipalId(String principalId) {
343        return getActionItemDAO().findByPrincipalId(principalId);
344    }
345
346}