Skip to content

Commit

Permalink
SAK-46986 Tasks: remove tasks for removed site users (sakaiproject#10302
Browse files Browse the repository at this point in the history
  • Loading branch information
adrianfish authored Apr 12, 2022
1 parent eff3692 commit c98f77c
Show file tree
Hide file tree
Showing 11 changed files with 219 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1479,6 +1479,7 @@ public void updateAssignment(Assignment assignment) throws PermissionException {
task.setReference(reference);
task.setSystem(true);
task.setDescription(assignment.getTitle());
task.setGroups(assignment.getGroups());

if (!assignment.getHideDueDate()) {
task.setDue(assignment.getDueDate());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4859,7 +4859,7 @@ else if( event_startTime.before(now_time) && state.getIsPastAlertOff() ) {
List attachments = state.getAttachments();

// prepare to create the event
Collection groups = new Vector();
Collection<Group> groups = new Vector<>();
CalendarEvent.EventAccess access = CalendarEvent.EventAccess.GROUPED;
if (scheduleTo.equals("site")) access = CalendarEvent.EventAccess.SITE;

Expand Down Expand Up @@ -4929,14 +4929,13 @@ else if( event_startTime.before(now_time) && state.getIsPastAlertOff() ) {
Site site = SiteService.getSite(calendarObj.getContext());
users = site.getUsersIsAllowed("section.role.student");
} else if (CalendarEvent.EventAccess.GROUPED.equals(access)){
for (Iterator groupsIter = groups.iterator(); groupsIter.hasNext();) {
Group groupTask = (Group) groupsIter.next();
Set<Member> members = groupTask.getMembers();
for (Iterator membersIter = members.iterator(); membersIter.hasNext();) {
Member member = (Member) membersIter.next();
users.add(member.getUserId());
}
Set<String> groupRefs = new HashSet<>();
for (Group group : groups) {
groupRefs.add(group.getReference());
users.addAll(group.getMembers().stream()
.map(m -> m.getUserId()).collect(Collectors.toSet()));
}
task.setGroups(groupRefs);
}
if (users.size() == 0) {
users.add(UserDirectoryService.getCurrentUser().getId());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* Copyright (c) 2003-2017 The Apereo Foundation
*
* Licensed under the Educational Community License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://opensource.org/licenses/ecl2
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sakaiproject.authz.api;

import lombok.Builder;
import org.sakaiproject.entity.api.Entity;

public class AuthzGroupReferenceBuilder {

@Builder
public static String buildReference(String site, String group) {

String ref = "";
if (site != null) {
ref += Entity.SEPARATOR + "site" + Entity.SEPARATOR + site;
}

if (group != null) {
ref += Entity.SEPARATOR + "group" + Entity.SEPARATOR + group;
}

return ref;
}
}
10 changes: 10 additions & 0 deletions kernel/api/src/main/java/org/sakaiproject/tasks/api/Task.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,18 @@
package org.sakaiproject.tasks.api;

import java.time.Instant;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Index;
import javax.persistence.JoinColumn;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;

Expand Down Expand Up @@ -57,6 +62,11 @@ public class Task implements PersistableEntity<Long> {
@Column(name = "SITE_ID", length = 99)
private String siteId;

@ElementCollection
@CollectionTable(name = "TASK_GROUPS", joinColumns = @JoinColumn(name = "TASK_ID"))
@Column(name = "GROUP_ID", length = 99)
private Set<String> groups = new HashSet<>();

@Column(name = "REFERENCE", length = 255, nullable = false)
private String reference;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

package org.sakaiproject.tasks.api.repository;

import java.util.List;
import java.util.Optional;

import org.sakaiproject.tasks.api.Task;
Expand All @@ -29,4 +30,5 @@
public interface TaskRepository extends SpringCrudRepository<Task, Long> {

Optional<Task> findByReference(String reference);
List<Task> findByGroupsContaining(String groupId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
package org.sakaiproject.tasks.api.repository;

import java.util.List;
import java.util.Set;
import java.time.Instant;

import org.sakaiproject.tasks.api.Task;
Expand All @@ -33,5 +34,7 @@ public interface UserTaskRepository extends SpringCrudRepository<UserTask, Long>
List<UserTask> findByTaskIdAndUserIdIn(Long taskId, List<String> userIds);
List<UserTask> findByUserId(String userId);
List<UserTask> findByUserIdAndTask_StartsLessThanEqual(String userId, Instant instant);
List<UserTask> findByTask_SiteId(String siteId);
void deleteByTask(Task task);
void deleteByTaskAndUserIdNotIn(Task task, Set<String> users);
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,23 @@
package org.sakaiproject.tasks.impl;

import java.time.Instant;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import org.sakaiproject.authz.api.AuthzGroupReferenceBuilder;
import org.sakaiproject.authz.api.AuthzGroupService;
import org.sakaiproject.authz.api.GroupNotDefinedException;
import org.sakaiproject.entity.api.EntityManager;
import org.sakaiproject.event.api.Event;
import org.sakaiproject.event.api.EventTrackingService;
import org.sakaiproject.site.api.SiteService;
import org.sakaiproject.tasks.api.Task;
import org.sakaiproject.tasks.api.UserTask;
import org.sakaiproject.tasks.api.TaskService;
Expand All @@ -39,24 +51,80 @@
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import org.springframework.transaction.support.TransactionTemplate;

import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Setter
@Transactional(readOnly = true)
public class TaskServiceImpl implements TaskService {
public class TaskServiceImpl implements TaskService, Observer {

@Autowired private AuthzGroupService authzGroupService;
@Autowired private EntityManager entityManager;
@Autowired private EventTrackingService eventTrackingService;
@Autowired private SessionManager sessionManager;
@Autowired private SiteService siteService;
@Autowired private TaskRepository taskRepository;
@Autowired private UserTaskRepository userTaskRepository;

@Setter private TransactionTemplate transactionTemplate;

public void init() {

@Resource
private TaskRepository taskRepository;
eventTrackingService.addObserver(this);
}

@Resource
private UserTaskRepository userTaskRepository;
public void update(Observable o, Object arg) {

if (arg instanceof Event) {
Event event = (Event) arg;
if (event.getEvent().equals(SiteService.SECURE_UPDATE_SITE_MEMBERSHIP)) {
try {
Set<String> siteUsers = siteService.getSite(event.getContext()).getUsers();
transactionTemplate.executeWithoutResult(status -> {

userTaskRepository.findByTask_SiteId(event.getContext()).forEach(userTask -> {

if (!siteUsers.contains(userTask.getUserId())) {
// This user task's user has been removed from the site
userTaskRepository.deleteById(userTask.getId());
}
});
});
} catch (Exception e) {
log.error("Failed to update user tasks for site {}: {}", event.getContext(), e.toString());
}
} else if (event.getEvent().equals(SiteService.SECURE_UPDATE_GROUP_MEMBERSHIP)) {
String groupId = event.getResource();
try {
String groupRef = AuthzGroupReferenceBuilder.builder().site(event.getContext()).group(groupId).build();
transactionTemplate.executeWithoutResult(status -> {

// Find any task containing this group
taskRepository.findByGroupsContaining(groupRef).forEach(t -> {

// Get the set of users in all this tasks's groups
Set<String> users = t.getGroups().stream().map(group -> {

try {
return authzGroupService.getAuthzGroup(group).getUsers();
} catch (GroupNotDefinedException gnde) {
return Collections.<String>emptySet();
}

}).flatMap(Collection::stream).collect(Collectors.toSet());

userTaskRepository.deleteByTaskAndUserIdNotIn(t, users);
});
});
} catch (Exception e) {
log.error("Failed to update user tasks for group {}: {}", groupId, e.toString());
}
}
}
}

@Transactional
public UserTask createSingleUserTask(UserTaskAdapterBean transfer) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

package org.sakaiproject.tasks.impl.repository;

import java.util.List;
import java.util.Optional;

import javax.persistence.criteria.CriteriaBuilder;
Expand All @@ -47,4 +48,16 @@ public Optional<Task> findByReference(String reference) {

return Optional.ofNullable(session.createQuery(cq).uniqueResult());
}

public List<Task> findByGroupsContaining(String groupId) {

Session session = sessionFactory.getCurrentSession();

CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<Task> cq = cb.createQuery(Task.class);
Root<Task> root = cq.from(Task.class);
cq.select(root).where(cb.isMember(groupId, root.get("groups")));

return session.createQuery(cq).list();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,18 @@
package org.sakaiproject.tasks.impl.repository;

import java.util.List;
import java.util.Set;
import java.time.Instant;

import org.hibernate.Session;
import org.hibernate.criterion.Restrictions;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaDelete;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.Root;

import org.sakaiproject.tasks.api.Task;
import org.sakaiproject.tasks.api.UserTask;
import org.sakaiproject.tasks.api.repository.UserTaskRepository;
Expand Down Expand Up @@ -60,6 +67,21 @@ public List<UserTask> findByUserIdAndTask_StartsLessThanEqual(String userId, Ins
.add(Restrictions.le("task.starts", instant)).list();
}

public List<UserTask> findByTask_SiteId(String siteId) {

Session session = sessionFactory.getCurrentSession();

CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<UserTask> cq = cb.createQuery(UserTask.class);
Root<UserTask> root = cq.from(UserTask.class);
Join<UserTask, Task> taskJoin = root.join("task");
cq.select(root);
cq.where(cb.equal(taskJoin.get("siteId"), siteId));

return session.createQuery(cq).list();

}

public void deleteByTask(Task task) {

Session session = sessionFactory.getCurrentSession();
Expand All @@ -68,4 +90,16 @@ public void deleteByTask(Task task) {
.setParameter("task", task).executeUpdate();
session.delete(task);
}

public void deleteByTaskAndUserIdNotIn(Task task, Set<String> users) {

Session session = sessionFactory.getCurrentSession();

CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaDelete<UserTask> cd = cb.createCriteriaDelete(UserTask.class);
Root<UserTask> root = cd.from(UserTask.class);
cd.where(cb.equal(root.get("task"), task), cb.not(root.get("userId").in(users)));

session.createQuery(cd).executeUpdate();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@
</bean>

<bean id="org.sakaiproject.tasks.api.TaskService"
class="org.sakaiproject.tasks.impl.TaskServiceImpl">
class="org.sakaiproject.tasks.impl.TaskServiceImpl" init-method="init">

<property name="transactionTemplate">
<bean class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="org.sakaiproject.springframework.orm.hibernate.GlobalTransactionManager"/>
</bean>
</property>
</bean>

<bean id="tasksHibernateMappings"
Expand Down
Loading

0 comments on commit c98f77c

Please sign in to comment.