diff --git a/src/main/java/org/olat/basesecurity/manager/OrganisationServiceImpl.java b/src/main/java/org/olat/basesecurity/manager/OrganisationServiceImpl.java
index 837abe9a8347fcf6ba5f20d63bbd0c5b1be108ab..62606cac7b8bf55c13eb564459fbdabeae6e7c15 100644
--- a/src/main/java/org/olat/basesecurity/manager/OrganisationServiceImpl.java
+++ b/src/main/java/org/olat/basesecurity/manager/OrganisationServiceImpl.java
@@ -28,6 +28,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import org.apache.logging.log4j.Logger;
 import org.olat.basesecurity.Group;
 import org.olat.basesecurity.GroupMembership;
 import org.olat.basesecurity.GroupMembershipInheritance;
@@ -39,6 +40,7 @@ import org.olat.basesecurity.OrganisationService;
 import org.olat.basesecurity.OrganisationStatus;
 import org.olat.basesecurity.OrganisationType;
 import org.olat.basesecurity.OrganisationTypeRef;
+import org.olat.basesecurity.model.IdentityToRoleKey;
 import org.olat.basesecurity.model.OrganisationImpl;
 import org.olat.basesecurity.model.OrganisationMember;
 import org.olat.basesecurity.model.OrganisationNode;
@@ -50,7 +52,6 @@ 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.apache.logging.log4j.Logger;
 import org.olat.core.logging.Tracing;
 import org.springframework.beans.factory.InitializingBean;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -229,13 +230,13 @@ public class OrganisationServiceImpl implements OrganisationService, Initializin
 	private void propagateMembership(OrganisationNode node, List<GroupMembership> membershipsToPropagate) {
 		Group group = node.getOrganisation().getGroup();
 		List<GroupMembership> nodeMemberships = groupDao.getMemberships(group);
-		Map<IdentityRoleKey,GroupMembership> identityRoleToMembership = new HashMap<>();
+		Map<IdentityToRoleKey,GroupMembership> identityRoleToMembership = new HashMap<>();
 		for(GroupMembership nodeMembership:nodeMemberships) {
-			identityRoleToMembership.put(new IdentityRoleKey(nodeMembership), nodeMembership);
+			identityRoleToMembership.put(new IdentityToRoleKey(nodeMembership), nodeMembership);
 		}
 
 		for(GroupMembership membershipToPropagate:membershipsToPropagate) {
-			GroupMembership nodeMembership = identityRoleToMembership.get(new IdentityRoleKey(membershipToPropagate));
+			GroupMembership nodeMembership = identityRoleToMembership.get(new IdentityToRoleKey(membershipToPropagate));
 			if(nodeMembership == null) {
 				groupDao.addMembershipOneWay(group, membershipToPropagate.getIdentity(), membershipToPropagate.getRole(), GroupMembershipInheritance.inherited);
 			} else if(nodeMembership.getInheritanceMode() != GroupMembershipInheritance.inherited)  {
@@ -251,15 +252,15 @@ public class OrganisationServiceImpl implements OrganisationService, Initializin
 		}
 	}
 	
-	private void cleanMembership(OrganisationNode node, Set<IdentityRoleKey> inheritance) {
+	private void cleanMembership(OrganisationNode node, Set<IdentityToRoleKey> inheritance) {
 		List<GroupMembership> memberships = groupDao.getMemberships(node.getOrganisation().getGroup());
 		for(GroupMembership membership:memberships) {
 			if(membership.getInheritanceMode() == GroupMembershipInheritance.inherited) {
-				if(!inheritance.contains(new IdentityRoleKey(membership))) {
+				if(!inheritance.contains(new IdentityToRoleKey(membership))) {
 					groupDao.removeMembership(node.getOrganisation().getGroup(), membership.getIdentity(), membership.getRole());
 				}
 			} else if(membership.getInheritanceMode() == GroupMembershipInheritance.root) {
-				inheritance.add(new IdentityRoleKey(membership));
+				inheritance.add(new IdentityToRoleKey(membership));
 			}
 		}
 		
@@ -270,34 +271,6 @@ public class OrganisationServiceImpl implements OrganisationService, Initializin
 			}
 		}
 	}
-	
-	private static class IdentityRoleKey {
-		
-		private final Long identityKey;
-		private final String role;
-		
-		public IdentityRoleKey(GroupMembership membership) {
-			identityKey = membership.getIdentity().getKey();
-			role = membership.getRole();
-		}
-
-		@Override
-		public int hashCode() {
-			return identityKey.hashCode() + role.hashCode();
-		}
-
-		@Override
-		public boolean equals(Object obj) {
-			if(this == obj) {
-				return true;
-			}
-			if(obj instanceof IdentityRoleKey) {
-				IdentityRoleKey m = (IdentityRoleKey)obj;
-				return identityKey.equals(m.identityKey) && role.equals(m.role);
-			}
-			return false;
-		}
-	}
 
 	@Override
 	public Organisation getDefaultOrganisation() {
diff --git a/src/main/java/org/olat/basesecurity/model/GroupMembershipImpl.java b/src/main/java/org/olat/basesecurity/model/GroupMembershipImpl.java
index 5f40f1cc9eb5d78a89bbc1978dd79b54b6bd806b..5e58a5abadde24ddedc607dfedc6527296d5dbda 100644
--- a/src/main/java/org/olat/basesecurity/model/GroupMembershipImpl.java
+++ b/src/main/java/org/olat/basesecurity/model/GroupMembershipImpl.java
@@ -94,7 +94,7 @@ public class GroupMembershipImpl implements GroupMembership, ModifiedInfo, Persi
 	@Column(name="g_role", nullable=false, insertable=true, updatable=false)
 	private String role;
 	
-	@Column(name="g_inheritance_mode", nullable=false, insertable=true, updatable=false)
+	@Column(name="g_inheritance_mode", nullable=false, insertable=true, updatable=true)
 	private String inheritanceModeString;
 	
 	@ManyToOne(targetEntity=GroupImpl.class,fetch=FetchType.LAZY,optional=false)
diff --git a/src/main/java/org/olat/basesecurity/model/IdentityToRoleKey.java b/src/main/java/org/olat/basesecurity/model/IdentityToRoleKey.java
new file mode 100644
index 0000000000000000000000000000000000000000..9842879397cfa72360b09ab33be1069d829874d2
--- /dev/null
+++ b/src/main/java/org/olat/basesecurity/model/IdentityToRoleKey.java
@@ -0,0 +1,61 @@
+/**
+ * <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.basesecurity.model;
+
+import org.olat.basesecurity.GroupMembership;
+
+/**
+ * 
+ * Initial date: 20 mai 2019<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class IdentityToRoleKey {
+	
+	private final Long identityKey;
+	private final String role;
+	
+	public IdentityToRoleKey(GroupMembership membership) {
+		identityKey = membership.getIdentity().getKey();
+		role = membership.getRole();
+	}
+	
+	public IdentityToRoleKey(Long identityKey, String role) {
+		this.identityKey = identityKey;
+		this.role = role;
+	}
+
+	@Override
+	public int hashCode() {
+		return identityKey.hashCode() + role.hashCode();
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if(this == obj) {
+			return true;
+		}
+		if(obj instanceof IdentityToRoleKey) {
+			IdentityToRoleKey m = (IdentityToRoleKey)obj;
+			return identityKey.equals(m.identityKey) && role.equals(m.role);
+		}
+		return false;
+	}
+}
diff --git a/src/main/java/org/olat/course/CourseFactory.java b/src/main/java/org/olat/course/CourseFactory.java
index 4d1e75ae6fc5ec8c08ee14d8978ae4fd1bf2d1fa..2c749c57f57e623a127a5d49d44ccf21c3dfdb15 100644
--- a/src/main/java/org/olat/course/CourseFactory.java
+++ b/src/main/java/org/olat/course/CourseFactory.java
@@ -650,7 +650,7 @@ public class CourseFactory {
 
 			ContextEntry ce = BusinessControlFactory.getInstance().createContextEntry(entry);
 			WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(ce, wControl);
-			RepositoryEntrySecurity reSecurity = new RepositoryEntrySecurity(false, false, false, false, false, false, false, false, false, false, false, true, false);
+			RepositoryEntrySecurity reSecurity = new RepositoryEntrySecurity(false, false, false, false, false, false, false, false, false, false, false, false, true, false);
 			return new RunMainController(ureq, bwControl, null, course, entry, reSecurity, null);
 		}
 	}
diff --git a/src/main/java/org/olat/course/run/CourseRuntimeController.java b/src/main/java/org/olat/course/run/CourseRuntimeController.java
index 1708321b2ce3e7f8770f5c280c27d0f9d3d563cb..16535f4e970b03a4223f281a13aa6250b89d2fc7 100644
--- a/src/main/java/org/olat/course/run/CourseRuntimeController.java
+++ b/src/main/java/org/olat/course/run/CourseRuntimeController.java
@@ -268,8 +268,8 @@ public class CourseRuntimeController extends RepositoryEntryRuntimeController im
 		// group rights
 		UserCourseEnvironmentImpl uce = getUserCourseEnvironment();
 		if(uce != null) {
-			uce.setUserRoles(security.isEntryAdmin() || security.isPrincipal(), security.isCoach(), security.isParticipant());
-			if(security.isOnlyPrincipal()) {
+			uce.setUserRoles(security.isEntryAdmin() || security.isPrincipal() || security.isMasterCoach(), security.isCoach(), security.isParticipant());
+			if(security.isOnlyPrincipal() || security.isOnlyMasterCoach()) {
 				uce.setCourseReadOnly(Boolean.TRUE);
 			} else if(security.isReadOnly()) {
 				if(overrideReadOnly) {
@@ -470,7 +470,7 @@ public class CourseRuntimeController extends RepositoryEntryRuntimeController im
 	@Override
 	protected void initToolsMenuSettings(Dropdown tools) {
 		// 1) administrative tools
-		if (reSecurity.isEntryAdmin() || reSecurity.isPrincipal() || reSecurity.isCoach()
+		if (reSecurity.isEntryAdmin() || reSecurity.isPrincipal() || reSecurity.isMasterCoach() || reSecurity.isCoach()
 				|| hasCourseRight(CourseRights.RIGHT_COURSEEDITOR) || hasCourseRight(CourseRights.RIGHT_MEMBERMANAGEMENT)
 				|| hasCourseRight(CourseRights.RIGHT_GROUPMANAGEMENT)) {
 
@@ -484,7 +484,7 @@ public class CourseRuntimeController extends RepositoryEntryRuntimeController im
 				tools.addComponent(settingsLink);
 			}
 			
-			if (reSecurity.isEntryAdmin() || reSecurity.isPrincipal() || hasCourseRight(CourseRights.RIGHT_GROUPMANAGEMENT) || hasCourseRight(CourseRights.RIGHT_MEMBERMANAGEMENT)) {
+			if (reSecurity.isEntryAdmin() || reSecurity.isPrincipal() || reSecurity.isMasterCoach() || hasCourseRight(CourseRights.RIGHT_GROUPMANAGEMENT) || hasCourseRight(CourseRights.RIGHT_MEMBERMANAGEMENT)) {
 				membersLink = LinkFactory.createToolLink("unifiedusermngt", translate("command.opensimplegroupmngt"), this, "o_icon_membersmanagement");
 				membersLink.setElementCssClass("o_sel_course_members");
 				tools.addComponent(membersLink);
@@ -506,7 +506,7 @@ public class CourseRuntimeController extends RepositoryEntryRuntimeController im
 			tools.addComponent(editLink);
 		}
 			
-		if (reSecurity.isEntryAdmin() || reSecurity.isPrincipal() || hasCourseRight(CourseRights.RIGHT_COURSEEDITOR)) {
+		if (reSecurity.isEntryAdmin() || reSecurity.isPrincipal() || reSecurity.isMasterCoach() || hasCourseRight(CourseRights.RIGHT_COURSEEDITOR)) {
 			folderLink = LinkFactory.createToolLink("cfd", translate("command.coursefolder"), this, "o_icon_coursefolder");
 			folderLink.setElementCssClass("o_sel_course_folder");
 			tools.addComponent(folderLink);
@@ -516,19 +516,19 @@ public class CourseRuntimeController extends RepositoryEntryRuntimeController im
 	
 	private void initToolsMenuRuntime(Dropdown tools, final UserCourseEnvironmentImpl uce) {
 		boolean courseAuthorRight = reSecurity.isEntryAdmin() || hasCourseRight(CourseRights.RIGHT_COURSEEDITOR);
-		if (courseAuthorRight || reSecurity.isPrincipal() || reSecurity.isCoach()
+		if (courseAuthorRight || reSecurity.isPrincipal() || reSecurity.isMasterCoach() || reSecurity.isCoach()
 				|| hasCourseRight(CourseRights.RIGHT_DB)
 				|| hasCourseRight(CourseRights.RIGHT_ASSESSMENT) || hasCourseRight(CourseRights.RIGHT_ASSESSMENT_MODE)) {	
 
 			tools.addComponent(new Spacer(""));
 			
-			if (reSecurity.isEntryAdmin() || reSecurity.isPrincipal() || reSecurity.isCoach() || hasCourseRight(CourseRights.RIGHT_ASSESSMENT)) {
+			if (reSecurity.isEntryAdmin() || reSecurity.isPrincipal() || reSecurity.isMasterCoach() || reSecurity.isCoach() || hasCourseRight(CourseRights.RIGHT_ASSESSMENT)) {
 				assessmentLink = LinkFactory.createToolLink("assessment", translate("command.openassessment"), this, "o_icon_assessment_tool");
 				assessmentLink.setElementCssClass("o_sel_course_assessment_tool");
 				tools.addComponent(assessmentLink);
 			}
 			
-			if(lectureModule.isEnabled() && (courseAuthorRight || reSecurity.isPrincipal()) && isLectureEnabled()) {
+			if(lectureModule.isEnabled() && (courseAuthorRight || reSecurity.isPrincipal() || reSecurity.isMasterCoach()) && isLectureEnabled()) {
 				lecturesAdminLink = LinkFactory.createToolLink("lectures.admin.cmd", translate("command.options.lectures.admin"), this, "o_icon_lecture");
 				lecturesAdminLink.setElementCssClass("o_sel_course_lectures_admin");
 				lecturesAdminLink.setVisible(!uce.isCourseReadOnly());
@@ -542,7 +542,7 @@ public class CourseRuntimeController extends RepositoryEntryRuntimeController im
 				tools.addComponent(reminderLink);
 			}
 			
-			if (courseAuthorRight || reSecurity.isPrincipal() || hasCourseRight(CourseRights.RIGHT_ASSESSMENT_MODE)) {
+			if (courseAuthorRight || reSecurity.isPrincipal() || reSecurity.isMasterCoach() || hasCourseRight(CourseRights.RIGHT_ASSESSMENT_MODE)) {
 				//course author right or the assessment mode access right 
 				boolean managed = RepositoryEntryManagedFlag.isManaged(getRepositoryEntry(), RepositoryEntryManagedFlag.editcontent);
 				assessmentModeLink = LinkFactory.createToolLink("assessment.mode.cmd", translate("command.assessment.mode"), this, "o_icon_assessment_mode");
@@ -571,17 +571,17 @@ public class CourseRuntimeController extends RepositoryEntryRuntimeController im
 	}
 	
 	private void initToolsMenuStatistics(Dropdown tools, ICourse course, final UserCourseEnvironmentImpl uce) {
-		if (reSecurity.isEntryAdmin() || reSecurity.isPrincipal() || reSecurity.isCoach()
+		if (reSecurity.isEntryAdmin() || reSecurity.isPrincipal() || reSecurity.isMasterCoach() || reSecurity.isCoach()
 				|| hasCourseRight(CourseRights.RIGHT_ARCHIVING) || hasCourseRight(CourseRights.RIGHT_STATISTICS)) {	
 
 			tools.addComponent(new Spacer(""));
 			
-			if (reSecurity.isEntryAdmin() || reSecurity.isPrincipal() || hasCourseRight(CourseRights.RIGHT_STATISTICS)) {
+			if (reSecurity.isEntryAdmin() || reSecurity.isPrincipal() || reSecurity.isMasterCoach() || hasCourseRight(CourseRights.RIGHT_STATISTICS)) {
 				courseStatisticLink = LinkFactory.createToolLink("statistic",translate("command.openstatistic"), this, "o_icon_statistics_tool");
 				tools.addComponent(courseStatisticLink);
 			}
 			
-			if (uce != null && (reSecurity.isEntryAdmin() || reSecurity.isPrincipal() || reSecurity.isCoach() || hasCourseRight(CourseRights.RIGHT_STATISTICS))) {
+			if (uce != null && (reSecurity.isEntryAdmin() || reSecurity.isPrincipal() || reSecurity.isMasterCoach() || reSecurity.isCoach() || hasCourseRight(CourseRights.RIGHT_STATISTICS))) {
 				final AtomicInteger testNodes = new AtomicInteger();
 				final AtomicInteger surveyNodes = new AtomicInteger();
 				new TreeVisitor(node -> {
@@ -1122,7 +1122,7 @@ public class CourseRuntimeController extends RepositoryEntryRuntimeController im
 				}	
 			} else if ("assessmentTool".equalsIgnoreCase(type) || "assessmentToolv2".equalsIgnoreCase(type)) {
 				//check the security before, the link is perhaps in the wrong hands
-				if(reSecurity.isEntryAdmin() || reSecurity.isPrincipal() || reSecurity.isCoach() || hasCourseRight(CourseRights.RIGHT_ASSESSMENT)) {
+				if(reSecurity.isEntryAdmin() || reSecurity.isPrincipal() || reSecurity.isMasterCoach() || reSecurity.isCoach() || hasCourseRight(CourseRights.RIGHT_ASSESSMENT)) {
 					try {
 						Activateable2 assessmentCtrl = doAssessmentTool(ureq);
 						if(assessmentCtrl != null) {
@@ -1140,7 +1140,7 @@ public class CourseRuntimeController extends RepositoryEntryRuntimeController im
 				}
 			} else if ("TestStatistics".equalsIgnoreCase(type) || "SurveyStatistics".equalsIgnoreCase(type)) {
 				//check the security before, the link is perhaps in the wrong hands
-				if(reSecurity.isEntryAdmin() || reSecurity.isPrincipal() || reSecurity.isCoach() || hasCourseRight(CourseRights.RIGHT_ASSESSMENT)) {
+				if(reSecurity.isEntryAdmin() || reSecurity.isPrincipal() || reSecurity.isMasterCoach() || reSecurity.isCoach() || hasCourseRight(CourseRights.RIGHT_ASSESSMENT)) {
 					try {
 						Activateable2 assessmentCtrl = null;
 						if("TestStatistics".equalsIgnoreCase(type)) {
@@ -1226,14 +1226,14 @@ public class CourseRuntimeController extends RepositoryEntryRuntimeController im
 	@Override
 	protected Activateable2 doMembers(UserRequest ureq) {
 		if(delayedClose == Delayed.members || requestForClose(ureq)) {
-			if (reSecurity.isEntryAdmin() || reSecurity.isPrincipal() || hasCourseRight(CourseRights.RIGHT_GROUPMANAGEMENT) || hasCourseRight(CourseRights.RIGHT_MEMBERMANAGEMENT)) {
+			if (reSecurity.isEntryAdmin() || reSecurity.isPrincipal() || reSecurity.isMasterCoach() || hasCourseRight(CourseRights.RIGHT_GROUPMANAGEMENT) || hasCourseRight(CourseRights.RIGHT_MEMBERMANAGEMENT)) {
 				removeCustomCSS();
 				if(currentToolCtr instanceof MembersManagementMainController) {
 					((MembersManagementMainController)currentToolCtr).activate(ureq, null, null);
 				} else {
 					WindowControl bwControl = getSubWindowControl("MembersMgmt");
 					MembersManagementMainController ctrl = new MembersManagementMainController(ureq, addToHistory(ureq, bwControl), toolbarPanel,
-							getRepositoryEntry(), getUserCourseEnvironment(), reSecurity.isEntryAdmin(), reSecurity.isPrincipal(),
+							getRepositoryEntry(), getUserCourseEnvironment(), reSecurity.isEntryAdmin(), reSecurity.isPrincipal() || reSecurity.isMasterCoach(),
 							hasCourseRight(CourseRights.RIGHT_GROUPMANAGEMENT), hasCourseRight(CourseRights.RIGHT_MEMBERMANAGEMENT));
 					listenTo(ctrl);
 					membersCtrl = pushController(ureq, translate("command.opensimplegroupmngt"), ctrl);
@@ -1301,7 +1301,8 @@ public class CourseRuntimeController extends RepositoryEntryRuntimeController im
 	
 	private void doAssessmentMode(UserRequest ureq) {
 		if(delayedClose == Delayed.assessmentMode || requestForClose(ureq)) {
-			if (reSecurity.isEntryAdmin() || reSecurity.isPrincipal() || hasCourseRight(CourseRights.RIGHT_COURSEEDITOR) || hasCourseRight(CourseRights.RIGHT_ASSESSMENT_MODE)) {
+			if (reSecurity.isEntryAdmin() || reSecurity.isPrincipal() || reSecurity.isMasterCoach()
+					|| hasCourseRight(CourseRights.RIGHT_COURSEEDITOR) || hasCourseRight(CourseRights.RIGHT_ASSESSMENT_MODE)) {
 				removeCustomCSS();
 				
 				boolean canManage = reSecurity.isEntryAdmin() || hasCourseRight(CourseRights.RIGHT_COURSEEDITOR) || hasCourseRight(CourseRights.RIGHT_ASSESSMENT_MODE);
@@ -1334,7 +1335,7 @@ public class CourseRuntimeController extends RepositoryEntryRuntimeController im
 	
 	private LectureRepositoryAdminController doLecturesAdmin(UserRequest ureq) {
 		if(delayedClose == Delayed.lecturesAdmin || requestForClose(ureq)) {
-			if (reSecurity.isEntryAdmin() || reSecurity.isPrincipal() || hasCourseRight(CourseRights.RIGHT_COURSEEDITOR)) {
+			if (reSecurity.isEntryAdmin() || reSecurity.isPrincipal() || reSecurity.isMasterCoach() || hasCourseRight(CourseRights.RIGHT_COURSEEDITOR)) {
 				removeCustomCSS();
 
 				OLATResourceable ores = OresHelper.createOLATResourceableType("LecturesAdmin");
@@ -1450,7 +1451,8 @@ public class CourseRuntimeController extends RepositoryEntryRuntimeController im
 	
 	private void doCourseStatistics(UserRequest ureq) {
 		if(delayedClose == Delayed.courseStatistics || requestForClose(ureq)) {
-			if (reSecurity.isEntryAdmin() || reSecurity.isPrincipal()|| hasCourseRight(CourseRights.RIGHT_STATISTICS)) {
+			if (reSecurity.isEntryAdmin() || reSecurity.isPrincipal() || reSecurity.isMasterCoach()
+					|| hasCourseRight(CourseRights.RIGHT_STATISTICS)) {
 				removeCustomCSS();
 				ICourse course = CourseFactory.loadCourse(getRepositoryEntry());
 				StatisticMainController ctrl = new StatisticMainController(ureq, getWindowControl(), course);
@@ -1498,7 +1500,7 @@ public class CourseRuntimeController extends RepositoryEntryRuntimeController im
 		OLATResourceable ores = OresHelper.createOLATResourceableType(typeName);
 		ThreadLocalUserActivityLogger.addLoggingResourceInfo(LoggingResourceable.wrapBusinessPath(ores));
 		WindowControl swControl = addToHistory(ureq, ores, null);
-		if (reSecurity.isEntryAdmin() || reSecurity.isPrincipal() || reSecurity.isCoach() || hasCourseRight(CourseRights.RIGHT_STATISTICS)) {
+		if (reSecurity.isEntryAdmin() || reSecurity.isPrincipal() || reSecurity.isMasterCoach() || reSecurity.isCoach() || hasCourseRight(CourseRights.RIGHT_STATISTICS)) {
 			removeCustomCSS();
 			UserCourseEnvironmentImpl uce = getUserCourseEnvironment();
 			StatisticCourseNodesController ctrl = new StatisticCourseNodesController(ureq, swControl, toolbarPanel,  reSecurity, uce, type);
@@ -1517,7 +1519,7 @@ public class CourseRuntimeController extends RepositoryEntryRuntimeController im
 			ThreadLocalUserActivityLogger.addLoggingResourceInfo(LoggingResourceable.wrapBusinessPath(ores));
 			WindowControl swControl = addToHistory(ureq, ores, null);
 			
-			boolean admin = reSecurity.isEntryAdmin() || reSecurity.isPrincipal() || hasCourseRight(CourseRights.RIGHT_ASSESSMENT);
+			boolean admin = reSecurity.isEntryAdmin() || reSecurity.isPrincipal() || reSecurity.isMasterCoach() || hasCourseRight(CourseRights.RIGHT_ASSESSMENT);
 			boolean nonMembers = reSecurity.isEntryAdmin();
 			List<BusinessGroup> coachedGroups = null;
 			UserCourseEnvironment userCourseEnv = getUserCourseEnvironment();
diff --git a/src/main/java/org/olat/course/run/userview/UserCourseEnvironmentImpl.java b/src/main/java/org/olat/course/run/userview/UserCourseEnvironmentImpl.java
index 75087fcd01b2d3b8e49bc728986d7ed3036b9510..0dadf7345730a039f4c35c323d14feae5239f79a 100644
--- a/src/main/java/org/olat/course/run/userview/UserCourseEnvironmentImpl.java
+++ b/src/main/java/org/olat/course/run/userview/UserCourseEnvironmentImpl.java
@@ -125,8 +125,8 @@ public class UserCourseEnvironmentImpl implements UserCourseEnvironment {
 
 		return new UserCourseEnvironmentImpl(ureq.getUserSession().getIdentityEnvironment(), course.getCourseEnvironment(), wControl,
 				coachedGroups, participatedGroups, waitingLists,
-				reSecurity.isCoach(), reSecurity.isEntryAdmin() || reSecurity.isPrincipal(), reSecurity.isParticipant(),
-				reSecurity.isReadOnly() || reSecurity.isOnlyPrincipal());
+				reSecurity.isCoach(), reSecurity.isEntryAdmin() || reSecurity.isPrincipal() || reSecurity.isMasterCoach() , reSecurity.isParticipant(),
+				reSecurity.isReadOnly() || reSecurity.isOnlyPrincipal() || reSecurity.isOnlyMasterCoach());
 	}
 
 	/**
diff --git a/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectMeetingDefaultConfiguration.java b/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectMeetingDefaultConfiguration.java
index 1b6e4cadedec051d37c576818695945025163374..72ce6eb18166e732b89405d0981f767243274b39 100644
--- a/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectMeetingDefaultConfiguration.java
+++ b/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectMeetingDefaultConfiguration.java
@@ -1,3 +1,22 @@
+/**
+ * <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.adobeconnect.ui;
 
 /**
diff --git a/src/main/java/org/olat/modules/coach/manager/CoachingDAO.java b/src/main/java/org/olat/modules/coach/manager/CoachingDAO.java
index d13a058e74c6a4b9d8e49947f161cbe24a42c3b9..d1532fd9d44d496dc51b001a3cbd3b93cb1f1657 100644
--- a/src/main/java/org/olat/modules/coach/manager/CoachingDAO.java
+++ b/src/main/java/org/olat/modules/coach/manager/CoachingDAO.java
@@ -388,7 +388,7 @@ public class CoachingDAO {
 		  .append(" inner join v.groups as relGroup")
 		  .append(" inner join relGroup.group as baseGroup")
 		  .append(" inner join baseGroup.members as coach on coach.role ")
-		  		.in(GroupRoles.coach, GroupRoles.owner.name())
+		  		.in(GroupRoles.coach, GroupRoles.owner)
 		  .append(" where coach.identity.key=:coachKey and res.resName='CourseModule'")
 		  .append(" and v.status ").in(RepositoryEntryStatusEnum.coachPublishedToClosed());
 
diff --git a/src/main/java/org/olat/modules/coach/ui/UserDetailsController.java b/src/main/java/org/olat/modules/coach/ui/UserDetailsController.java
index e85982c001bcbe480543a5695b32ca36a14f9b2c..6573953ed04653ba37f5cd2ccf90d8468d64bd88 100644
--- a/src/main/java/org/olat/modules/coach/ui/UserDetailsController.java
+++ b/src/main/java/org/olat/modules/coach/ui/UserDetailsController.java
@@ -159,8 +159,8 @@ public class UserDetailsController extends BasicController implements Activateab
 		RepositoryEntrySecurity reSecurity = repositoryManager.isAllowed(ureq, entry);
 		return new UserCourseEnvironmentImpl(ureq.getUserSession().getIdentityEnvironment(), null, getWindowControl(),
 				null, null, null,
-				reSecurity.isCoach(), reSecurity.isEntryAdmin() || reSecurity.isPrincipal(), reSecurity.isParticipant(),
-				reSecurity.isReadOnly() || reSecurity.isOnlyPrincipal());
+				reSecurity.isCoach(), reSecurity.isEntryAdmin() || reSecurity.isPrincipal() || reSecurity.isMasterCoach(), reSecurity.isParticipant(),
+				reSecurity.isReadOnly() || reSecurity.isOnlyPrincipal() || reSecurity.isOnlyMasterCoach());
 	}
 
 	@Override
diff --git a/src/main/java/org/olat/modules/curriculum/CurriculumRoles.java b/src/main/java/org/olat/modules/curriculum/CurriculumRoles.java
index a251b9131f0b7c9583bd83375e1499a58358856f..80a99939dc8d6d4033ae1ba18d8f352e7cf9f971 100644
--- a/src/main/java/org/olat/modules/curriculum/CurriculumRoles.java
+++ b/src/main/java/org/olat/modules/curriculum/CurriculumRoles.java
@@ -37,6 +37,7 @@ public enum CurriculumRoles {
 	curriculumowner,// own a piece of a curriculum
 	curriculumelementowner,// own a piece of curriculum
 	owner, //same as GroupRoles
+	mastercoach,
 	coach, //same as GroupRoles
 	participant; //same as GroupRoles
 	
@@ -62,4 +63,12 @@ public enum CurriculumRoles {
 		}
 		return false;
 	}
+	
+	/**
+	 * @param role The role to check
+	 * @return true if the role is by default inherited in the curriculum tree
+	 */
+	public static boolean isInheritedByDefault(CurriculumRoles role) {
+		return role == CurriculumRoles.mastercoach;
+	}
 }
diff --git a/src/main/java/org/olat/modules/curriculum/manager/CurriculumElementDAO.java b/src/main/java/org/olat/modules/curriculum/manager/CurriculumElementDAO.java
index 1dc5277223deb3b08066d4812b0da709840318f3..9665ad2c8a7cd5e5de4204f8d597717e91466ddd 100644
--- a/src/main/java/org/olat/modules/curriculum/manager/CurriculumElementDAO.java
+++ b/src/main/java/org/olat/modules/curriculum/manager/CurriculumElementDAO.java
@@ -60,6 +60,7 @@ import org.olat.modules.curriculum.CurriculumRoles;
 import org.olat.modules.curriculum.model.CurriculumElementImpl;
 import org.olat.modules.curriculum.model.CurriculumElementInfos;
 import org.olat.modules.curriculum.model.CurriculumElementMembershipImpl;
+import org.olat.modules.curriculum.model.CurriculumElementNode;
 import org.olat.modules.curriculum.model.CurriculumElementSearchInfos;
 import org.olat.modules.curriculum.model.CurriculumElementSearchParams;
 import org.olat.modules.curriculum.model.CurriculumImpl;
@@ -745,7 +746,34 @@ public class CurriculumElementDAO {
 				.getResultList();
 	}
 	
-	
+	public CurriculumElementNode getDescendantTree(CurriculumElement rootElement) {
+		List<CurriculumElement> descendants = getChildren(rootElement);
+		CurriculumElementNode rootNode = new CurriculumElementNode(rootElement);
+		
+		Map<Long,CurriculumElementNode> keyToOrganisations = new HashMap<>();
+		for(CurriculumElement descendant:descendants) {
+			keyToOrganisations.put(descendant.getKey(), new CurriculumElementNode(descendant));
+		}
+
+		for(CurriculumElement descendant:descendants) {
+			Long key = descendant.getKey();
+			if(key.equals(rootElement.getKey())) {
+				continue;
+			}
+			
+			CurriculumElementNode node = keyToOrganisations.get(key);
+			CurriculumElement parentOrganisation = descendant.getParent();
+			Long parentKey = parentOrganisation.getKey();
+			if(parentKey.equals(rootElement.getKey())) {
+				//this is a root, or the user has not access to parent
+				rootNode.addChild(node);
+			} else {
+				CurriculumElementNode parentNode = keyToOrganisations.get(parentKey);
+				parentNode.addChild(node);
+			}
+		}
+		return rootNode;
+	}
 	
 	public List<Identity> getMembersIdentity(CurriculumElementRef element, String role) {
 		StringBuilder sb = new StringBuilder(256);
diff --git a/src/main/java/org/olat/modules/curriculum/manager/CurriculumMemberQueries.java b/src/main/java/org/olat/modules/curriculum/manager/CurriculumMemberQueries.java
index 949600910af388a9d46634e2ca46e3fd3b0fc842..cf13b36b3bdb75b803aaeb69bdde3232091c6070 100644
--- a/src/main/java/org/olat/modules/curriculum/manager/CurriculumMemberQueries.java
+++ b/src/main/java/org/olat/modules/curriculum/manager/CurriculumMemberQueries.java
@@ -52,7 +52,7 @@ public class CurriculumMemberQueries {
 	
 	public List<CurriculumMember> getMembers(CurriculumRef curriculum, SearchMemberParameters params) {
 		StringBuilder sb = new StringBuilder(256);
-		sb.append("select ident, membership.role from curriculum cur")
+		sb.append("select ident, membership.role, membership.inheritanceModeString from curriculum cur")
 		  .append(" inner join cur.group baseGroup")
 		  .append(" inner join baseGroup.members membership")
 		  .append(" inner join membership.identity ident")
@@ -70,7 +70,12 @@ public class CurriculumMemberQueries {
 		for(Object[] object:rawObjects) {
 			Identity identity = (Identity)object[0];
 			String role = (String)object[1];
-			members.add(new CurriculumMember(identity, role));
+			String inheritanceModeString = (String)object[2];
+			GroupMembershipInheritance inheritanceMode = GroupMembershipInheritance.none;
+			if(StringHelper.containsNonWhitespace(inheritanceModeString)) {
+				inheritanceMode = GroupMembershipInheritance.valueOf(inheritanceModeString);
+			}
+			members.add(new CurriculumMember(identity, role, inheritanceMode));
 		}
 		return members;
 	}
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 f8ba5b3ac81308a9c14d117fe4887e09e2128fe5..d476d86de754ae3caf1236c21086b258916bcc57 100644
--- a/src/main/java/org/olat/modules/curriculum/manager/CurriculumServiceImpl.java
+++ b/src/main/java/org/olat/modules/curriculum/manager/CurriculumServiceImpl.java
@@ -30,18 +30,22 @@ import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
 
+import org.olat.basesecurity.Group;
+import org.olat.basesecurity.GroupMembership;
 import org.olat.basesecurity.GroupMembershipInheritance;
 import org.olat.basesecurity.GroupRoles;
 import org.olat.basesecurity.IdentityRef;
 import org.olat.basesecurity.OrganisationDataDeletable;
 import org.olat.basesecurity.OrganisationRoles;
 import org.olat.basesecurity.manager.GroupDAO;
+import org.olat.basesecurity.model.IdentityToRoleKey;
 import org.olat.core.CoreSpringFactory;
 import org.olat.core.commons.persistence.DB;
 import org.olat.core.id.Identity;
 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.modules.curriculum.Curriculum;
 import org.olat.modules.curriculum.CurriculumCalendars;
 import org.olat.modules.curriculum.CurriculumDataDeletable;
@@ -63,6 +67,7 @@ import org.olat.modules.curriculum.model.CurriculumCopySettings.CopyResources;
 import org.olat.modules.curriculum.model.CurriculumElementImpl;
 import org.olat.modules.curriculum.model.CurriculumElementInfos;
 import org.olat.modules.curriculum.model.CurriculumElementMembershipChange;
+import org.olat.modules.curriculum.model.CurriculumElementNode;
 import org.olat.modules.curriculum.model.CurriculumElementRepositoryEntryViews;
 import org.olat.modules.curriculum.model.CurriculumElementSearchInfos;
 import org.olat.modules.curriculum.model.CurriculumElementSearchParams;
@@ -272,8 +277,19 @@ public class CurriculumServiceImpl implements CurriculumService, OrganisationDat
 		if(parentRef == null) {
 			curriculum = getCurriculum(curriculum);
 		}
-		return curriculumElementDao.createCurriculumElement(identifier, displayName, status,
+		CurriculumElement element = curriculumElementDao.createCurriculumElement(identifier, displayName, status,
 				beginDate, endDate, parentRef, elementType, calendars, lectures, curriculum);
+		if(element.getParent() != null) {
+			Group organisationGroup = element.getGroup();
+			List<GroupMembership> memberships = groupDao.getMemberships(element.getParent().getGroup());
+			for(GroupMembership membership:memberships) {
+				if(membership.getInheritanceMode() == GroupMembershipInheritance.inherited
+						|| membership.getInheritanceMode() == GroupMembershipInheritance.root) {
+					groupDao.addMembershipOneWay(organisationGroup, membership.getIdentity(), membership.getRole(), GroupMembershipInheritance.inherited);
+				}
+			}
+		}
+		return element;
 	}
 
 	@Override
@@ -388,11 +404,65 @@ public class CurriculumServiceImpl implements CurriculumService, OrganisationDat
 	
 	@Override
 	public CurriculumElement moveCurriculumElement(CurriculumElement elementToMove, CurriculumElement newParent, CurriculumElement siblingBefore) {
-		return curriculumElementDao.move(elementToMove, newParent, siblingBefore);
+		CurriculumElement element = curriculumElementDao.move(elementToMove, newParent, siblingBefore);
+		
+		// propagate inheritance of the new parent
+		List<GroupMembership> memberships = groupDao.getMemberships(newParent.getGroup());
+		List<GroupMembership> membershipsToPropagate = new ArrayList<>();
+		Map<IdentityToRoleKey,GroupMembership> identityRoleToNewParentMembership = new HashMap<>();
+		for(GroupMembership membership:memberships) {
+			if(membership.getInheritanceMode() == GroupMembershipInheritance.inherited || membership.getInheritanceMode() == GroupMembershipInheritance.root) {
+				membershipsToPropagate.add(membership);
+				identityRoleToNewParentMembership.put(new IdentityToRoleKey(membership), membership);
+			}
+		}
+		
+		// inherited of the moved element eventuel -> root
+		List<GroupMembership> movedElementMemberships = groupDao.getMemberships(element.getGroup());
+		for(GroupMembership movedElementMembership:movedElementMemberships) {
+			if(movedElementMembership.getInheritanceMode() == GroupMembershipInheritance.inherited) {
+				GroupMembership newParentMembership = identityRoleToNewParentMembership.get(new IdentityToRoleKey(movedElementMembership));
+				if(newParentMembership == null || newParentMembership.getInheritanceMode() == GroupMembershipInheritance.none) {
+					groupDao.updateInheritanceMode(movedElementMembership, GroupMembershipInheritance.root);
+				}
+			}
+		}
+		
+		CurriculumElementNode treeToMove = curriculumElementDao.getDescendantTree(element);
+		if(!membershipsToPropagate.isEmpty()) {
+			propagateMembership(treeToMove, membershipsToPropagate);
+		}
+		return element;
+	}
+	
+	private void propagateMembership(CurriculumElementNode node, List<GroupMembership> membershipsToPropagate) {
+		Group group = node.getElement().getGroup();
+		List<GroupMembership> nodeMemberships = groupDao.getMemberships(group);
+		Map<IdentityToRoleKey,GroupMembership> identityRoleToMembership = new HashMap<>();
+		for(GroupMembership nodeMembership:nodeMemberships) {
+			identityRoleToMembership.put(new IdentityToRoleKey(nodeMembership), nodeMembership);
+		}
+
+		for(GroupMembership membershipToPropagate:membershipsToPropagate) {
+			GroupMembership nodeMembership = identityRoleToMembership.get(new IdentityToRoleKey(membershipToPropagate));
+			if(nodeMembership == null) {
+				groupDao.addMembershipOneWay(group, membershipToPropagate.getIdentity(), membershipToPropagate.getRole(), GroupMembershipInheritance.inherited);
+			} else if(nodeMembership.getInheritanceMode() != GroupMembershipInheritance.inherited)  {
+				groupDao.updateInheritanceMode(nodeMembership, GroupMembershipInheritance.inherited);
+			}
+		}
+
+		List<CurriculumElementNode> children = node.getChildrenNode();
+		if(children != null && !children.isEmpty()) {
+			for(CurriculumElementNode child:children) {
+				propagateMembership(child, membershipsToPropagate);
+			}
+		}
 	}
 
 	@Override
 	public CurriculumElement moveCurriculumElement(CurriculumElement rootElement, Curriculum curriculum) {
+		// root element move they entire memberships with, don't need to change them
 		CurriculumElement element = curriculumElementDao.move(rootElement, curriculum);
 		dbInstance.commit();
 		return element;
@@ -525,14 +595,81 @@ public class CurriculumServiceImpl implements CurriculumService, OrganisationDat
 
 	@Override
 	public void addMember(CurriculumElement element, Identity member, CurriculumRoles role) {
-		if(!groupDao.hasRole(element.getGroup(), member, role.name())) {
-			groupDao.addMembershipOneWay(element.getGroup(), member, role.name(), GroupMembershipInheritance.none);
-		} 
+		GroupMembershipInheritance inheritanceMode;
+		if(CurriculumRoles.isInheritedByDefault(role)) {
+			inheritanceMode = GroupMembershipInheritance.root;
+		} else {
+			inheritanceMode = GroupMembershipInheritance.none;
+		}
+		addMember(element, member, role, inheritanceMode);
+	}
+	
+	public void addMember(CurriculumElement element, Identity member, CurriculumRoles role, GroupMembershipInheritance inheritanceMode) {
+		if(inheritanceMode == GroupMembershipInheritance.inherited) {
+			throw new AssertException("Inherited are automatic");
+		}
+		
+		GroupMembership membership = groupDao.getMembership(element.getGroup(), member, role.name());
+		if(membership == null) {
+			groupDao.addMembershipOneWay(element.getGroup(), member, role.name(), inheritanceMode);
+		} else if(membership.getInheritanceMode() != inheritanceMode) {
+			groupDao.updateInheritanceMode(membership, inheritanceMode);
+		}
+		
+		if(inheritanceMode == GroupMembershipInheritance.root) {
+			List<CurriculumElement> descendants = curriculumElementDao.getDescendants(element);
+			for(CurriculumElement descendant:descendants) {
+				GroupMembership inheritedMembership = groupDao.getMembership(descendant.getGroup(), member, role.name());
+				if(inheritedMembership == null) {
+					groupDao.addMembershipOneWay(descendant.getGroup(), member, role.name(), GroupMembershipInheritance.inherited);
+				} else if(inheritedMembership.getInheritanceMode() == GroupMembershipInheritance.none) {
+					groupDao.updateInheritanceMode(inheritedMembership, GroupMembershipInheritance.inherited);
+				}
+			}
+		}
 	}
 
 	@Override
 	public void removeMember(CurriculumElement element, IdentityRef member) {
 		groupDao.removeMembership(element.getGroup(), member);
+
+		List<GroupMembership> memberships = groupDao.getMemberships(element.getGroup(), member);
+		
+		CurriculumElementNode elementNode = null;
+		for(GroupMembership membership:memberships) {
+			if(membership.getInheritanceMode() == GroupMembershipInheritance.none) {
+				groupDao.removeMembership(membership);
+			} else if(membership.getInheritanceMode() == GroupMembershipInheritance.root) {
+				String role = membership.getRole();
+				groupDao.removeMembership(membership);
+				
+				if(elementNode == null) {
+					elementNode = curriculumElementDao.getDescendantTree(element);
+				}
+				for(CurriculumElementNode child:elementNode.getChildrenNode()) {
+					removeInherithedMembership(child, member, role);
+				}
+			}
+		}
+	}
+	
+	/**
+	 * The method will recursively delete the inherithed membership. If it
+	 * found a mebership marked as "root" or "none". It will stop.
+	 * 
+	 * @param elementNode The organization node
+	 * @param member The user to remove
+	 * @param role The role
+	 */
+	private void removeInherithedMembership(CurriculumElementNode elementNode, IdentityRef member, String role) {
+		GroupMembership membership = groupDao
+				.getMembership(elementNode.getElement().getGroup(), member, role);
+		if(membership != null && membership.getInheritanceMode() == GroupMembershipInheritance.inherited) {
+			groupDao.removeMembership(membership);
+			for(CurriculumElementNode child:elementNode.getChildrenNode()) {
+				removeInherithedMembership(child, member, role);
+			}
+		}
 	}
 
 	@Override
diff --git a/src/main/java/org/olat/modules/curriculum/model/CurriculumElementNode.java b/src/main/java/org/olat/modules/curriculum/model/CurriculumElementNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..6a8b59d2aa525f949a4327e81628b9a7d12d3608
--- /dev/null
+++ b/src/main/java/org/olat/modules/curriculum/model/CurriculumElementNode.java
@@ -0,0 +1,57 @@
+/**
+ * <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.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.olat.modules.curriculum.CurriculumElement;
+
+/**
+ * 
+ * Initial date: 20 mai 2019<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class CurriculumElementNode {
+	
+	private final CurriculumElement element;
+	private final List<CurriculumElementNode> children = new ArrayList<>();
+	
+	public CurriculumElementNode(CurriculumElement element) {
+		this.element = element;
+	}
+	
+	public CurriculumElement getElement() {
+		return element;
+	}
+	
+	/**
+	 * @return A copy of the list of direct children.
+	 */
+	public List<CurriculumElementNode> getChildrenNode() {
+		return new ArrayList<>(children);
+	}
+	
+	public void addChild(CurriculumElementNode node) {
+		children.add(node);
+	}
+
+}
diff --git a/src/main/java/org/olat/modules/curriculum/model/CurriculumMember.java b/src/main/java/org/olat/modules/curriculum/model/CurriculumMember.java
index 4cd8bb7e28ae87f75788a525a21b68dc2d2b21ef..4070439047ce3a9053310ac5c9aac09513f948b5 100644
--- a/src/main/java/org/olat/modules/curriculum/model/CurriculumMember.java
+++ b/src/main/java/org/olat/modules/curriculum/model/CurriculumMember.java
@@ -34,10 +34,6 @@ public class CurriculumMember {
 	private final String role;
 	private final GroupMembershipInheritance inheritanceMode;
 	
-	public CurriculumMember(Identity identity, String role) {
-		this(identity, role, GroupMembershipInheritance.none);
-	}
-	
 	public CurriculumMember(Identity identity, String role, GroupMembershipInheritance inheritanceMode) {
 		this.identity = identity;
 		this.role = role;
diff --git a/src/main/java/org/olat/modules/curriculum/ui/CurriculumElementUserManagementController.java b/src/main/java/org/olat/modules/curriculum/ui/CurriculumElementUserManagementController.java
index a102a9332d121b83cf32bdcab1ad0504287484a0..734df439eb4f49d35ea12c3922f8c83d7550c771 100644
--- a/src/main/java/org/olat/modules/curriculum/ui/CurriculumElementUserManagementController.java
+++ b/src/main/java/org/olat/modules/curriculum/ui/CurriculumElementUserManagementController.java
@@ -27,6 +27,7 @@ import java.util.Set;
 import org.olat.admin.user.UserSearchController;
 import org.olat.admin.user.UserTableDataModel;
 import org.olat.basesecurity.BaseSecurityModule;
+import org.olat.basesecurity.GroupMembershipInheritance;
 import org.olat.basesecurity.events.MultiIdentityChosenEvent;
 import org.olat.basesecurity.events.SingleIdentityChosenEvent;
 import org.olat.basesecurity.model.IdentityRefImpl;
@@ -238,13 +239,17 @@ public class CurriculumElementUserManagementController extends FormBasicControll
 	
 	private void doConfirmRemoveAllMemberships(UserRequest ureq) {
 		Set<Integer> selectedRows = tableEl.getMultiSelectedIndex();
-		if(selectedRows.isEmpty()) {
+		List<CurriculumMemberRow> rows = new ArrayList<>(selectedRows.size());
+		for(Integer selectedRow:selectedRows) {
+			CurriculumMemberRow row = tableModel.getObject(selectedRow.intValue());
+			if(row.getInheritanceMode() == GroupMembershipInheritance.root || row.getInheritanceMode() == GroupMembershipInheritance.none) {
+				rows.add(row);
+			}
+		}
+		
+		if(rows.isEmpty()) {
 			showWarning("warning.atleastone.member");
 		} else {
-			List<CurriculumMemberRow> rows = new ArrayList<>(selectedRows.size());
-			for(Integer selectedRow:selectedRows) {
-				rows.add(tableModel.getObject(selectedRow.intValue()));
-			}
 			String title = translate("confirm.remove.member.title");
 			confirmRemoveCtrl = activateYesNoDialog(ureq, title, translate("confirm.remove.member.text", ""), confirmRemoveCtrl);
 			confirmRemoveCtrl.setUserObject(rows);
@@ -267,7 +272,7 @@ public class CurriculumElementUserManagementController extends FormBasicControll
 		
 		String title = translate("add.member");
 		CurriculumRoles[] roles = new CurriculumRoles[] {
-				CurriculumRoles.curriculumelementowner,
+				CurriculumRoles.curriculumelementowner, CurriculumRoles.mastercoach,
 				CurriculumRoles.owner, CurriculumRoles.coach, CurriculumRoles.participant
 		};
 		roleListCtrl = new RoleListController(ureq, getWindowControl(), roles);
diff --git a/src/main/java/org/olat/modules/curriculum/ui/CurriculumUserManagementTableModel.java b/src/main/java/org/olat/modules/curriculum/ui/CurriculumUserManagementTableModel.java
index 5a1d73db8f017ecd0b285cc85c274f0072519ddf..c16c54229d501278ab7fd044a20c4681ef9b8bb3 100644
--- a/src/main/java/org/olat/modules/curriculum/ui/CurriculumUserManagementTableModel.java
+++ b/src/main/java/org/olat/modules/curriculum/ui/CurriculumUserManagementTableModel.java
@@ -21,6 +21,7 @@ package org.olat.modules.curriculum.ui;
 
 import java.util.Locale;
 
+import org.olat.basesecurity.GroupMembershipInheritance;
 import org.olat.core.commons.persistence.SortKey;
 import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiTableDataModel;
 import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiSortableColumnDef;
@@ -50,6 +51,13 @@ implements SortableFlexiTableDataModel<CurriculumMemberRow> {
 			super.setObjects(sort.sort());
 		}
 	}
+	
+	@Override
+	public boolean isSelectable(int row) {
+		CurriculumMemberRow member = getObject(row);
+		return member.getInheritanceMode() == GroupMembershipInheritance.root
+				|| member.getInheritanceMode() == GroupMembershipInheritance.none;
+	}
 
 	@Override
 	public Object getValueAt(int row, int col) {
diff --git a/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_de.properties
index c0ce34ff1424e6318b4fbe334c36e8f52ee3bdd1..440d5cca7eb2f520c11ae7bc9703e9766922721f 100644
--- a/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_de.properties
@@ -76,6 +76,7 @@ role.coach=Betreuer
 role.curriculumelementowner=Elementbesitzer
 role.curriculummanager=Curriculumverwalter
 role.curriculumowner=Curriculumbesitzer
+role.mastercoach=Klassenlehrer
 role.owner=Kursbesitzer
 role.participant=Teilnehmer
 search.element.id=Element ID
diff --git a/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_en.properties
index 5a9b288cd2d65d5ad653d9b68e5b5080f7d69099..d66b8c97e91abd392485ff27896ce584ccce3457 100644
--- a/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_en.properties
@@ -76,6 +76,7 @@ role.coach=Coach
 role.curriculumelementowner=Element owner
 role.curriculummanager=Curriculum manager
 role.curriculumowner=Curriculum owner
+role.mastercoach=Master coach
 role.owner=Course owner
 role.participant=Participant
 search.element.begin=From
diff --git a/src/main/java/org/olat/modules/vitero/manager/ViteroManager.java b/src/main/java/org/olat/modules/vitero/manager/ViteroManager.java
index e1afe86ba52039ac00096559bd245d11071fe2b0..49067eb5458ceb07e03a83269b883365e579e60c 100644
--- a/src/main/java/org/olat/modules/vitero/manager/ViteroManager.java
+++ b/src/main/java/org/olat/modules/vitero/manager/ViteroManager.java
@@ -23,6 +23,7 @@ import java.io.File;
 import java.io.StringWriter;
 import java.math.BigInteger;
 import java.net.ConnectException;
+import java.net.SocketTimeoutException;
 import java.net.URI;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
@@ -48,7 +49,7 @@ import javax.xml.ws.handler.HandlerResolver;
 import javax.xml.ws.handler.PortInfo;
 import javax.xml.ws.soap.SOAPFaultException;
 
-import org.apache.commons.httpclient.ConnectTimeoutException;
+import org.apache.logging.log4j.Logger;
 import org.olat.basesecurity.Authentication;
 import org.olat.basesecurity.BaseSecurity;
 import org.olat.core.commons.persistence.DB;
@@ -56,7 +57,6 @@ import org.olat.core.id.Identity;
 import org.olat.core.id.OLATResourceable;
 import org.olat.core.id.User;
 import org.olat.core.id.UserConstants;
-import org.apache.logging.log4j.Logger;
 import org.olat.core.logging.Tracing;
 import org.olat.core.util.StringHelper;
 import org.olat.core.util.WebappHelper;
@@ -1490,7 +1490,7 @@ public class ViteroManager implements UserDataDeletable {
 				return ErrorCode.find(code);
 			}
 			return ErrorCode.unkown;
-		} else if (f.getCause() instanceof ConnectTimeoutException) {
+		} else if (f.getCause() instanceof SocketTimeoutException) {
 			throw new VmsNotAvailableException(f);
 		}
 		return ErrorCode.unkown;
diff --git a/src/main/java/org/olat/note/NoteController.java b/src/main/java/org/olat/note/NoteController.java
index 8428ceefe7c3fe39d5de2a8a7087867eef29a077..52503418aa42052de39ac507c304ab1ec443084f 100644
--- a/src/main/java/org/olat/note/NoteController.java
+++ b/src/main/java/org/olat/note/NoteController.java
@@ -25,6 +25,7 @@
 
 package org.olat.note;
 
+import org.olat.core.commons.persistence.DB;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.form.flexible.FormItem;
 import org.olat.core.gui.components.form.flexible.FormItemContainer;
@@ -65,6 +66,8 @@ public class NoteController extends FormBasicController implements GenericEventL
 	private FormLink editButton;
 	private FormSubmit submitButton;
 	
+	@Autowired
+	private DB dbInstance;
 	@Autowired
 	private NoteManager noteManager;
 
@@ -129,7 +132,11 @@ public class NoteController extends FormBasicController implements GenericEventL
 		
 		noteField = uifactory.addRichTextElementForStringData("noteField", null, n.getNoteText(), 20, -1, false, null, null, formLayout, ureq.getUserSession(), getWindowControl());
 		noteField.setEnabled(false);
-		noteField.setMaxLength(4000);
+		if(dbInstance.isOracle()) {
+			noteField.setMaxLength(4000);
+		} else {
+			noteField.setMaxLength(400000);
+		}
 
 		submitButton = uifactory.addFormSubmitButton("submit", formLayout);
 		submitButton.setVisible(false);
diff --git a/src/main/java/org/olat/repository/RepositoryManager.java b/src/main/java/org/olat/repository/RepositoryManager.java
index cf93395e67e77589e62dde9fb2109353e01ecfdd..3fb56fc3c237d33ff97eefe14cd92a6b5712e170 100644
--- a/src/main/java/org/olat/repository/RepositoryManager.java
+++ b/src/main/java/org/olat/repository/RepositoryManager.java
@@ -39,6 +39,7 @@ import java.util.Set;
 
 import javax.persistence.TypedQuery;
 
+import org.apache.logging.log4j.Logger;
 import org.olat.admin.securitygroup.gui.IdentitiesAddEvent;
 import org.olat.basesecurity.GroupRoles;
 import org.olat.basesecurity.IdentityImpl;
@@ -57,7 +58,6 @@ import org.olat.core.id.OLATResourceable;
 import org.olat.core.id.Organisation;
 import org.olat.core.id.Roles;
 import org.olat.core.logging.AssertException;
-import org.apache.logging.log4j.Logger;
 import org.olat.core.logging.Tracing;
 import org.olat.core.logging.activity.ActionType;
 import org.olat.core.logging.activity.OlatResourceableType;
@@ -77,6 +77,7 @@ import org.olat.core.util.vfs.VFSManager;
 import org.olat.course.PersistingCourseImpl;
 import org.olat.fileresource.FileResourceManager;
 import org.olat.group.GroupLoggingAction;
+import org.olat.modules.curriculum.CurriculumRoles;
 import org.olat.modules.taxonomy.TaxonomyLevel;
 import org.olat.repository.manager.RepositoryEntryDAO;
 import org.olat.repository.manager.RepositoryEntryQueries;
@@ -527,6 +528,7 @@ public class RepositoryManager {
 		boolean isPrincipal = false;
 		boolean isAdministrator = false;
 		boolean isLearnRessourceManager = false;
+		boolean isMasterCoach = false;
 		
 		boolean canLaunch = false;
 		RepositoryEntryStatusEnum status = re.getEntryStatus();
@@ -592,6 +594,13 @@ public class RepositoryManager {
 							break;
 						default: break;
 					}
+				} else if(CurriculumRoles.isValueOf(role)) {
+					switch(CurriculumRoles.valueOf(role)) {
+						case mastercoach:
+							isMasterCoach = true;
+							break;
+						default: break;
+					}
 				}
 			}
 
@@ -617,7 +626,7 @@ public class RepositoryManager {
 					}
 				}
 				
-				if(!canLaunch && (isGroupCoach || isCourseCoach || isCurriculumCoach)) {
+				if(!canLaunch && (isGroupCoach || isCourseCoach || isCurriculumCoach || isMasterCoach)) {
 					canLaunch = status == RepositoryEntryStatusEnum.coachpublished
 							|| status == RepositoryEntryStatusEnum.published
 							|| status == RepositoryEntryStatusEnum.closed;
@@ -636,7 +645,7 @@ public class RepositoryManager {
 		return new RepositoryEntrySecurity(isEntryAdmin, isOwner,
 				isCourseParticipant, isCourseCoach,
 				isGroupParticipant, isGroupCoach, isGroupWaiting,
-				isCurriculumParticipant, isCurriculumCoach,
+				isCurriculumParticipant, isCurriculumCoach, isMasterCoach,
 				isAuthor, isPrincipal, canLaunch, readOnly);
 	}
 
diff --git a/src/main/java/org/olat/repository/model/RepositoryEntrySecurity.java b/src/main/java/org/olat/repository/model/RepositoryEntrySecurity.java
index 68f9bb262a0f73651fe430259a044b11aac2fbd2..3f4486f967ef5d00231a6f8eb345460b1940e033 100644
--- a/src/main/java/org/olat/repository/model/RepositoryEntrySecurity.java
+++ b/src/main/java/org/olat/repository/model/RepositoryEntrySecurity.java
@@ -33,6 +33,7 @@ public class RepositoryEntrySecurity {
 	private final boolean readOnly;
 	private final boolean author;
 	private final boolean principal;
+	private final boolean masterCoach;
 	
 	private final boolean courseParticipant;
 	private final boolean courseCoach;
@@ -46,7 +47,7 @@ public class RepositoryEntrySecurity {
 	public RepositoryEntrySecurity(boolean entryAdmin, boolean owner,
 			boolean courseParticipant, boolean courseCoach,
 			boolean groupParticipant, boolean groupCoach, boolean groupWaiting,
-			boolean curriculumParticipant, boolean curriculumCoach,
+			boolean curriculumParticipant, boolean curriculumCoach, boolean masterCoach,
 			boolean author, boolean principal,
 			boolean canLaunch, boolean readOnly) {
 		this.owner = owner;
@@ -54,6 +55,7 @@ public class RepositoryEntrySecurity {
 		this.entryAdmin = entryAdmin;
 		this.author = author;
 		this.principal = principal;
+		this.masterCoach = masterCoach;
 		
 		this.courseParticipant = courseParticipant;
 		this.courseCoach = courseCoach;
@@ -87,6 +89,10 @@ public class RepositoryEntrySecurity {
 		return courseParticipant || groupParticipant || curriculumParticipant;
 	}
 	
+	public boolean isMasterCoach() {
+		return masterCoach;
+	}
+	
 	public boolean isEntryAdmin() {
 		return entryAdmin;
 	}
@@ -154,4 +160,8 @@ public class RepositoryEntrySecurity {
 	public boolean isOnlyPrincipal() {
 		return principal && !isMember() && !isEntryAdmin();
 	}
+	
+	public boolean isOnlyMasterCoach() {
+		return masterCoach && !isMember() && !isEntryAdmin();
+	}
 }
diff --git a/src/main/java/org/olat/repository/ui/RepositoryEntryRuntimeController.java b/src/main/java/org/olat/repository/ui/RepositoryEntryRuntimeController.java
index 1006254e1c75c476a9f8083f2f2cad30aabd64e1..63d5536076316c10fd7fcdfd3d722e03d5f04766 100644
--- a/src/main/java/org/olat/repository/ui/RepositoryEntryRuntimeController.java
+++ b/src/main/java/org/olat/repository/ui/RepositoryEntryRuntimeController.java
@@ -883,7 +883,7 @@ public class RepositoryEntryRuntimeController extends MainLayoutBasicController
 	}
 	
 	private void doRun(UserRequest ureq, RepositoryEntrySecurity security) {
-		if(security.isEntryAdmin() || security.isPrincipal()) {
+		if(security.isEntryAdmin() || security.isPrincipal() || reSecurity.isMasterCoach()) {
 			launchContent(ureq, security);
 		} else {
 			// guest are allowed to see resource with BARG
diff --git a/src/main/java/org/olat/restapi/repository/course/CourseWebService.java b/src/main/java/org/olat/restapi/repository/course/CourseWebService.java
index 7d33201d949cf1d12f6c8a981d204887a4428319..a0590a225ef8b1ca63426ad72f117cbe242c66a5 100644
--- a/src/main/java/org/olat/restapi/repository/course/CourseWebService.java
+++ b/src/main/java/org/olat/restapi/repository/course/CourseWebService.java
@@ -187,8 +187,8 @@ public class CourseWebService {
 
 			UserCourseEnvironment userCourseEnv = new UserCourseEnvironmentImpl(ienv, course.getCourseEnvironment(), null,
 					null, null, null,
-					reSecurity.isCoach(), reSecurity.isEntryAdmin() || reSecurity.isPrincipal(), reSecurity.isParticipant(),
-					reSecurity.isReadOnly() || reSecurity.isOnlyPrincipal());
+					reSecurity.isCoach(), reSecurity.isEntryAdmin() || reSecurity.isPrincipal() || reSecurity.isMasterCoach(), reSecurity.isParticipant(),
+					reSecurity.isReadOnly() || reSecurity.isOnlyPrincipal() || reSecurity.isOnlyMasterCoach());
 			KalendarRenderWrapper wrapper = CourseCalendars.getCourseCalendarWrapper(ureq, userCourseEnv, null);
 			return new CalWebService(wrapper);
 		}