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}