package com.constellio.app.modules.tasks.extensions;
import static com.constellio.app.modules.tasks.TasksEmailTemplates.ACTUAL_ASSIGNEE;
import static com.constellio.app.modules.tasks.TasksEmailTemplates.ACTUAL_STATUS;
import static com.constellio.app.modules.tasks.TasksEmailTemplates.COMPLETE_TASK;
import static com.constellio.app.modules.tasks.TasksEmailTemplates.CONSTELLIO_URL;
import static com.constellio.app.modules.tasks.TasksEmailTemplates.DISPLAY_TASK;
import static com.constellio.app.modules.tasks.TasksEmailTemplates.PARENT_TASK_TITLE;
import static com.constellio.app.modules.tasks.TasksEmailTemplates.PREVIOUS_ASSIGNEE;
import static com.constellio.app.modules.tasks.TasksEmailTemplates.PREVIOUS_STATUS;
import static com.constellio.app.modules.tasks.TasksEmailTemplates.TASK_ASSIGNED;
import static com.constellio.app.modules.tasks.TasksEmailTemplates.TASK_ASSIGNED_BY;
import static com.constellio.app.modules.tasks.TasksEmailTemplates.TASK_ASSIGNED_ON;
import static com.constellio.app.modules.tasks.TasksEmailTemplates.TASK_ASSIGNED_TO_YOU;
import static com.constellio.app.modules.tasks.TasksEmailTemplates.TASK_ASSIGNEE_MODIFIED;
import static com.constellio.app.modules.tasks.TasksEmailTemplates.TASK_COMPLETED;
import static com.constellio.app.modules.tasks.TasksEmailTemplates.TASK_DELETED;
import static com.constellio.app.modules.tasks.TasksEmailTemplates.TASK_DESCRIPTION;
import static com.constellio.app.modules.tasks.TasksEmailTemplates.TASK_DUE_DATE;
import static com.constellio.app.modules.tasks.TasksEmailTemplates.TASK_STATUS;
import static com.constellio.app.modules.tasks.TasksEmailTemplates.TASK_STATUS_MODIFIED;
import static com.constellio.app.modules.tasks.TasksEmailTemplates.TASK_SUB_TASKS_MODIFIED;
import static com.constellio.app.modules.tasks.TasksEmailTemplates.TASK_TITLE_PARAMETER;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.constellio.app.modules.tasks.navigation.TasksNavigationConfiguration;
import com.constellio.model.entities.records.Record;
import com.constellio.model.extensions.events.records.RecordInCreationBeforeSaveEvent;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.joda.time.LocalDate;
import com.constellio.app.modules.tasks.model.wrappers.Task;
import com.constellio.app.modules.tasks.model.wrappers.structures.TaskFollower;
import com.constellio.app.modules.tasks.model.wrappers.structures.TaskReminder;
import com.constellio.app.modules.tasks.model.wrappers.types.TaskStatus;
import com.constellio.app.modules.tasks.services.TasksSchemasRecordsServices;
import com.constellio.app.services.factories.AppLayerFactory;
import com.constellio.data.dao.dto.records.RecordsFlushing;
import com.constellio.data.utils.TimeProvider;
import com.constellio.model.entities.records.Transaction;
import com.constellio.model.entities.records.wrappers.EmailToSend;
import com.constellio.model.entities.records.wrappers.Group;
import com.constellio.model.entities.records.wrappers.User;
import com.constellio.model.entities.security.global.UserCredential;
import com.constellio.model.entities.structures.EmailAddress;
import com.constellio.model.extensions.behaviors.RecordExtension;
import com.constellio.model.extensions.events.records.RecordInCreationBeforeValidationAndAutomaticValuesCalculationEvent;
import com.constellio.model.extensions.events.records.RecordInModificationBeforeValidationAndAutomaticValuesCalculationEvent;
import com.constellio.model.extensions.events.records.RecordLogicalDeletionEvent;
import com.constellio.model.services.factories.ModelLayerFactory;
import com.constellio.model.services.migrations.ConstellioEIMConfigs;
import com.constellio.model.services.records.RecordServices;
import com.constellio.model.services.records.RecordServicesException;
import com.constellio.model.services.users.UserServices;
import com.constellio.model.services.users.UserServicesRuntimeException.UserServicesRuntimeException_NoSuchGroup;
import com.constellio.model.services.users.UserServicesRuntimeException.UserServicesRuntimeException_NoSuchUser;
public class TaskRecordExtension extends RecordExtension {
private static final Logger LOGGER = LogManager.getLogger(TaskRecordExtension.class);
private final TasksSchemasRecordsServices tasksSchema;
String collection;
ModelLayerFactory modelLayerFactory;
RecordServices recordServices;
UserServices userServices;
ConstellioEIMConfigs eimConfigs;
public TaskRecordExtension(String collection, AppLayerFactory appLayerFactory) {
this.modelLayerFactory = appLayerFactory.getModelLayerFactory();
this.collection = collection;
tasksSchema = new TasksSchemasRecordsServices(collection, appLayerFactory);
recordServices = this.modelLayerFactory.newRecordServices();
userServices = appLayerFactory.getModelLayerFactory().newUserServices();
eimConfigs = new ConstellioEIMConfigs(appLayerFactory.getModelLayerFactory().getSystemConfigurationsManager());
}
@Override
public void recordLogicallyDeleted(RecordLogicalDeletionEvent event) {
if (event.getRecord().getSchemaCode().startsWith(Task.SCHEMA_TYPE)) {
Task task = tasksSchema.wrapTask(event.getRecord());
sendDeletionEventToFollowers(task);
}
}
@Override
public void recordInModificationBeforeValidationAndAutomaticValuesCalculation(
RecordInModificationBeforeValidationAndAutomaticValuesCalculationEvent event) {
if (event.getRecord().getSchemaCode().startsWith(Task.SCHEMA_TYPE)) {
Task task = tasksSchema.wrapTask(event.getRecord());
taskInModification(task, event);
}
}
@Override
public void recordInCreationBeforeValidationAndAutomaticValuesCalculation(
RecordInCreationBeforeValidationAndAutomaticValuesCalculationEvent event) {
if (event.getRecord().getSchemaCode().startsWith(Task.SCHEMA_TYPE)) {
Task task = tasksSchema.wrapTask(event.getRecord());
taskInCreation(task, event);
}
}
private void taskInCreation(Task task, RecordInCreationBeforeValidationAndAutomaticValuesCalculationEvent event) {
sendEmailToAssignee(task);
TaskStatus currentStatus = (task.getStatus() == null) ? null : tasksSchema.getTaskStatus(task.getStatus());
updateEndDateAndStartDateIfNecessary(task, currentStatus);
}
void sendDeletionEventToFollowers(Task task) {
Set<String> followersIds = getTaskDeletionFollowers(task);
if (followersIds.isEmpty()) {
return;
}
EmailToSend emailToSend = prepareEmailToSend(task, followersIds, TASK_DELETED);
saveEmailToSend(emailToSend, task);
}
private void saveEmailToSend(EmailToSend emailToSend, Task task) {
prepareTaskParameters(emailToSend, task);
Transaction transaction = new Transaction();
transaction.setRecordFlushing(RecordsFlushing.LATER());
transaction.add(emailToSend);
try {
recordServices.execute(transaction);
} catch (RecordServicesException e) {
throw new RuntimeException(e);
}
}
private EmailToSend prepareEmailToSend(Task task, Set<String> followersIds, String templateId) {
EmailToSend emailToSend = tasksSchema.newEmailToSend().setTryingCount(0d);
List<EmailAddress> followersEmails = getEmails(followersIds);
emailToSend.setTo(followersEmails);
emailToSend.setSendOn(TimeProvider.getLocalDateTime());
emailToSend.setTemplate(templateId);
return emailToSend;
}
private void prepareTaskParameters(EmailToSend emailToSend, Task task) {
List<String> newParameters = new ArrayList<>();
List<String> parameters = emailToSend.getParameters();
newParameters.addAll(parameters);
String parentTaskTitle = "";
String assignerFullName = getUserFullNameById(task.getAssigner());
String assigneeFullName = getUserFullNameById(task.getAssignee());
if (task.getParentTask() != null) {
Task parentTask = tasksSchema.getTask(task.getParentTask());
parentTaskTitle = parentTask.getTitle();
}
String status = tasksSchema.getTaskStatus(task.getStatus()).getTitle();
newParameters.add(TASK_TITLE_PARAMETER + ":" + formatToParameter(task.getTitle()));
newParameters.add(PARENT_TASK_TITLE + ":" + formatToParameter(parentTaskTitle));
newParameters.add(TASK_ASSIGNED_BY + ":" + formatToParameter(assignerFullName));
newParameters.add(TASK_ASSIGNED_ON + ":" + formatToParameter(task.getAssignedOn()));
newParameters.add(TASK_ASSIGNED + ":" + formatToParameter(assigneeFullName));
newParameters.add(TASK_DUE_DATE + ":" + formatToParameter(task.getDueDate()));
newParameters.add(TASK_STATUS + ":" + formatToParameter(status));
newParameters.add(TASK_DESCRIPTION + ":" + formatToParameter(task.getDescription()));
String constellioURL = eimConfigs.getConstellioUrl();
newParameters
.add(DISPLAY_TASK + ":" + constellioURL + "#!" + TasksNavigationConfiguration.DISPLAY_TASK + "/" + task.getId());
newParameters.add(COMPLETE_TASK + ":" + constellioURL + "#!" + TasksNavigationConfiguration.EDIT_TASK
+ "/completeTask%253Dtrue%253Bid%253D" + task.getId());
newParameters.add(CONSTELLIO_URL + ":" + constellioURL);
emailToSend.setParameters(newParameters);
}
private Object formatToParameter(Object parameter) {
if(parameter == null) {
return "";
}
return parameter;
}
private List<EmailAddress> getEmails(Set<String> usersIds) {
List<EmailAddress> emailAddressesTo = new ArrayList<>();
for (String userId : usersIds) {
User user = tasksSchema.wrapUser(recordServices.getDocumentById(userId));
emailAddressesTo.addAll(buildEmailAddressList(user.getTitle(), user.getEmail(), user.getPersonalEmails()));
}
return emailAddressesTo;
}
void taskInModification(Task task, RecordInModificationBeforeValidationAndAutomaticValuesCalculationEvent event) {
if (event.hasModifiedMetadata(Task.STATUS)) {
TaskStatus currentStatus = (task.getStatus() == null) ? null : tasksSchema.getTaskStatus(task.getStatus());
updateEndDateAndStartDateIfNecessary(task, currentStatus);
if (currentStatus != null && currentStatus.isFinished()) {
sendTaskCompletedEmail(task, event);
} else {
sendStatusModificationToFollowers(task, event);
}
}
//FIXME Francis plusieurs courriels envoyés si plusieurs sous taches modifiees
if (event.hasModifiedMetadata(Task.PARENT_TASK)) {
String parentId = task.getParentTask();
if (StringUtils.isNotBlank(parentId)) {
Task parent = tasksSchema.getTask(parentId);
sendSubTasksModification(parent, task);
}
String previousParent = event.getPreviousValue(Task.PARENT_TASK);
if (previousParent != null) {
sendSubTasksModification(tasksSchema.getTask(previousParent), task);
}
}
if (event.hasModifiedMetadata(Task.ASSIGNEE)) {
sendAssigneeModificationEvent(task, event);
} else if (event.hasModifiedMetadata(Task.ASSIGNEE_GROUPS_CANDIDATES) || event
.hasModifiedMetadata(Task.ASSIGNEE_USERS_CANDIDATES)) {
sendEmailToAssignee(task);
}
boolean startDateModified = event.hasModifiedMetadata(Task.START_DATE);
boolean dueDateModified = event.hasModifiedMetadata(Task.DUE_DATE);
if (startDateModified || dueDateModified) {
updateRemindersStatus(task, startDateModified, dueDateModified);
}
}
private void updateEndDateAndStartDateIfNecessary(Task task, TaskStatus currentStatus) {
if (currentStatus == null) {
task.setEndDate(null);
task.setStartDate(null);
} else {
switch (currentStatus.getStatusType()) {
case STANDBY:
task.setEndDate(null);
task.setStartDate(null);
break;
case IN_PROGRESS:
updateStartDateIfNotNull(task);
task.setEndDate(null);
break;
case FINISHED:
case CLOSED:
updateStartDateIfNotNull(task);
updateEndDateIfNotNull(task);
break;
}
}
}
private void updateStartDateIfNotNull(Task task) {
if (task.getStartDate() == null) {
task.setStartDate(TimeProvider.getLocalDate());
}
}
private void updateEndDateIfNotNull(Task task) {
if (task.getEndDate() == null) {
task.setEndDate(TimeProvider.getLocalDate());
}
}
List<TaskReminder> updateRemindersStatus(Task task, boolean startDateModified,
boolean dueDate) {
List<TaskReminder> reminders = task.getReminders();
if (reminders == null || reminders.isEmpty()) {
return reminders;
}
for (TaskReminder taskReminder : reminders) {
if (taskReminder.getFixedDate() == null && taskReminder.isProcessed()) {
if (startDateModified && taskReminder.isRelativeToStartDate()) {
LocalDate newRelativeDate = taskReminder.computeDate(task);
if (newRelativeDate.isAfter(TimeProvider.getLocalDate())) {
taskReminder.setProcessed(false);
}
} else if (dueDate && taskReminder.isRelativeToDueDate()) {
LocalDate newRelativeDate = taskReminder.computeDate(task);
if (newRelativeDate.isAfter(TimeProvider.getLocalDate())) {
taskReminder.setProcessed(false);
}
}
}
}
return reminders;
}
private void sendAssigneeModificationEvent(Task task,
RecordInModificationBeforeValidationAndAutomaticValuesCalculationEvent event) {
sendEmailToAssignee(task);
Set<String> followersIds = getTaskAssigneeModificationFollowers(task);
if (followersIds.isEmpty()) {
return;
}
EmailToSend emailToSend = prepareEmailToSend(task, followersIds, TASK_ASSIGNEE_MODIFIED);
List<String> parameters = new ArrayList<>(emailToSend.getParameters());
parameters.add(PREVIOUS_ASSIGNEE + ":" + getUserNameById((String) event.getPreviousValue(Task.ASSIGNEE)));
parameters.add(ACTUAL_ASSIGNEE + ":" + getUserNameById(task.getAssignee()));
emailToSend.setParameters(parameters);
saveEmailToSend(emailToSend, task);
}
private void sendEmailToAssignee(Task task) {
Set<EmailAddress> assigneeEmails = getTaskAssigneesEmails(task);
if (!assigneeEmails.isEmpty()) {
EmailToSend emailToSend = tasksSchema.newEmailToSend().setTryingCount(0d);
emailToSend.setTo(new ArrayList<>(assigneeEmails));
emailToSend.setSendOn(TimeProvider.getLocalDateTime());
emailToSend.setTemplate(TASK_ASSIGNED_TO_YOU);
List<String> parameters = new ArrayList<>(emailToSend.getParameters());
emailToSend.setParameters(parameters);
saveEmailToSend(emailToSend, task);
}
}
private Set<EmailAddress> getTaskAssigneesEmails(Task task) {
Set<EmailAddress> assigneeEmails = new HashSet<>();
if (task.getAssignee() != null) {
assigneeEmails.addAll(emailAddress(task.getAssignee()));
}
if (task.getAssigneeUsersCandidates() != null) {
for (String userId : task.getAssigneeUsersCandidates()) {
assigneeEmails.addAll(emailAddress(userId));
}
}
if (task.getAssigneeGroupsCandidates() != null) {
for (String groupId : task.getAssigneeGroupsCandidates()) {
UserServices userServices = modelLayerFactory.newUserServices();
try {
Group group = tasksSchema.getGroup(groupId);
List<UserCredential> groupUsers = userServices.getGlobalGroupActifUsers(group.getCode());
for (UserCredential user : groupUsers) {
assigneeEmails.addAll(buildEmailAddressList(user.getTitle(), user.getEmail(), user.getPersonalEmails()));
}
} catch (UserServicesRuntimeException_NoSuchGroup e) {
LOGGER.warn("Group assigned in task " + task.getTitle() + " does not exist " + groupId);
}
}
}
return assigneeEmails;
}
private List<EmailAddress> emailAddress(String userId) {
List<EmailAddress> emailAddressList = new ArrayList<>();
try {
final User user = tasksSchema.getUser(userId);
emailAddressList = buildEmailAddressList(user.getTitle(), user.getEmail(), user.getPersonalEmails());
} catch (UserServicesRuntimeException_NoSuchUser e) {
LOGGER.warn("User assignee does not exists " + userId);
}
return emailAddressList;
}
private List<EmailAddress> buildEmailAddressList(final String title, final String principalEmail, final List<String> personalEmails) {
final List<EmailAddress> emailAddressList = new ArrayList<>();
if (StringUtils.isNotBlank(principalEmail)) {
emailAddressList.add(new EmailAddress(title, principalEmail));
}
if (!CollectionUtils.isEmpty(personalEmails)) {
for (final String personalEmail : personalEmails) {
emailAddressList.add(new EmailAddress(title, personalEmail));
}
}
return emailAddressList;
}
private String getUserNameById(String userId) {
if (StringUtils.isBlank(userId)) {
return "";
}
return tasksSchema.wrapUser(recordServices.getDocumentById(userId)).getUsername();
}
private String getUserFullNameById(String userId) {
if (StringUtils.isBlank(userId)) {
return "";
}
return tasksSchema.wrapUser(recordServices.getDocumentById(userId)).getFirstName() + " " +
tasksSchema.wrapUser(recordServices.getDocumentById(userId)).getLastName();
}
private void sendSubTasksModification(Task parentTask, Task task) {
Set<String> followersIds = getTaskSubTasksModificationFollowers(parentTask);
if (followersIds.isEmpty()) {
return;
}
EmailToSend emailToSend = prepareEmailToSend(task, followersIds, TASK_SUB_TASKS_MODIFIED);
saveEmailToSend(emailToSend, task);
}
private void sendStatusModificationToFollowers(Task task,
RecordInModificationBeforeValidationAndAutomaticValuesCalculationEvent event) {
Set<String> followersIds = getTaskStatusModificationFollowers(task);
if (followersIds.isEmpty()) {
return;
}
EmailToSend emailToSend = prepareEmailToSend(task, followersIds, TASK_STATUS_MODIFIED);
List<String> parameters = new ArrayList<>(emailToSend.getParameters());
parameters.add(PREVIOUS_STATUS + ":" + getStatusLabel((String) event.getPreviousValue(Task.STATUS)));
parameters.add(ACTUAL_STATUS + ":" + getStatusLabel(task.getStatus()));
emailToSend.setParameters(parameters);
saveEmailToSend(emailToSend, task);
}
private String getStatusLabel(String statusId) {
if (StringUtils.isBlank(statusId)) {
return "";
}
return tasksSchema.getTaskStatus(statusId).getTitle();
}
private void sendTaskCompletedEmail(Task task, RecordInModificationBeforeValidationAndAutomaticValuesCalculationEvent event) {
Set<String> followersIds = getTaskCompetedFollowers(task);
if (followersIds.isEmpty()) {
return;
}
EmailToSend emailToSend = prepareEmailToSend(task, followersIds, TASK_COMPLETED);
saveEmailToSend(emailToSend, task);
}
private Set<String> getTaskAssigneeModificationFollowers(Task task) {
Set<String> followersIds = new HashSet<>();
List<TaskFollower> taskFollowers = task.getTaskFollowers();
if (taskFollowers != null) {
for (TaskFollower taskFollower : taskFollowers) {
if (taskFollower.getFollowTaskAssigneeModified()) {
followersIds.add(taskFollower.getFollowerId());
}
}
}
return followersIds;
}
private Set<String> getTaskSubTasksModificationFollowers(Task task) {
Set<String> followersIds = new HashSet<>();
List<TaskFollower> taskFollowers = task.getTaskFollowers();
if (taskFollowers != null) {
for (TaskFollower taskFollower : taskFollowers) {
if (taskFollower.getFollowSubTasksModified()) {
followersIds.add(taskFollower.getFollowerId());
}
}
}
return followersIds;
}
private Set<String> getTaskStatusModificationFollowers(Task task) {
Set<String> followersIds = new HashSet<>();
List<TaskFollower> taskFollowers = task.getTaskFollowers();
if (taskFollowers != null) {
for (TaskFollower taskFollower : taskFollowers) {
if (taskFollower.getFollowTaskStatusModified()) {
followersIds.add(taskFollower.getFollowerId());
}
}
}
return followersIds;
}
private Set<String> getTaskCompetedFollowers(Task task) {
Set<String> followersIds = new HashSet<>();
List<TaskFollower> taskFollowers = task.getTaskFollowers();
if (taskFollowers != null) {
for (TaskFollower taskFollower : taskFollowers) {
if (taskFollower.getFollowTaskCompleted()) {
followersIds.add(taskFollower.getFollowerId());
}
}
}
return followersIds;
}
private Set<String> getTaskDeletionFollowers(Task task) {
List<TaskFollower> taskFollowers = task.getTaskFollowers();
Set<String> followersIds = new HashSet<>();
if (taskFollowers != null) {
for (TaskFollower taskFollower : taskFollowers) {
if (taskFollower.getFollowTaskDeleted()) {
followersIds.add(taskFollower.getFollowerId());
}
}
}
return followersIds;
}
@Override
public void recordInCreationBeforeSave(final RecordInCreationBeforeSaveEvent event) {
final Record record = event.getRecord();
if (record.getSchemaCode().startsWith(Task.SCHEMA_TYPE)) {
final Task task = tasksSchema.wrapTask(record);
//
addAssignerAsCompletionEventFollower(task);
}
}
private void addAssignerAsCompletionEventFollower(final Task task) {
final List<TaskFollower> currentFollowersList = task.getTaskFollowers();
if (!(task.isModel() || task.getAssigner() == null)) {
final TaskFollower assignerAsCompletionEventFollower = new TaskFollower().setFollowerId(task.getAssigner()).setFollowTaskCompleted(true).setDirty(true);
if (!currentFollowersList.contains(assignerAsCompletionEventFollower)) {
final List<TaskFollower> newFollowersList = new ArrayList<>(task.getTaskFollowers());
newFollowersList.add(assignerAsCompletionEventFollower);
task.setTaskFollowers(Collections.unmodifiableList(newFollowersList));
}
}
}
}