From 60e97fd6f5405f77d2bbfd784b569d88eaa3d8ae Mon Sep 17 00:00:00 2001
From: uhensler <urs.hensler@frentix.com>
Date: Mon, 9 Dec 2019 14:24:50 +0100
Subject: [PATCH] OO-4207: Create assessment entries when user got course
 participant

---
 .../manager/CourseAssessmentServiceImpl.java  |   1 -
 .../manager/CourseMembershipProcessor.java    | 240 ++++++++++++++++++
 .../org/olat/group/BusinessGroupService.java  |   2 +-
 .../manager/BusinessGroupServiceImpl.java     |   5 +-
 .../ui/edit/BusinessGroupModifiedEvent.java   |   2 -
 .../BusinessGroupRepositoryEntryEvent.java    |  65 +++++
 .../CurriculumElementMembershipEvent.java     |  63 +++++
 ...CurriculumElementRepositoryEntryEvent.java |  52 ++++
 .../manager/CurriculumServiceImpl.java        |  31 +++
 .../olat/repository/RepositoryManager.java    |   6 +
 .../manager/RepositoryServiceImpl.java        |   2 +-
 ...epositoryEntryMembershipModifiedEvent.java |   7 +
 12 files changed, 470 insertions(+), 6 deletions(-)
 create mode 100644 src/main/java/org/olat/course/assessment/manager/CourseMembershipProcessor.java
 create mode 100644 src/main/java/org/olat/group/ui/edit/BusinessGroupRepositoryEntryEvent.java
 create mode 100644 src/main/java/org/olat/modules/curriculum/CurriculumElementMembershipEvent.java
 create mode 100644 src/main/java/org/olat/modules/curriculum/CurriculumElementRepositoryEntryEvent.java

diff --git a/src/main/java/org/olat/course/assessment/manager/CourseAssessmentServiceImpl.java b/src/main/java/org/olat/course/assessment/manager/CourseAssessmentServiceImpl.java
index 4e3806f0ff2..9158065f1e5 100644
--- a/src/main/java/org/olat/course/assessment/manager/CourseAssessmentServiceImpl.java
+++ b/src/main/java/org/olat/course/assessment/manager/CourseAssessmentServiceImpl.java
@@ -403,5 +403,4 @@ public class CourseAssessmentServiceImpl implements CourseAssessmentService, Nod
 		UserCourseEnvironment userCourseEnv = new UserCourseEnvironmentImpl(identityEnv, courseEnv);
 		userCourseEnv.getScoreAccounting().evaluateAll(true);
 	}
-
 }
diff --git a/src/main/java/org/olat/course/assessment/manager/CourseMembershipProcessor.java b/src/main/java/org/olat/course/assessment/manager/CourseMembershipProcessor.java
new file mode 100644
index 00000000000..1c8012c6bc3
--- /dev/null
+++ b/src/main/java/org/olat/course/assessment/manager/CourseMembershipProcessor.java
@@ -0,0 +1,240 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.course.assessment.manager;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import javax.annotation.PostConstruct;
+
+import org.apache.logging.log4j.Logger;
+import org.olat.basesecurity.BaseSecurity;
+import org.olat.basesecurity.GroupRoles;
+import org.olat.basesecurity.IdentityRef;
+import org.olat.basesecurity.model.IdentityRefImpl;
+import org.olat.core.gui.control.Event;
+import org.olat.core.id.Identity;
+import org.olat.core.id.IdentityEnvironment;
+import org.olat.core.logging.Tracing;
+import org.olat.core.util.coordinate.CoordinatorManager;
+import org.olat.core.util.event.GenericEventListener;
+import org.olat.core.util.resource.OresHelper;
+import org.olat.course.CourseFactory;
+import org.olat.course.ICourse;
+import org.olat.course.run.environment.CourseEnvironment;
+import org.olat.course.run.userview.UserCourseEnvironment;
+import org.olat.course.run.userview.UserCourseEnvironmentImpl;
+import org.olat.group.BusinessGroupRef;
+import org.olat.group.BusinessGroupService;
+import org.olat.group.model.BusinessGroupRefImpl;
+import org.olat.group.ui.edit.BusinessGroupModifiedEvent;
+import org.olat.group.ui.edit.BusinessGroupRepositoryEntryEvent;
+import org.olat.modules.curriculum.CurriculumElement;
+import org.olat.modules.curriculum.CurriculumElementMembershipEvent;
+import org.olat.modules.curriculum.CurriculumElementRepositoryEntryEvent;
+import org.olat.modules.curriculum.CurriculumRoles;
+import org.olat.modules.curriculum.CurriculumService;
+import org.olat.modules.curriculum.model.CurriculumElementRefImpl;
+import org.olat.repository.RepositoryEntry;
+import org.olat.repository.RepositoryService;
+import org.olat.repository.model.RepositoryEntryMembershipModifiedEvent;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * 
+ * Initial date: 9 Dec 2019<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+@Service
+public class CourseMembershipProcessor implements GenericEventListener {
+
+	private static final Logger log = Tracing.createLoggerFor(CourseMembershipProcessor.class);
+	
+	@Autowired
+	private BaseSecurity securityManager;
+	@Autowired
+	private RepositoryService repositoryService;
+	@Autowired
+	private BusinessGroupService groupService;
+	@Autowired
+	private CurriculumService curriculumService;
+	@Autowired
+	private CoordinatorManager coordinator;
+	
+	@PostConstruct
+	void initProviders() {
+		coordinator.getCoordinator().getEventBus().registerFor(this, null, OresHelper.lookupType(RepositoryEntry.class));
+		coordinator.getCoordinator().getEventBus().registerFor(this, null, OresHelper.lookupType(CurriculumElement.class));
+	}
+
+	@Override
+	public void event(Event event) {
+		if (event instanceof RepositoryEntryMembershipModifiedEvent) {
+			// Identity was added to course as participant.
+			// Course member got role participant.
+			RepositoryEntryMembershipModifiedEvent e = (RepositoryEntryMembershipModifiedEvent)event;
+			if (RepositoryEntryMembershipModifiedEvent.ROLE_PARTICIPANT_ADDED.equals(e.getCommand())) {
+				tryProcessAddedToRepository(e.getIdentityKey(), e.getRepositoryEntryKey());
+			}
+		} else if (event instanceof BusinessGroupModifiedEvent) {
+			// Identity was added to a group.
+			// Group member got other roles in group.
+			BusinessGroupModifiedEvent e = (BusinessGroupModifiedEvent)event;
+			if (BusinessGroupModifiedEvent.IDENTITY_ADDED_EVENT.equals(e.getCommand())) {
+				tryProcessAddedToGroup(e.getAffectedIdentityKey(), e.getModifiedGroupKey());
+			}
+		} else if (event instanceof BusinessGroupRepositoryEntryEvent) {
+			// Group was added to a repository entry
+			BusinessGroupRepositoryEntryEvent e = (BusinessGroupRepositoryEntryEvent)event;
+			if (BusinessGroupRepositoryEntryEvent.REPOSITORY_ENTRY_ADDED.equals(e.getCommand())) {
+				tryProcessRepositoryAdded(e.getEntryKey(), e.getGroupKey());
+			}
+		} else if (event instanceof CurriculumElementMembershipEvent) {
+			// Identity was added to curriculum entries
+			CurriculumElementMembershipEvent e = (CurriculumElementMembershipEvent)event;
+			if (CurriculumElementMembershipEvent.MEMEBER_ADDED.equals(e.getCommand())) {
+				if (CurriculumRoles.participant == e.getRole()) {
+					tryProcessCurriculumElementMemberAdded(e.getIdentityKey(), e.getCurriculumElementKeys());
+				}
+			}
+		} else if (event instanceof CurriculumElementRepositoryEntryEvent) {
+			CurriculumElementRepositoryEntryEvent e = (CurriculumElementRepositoryEntryEvent) event;
+			if (CurriculumElementRepositoryEntryEvent.REPOSITORY_ENTRY_ADDED.equals(e.getCommand())) {
+				tryProcessCurriculumElementRepositoryAdded(e.getCurriculumElementKey(), e.getEntryKey());
+			}
+		}
+		
+	}
+
+	private void tryProcessAddedToRepository(Long identityKey, Long courseEntryKey) {
+		try {
+			Identity identity = securityManager.loadIdentityByKey(identityKey);
+			RepositoryEntry courseEntry = repositoryService.loadByKey(courseEntryKey);
+			evaluateAll(identity, courseEntry);
+		} catch (Exception e) {
+			log.error("Error when tried to evaluate all assessment entries of Identity {} in RepositoryEntry {}",
+					courseEntryKey, identityKey);
+		}
+	}
+	
+	private void tryProcessAddedToGroup(Long identityKey, Long groupKey) {
+		try {
+			processAddedToGroup(identityKey, groupKey);
+		} catch (Exception e) {
+			log.error("Error when tried to process added to group of Identity {} to Group {}",
+					groupKey, identityKey);
+		}
+	}
+	
+	private void processAddedToGroup(Long identityKey, Long groupKey) {
+		if (!isParticipant(identityKey, groupKey)) return;
+	
+		Identity identity = securityManager.loadIdentityByKey(identityKey);
+		List<RepositoryEntry> repositoryEntries = groupService
+				.findRepositoryEntries(Collections.singletonList(new BusinessGroupRefImpl(groupKey)), 0, -1);
+		
+		for (RepositoryEntry repositoryEntry : repositoryEntries) {
+			evaluateAll(identity, repositoryEntry);
+		}
+	}
+
+	private boolean isParticipant(Long identityKey, Long groupKey) {
+		BusinessGroupRef groupRef = new BusinessGroupRefImpl(groupKey);
+		IdentityRef identityRef = new IdentityRefImpl(identityKey);
+		return groupService.hasRoles(identityRef, groupRef, GroupRoles.participant.name());
+	}
+	
+	private void tryProcessRepositoryAdded(Long entryKey, Long groupKey) {
+		try {
+			processRepositoryAdded(entryKey, groupKey);
+		} catch (Exception e) {
+			log.error("Error when tried to process added RepositoryEntry {} to Group {}",
+					entryKey, groupKey);
+		}
+	}
+	
+	private void processRepositoryAdded(Long entryKey, Long groupKey) {
+		BusinessGroupRefImpl group = new BusinessGroupRefImpl(groupKey);
+		List<Identity> participants = groupService.getMembers(group, GroupRoles.participant.name());
+		RepositoryEntry courseEntry = repositoryService.loadByKey(entryKey);
+		
+		for (Identity identity : participants) {
+			evaluateAll(identity, courseEntry);
+		}
+	}
+	
+	private void tryProcessCurriculumElementMemberAdded(Long identityKey, Collection<Long> curriculumElementKeys) {
+		try {
+			processCurriculumElementMemberAdded(identityKey, curriculumElementKeys);
+		} catch (Exception e) {
+			log.error("Error when tried to process added Identity {} to CurriculumElements {}",
+					identityKey, curriculumElementKeys);
+		}
+	}
+
+	private void processCurriculumElementMemberAdded(Long identityKey, Collection<Long> curriculumElementKeys) {
+		Identity identity = securityManager.loadIdentityByKey(identityKey);
+		
+		for (Long curriculumElementKey : curriculumElementKeys) {
+			List<RepositoryEntry> repositoryEntries = curriculumService
+					.getRepositoryEntries(new CurriculumElementRefImpl(curriculumElementKey));
+			
+			for (RepositoryEntry repositoryEntry : repositoryEntries) {
+				evaluateAll(identity, repositoryEntry);
+			}
+		}
+	}
+	
+	private void tryProcessCurriculumElementRepositoryAdded(Long curriculumElementKey, Long entryKey) {
+		try {
+			processCurriculumElementRepositoryAdded(curriculumElementKey, entryKey);
+		} catch (Exception e) {
+			log.error("Error when tried to process added RepositoryEntry {} to CurriculumElement {}",
+					entryKey, curriculumElementKey);
+		}	
+	}
+
+	private void processCurriculumElementRepositoryAdded(Long curriculumElementKey, Long entryKey) {
+		RepositoryEntry courseEntry = repositoryService.loadByKey(entryKey);
+		List<Identity> participants = curriculumService.getMembersIdentity(new CurriculumElementRefImpl(curriculumElementKey), CurriculumRoles.participant);
+		for (Identity identity : participants) {
+			evaluateAll(identity, courseEntry);
+		}
+	}
+
+	private void evaluateAll(Identity identity, RepositoryEntry courseEntry) {
+		if (identity == null || courseEntry == null) return;
+		
+		IdentityEnvironment identityEnv = new IdentityEnvironment();
+		identityEnv.setIdentity(identity);
+		
+		ICourse course = CourseFactory.loadCourse(courseEntry);
+		if (course == null) return;
+		CourseEnvironment courseEnv = course.getCourseEnvironment();
+		
+		UserCourseEnvironment userCourseEnv = new UserCourseEnvironmentImpl(identityEnv, courseEnv);
+		userCourseEnv.getScoreAccounting().evaluateAll(true);
+		log.debug("Evaluated all assessment entries of {} in {}", identity, courseEntry);
+	}
+	
+}
diff --git a/src/main/java/org/olat/group/BusinessGroupService.java b/src/main/java/org/olat/group/BusinessGroupService.java
index 0e733b8862c..d4b0875dc6d 100644
--- a/src/main/java/org/olat/group/BusinessGroupService.java
+++ b/src/main/java/org/olat/group/BusinessGroupService.java
@@ -369,7 +369,7 @@ public interface BusinessGroupService {
 	 */
 	public boolean hasRoles(IdentityRef identity, BusinessGroupRef businessGroup, String role);
 	
-	public List<Identity> getMembers(BusinessGroup businessGroup, String... roles);
+	public List<Identity> getMembers(BusinessGroupRef businessGroup, String... roles);
 	
 	public List<Identity> getMembers(List<BusinessGroup> businessGroups, String... roles);
 	
diff --git a/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java b/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java
index 7466ff2116a..de4a0f03afa 100644
--- a/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java
+++ b/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java
@@ -104,6 +104,7 @@ import org.olat.group.right.BGRightManager;
 import org.olat.group.right.BGRightsRole;
 import org.olat.group.ui.BGMailHelper;
 import org.olat.group.ui.edit.BusinessGroupModifiedEvent;
+import org.olat.group.ui.edit.BusinessGroupRepositoryEntryEvent;
 import org.olat.properties.PropertyManager;
 import org.olat.repository.LeavingStatusList;
 import org.olat.repository.RepositoryEntry;
@@ -1517,6 +1518,7 @@ public class BusinessGroupServiceImpl implements BusinessGroupService {
 	@Override
 	public void addResourceTo(BusinessGroup group, RepositoryEntry re) {
 		businessGroupRelationDAO.addRelationToResource(group, re);
+		BusinessGroupRepositoryEntryEvent.fireEvents(BusinessGroupRepositoryEntryEvent.REPOSITORY_ENTRY_ADDED, group, re);
 	}
 
 	@Override
@@ -1547,6 +1549,7 @@ public class BusinessGroupServiceImpl implements BusinessGroupService {
 				}
 				if(!found) {
 					repositoryEntryRelationDao.createRelation(baseGroup, re);
+					BusinessGroupRepositoryEntryEvent.fireEvents(BusinessGroupRepositoryEntryEvent.REPOSITORY_ENTRY_ADDED, group, re);
 				}
 			}
 		}
@@ -1696,7 +1699,7 @@ public class BusinessGroupServiceImpl implements BusinessGroupService {
 	}
 
 	@Override
-	public List<Identity> getMembers(BusinessGroup businessGroup, String... roles) {
+	public List<Identity> getMembers(BusinessGroupRef businessGroup, String... roles) {
 		return businessGroupRelationDAO.getMembers(businessGroup, roles);
 	}
 	
diff --git a/src/main/java/org/olat/group/ui/edit/BusinessGroupModifiedEvent.java b/src/main/java/org/olat/group/ui/edit/BusinessGroupModifiedEvent.java
index 54511c5b4f0..7997f19129e 100644
--- a/src/main/java/org/olat/group/ui/edit/BusinessGroupModifiedEvent.java
+++ b/src/main/java/org/olat/group/ui/edit/BusinessGroupModifiedEvent.java
@@ -36,7 +36,6 @@ import org.olat.core.id.Identity;
 import org.olat.core.util.coordinate.CoordinatorManager;
 import org.olat.core.util.event.EventBus;
 import org.olat.core.util.event.MultiUserEvent;
-import org.olat.core.util.resource.OresHelper;
 import org.olat.group.BusinessGroup;
 import org.olat.group.BusinessGroupService;
 import org.olat.repository.RepositoryEntry;
@@ -179,7 +178,6 @@ public class BusinessGroupModifiedEvent extends MultiUserEvent {
 		EventBus eventBus = CoordinatorManager.getInstance().getCoordinator().getEventBus();
 		// 1) notify listeners of group events
 		eventBus.fireEventToListenersOf(modifiedEvent, group);
-		eventBus.fireEventToListenersOf(modifiedEvent, OresHelper.lookupType(BusinessGroup.class));
 		// 2) notify listeners of learning resources of this group
 		BusinessGroupService bgs = CoreSpringFactory.getImpl(BusinessGroupService.class);
 		List<RepositoryEntry> repoEntries = bgs.findRepositoryEntries(Collections.singletonList(group), 0, -1);
diff --git a/src/main/java/org/olat/group/ui/edit/BusinessGroupRepositoryEntryEvent.java b/src/main/java/org/olat/group/ui/edit/BusinessGroupRepositoryEntryEvent.java
new file mode 100644
index 00000000000..5d7280327fe
--- /dev/null
+++ b/src/main/java/org/olat/group/ui/edit/BusinessGroupRepositoryEntryEvent.java
@@ -0,0 +1,65 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.group.ui.edit;
+
+import org.olat.core.util.coordinate.CoordinatorManager;
+import org.olat.core.util.event.EventBus;
+import org.olat.core.util.event.MultiUserEvent;
+import org.olat.group.BusinessGroup;
+import org.olat.repository.RepositoryEntry;
+
+/**
+ * 
+ * Initial date: 9 Dec 2019<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+public class BusinessGroupRepositoryEntryEvent extends MultiUserEvent {
+
+	private static final long serialVersionUID = 4794247345720510726L;
+	
+	public static final String REPOSITORY_ENTRY_ADDED = "repository.entry.added.event";
+	
+	private final Long groupKey;
+	private final Long entryKey;
+	
+	private BusinessGroupRepositoryEntryEvent(String command, Long groupKey, Long entryKey) {
+		super(command);
+		this.groupKey = groupKey;
+		this.entryKey = entryKey;
+	}
+
+	public Long getGroupKey() {
+		return groupKey;
+	}
+
+	public Long getEntryKey() {
+		return entryKey;
+	}
+
+	public static void fireEvents(String command, BusinessGroup group, RepositoryEntry entry) {
+		BusinessGroupRepositoryEntryEvent event = new BusinessGroupRepositoryEntryEvent(command, group.getKey(),entry.getKey());
+		EventBus eventBus = CoordinatorManager.getInstance().getCoordinator().getEventBus();
+		// 1) notify listeners of group event
+		eventBus.fireEventToListenersOf(event, group);
+		// 2) notify listeners of the repository entry events
+		eventBus.fireEventToListenersOf(event, entry);
+	}
+}
diff --git a/src/main/java/org/olat/modules/curriculum/CurriculumElementMembershipEvent.java b/src/main/java/org/olat/modules/curriculum/CurriculumElementMembershipEvent.java
new file mode 100644
index 00000000000..cba7422fef0
--- /dev/null
+++ b/src/main/java/org/olat/modules/curriculum/CurriculumElementMembershipEvent.java
@@ -0,0 +1,63 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.modules.curriculum;
+
+import java.util.Collection;
+import java.util.stream.Collectors;
+
+import org.olat.core.id.Identity;
+import org.olat.core.util.event.MultiUserEvent;
+
+/**
+ * 
+ * Initial date: 9 Dec 2019<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+public class CurriculumElementMembershipEvent extends MultiUserEvent {
+
+	private static final long serialVersionUID = -4147709424125454372L;
+	
+	public static final String MEMEBER_ADDED = "curriculum.element.member.added.event";
+
+	private final Collection<Long> curriculumElementKeys;
+	private final Long identityKey;
+	private final CurriculumRoles role;
+	
+	public CurriculumElementMembershipEvent(String command, Collection<? extends CurriculumElementRef> elements, Identity identity, CurriculumRoles role) {
+		super(command);
+		this.curriculumElementKeys = elements.stream().map(CurriculumElementRef::getKey).collect(Collectors.toList());
+		this.identityKey = identity.getKey();
+		this.role = role;
+	}
+
+	public Collection<Long>  getCurriculumElementKeys() {
+		return curriculumElementKeys;
+	}
+
+	public Long getIdentityKey() {
+		return identityKey;
+	}
+
+	public CurriculumRoles getRole() {
+		return role;
+	}
+
+}
diff --git a/src/main/java/org/olat/modules/curriculum/CurriculumElementRepositoryEntryEvent.java b/src/main/java/org/olat/modules/curriculum/CurriculumElementRepositoryEntryEvent.java
new file mode 100644
index 00000000000..a0831ed9c3d
--- /dev/null
+++ b/src/main/java/org/olat/modules/curriculum/CurriculumElementRepositoryEntryEvent.java
@@ -0,0 +1,52 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.modules.curriculum;
+
+import org.olat.core.util.event.MultiUserEvent;
+
+/**
+ * 
+ * Initial date: 9 Dec 2019<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+public class CurriculumElementRepositoryEntryEvent extends MultiUserEvent {
+
+	private static final long serialVersionUID = 4794247345720510725L;
+	
+	public static final String REPOSITORY_ENTRY_ADDED = "curriculum.element.repository.entry.added.event";
+	
+	private final Long curriculumElementKey;
+	private final Long entryKey;
+	
+	public CurriculumElementRepositoryEntryEvent(String command, Long repositoryEntries, Long entryKey) {
+		super(command);
+		this.curriculumElementKey = repositoryEntries;
+		this.entryKey = entryKey;
+	}
+
+	public Long getCurriculumElementKey() {
+		return curriculumElementKey;
+	}
+
+	public Long getEntryKey() {
+		return entryKey;
+	}
+}
diff --git a/src/main/java/org/olat/modules/curriculum/manager/CurriculumServiceImpl.java b/src/main/java/org/olat/modules/curriculum/manager/CurriculumServiceImpl.java
index 49d41b5a967..a7a47380404 100644
--- a/src/main/java/org/olat/modules/curriculum/manager/CurriculumServiceImpl.java
+++ b/src/main/java/org/olat/modules/curriculum/manager/CurriculumServiceImpl.java
@@ -19,6 +19,8 @@
  */
 package org.olat.modules.curriculum.manager;
 
+import static org.olat.modules.curriculum.CurriculumElementRepositoryEntryEvent.REPOSITORY_ENTRY_ADDED;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -47,13 +49,17 @@ import org.olat.core.id.Organisation;
 import org.olat.core.id.OrganisationRef;
 import org.olat.core.id.Roles;
 import org.olat.core.logging.AssertException;
+import org.olat.core.util.coordinate.CoordinatorManager;
+import org.olat.core.util.resource.OresHelper;
 import org.olat.modules.curriculum.Curriculum;
 import org.olat.modules.curriculum.CurriculumCalendars;
 import org.olat.modules.curriculum.CurriculumDataDeletable;
 import org.olat.modules.curriculum.CurriculumElement;
 import org.olat.modules.curriculum.CurriculumElementManagedFlag;
 import org.olat.modules.curriculum.CurriculumElementMembership;
+import org.olat.modules.curriculum.CurriculumElementMembershipEvent;
 import org.olat.modules.curriculum.CurriculumElementRef;
+import org.olat.modules.curriculum.CurriculumElementRepositoryEntryEvent;
 import org.olat.modules.curriculum.CurriculumElementStatus;
 import org.olat.modules.curriculum.CurriculumElementToTaxonomyLevel;
 import org.olat.modules.curriculum.CurriculumElementType;
@@ -131,6 +137,8 @@ public class CurriculumServiceImpl implements CurriculumService, OrganisationDat
 	private CurriculumElementToTaxonomyLevelDAO curriculumElementToTaxonomyLevelDao;
 	@Autowired
 	private CurriculumRepositoryEntryRelationDAO curriculumRepositoryEntryRelationDao;
+	@Autowired
+	private CoordinatorManager coordinator;
 
 	@Override
 	public Curriculum createCurriculum(String identifier, String displayName, String description, Organisation organisation) {
@@ -352,6 +360,7 @@ public class CurriculumServiceImpl implements CurriculumService, OrganisationDat
 			List<RepositoryEntry> entries = getRepositoryEntries(elementToClone);
 			for(RepositoryEntry entry:entries) {
 				repositoryEntryRelationDao.createRelation(clone.getGroup(), entry);
+				fireRepositoryEntryAddedEvent(clone, entry);
 			}
 		} else if(settings.getCopyResources() == CopyResources.resource) {
 			List<RepositoryEntry> entries = getRepositoryEntries(elementToClone);
@@ -360,6 +369,7 @@ public class CurriculumServiceImpl implements CurriculumService, OrganisationDat
 				if(repositoryService.canCopy(entry, identity)) {
 					RepositoryEntry entryCopy = repositoryService.copy(entry, identity, entry.getDisplayname());
 					repositoryEntryRelationDao.createRelation(clone.getGroup(), entryCopy);
+					fireRepositoryEntryAddedEvent(clone, entryCopy);
 				}
 			}
 		}
@@ -647,9 +657,11 @@ public class CurriculumServiceImpl implements CurriculumService, OrganisationDat
 			throw new AssertException("Inherited are automatic");
 		}
 		
+		List<CurriculumElement> membershipAdded = new ArrayList<>();
 		GroupMembership membership = groupDao.getMembership(element.getGroup(), member, role.name());
 		if(membership == null) {
 			groupDao.addMembershipOneWay(element.getGroup(), member, role.name(), inheritanceMode);
+			membershipAdded.add(element);
 		} else if(membership.getInheritanceMode() != inheritanceMode) {
 			groupDao.updateInheritanceMode(membership, inheritanceMode);
 		}
@@ -660,11 +672,14 @@ public class CurriculumServiceImpl implements CurriculumService, OrganisationDat
 				GroupMembership inheritedMembership = groupDao.getMembership(descendant.getGroup(), member, role.name());
 				if(inheritedMembership == null) {
 					groupDao.addMembershipOneWay(descendant.getGroup(), member, role.name(), GroupMembershipInheritance.inherited);
+					membershipAdded.add(element);
 				} else if(inheritedMembership.getInheritanceMode() == GroupMembershipInheritance.none) {
 					groupDao.updateInheritanceMode(inheritedMembership, GroupMembershipInheritance.inherited);
 				}
 			}
 		}
+		
+		fireMemberAddedEvent(membershipAdded, member, role);
 	}
 
 	@Override
@@ -779,6 +794,7 @@ public class CurriculumServiceImpl implements CurriculumService, OrganisationDat
 	public void addRepositoryEntry(CurriculumElement element, RepositoryEntryRef entry, boolean master) {
 		RepositoryEntry repoEntry = repositoryEntryDao.loadByKey(entry.getKey());
 		repositoryEntryRelationDao.createRelation(element.getGroup(), repoEntry);
+		fireRepositoryEntryAddedEvent(element, entry);
 	}
 
 	@Override
@@ -914,4 +930,19 @@ public class CurriculumServiceImpl implements CurriculumService, OrganisationDat
 		return true;
 	}
 	
+	private void fireMemberAddedEvent(Collection<? extends CurriculumElementRef> elements, Identity member,
+			CurriculumRoles role) {
+		CurriculumElementMembershipEvent event = new CurriculumElementMembershipEvent(
+				CurriculumElementMembershipEvent.MEMEBER_ADDED, elements, member, role);
+		coordinator.getCoordinator().getEventBus().fireEventToListenersOf(event,
+				OresHelper.lookupType(CurriculumElement.class));
+	}
+	
+	private void fireRepositoryEntryAddedEvent(CurriculumElementRef element, RepositoryEntryRef entry) {
+		CurriculumElementRepositoryEntryEvent event = new CurriculumElementRepositoryEntryEvent(REPOSITORY_ENTRY_ADDED,
+				element.getKey(), entry.getKey());
+		coordinator.getCoordinator().getEventBus().fireEventToListenersOf(event,
+				OresHelper.lookupType(CurriculumElement.class));
+	}
+	
 }
diff --git a/src/main/java/org/olat/repository/RepositoryManager.java b/src/main/java/org/olat/repository/RepositoryManager.java
index fe046eaa29b..af87622977b 100644
--- a/src/main/java/org/olat/repository/RepositoryManager.java
+++ b/src/main/java/org/olat/repository/RepositoryManager.java
@@ -1555,6 +1555,11 @@ public class RepositoryManager {
 	 */
 	private void addInternalParticipant(Identity ureqIdentity, Identity identity, RepositoryEntry re) {
 		repositoryEntryRelationDao.addRole(identity, re, GroupRoles.participant.name());
+		
+		List<RepositoryEntryMembershipModifiedEvent> deferredEvents = new ArrayList<>();
+		deferredEvents.add(RepositoryEntryMembershipModifiedEvent.roleParticipantAdded(identity, re));
+		dbInstance.commit();
+		sendDeferredEvents(deferredEvents, re);
 
 		ActionType actionType = ThreadLocalUserActivityLogger.getStickyActionType();
 		ThreadLocalUserActivityLogger.setStickyActionType(ActionType.admin);
@@ -2032,6 +2037,7 @@ public class RepositoryManager {
 		if(changes.getRepoParticipant() != null) {
 			if(changes.getRepoParticipant().booleanValue()) {
 				addParticipants(ureqIdentity, ureqRoles, new IdentitiesAddEvent(changes.getMember()), re, mailing);
+				deferredEvents.add(RepositoryEntryMembershipModifiedEvent.roleParticipantAdded(changes.getMember(), re));
 			} else {
 				removeParticipant(ureqIdentity, changes.getMember(), re, mailing, true);
 				deferredEvents.add(RepositoryEntryMembershipModifiedEvent.removed(changes.getMember(), re));
diff --git a/src/main/java/org/olat/repository/manager/RepositoryServiceImpl.java b/src/main/java/org/olat/repository/manager/RepositoryServiceImpl.java
index e92ceb53cdf..3015bfeda7f 100644
--- a/src/main/java/org/olat/repository/manager/RepositoryServiceImpl.java
+++ b/src/main/java/org/olat/repository/manager/RepositoryServiceImpl.java
@@ -29,6 +29,7 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 
+import org.apache.logging.log4j.Logger;
 import org.olat.basesecurity.Group;
 import org.olat.basesecurity.GroupRoles;
 import org.olat.basesecurity.IdentityRef;
@@ -45,7 +46,6 @@ import org.olat.core.id.OLATResourceable;
 import org.olat.core.id.Organisation;
 import org.olat.core.id.OrganisationRef;
 import org.olat.core.id.Roles;
-import org.apache.logging.log4j.Logger;
 import org.olat.core.logging.Tracing;
 import org.olat.core.logging.activity.LearningResourceLoggingAction;
 import org.olat.core.logging.activity.OlatResourceableType;
diff --git a/src/main/java/org/olat/repository/model/RepositoryEntryMembershipModifiedEvent.java b/src/main/java/org/olat/repository/model/RepositoryEntryMembershipModifiedEvent.java
index a41033967ec..c85c523ad26 100644
--- a/src/main/java/org/olat/repository/model/RepositoryEntryMembershipModifiedEvent.java
+++ b/src/main/java/org/olat/repository/model/RepositoryEntryMembershipModifiedEvent.java
@@ -20,7 +20,9 @@
 package org.olat.repository.model;
 
 import org.olat.basesecurity.IdentityRef;
+import org.olat.core.id.Identity;
 import org.olat.core.util.event.MultiUserEvent;
+import org.olat.repository.RepositoryEntry;
 import org.olat.repository.RepositoryEntryRef;
 
 /**
@@ -34,6 +36,7 @@ public class RepositoryEntryMembershipModifiedEvent extends MultiUserEvent {
 	private static final long serialVersionUID = -8624039692057985920L;
 	
 	public static final String IDENTITY_REMOVED = "identity.removed.re";
+	public static final String ROLE_PARTICIPANT_ADDED = "identity.role.participant.added";
 	private Long identityKey;
 	private Long repositoryEntryKey;
 
@@ -60,4 +63,8 @@ public class RepositoryEntryMembershipModifiedEvent extends MultiUserEvent {
 	public static RepositoryEntryMembershipModifiedEvent removed(IdentityRef identity, RepositoryEntryRef re) {
 		return new RepositoryEntryMembershipModifiedEvent(IDENTITY_REMOVED, identity.getKey(), re.getKey());
 	}
+
+	public static RepositoryEntryMembershipModifiedEvent roleParticipantAdded(Identity identity, RepositoryEntry re) {
+		return new RepositoryEntryMembershipModifiedEvent(ROLE_PARTICIPANT_ADDED, identity.getKey(), re.getKey());
+	}
 }
-- 
GitLab