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.web; 017 018import org.apache.struts.action.ActionForm; 019import org.apache.struts.action.ActionForward; 020import org.apache.struts.action.ActionMapping; 021import org.kuali.rice.core.api.config.property.RuntimeConfig; 022import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader; 023import org.kuali.rice.core.api.util.RiceConstants; 024import org.kuali.rice.kew.doctype.service.DocumentTypeService; 025import org.kuali.rice.kew.impl.stuck.StuckDocument; 026import org.kuali.rice.kew.impl.stuck.StuckDocumentFixAttempt; 027import org.kuali.rice.kew.impl.stuck.StuckDocumentIncident; 028import org.kuali.rice.kew.impl.stuck.StuckDocumentNotificationJob; 029import org.kuali.rice.kew.impl.stuck.StuckDocumentService; 030import org.kuali.rice.kew.service.KEWServiceLocator; 031import org.kuali.rice.kim.api.role.RoleService; 032import org.kuali.rice.kim.api.services.KimApiServiceLocator; 033import org.kuali.rice.kns.web.struts.action.KualiAction; 034import org.kuali.rice.krad.exception.AuthorizationException; 035import org.kuali.rice.krad.util.GlobalVariables; 036 037import javax.servlet.http.HttpServletRequest; 038import javax.servlet.http.HttpServletResponse; 039import java.time.format.DateTimeFormatter; 040import java.time.format.FormatStyle; 041import java.util.Collections; 042import java.util.HashMap; 043import java.util.List; 044import java.util.stream.Collectors; 045 046public class StuckDocumentsAction extends KualiAction { 047 048 private static final String NOTIFICATION_ENABLED = "stuckDocumentsNotificationEnabledParam"; 049 private static final String NOTIFICATION_CRON_EXPRESSION = "stuckDocumentsNotificationCronExpressionParam"; 050 private static final String NOTIFICATION_FROM = "stuckDocumentsNotificationFromParam"; 051 private static final String NOTIFICATION_TO = "stuckDocumentsNotificationToParam"; 052 private static final String NOTIFICATION_SUBJECT = "stuckDocumentsNotificationSubjectParam"; 053 054 private static final String AUTOFIX_ENABLED = "stuckDocumentsAutofixEnabledParam"; 055 private static final String AUTOFIX_CRON_EXPRESSION = "stuckDocumentsAutofixCronExpressionParam"; 056 private static final String AUTOFIX_QUIET_PERIOD = "stuckDocumentsAutofixQuietPeriodParam"; 057 private static final String AUTOFIX_MAX_ATTEMPTS = "stuckDocumentsAutofixMaxAttemptsParam"; 058 private static final String AUTOFIX_NOTIFICATION_ENABLED = "stuckDocumentsAutofixNotificationEnabledParam"; 059 private static final String AUTOFIX_NOTIFICATION_SUBJECT = "stuckDocumentsAutofixNotificationSubjectParam"; 060 061 private static final int MAX_INCIDENTS = 1000; 062 063 /** 064 * To avoid having to go through the pain of setting up a KIM permission for "Use Screen" for this utility screen, 065 * we'll hardcode this screen to the "KR-SYS Technical Administrator" role. Without doing this, the screen is open 066 * to all users until that permission is setup which could be considered a security issue. 067 */ 068 protected void checkAuthorization(ActionForm form, String methodToCall) throws AuthorizationException 069 { 070 boolean authorized = false; 071 String principalId = GlobalVariables.getUserSession().getPrincipalId(); 072 RoleService roleService = KimApiServiceLocator.getRoleService(); 073 String roleId = roleService.getRoleIdByNamespaceCodeAndName("KR-SYS", "Technical Administrator"); 074 if (roleId != null) { 075 authorized = roleService.principalHasRole(principalId, Collections.singletonList(roleId), 076 new HashMap<String, String>(), true); 077 } 078 079 if (!authorized) { 080 throw new AuthorizationException(GlobalVariables.getUserSession().getPerson().getPrincipalName(), 081 methodToCall, 082 this.getClass().getSimpleName()); 083 } 084 } 085 086 @Override 087 protected ActionForward defaultDispatch(ActionMapping mapping, ActionForm actionForm, HttpServletRequest request, HttpServletResponse response) throws Exception { 088 StuckDocumentsForm form = (StuckDocumentsForm) actionForm; 089 090 form.setNotificationEnabled(getNotificationEnabled().getValue()); 091 form.setNotificationCronExpression(getNotificationCronExpression().getValue()); 092 form.setNotificationFrom(getNotificationFrom().getValue()); 093 form.setNotificationTo(getNotificationTo().getValue()); 094 form.setNotificationSubject(getNotificationSubject().getValue()); 095 096 form.setAutofixEnabled(getAutofixEnabled().getValue()); 097 form.setAutofixCronExpression(getAutofixCronExpression().getValue()); 098 form.setAutofixQuietPeriod(getAutofixQuietPeriod().getValue()); 099 form.setAutofixMaxAttempts(getAutofixMaxAttempts().getValue()); 100 form.setAutofixNotificationEnabled(getAutofixNotificationEnabled().getValue()); 101 form.setAutofixNotificationSubject(getAutofixNotificationSubject().getValue()); 102 103 return super.defaultDispatch(mapping, form, request, response); 104 } 105 106 public ActionForward updateConfig(ActionMapping mapping, ActionForm actionForm, HttpServletRequest request, HttpServletResponse response) throws Exception { 107 StuckDocumentsForm form = (StuckDocumentsForm)actionForm; 108 109 getNotificationEnabled().setValue(form.getNotificationEnabled()); 110 getNotificationCronExpression().setValue(form.getNotificationCronExpression()); 111 getNotificationFrom().setValue(form.getNotificationFrom()); 112 getNotificationTo().setValue(form.getNotificationTo()); 113 getNotificationSubject().setValue(form.getNotificationSubject()); 114 115 getAutofixEnabled().setValue(form.getAutofixEnabled()); 116 getAutofixCronExpression().setValue(form.getAutofixCronExpression()); 117 getAutofixQuietPeriod().setValue(form.getAutofixQuietPeriod()); 118 getAutofixMaxAttempts().setValue(form.getAutofixMaxAttempts()); 119 getAutofixNotificationEnabled().setValue(form.getAutofixNotificationEnabled()); 120 getAutofixNotificationSubject().setValue(form.getAutofixNotificationSubject()); 121 122 return mapping.findForward(RiceConstants.MAPPING_BASIC); 123 } 124 125 public ActionForward runStuckNotificationNow(ActionMapping mapping, ActionForm actionForm, HttpServletRequest request, HttpServletResponse response) throws Exception { 126 // make sure we update any config first 127 updateConfig(mapping, actionForm, request, response); 128 // a little hacky, we are depending on that fact that this job doesn't use the JobExecutionContext 129 new StuckDocumentNotificationJob().execute(null); 130 return mapping.findForward(RiceConstants.MAPPING_BASIC); 131 } 132 133 public ActionForward report(ActionMapping mapping, ActionForm actionForm, HttpServletRequest request, HttpServletResponse response) throws Exception { 134 List<StuckDocument> stuckDocuments = getStuckDocumentService().findAllStuckDocuments(); 135 request.setAttribute("stuckDocuments", stuckDocuments); 136 return mapping.findForward("report"); 137 } 138 139 public ActionForward autofixReport(ActionMapping mapping, ActionForm actionForm, HttpServletRequest request, HttpServletResponse response) throws Exception { 140 StuckDocumentService stuckDocumentService = getStuckDocumentService(); 141 StuckDocumentsForm form = (StuckDocumentsForm)actionForm; 142 StuckDocumentsForm.Status selectedStatus = form.getSelectedStatus(); 143 List<StuckDocumentIncident> incidents; 144 if (selectedStatus == null || selectedStatus.getValue().equals("All")) { 145 incidents = stuckDocumentService.findAllIncidents(MAX_INCIDENTS); 146 } else { 147 incidents = stuckDocumentService.findIncidentsByStatus(MAX_INCIDENTS, StuckDocumentIncident.Status.valueOf(selectedStatus.getValue())); 148 } 149 List<IncidentHistory> history = incidents.stream().map(incident -> { 150 List<StuckDocumentFixAttempt> attempts = stuckDocumentService.findAllFixAttempts(incident.getStuckDocumentIncidentId()); 151 String documentTypeLabel = getDocumentTypeService().findByDocumentId(incident.getDocumentId()).getLabel(); 152 return new IncidentHistory(incident, attempts, documentTypeLabel); 153 }).collect(Collectors.toList()); 154 request.setAttribute("history", history); 155 return mapping.findForward("autofixReport"); 156 } 157 158 private RuntimeConfig getNotificationEnabled() { 159 return GlobalResourceLoader.getService(NOTIFICATION_ENABLED); 160 } 161 162 private RuntimeConfig getNotificationCronExpression() { 163 return GlobalResourceLoader.getService(NOTIFICATION_CRON_EXPRESSION); 164 } 165 166 private RuntimeConfig getNotificationFrom() { 167 return GlobalResourceLoader.getService(NOTIFICATION_FROM); 168 } 169 170 private RuntimeConfig getNotificationTo() { 171 return GlobalResourceLoader.getService(NOTIFICATION_TO); 172 } 173 174 private RuntimeConfig getNotificationSubject() { 175 return GlobalResourceLoader.getService(NOTIFICATION_SUBJECT); 176 } 177 178 private RuntimeConfig getAutofixEnabled() { 179 return GlobalResourceLoader.getService(AUTOFIX_ENABLED); 180 } 181 182 private RuntimeConfig getAutofixCronExpression() { 183 return GlobalResourceLoader.getService(AUTOFIX_CRON_EXPRESSION); 184 } 185 186 private RuntimeConfig getAutofixQuietPeriod() { 187 return GlobalResourceLoader.getService(AUTOFIX_QUIET_PERIOD); 188 } 189 190 private RuntimeConfig getAutofixMaxAttempts() { 191 return GlobalResourceLoader.getService(AUTOFIX_MAX_ATTEMPTS); 192 } 193 194 private RuntimeConfig getAutofixNotificationEnabled() { 195 return GlobalResourceLoader.getService(AUTOFIX_NOTIFICATION_ENABLED); 196 } 197 198 private RuntimeConfig getAutofixNotificationSubject() { 199 return GlobalResourceLoader.getService(AUTOFIX_NOTIFICATION_SUBJECT); 200 } 201 202 private StuckDocumentService getStuckDocumentService() { 203 return KEWServiceLocator.getStuckDocumentService(); 204 } 205 206 private DocumentTypeService getDocumentTypeService() { 207 return KEWServiceLocator.getDocumentTypeService(); 208 } 209 210 public static class IncidentHistory { 211 212 private final StuckDocumentIncident incident; 213 private final List<StuckDocumentFixAttempt> attempts; 214 private final String documentTypeLabel; 215 216 IncidentHistory(StuckDocumentIncident incident, List<StuckDocumentFixAttempt> attempts, String documentTypeLabel) { 217 this.incident = incident; 218 this.attempts = attempts; 219 this.documentTypeLabel = documentTypeLabel; 220 } 221 222 public String getDocumentId() { 223 return incident.getDocumentId(); 224 } 225 226 public String getStartDate() { 227 return incident.getStartDate().toString(); 228 } 229 230 public String getEndDate() { 231 if (incident.getEndDate() == null) { 232 return ""; 233 } 234 return incident.getEndDate().toString(); 235 } 236 237 public String getStatus() { 238 return incident.getStatus().name(); 239 } 240 241 public String getFixAttempts() { 242 return attempts.stream(). 243 map(attempt -> attempt.getTimestamp().toString()). 244 collect(Collectors.joining(", ")); 245 } 246 247 public String getDocumentTypeLabel() { 248 return documentTypeLabel; 249 } 250 } 251 252}