From 46ff406a01bcc721323eef6bdb41b66b7fd30818 Mon Sep 17 00:00:00 2001 From: Dirk Furrer <none@none> Date: Thu, 19 Mar 2015 12:26:44 +0100 Subject: [PATCH] OO-1472: extended EnrollmentNode configuration. changed EnrollmentManager, RunController and BusinessGroupTableModel to handle the new configurationState --- .../_content/group_or_area_selection.html | 2 +- .../org/olat/course/nodes/ENCourseNode.java | 12 +- .../en/ENEditGroupAreaFormController.java | 118 ++++++++++-------- .../olat/course/nodes/en/ENRunController.java | 86 +++++++------ .../course/nodes/en/EnrollmentManager.java | 53 +++----- .../nodes/en/_content/enrollmultiple.html | 46 ++++--- .../nodes/en/_i18n/LocalStrings_de.properties | 10 +- .../nodes/en/_i18n/LocalStrings_en.properties | 10 +- .../BusinessGroupTableModelWithMaxSize.java | 24 ++-- 9 files changed, 210 insertions(+), 151 deletions(-) diff --git a/src/main/java/org/olat/course/condition/_content/group_or_area_selection.html b/src/main/java/org/olat/course/condition/_content/group_or_area_selection.html index d1941a3a631..8d2dd1ffd50 100644 --- a/src/main/java/org/olat/course/condition/_content/group_or_area_selection.html +++ b/src/main/java/org/olat/course/condition/_content/group_or_area_selection.html @@ -4,6 +4,6 @@ </div> #end $r.render("entries") -<div class="o_button_group"> +<div class="o_button_group o_sel_group_selection_groups"> $r.render("subm") $r.render("cancel") </div> \ No newline at end of file diff --git a/src/main/java/org/olat/course/nodes/ENCourseNode.java b/src/main/java/org/olat/course/nodes/ENCourseNode.java index 3a77a0c3c9f..233bf4b0c06 100644 --- a/src/main/java/org/olat/course/nodes/ENCourseNode.java +++ b/src/main/java/org/olat/course/nodes/ENCourseNode.java @@ -99,12 +99,13 @@ public class ENCourseNode extends AbstractAccessableCourseNode { /** CONFIG_AREANAME configuration parameter key. */ public static final String CONFIG_AREA_IDS = "areakeys"; - + /** CONFIG_ALLOW_MULTIPLE_ENTROLL_COUNT configuration parameter */ + public static final String CONFIG_ALLOW_MULTIPLE_ENROLL_COUNT = "allow_multiple_enroll_count"; /** CONF_CANCEL_ENROLL_ENABLED configuration parameter key. */ public static final String CONF_CANCEL_ENROLL_ENABLED = "cancel_enroll_enabled"; - private static final int CURRENT_CONFIG_VERSION = 2; + private static final int CURRENT_CONFIG_VERSION = 3; /** * Constructor for enrollment buildig block @@ -258,7 +259,8 @@ public class ENCourseNode extends AbstractAccessableCourseNode { ModuleConfiguration config = getModuleConfiguration(); // defaults config.set(CONF_CANCEL_ENROLL_ENABLED, Boolean.TRUE); - config.setConfigurationVersion(CURRENT_CONFIG_VERSION); + config.set(CONFIG_ALLOW_MULTIPLE_ENROLL_COUNT,1); + config.setConfigurationVersion(CURRENT_CONFIG_VERSION); } @Override @@ -324,6 +326,10 @@ public class ENCourseNode extends AbstractAccessableCourseNode { // migrate V1 => V2 config.set(CONF_CANCEL_ENROLL_ENABLED, Boolean.TRUE); version = 2; + }else if(version <= 2){ + // migrate V2 -> V3 + config.set(CONFIG_ALLOW_MULTIPLE_ENROLL_COUNT, 1); + version = 3; } config.setConfigurationVersion(CURRENT_CONFIG_VERSION); } diff --git a/src/main/java/org/olat/course/nodes/en/ENEditGroupAreaFormController.java b/src/main/java/org/olat/course/nodes/en/ENEditGroupAreaFormController.java index 06ff7c572bf..c9482b557d1 100644 --- a/src/main/java/org/olat/course/nodes/en/ENEditGroupAreaFormController.java +++ b/src/main/java/org/olat/course/nodes/en/ENEditGroupAreaFormController.java @@ -36,6 +36,7 @@ import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.form.flexible.FormItem; import org.olat.core.gui.components.form.flexible.FormItemContainer; import org.olat.core.gui.components.form.flexible.elements.FormLink; +import org.olat.core.gui.components.form.flexible.elements.IntegerElement; import org.olat.core.gui.components.form.flexible.elements.MultipleSelectionElement; import org.olat.core.gui.components.form.flexible.elements.StaticTextElement; import org.olat.core.gui.components.form.flexible.impl.FormBasicController; @@ -43,7 +44,6 @@ import org.olat.core.gui.components.form.flexible.impl.FormEvent; import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer; import org.olat.core.gui.components.form.flexible.impl.elements.FormLinkImpl; import org.olat.core.gui.components.form.flexible.impl.elements.FormSubmit; -import org.olat.core.gui.components.link.Link; import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.Event; import org.olat.core.gui.control.WindowControl; @@ -87,14 +87,14 @@ class ENEditGroupAreaFormController extends FormBasicController implements Gener private ModuleConfiguration moduleConfig; private CourseEditorEnv cev; private MultipleSelectionElement enableCancelEnroll; + private MultipleSelectionElement allowMultipleEnroll; + private IntegerElement multipleEnrollCount; private StaticTextElement easyGroupList; private FormLink chooseGroupsLink; - private FormLink createGroupsLink; private StaticTextElement easyAreaList; private FormLink chooseAreasLink; - private FormLink createAreasLink; private boolean hasAreas; private boolean hasGroups; @@ -178,6 +178,10 @@ class ENEditGroupAreaFormController extends FormBasicController implements Gener moduleConfig.set(ENCourseNode.CONF_CANCEL_ENROLL_ENABLED, cancelEnrollEnabled); hasAreas = areaManager.countBGAreasInContext(cev.getCourseGroupManager().getCourseResource()) > 0; hasGroups = businessGroupService.countBusinessGroups(null, cev.getCourseGroupManager().getCourseEntry()) > 0; + //4. multiple groups flag + int enrollCount = multipleEnrollCount.getIntValue(); + if(!allowMultipleEnroll.isSelected(0)) enrollCount=1; + moduleConfig.set(ENCourseNode.CONFIG_ALLOW_MULTIPLE_ENROLL_COUNT, enrollCount); // Inform all listeners about the changed condition fireEvent(ureq, NodeEditController.NODECONFIG_CHANGED_EVENT); } @@ -202,14 +206,20 @@ class ENEditGroupAreaFormController extends FormBasicController implements Gener } groupInitVal = getGroupNames(groupKeys); - easyGroupList = uifactory.addStaticTextElement("group", "form.groupnames", groupInitVal == null ? "" : groupInitVal, formLayout); + chooseGroupsLink = uifactory.addFormLink("chooseGroup", formLayout, "btn btn-default o_xsmall o_form_groupchooser"); + chooseGroupsLink.setLabel("form.groupnames", null); + chooseGroupsLink.setIconLeftCSS("o_icon o_icon-fw o_icon_group"); + + easyGroupList = uifactory.addStaticTextElement("group", null, groupInitVal == null ? "" : groupInitVal, formLayout); easyGroupList.setUserObject(groupKeys); - - chooseGroupsLink = uifactory.addFormLink("chooseGroup", "choose", null, formLayout, Link.LINK); - chooseGroupsLink.setElementCssClass("o_sel_course_en_choose_group"); - createGroupsLink = uifactory.addFormLink("createGroup", "create", null, formLayout, Link.LINK); - createGroupsLink.setElementCssClass("o_sel_course_en_create_group"); + easyGroupList.setElementCssClass("text-muted"); + hasGroups = businessGroupService.countBusinessGroups(null, cev.getCourseGroupManager().getCourseEntry()) > 0; + if(hasGroups){ + chooseGroupsLink.setI18nKey("choose"); + }else{ + chooseGroupsLink.setI18nKey("create"); + } // areas String areaInitVal; @@ -220,12 +230,32 @@ class ENEditGroupAreaFormController extends FormBasicController implements Gener areaKeys = areaManager.toAreaKeys(areaInitVal, cev.getCourseGroupManager().getCourseResource()); } areaInitVal = getAreaNames(areaKeys); - easyAreaList = uifactory.addStaticTextElement("area", "form.areanames", areaInitVal == null ? "" : areaInitVal, formLayout); + + chooseAreasLink = uifactory.addFormLink("chooseArea", formLayout, "btn btn-default o_xsmall o_form_areachooser"); + chooseAreasLink.setLabel("form.areanames", null); + chooseAreasLink.setIconLeftCSS("o_icon o_icon-fw o_icon_courseareas"); + + easyAreaList = uifactory.addStaticTextElement("area", null, areaInitVal == null ? "" : areaInitVal, formLayout); easyAreaList.setUserObject(areaKeys); + easyAreaList.setElementCssClass("text-muted"); + + if(areaInitVal.isEmpty()){ + chooseAreasLink.setI18nKey("create"); + } else { + chooseAreasLink.setI18nKey("choose"); + } + + //multiple group selection + int enrollCountConfig = moduleConfig.getIntegerSafe(ENCourseNode.CONFIG_ALLOW_MULTIPLE_ENROLL_COUNT,1); + Boolean multipleEnroll = (enrollCountConfig > 1); + allowMultipleEnroll = uifactory.addCheckboxesHorizontal("allowMultipleEnroll", "form.allowMultiEnroll", formLayout, new String[] { "multiEnroll" }, new String[] { "" }); + allowMultipleEnroll.select("multiEnroll", multipleEnroll); + allowMultipleEnroll.addActionListener(FormEvent.ONCLICK); + + multipleEnrollCount = uifactory.addIntegerElement("form.multipleEnrollCount", enrollCountConfig, formLayout); + multipleEnrollCount.setMinValueCheck(1, "error.multipleEnroll"); + multipleEnrollCount.setVisible(allowMultipleEnroll.isSelected(0)); - chooseAreasLink = uifactory.addFormLink("chooseArea", "choose", null, formLayout, Link.LINK); - createAreasLink = uifactory.addFormLink("createArea", "create", null, formLayout, Link.LINK); - // enrolment Boolean initialCancelEnrollEnabled = (Boolean) moduleConfig.get(ENCourseNode.CONF_CANCEL_ENROLL_ENABLED); enableCancelEnroll = uifactory.addCheckboxesHorizontal("enableCancelEnroll", "form.enableCancelEnroll", formLayout, new String[] { "ison" }, new String[] { "" }); @@ -373,7 +403,13 @@ class ENEditGroupAreaFormController extends FormBasicController implements Gener @Override protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { - if (source == chooseGroupsLink) { + if(source == allowMultipleEnroll){ + if(allowMultipleEnroll.isSelected(0)){ + multipleEnrollCount.setVisible(true); + }else{ + multipleEnrollCount.setVisible(false); + } + }else if (source == chooseGroupsLink) { removeAsListenerAndDispose(groupChooseC); groupChooseC = new GroupSelectionController(ureq, getWindowControl(), true, cev.getCourseGroupManager(), getKeys(easyGroupList)); @@ -386,20 +422,6 @@ class ENEditGroupAreaFormController extends FormBasicController implements Gener cmc.activate(); subm.setEnabled(false); - } else if (source == createGroupsLink) { - removeAsListenerAndDispose(cmc); - removeAsListenerAndDispose(groupCreateCntrllr); - // no groups in group management -> directly show group create dialog - - OLATResource courseResource = cev.getCourseGroupManager().getCourseResource(); - RepositoryEntry courseRe = RepositoryManager.getInstance().lookupRepositoryEntry(courseResource, false); - groupCreateCntrllr = new NewBGController(ureq, getWindowControl(), courseRe, true, null); - listenTo(groupCreateCntrllr); - - cmc = new CloseableModalController(getWindowControl(), "close", groupCreateCntrllr.getInitialComponent()); - listenTo(cmc); - cmc.activate(); - subm.setEnabled(false); } else if (source == chooseAreasLink) { removeAsListenerAndDispose(cmc); removeAsListenerAndDispose(areaChooseC); @@ -413,18 +435,6 @@ class ENEditGroupAreaFormController extends FormBasicController implements Gener listenTo(cmc); cmc.activate(); subm.setEnabled(false); - } else if (source == createAreasLink) { - removeAsListenerAndDispose(cmc); - removeAsListenerAndDispose(areaCreateCntrllr); - // no areas -> directly show creation dialog - OLATResource courseResource = cev.getCourseGroupManager().getCourseResource(); - areaCreateCntrllr = new NewAreaController(ureq, getWindowControl(), courseResource, true, null); - listenTo(areaCreateCntrllr); - - cmc = new CloseableModalController(getWindowControl(), "close", areaCreateCntrllr.getInitialComponent()); - listenTo(cmc); - cmc.activate(); - subm.setEnabled(false); } else if (source == fixGroupError) { /* * user wants to fix problem with fixing group error link e.g. create one @@ -490,6 +500,7 @@ class ENEditGroupAreaFormController extends FormBasicController implements Gener easyGroupList.setValue(list == null ? "" : list); easyGroupList.setUserObject(groupChooseC.getSelectedKeys()); easyGroupList.getRootForm().submit(ureq); + chooseGroupsLink.setI18nKey("choose"); } else if (Event.CANCELLED_EVENT == event) { cmc.deactivate(); } @@ -505,6 +516,7 @@ class ENEditGroupAreaFormController extends FormBasicController implements Gener easyAreaList.setValue(list == null ? "" : list); easyAreaList.setUserObject(areaChooseC.getSelectedKeys()); easyAreaList.getRootForm().submit(ureq); + chooseAreasLink.setI18nKey("choose"); } else if (event == Event.CANCELLED_EVENT) { cmc.deactivate(); } @@ -524,8 +536,7 @@ class ENEditGroupAreaFormController extends FormBasicController implements Gener easyGroupList.setUserObject(c); if (groupCreateCntrllr.getCreatedGroupNames().size() > 0 && !hasGroups) { - chooseGroupsLink.setVisible(true); - createGroupsLink.setVisible(false); + chooseGroupsLink.setLinkTitle("select"); singleUserEventCenter.fireEventToListenersOf(new MultiUserEvent("changed"), groupConfigChangeEventOres); } @@ -546,8 +557,7 @@ class ENEditGroupAreaFormController extends FormBasicController implements Gener easyAreaList.setUserObject(c); if (areaCreateCntrllr.getCreatedAreaNames().size() > 0 && !hasAreas) { - chooseAreasLink.setVisible(true); - createAreasLink.setVisible(false); + chooseAreasLink.setLinkTitle("select"); singleUserEventCenter.fireEventToListenersOf(new MultiUserEvent("changed"), groupConfigChangeEventOres); } easyAreaList.getRootForm().submit(ureq); @@ -569,12 +579,18 @@ class ENEditGroupAreaFormController extends FormBasicController implements Gener private void updateGroupsAndAreasCheck() { hasGroups = businessGroupService.countBusinessGroups(null, cev.getCourseGroupManager().getCourseEntry()) > 0; - chooseGroupsLink.setVisible(hasGroups); - createGroupsLink.setVisible(!hasGroups && !managedGroups); + if(!hasGroups && !managedGroups){ + chooseGroupsLink.setLinkTitle("create"); + }else{ + chooseGroupsLink.setLinkTitle("choose"); + } hasAreas = areaManager.countBGAreasInContext(cev.getCourseGroupManager().getCourseResource()) > 0; - chooseAreasLink.setVisible(hasAreas); - createAreasLink.setVisible(!hasAreas); + if(hasAreas){ + chooseAreasLink.setLinkTitle("choose"); + }else{ + chooseAreasLink.setLinkTitle("create"); + } } private boolean isEmpty(StaticTextElement element) { @@ -608,7 +624,8 @@ class ENEditGroupAreaFormController extends FormBasicController implements Gener StringBuilder sb = new StringBuilder(); List<BusinessGroupShort> groups = businessGroupService.loadShortBusinessGroups(keys); for(BusinessGroupShort group:groups) { - if(sb.length() > 0) sb.append(", "); + if(sb.length() > 0) sb.append(" "); + sb.append("<i class='o_icon o_icon-fw o_icon_group'> </i> "); sb.append(StringHelper.escapeHtml(group.getName())); } return sb.toString(); @@ -618,7 +635,8 @@ class ENEditGroupAreaFormController extends FormBasicController implements Gener StringBuilder sb = new StringBuilder(); List<BGArea> areas = areaManager.loadAreas(keys); for(BGArea area:areas) { - if(sb.length() > 0) sb.append(", "); + if(sb.length() > 0) sb.append(" "); + sb.append("<i class='o_icon o_icon-fw o_icon_courseareas'> </i> "); sb.append(StringHelper.escapeHtml(area.getName())); } return sb.toString(); diff --git a/src/main/java/org/olat/course/nodes/en/ENRunController.java b/src/main/java/org/olat/course/nodes/en/ENRunController.java index 03652c6cc9a..7f3d4f7acf2 100644 --- a/src/main/java/org/olat/course/nodes/en/ENRunController.java +++ b/src/main/java/org/olat/course/nodes/en/ENRunController.java @@ -25,6 +25,7 @@ package org.olat.course.nodes.en; +import java.util.ArrayList; import java.util.List; import org.olat.NewControllerFactory; @@ -100,10 +101,11 @@ public class ENRunController extends BasicController implements GenericEventList private final CoursePropertyManager coursePropertyManager; // workflow variables - private BusinessGroup enrolledGroup; - private BusinessGroup waitingListGroup; + private List<BusinessGroup> enrolledGroups; + private List<BusinessGroup> waitingListGroups; private boolean cancelEnrollEnabled; + private int maxEnrollCount; /** * @param moduleConfiguration @@ -143,15 +145,15 @@ public class ENRunController extends BasicController implements GenericEventList cancelEnrollEnabled = ((Boolean) moduleConfig.get(ENCourseNode.CONF_CANCEL_ENROLL_ENABLED)).booleanValue(); Identity identity = userCourseEnv.getIdentityEnvironment().getIdentity(); - enrolledGroup = enrollmentManager.getBusinessGroupWhereEnrolled(identity, enrollableGroupKeys, enrollableAreaKeys, courseGroupManager.getCourseEntry()); - waitingListGroup = enrollmentManager.getBusinessGroupWhereInWaitingList(identity, enrollableGroupKeys, enrollableAreaKeys); + enrolledGroups = enrollmentManager.getBusinessGroupsWhereEnrolled(identity, enrollableGroupKeys, enrollableAreaKeys, courseGroupManager.getCourseEntry()); + waitingListGroups = enrollmentManager.getBusinessGroupsWhereInWaitingList(identity, enrollableGroupKeys, enrollableAreaKeys); registerGroupChangedEvents(enrollableGroupKeys, enrollableAreaKeys, ureq.getIdentity()); // Set correct view enrollVC = createVelocityContainer("enrollmultiple"); List<BusinessGroup> groups = enrollmentManager.loadGroupsFromNames(enrollableGroupKeys, enrollableAreaKeys); tableCtr = createTableController(ureq, enrollmentManager.hasAnyWaitingList(groups)); - + maxEnrollCount = moduleConfiguration.getIntegerSafe(ENCourseNode.CONFIG_ALLOW_MULTIPLE_ENROLL_COUNT, 1); doEnrollView(ureq); // push title and learning objectives, only visible on intro page @@ -195,37 +197,35 @@ public class ENRunController extends BasicController implements GenericEventList EnrollStatus enrollStatus = enrollmentManager.doEnroll(ureq.getIdentity(), ureq.getUserSession().getRoles(), choosenGroup, enNode, coursePropertyManager, getWindowControl(), getTranslator(), enrollableGroupKeys, enrollableAreaKeys, courseGroupManager); if (enrollStatus.isEnrolled() ) { - enrolledGroup = choosenGroup; + enrolledGroups.add(choosenGroup); } else if (enrollStatus.isInWaitingList() ) { - waitingListGroup = choosenGroup; + waitingListGroups.add(choosenGroup); } else { getWindowControl().setError(enrollStatus.getErrorMessage()); } // events are already fired BusinessGroupManager level :: BusinessGroupModifiedEvent.fireModifiedGroupEvents(BusinessGroupModifiedEvent.IDENTITY_ADDED_EVENT, choosenGroup, ureq.getIdentity()); // but async - doEnrollView(ureq); // fire event to indicate runmaincontroller that the menuview is to update - + doEnrollView(ureq); if (enrollStatus.isEnrolled() ) { - fireEvent(ureq, new BusinessGroupModifiedEvent(BusinessGroupModifiedEvent.IDENTITY_ADDED_EVENT, enrolledGroup, getIdentity())); + fireEvent(ureq, new BusinessGroupModifiedEvent(BusinessGroupModifiedEvent.IDENTITY_ADDED_EVENT, choosenGroup, getIdentity())); } else { fireEvent(ureq, Event.DONE_EVENT); } + } else if (actionid.equals(CMD_ENROLLED_CANCEL)) { - if (waitingListGroup != null) { + if (enrollmentManager.getBusinessGroupsWhereInWaitingList(getIdentity(), enrollableGroupKeys, enrollableAreaKeys).contains(choosenGroup)) { enrollmentManager.doCancelEnrollmentInWaitingList(ureq.getIdentity(), choosenGroup, enNode, coursePropertyManager, getWindowControl(), getTranslator()); - waitingListGroup = null; - } else { + waitingListGroups.remove(choosenGroup); + } else if(enrollmentManager.getBusinessGroupsWhereEnrolled(getIdentity(), enrollableGroupKeys, enrollableAreaKeys, courseGroupManager.getCourseEntry()).contains(choosenGroup)) { enrollmentManager.doCancelEnrollment(ureq.getIdentity(), choosenGroup, enNode, coursePropertyManager, getWindowControl(), getTranslator()); - enrolledGroup = null; - } - doEnrollView(ureq); - if (enrolledGroup == null) { - // fire event to indicate runmaincontroller that the menuview is to update - fireEvent(ureq, new BusinessGroupModifiedEvent(BusinessGroupModifiedEvent.IDENTITY_REMOVED_EVENT, choosenGroup, getIdentity())); + enrolledGroups.remove(choosenGroup); } + // fire event to indicate runmaincontroller that the menuview is to update + fireEvent(ureq, new BusinessGroupModifiedEvent(BusinessGroupModifiedEvent.IDENTITY_REMOVED_EVENT, choosenGroup, getIdentity())); // events are already fired BusinessGroupManager level :: BusinessGroupModifiedEvent.fireModifiedGroupEvents(BusinessGroupModifiedEvent.IDENTITY_REMOVED_EVENT, group, ureq.getIdentity()); // but async + doEnrollView(ureq); } else if(CMD_VISIT_CARD.equals(actionid)) { String businessPath; if(businessGroupService.isIdentityInBusinessGroup(getIdentity(), choosenGroup)) { @@ -246,32 +246,42 @@ public class ENRunController extends BasicController implements GenericEventList } private void doEnrollView(UserRequest ureq) { - //TODO read from config: 1) user can choose or 2) round robin - // for now only case 1 - if (enrolledGroup != null) { - enrollVC.contextPut("isEnrolledView", Boolean.TRUE); - enrollVC.contextPut("isWaitingList", Boolean.FALSE); - enrollVC.contextPut("groupName", StringHelper.escapeHtml(enrolledGroup.getName())); - String desc = StringHelper.xssScan(enrolledGroup.getDescription()); - enrollVC.contextPut("groupDesc", (desc == null) ? "" : desc); - } else if (waitingListGroup != null){ + enrollVC.contextPut("multiEnroll", (maxEnrollCount > 1 && enrolledGroups.size()+waitingListGroups.size()<maxEnrollCount)); + if(!enrolledGroups.isEmpty()||!waitingListGroups.isEmpty()){ + String[] hintNumbers = new String[2]; + hintNumbers[0]=String.valueOf(enrolledGroups.size()+waitingListGroups.size()); + hintNumbers[1]=String.valueOf(maxEnrollCount-enrolledGroups.size()-waitingListGroups.size()); + enrollVC.contextPut("multipleHint", translate("multiple.select.hint.outstanding", hintNumbers)); + }else{ + enrollVC.contextPut("multipleHint", translate("multiple.select.hint", String.valueOf(maxEnrollCount))); + } + + if (!enrolledGroups.isEmpty()) { enrollVC.contextPut("isEnrolledView", Boolean.TRUE); - enrollVC.contextPut("isWaitingList", Boolean.TRUE); - enrollVC.contextPut("groupName", StringHelper.escapeHtml(waitingListGroup.getName())); - String desc = StringHelper.xssScan(waitingListGroup.getDescription()); - enrollVC.contextPut("groupDesc", (desc == null) ? "" : desc); - } else { + ArrayList<String> groupnames = new ArrayList<String>(); + for(BusinessGroup group:enrolledGroups){ + groupnames.add(StringHelper.escapeHtml(group.getName())); + } + enrollVC.contextPut("groupNames", groupnames); + }else{ enrollVC.contextPut("isEnrolledView", Boolean.FALSE); + } + + if (!waitingListGroups.isEmpty()){ + enrollVC.contextPut("isInWaitingList", Boolean.TRUE); + ArrayList<String> waitingListNames = new ArrayList<String>(); + for(BusinessGroup waitingGroup:waitingListGroups){ + waitingListNames.add(StringHelper.escapeHtml(waitingGroup.getName())); + } + enrollVC.contextPut("waitingListNames", waitingListNames); + }else{ + enrollVC.contextPut("isInWaitingList", Boolean.FALSE); } - doEnrollMultipleView(ureq); - } - - private void doEnrollMultipleView(UserRequest ureq) { // 1. Fetch groups from database List<BusinessGroup> groups = enrollmentManager.loadGroupsFromNames(enrollableGroupKeys, enrollableAreaKeys); List<Integer> members = courseGroupManager.getNumberOfMembersFromGroups(groups); // 2. Build group list - groupListModel = new BusinessGroupTableModelWithMaxSize(groups, members, getTranslator(), ureq.getIdentity(), cancelEnrollEnabled); + groupListModel = new BusinessGroupTableModelWithMaxSize(groups, members, getTranslator(), ureq.getIdentity(), cancelEnrollEnabled, maxEnrollCount); tableCtr.setTableDataModel(groupListModel); tableCtr.modelChanged(); // 3. Add group list to view diff --git a/src/main/java/org/olat/course/nodes/en/EnrollmentManager.java b/src/main/java/org/olat/course/nodes/en/EnrollmentManager.java index 06da38432ac..a3a71b2d590 100644 --- a/src/main/java/org/olat/course/nodes/en/EnrollmentManager.java +++ b/src/main/java/org/olat/course/nodes/en/EnrollmentManager.java @@ -87,9 +87,11 @@ public class EnrollmentManager extends BasicManager { final EnrollStatus enrollStatus = new EnrollStatus(); if (isLogDebugEnabled()) logDebug("doEnroll"); - // check if the user is already enrolled (user can be enrooled only in one group) - if ( ( getBusinessGroupWhereEnrolled( identity, groupKeys, areaKeys, cgm.getCourseEntry()) == null) - && ( getBusinessGroupWhereInWaitingList(identity, groupKeys, areaKeys) == null) ) { + // check if the user is able to be enrolled + int groupsEnrolledCount = getBusinessGroupsWhereEnrolled(identity, groupKeys, areaKeys, cgm.getCourseEntry()).size(); + int waitingListCount = getBusinessGroupsWhereInWaitingList(identity, groupKeys, areaKeys).size(); + int enrollCountConfig = enNode.getModuleConfiguration().getIntegerSafe(ENCourseNode.CONFIG_ALLOW_MULTIPLE_ENROLL_COUNT, 1); + if ( (groupsEnrolledCount + waitingListCount) < enrollCountConfig ) { if (isLogDebugEnabled()) logDebug("Identity is not enrolled identity=" + identity.getName() + " group=" + group.getName()); // 1. Check if group has max size defined. If so check if group is full // o_clusterREVIEW cg please review it - also where does the group.getMaxParticipants().equals("") come from?? @@ -106,6 +108,7 @@ public class EnrollmentManager extends BasicManager { enrollStatus.setIsEnrolled(true); } else if(state.getEnrolled() == BGMembership.waiting) { addUserToWaitingList(identity, group, enNode, coursePropertyManager, wControl, trans); + enrollStatus.setIsInWaitingList(true); } } } else { @@ -173,42 +176,25 @@ public class EnrollmentManager extends BasicManager { // //////////////// /** * @param identity - * @param groupNames - * @return BusinessGroup in which the identity is enrolled, null if identity - * is nowhere enrolled. + * @param List<Long> groupKeys which are in the list + * @param List<Long> areaKeys which are in the list + * @return List<BusinessGroup> in which the identity is enrolled */ - protected BusinessGroup getBusinessGroupWhereEnrolled(Identity identity, List<Long> groupKeys, List<Long> areaKeys, RepositoryEntry courseResource) { - // 1. check in groups + protected List<BusinessGroup> getBusinessGroupsWhereEnrolled(Identity identity, List<Long> groupKeys, List<Long> areaKeys, RepositoryEntry courseResource) { + List<BusinessGroup> groups = new ArrayList<BusinessGroup>(); + //search in the enrollable bg keys for the groups where identity is attendee if(groupKeys != null && !groupKeys.isEmpty()) { SearchBusinessGroupParams params = new SearchBusinessGroupParams(); params.setAttendee(true); params.setIdentity(identity); params.setGroupKeys(groupKeys); - List<BusinessGroup> groups = businessGroupService.findBusinessGroups(params, courseResource, 0, 1); - if (groups.size() > 0) { - // Usually it is only possible to be in one group. However, - // theoretically the - // admin can put the user in a second enrollment group or the user could - // theoretically be in a second group context. For now, we only look for - // the first - // group. All groups found after the first one are discarded. - return groups.get(0); - } + groups.addAll(businessGroupService.findBusinessGroups(params, courseResource, 0, 0)); } - // 2. check in areas + //search in the enrollable area keys for the groups where identity is attendee if(areaKeys != null && !areaKeys.isEmpty()) { - List<BusinessGroup> groups = areaManager.findBusinessGroupsOfAreaAttendedBy(identity, areaKeys, courseResource.getOlatResource()); - if (groups.size() > 0) { - // Usually it is only possible to be in one group. However, - // theoretically the - // admin can put the user in a second enrollment group or the user could - // theoretically be in a second group context. For now, we only look for - // the first - // group. All groups found after the first one are discarded. - return groups.get(0); - } + groups.addAll(areaManager.findBusinessGroupsOfAreaAttendedBy(identity, areaKeys, courseResource.getOlatResource())); } - return null; + return groups; } /** @@ -217,15 +203,16 @@ public class EnrollmentManager extends BasicManager { * @return true if this identity is any waiting-list group in this course that * has a name that is in the group names list */ - protected BusinessGroup getBusinessGroupWhereInWaitingList(Identity identity, List<Long> groupKeys, List<Long> areaKeys) { + protected List<BusinessGroup> getBusinessGroupsWhereInWaitingList(Identity identity, List<Long> groupKeys, List<Long> areaKeys) { List<BusinessGroup> groups = loadGroupsFromNames(groupKeys, areaKeys); + List<BusinessGroup> waitingInTheseGroups = new ArrayList<BusinessGroup> (); // loop over all business-groups for (BusinessGroup businessGroup:groups) { if (businessGroupService.hasRoles(identity, businessGroup, GroupRoles.waiting.name())) { - return businessGroup; + waitingInTheseGroups.add(businessGroup); } } - return null; + return waitingInTheseGroups; } /** diff --git a/src/main/java/org/olat/course/nodes/en/_content/enrollmultiple.html b/src/main/java/org/olat/course/nodes/en/_content/enrollmultiple.html index 2fb6c434698..fad413fed2c 100644 --- a/src/main/java/org/olat/course/nodes/en/_content/enrollmultiple.html +++ b/src/main/java/org/olat/course/nodes/en/_content/enrollmultiple.html @@ -1,22 +1,40 @@ - #if ($isEnrolledView) + #if($isEnrolledView || $isInWaitingList) <div class="o_course_run_groupinfo"> - <p> - <strong>$r.translate("enrolled.group.name"):</strong> - <br /> - $groupName - </p> - #if ($groupDesc != "") - <p> - <strong>$r.translate("enrolled.group.desc"): </strong> - <br /> - $r.formatLatexFormulas($groupDesc) - </p> - #end + #if($isEnrolledView) + <div class="o_block"> + <h5>$r.translate("enrolled.group.name"):</h5> + <ul class="list-inline"> + #foreach( $group in $groupNames ) + <li> + <i class='o_icon o_icon-fw o_icon_group'> </i> $group + </li> + #end + </ul> + </div> + #end + #if( $isInWaitingList ) + <div class="o_block"> + <h5>$r.translate("enrolled.waitinglist.name"):</h5> + <ul class="list-inline"> + #foreach( $wlGroup in $waitingListNames ) + <li> + <i class='o_icon o_icon-fw o_icon_group'> </i> $wlGroup + </li> + #end + </ul> + </div> + #end + </div> + #end + + #if($multiEnroll ) + <div class="o_note"> + $multipleHint </div> #end <div class="o_course_run_statusinfo"> - #if ($isEnrolledView) + #if($isEnrolledView) <p> #if($isWaitingList) $r.translate("waitinglist.explain") diff --git a/src/main/java/org/olat/course/nodes/en/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/course/nodes/en/_i18n/LocalStrings_de.properties index 092088da2ce..6e1e32502c5 100644 --- a/src/main/java/org/olat/course/nodes/en/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/course/nodes/en/_i18n/LocalStrings_de.properties @@ -20,17 +20,21 @@ cmd.enrolled.cancel=Austragen condition.accessibility.title=Zugang config.header1=Einschreibung in Lerngruppen und Lernbereiche enroll.explain=W\u00E4hlen Sie eine der untenstehenden Lerngruppen aus, um sich einzuschreiben. -enrolled.explain=Sie sind in untenstehender Lerngruppe eingeschrieben. W\u00E4hlen Sie - sofern vorhanden - den Link Austragen, um sich aus der Gruppe auszutragen. <b>Achtung\:</b> Diese Einschreibung betrifft einzig die gew\u00E4hlte Gruppe im entsprechenden OLAT-Kurs. +enrolled.explain=Sie sind in die untenstehenden Lerngruppen eingeschrieben. W\u00E4hlen Sie - sofern vorhanden - den Link Austragen, um sich aus der entsprechenden Gruppe auszutragen. <b>Achtung\:</b> Diese Einschreibung betrifft einzig die gew\u00E4hlten Gruppen im entsprechenden OLAT-Kurs. enrolled.group.desc=Beschreibung -enrolled.group.name=Name der Lerngruppe +enrolled.group.name=Namen der Lerngruppen +enrolled.waitinglist.name=Namen der Wartelisten error.group.already.enrolled=Sie sind bereits in der Gruppe eingetragen. error.group.full=In der Zwischenzeit ist diese Gruppe vollst\u00E4ndig belegt. Bitte w\u00E4hlen Sie eine andere Gruppe. error.nogroupdefined.long=Es muss mindestens eine Lerngruppe oder ein Lernbereich f\u00FCr "{0}" aus dem Gruppenmanagement ausgew\u00E4hlt werden. error.nogroupdefined.short=F\u00FCr "{0}" fehlt eine Lerngruppe oder ein Lernbereich. +error.multipleEnroll=Der Wert muss 1 oder mehr betragen form.areanames=Lernbereiche form.areanames.example=(Beispiel\: Exkursionen) form.areanames.wrong=Geben Sie Namen von Lernbereichen getrennt mit Kommas ein oder lassen Sie dieses Feld leer. form.enableCancelEnroll=Austragen erlaubt +form.allowMultiEnroll= Mehrere Eintragungen erlauben +form.multipleEnrollCount=Anzahl der Gruppen form.groupnames=Lerngruppen form.groupnames.example=(Beispiel\: Rot,Gr\u00FCn,Blau) form.groupnames.wrong=Geben Sie Namen von Lerngruppen getrennt mit Kommas ein oder lassen Sie dieses Feld leer. @@ -70,3 +74,5 @@ pane.tab.enconfig=Konfiguration popupchooseareas=Lernbereich aus Gruppenmanagement w\u00E4hlen popupchoosegroups=Gruppe aus Gruppenmanagement w\u00E4hlen waitinglist.explain=Sie sind in untenstehender Lerngruppe auf der Warteliste eingeschrieben. W\u00E4hlen Sie den Link Austragen, um sich aus der Warteliste auszutragen. +multiple.select.hint=Sie k\u00F6nnen sich in eine der unten aufgeführen Lerngruppen einschreiben. Sie k\u00F6nnen sich in maximal <b>{0}</b> Gruppen einschreiben. +multiple.select.hint.outstanding=Sie haben sich in <b>{0}</b> Gruppen eingeschrieben. Sie k\u00F6nnen sich noch in <b>{1}</b> Gruppen einschreiben. \ No newline at end of file diff --git a/src/main/java/org/olat/course/nodes/en/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/course/nodes/en/_i18n/LocalStrings_en.properties index 21001db76f9..e3afcc02009 100644 --- a/src/main/java/org/olat/course/nodes/en/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/course/nodes/en/_i18n/LocalStrings_en.properties @@ -19,17 +19,21 @@ cmd.enrolled.cancel=Cancel condition.accessibility.title=Access config.header1=Enrolment in learning groups and learning areas enroll.explain=Choose one of the learning groups below to enrol. -enrolled.explain=You have already enroled for the learning group mentioned below. To cancel your enrolment please click on the button below (if available). <b>Attention\:</b> Your enrolment concerns only the group selected in the corresponding OLAT course. +enrolled.explain=You have already enrolled for the learning groups mentioned below. To cancel your enrollment please click on the button below (if available). <b>Attention\:</b> Your enrolment concerns only the groups selected in the corresponding OLAT course. enrolled.group.desc=Description -enrolled.group.name=Name of learning group +enrolled.group.name=Names of learning groups +enrolled.waitinglist.name=Names of waiting lists error.group.already.enrolled=You are already a registered member of this group. error.group.full=In the meantime this group is complete. Please select another one. error.nogroupdefined.long=There must be at least one learning group or one learning area selected for "{0}" from the section group management. error.nogroupdefined.short=Learning group or learning area missing for "{0}". +error.multipleEnroll=The Value must be 1 or higher form.areanames=Learning areas form.areanames.example=(Example\: Study trips) form.areanames.wrong=Enter names of learning areas separated by commas or leave this field empty. form.enableCancelEnroll=Delisting permitted +form.allowMultiEnroll=Allow multiple enrollments +form.multipleEnrollCount=Number of groups form.groupnames=Learning groups form.groupnames.example=(Example\: Green,Red,Blue) form.groupnames.wrong=Enter names of learning groups separated by commas or leave this field empty. @@ -70,3 +74,5 @@ popupchooseareas=Select learning areas from group management popupchoosegroups=Select groups from group management title_en=Enrolment waitinglist.explain=You are on the waiting list of the learning group mentioned below. Click 'Cancel enrolment' in order to delist. +multiple.select.hint=Choose from the learning groups below to enroll. You are allowed to enroll to a total of <b>{0}</b> groups. +multiple.select.hint.outstanding=You have enrolled to <b>{0}</b> Group. You can enroll to <b>{1}</b> more groups. \ No newline at end of file diff --git a/src/main/java/org/olat/group/ui/BusinessGroupTableModelWithMaxSize.java b/src/main/java/org/olat/group/ui/BusinessGroupTableModelWithMaxSize.java index 1e35c34fa73..1d0962c8b72 100644 --- a/src/main/java/org/olat/group/ui/BusinessGroupTableModelWithMaxSize.java +++ b/src/main/java/org/olat/group/ui/BusinessGroupTableModelWithMaxSize.java @@ -37,6 +37,7 @@ import org.olat.core.id.Identity; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.util.Formatter; +import org.olat.core.util.StringHelper; import org.olat.core.util.filter.FilterFactory; import org.olat.group.BusinessGroup; import org.olat.group.BusinessGroupService; @@ -59,6 +60,7 @@ public class BusinessGroupTableModelWithMaxSize extends DefaultTableDataModel<Bu private Identity identity; private boolean cancelEnrollEnabled; private BusinessGroupService businessGroupService; + private int maxEnrolCount; /** * @param groups List of business groups @@ -66,13 +68,14 @@ public class BusinessGroupTableModelWithMaxSize extends DefaultTableDataModel<Bu * The index of the list corresponds with the index of the group list * @param trans */ - public BusinessGroupTableModelWithMaxSize(List<BusinessGroup> groups, List<Integer> members, Translator trans, Identity identity, boolean cancelEnrollEnabled) { + public BusinessGroupTableModelWithMaxSize(List<BusinessGroup> groups, List<Integer> members, Translator trans, Identity identity, boolean cancelEnrollEnabled, int maxEnrolCount) { super(groups); this.members = members; this.trans = trans; this.identity = identity; businessGroupService = CoreSpringFactory.getImpl(BusinessGroupService.class); this.cancelEnrollEnabled = cancelEnrollEnabled; + this.maxEnrolCount = maxEnrolCount; } /** @@ -136,8 +139,8 @@ public class BusinessGroupTableModelWithMaxSize extends DefaultTableDataModel<Bu return trans.translate("grouplist.table.state.notEnrolled"); case 5: // Action enroll - if (isEnrolledInAnyGroup(identity)) { - // Allready enrolled => does not show action-link 'enroll' + if (getEnrolCount(identity) >= maxEnrolCount || isEnrolledIn(businessGroup, identity)) { + // Already too much enrollments or already enrolled in the bg of the row => does not show action-link 'enroll' return Boolean.FALSE; } if (max != null && !businessGroup.getWaitingListEnabled().booleanValue() && (numbParts.intValue() >= max.intValue()) ) { @@ -166,7 +169,7 @@ public class BusinessGroupTableModelWithMaxSize extends DefaultTableDataModel<Bu @Override public Object createCopyWithEmptyList() { - return new BusinessGroupTableModelWithMaxSize(new ArrayList<BusinessGroup>(), members, trans, identity, cancelEnrollEnabled); + return new BusinessGroupTableModelWithMaxSize(new ArrayList<BusinessGroup>(), members, trans, identity, cancelEnrollEnabled, maxEnrolCount); } /** @@ -201,16 +204,21 @@ public class BusinessGroupTableModelWithMaxSize extends DefaultTableDataModel<Bu /** * Check if an identity is in any security-group. * @param ident - * @return true: Found identity in any security-group of this table model. + * @return amount of business groups the identity is enrolled in */ - private boolean isEnrolledInAnyGroup(Identity ident) { + private int getEnrolCount(Identity ident) { + int enrolCount=0; // loop over all business-groups for (BusinessGroup businessGroup:objects) { if (isEnrolledIn(businessGroup, ident) ) { - return true; + enrolCount++; + // optimize, enough is enough + if (maxEnrolCount == enrolCount) { + return enrolCount; + } } } - return false; + return enrolCount; } @Override -- GitLab