From d7dcc7303f1ec0b444573f5ec548600c2505dfa4 Mon Sep 17 00:00:00 2001 From: srosse <stephane.rosse@frentix.com> Date: Sun, 22 Mar 2020 18:05:21 +0100 Subject: [PATCH] OO-4584: implement quota for templates Add quota of concurrent meetings per template, add informations while selecting a template, check server availability, show the template of the meeting in admin./edit table, permanent meeting can be disabled, dates of non-permanent meeting are strictly observed and mandatory, duration of meeting is exact (+ 1 minute)... --- .../course/nodes/BigBlueButtonCourseNode.java | 7 + .../BigBlueButtonConfigForm.java | 3 +- .../BigBlueButtonPeekViewController.java | 56 +++++++- .../bigbluebutton/_content/peekview.html | 1 + .../bigbluebutton/BigBlueButtonManager.java | 26 +++- .../BigBlueButtonMeetingTemplate.java | 4 + .../bigbluebutton/BigBlueButtonModule.java | 41 ++++++ .../manager/BigBlueButtonManagerImpl.java | 65 ++++++--- .../manager/BigBlueButtonMeetingDAO.java | 86 ++++++++++-- .../manager/BigBlueButtonUtils.java | 1 - .../BigBlueButtonMeetingTemplateImpl.java | 13 ++ .../ui/BigBlueButtonAdminController.java | 27 +++- .../BigBlueButtonAdminMeetingsController.java | 8 +- ...BigBlueButtonAdminTemplatesController.java | 32 +++-- .../BigBlueButtonConfigurationController.java | 13 +- .../BigBlueButtonEditMeetingsController.java | 10 +- .../ui/BigBlueButtonMeetingController.java | 64 ++++----- .../ui/BigBlueButtonMeetingTableModel.java | 8 ++ .../ui/BigBlueButtonMeetingsController.java | 11 +- .../ui/BigBlueButtonTemplateTableModel.java | 8 +- .../EditBigBlueButtonMeetingController.java | 129 ++++++++++++------ .../EditBigBlueButtonTemplateController.java | 79 +++++++---- .../bigbluebutton/ui/_content/meeting.html | 2 +- .../bigbluebutton/ui/_content/meetings.html | 2 + .../bigbluebutton/ui/_content/run_admin.html | 2 +- .../ui/_i18n/LocalStrings_de.properties | 15 +- .../ui/_i18n/LocalStrings_en.properties | 15 +- .../ui/_i18n/LocalStrings_fr.properties | 2 +- .../database/mysql/alter_14_2_x_to_14_2_5.sql | 1 + .../database/mysql/setupDatabase.sql | 1 + .../oracle/alter_14_2_x_to_14_2_5.sql | 1 + .../database/oracle/setupDatabase.sql | 1 + .../postgresql/alter_14_2_x_to_14_2_5.sql | 3 +- .../database/postgresql/setupDatabase.sql | 1 + .../manager/BigBlueButtonMeetingDAOTest.java | 68 +++++++++ 35 files changed, 631 insertions(+), 175 deletions(-) create mode 100644 src/main/java/org/olat/course/nodes/bigbluebutton/_content/peekview.html diff --git a/src/main/java/org/olat/course/nodes/BigBlueButtonCourseNode.java b/src/main/java/org/olat/course/nodes/BigBlueButtonCourseNode.java index b8d5af91ede..8a2cc327697 100644 --- a/src/main/java/org/olat/course/nodes/BigBlueButtonCourseNode.java +++ b/src/main/java/org/olat/course/nodes/BigBlueButtonCourseNode.java @@ -39,6 +39,7 @@ import org.olat.course.editor.NodeEditController; import org.olat.course.editor.StatusDescription; import org.olat.course.groupsandrights.CourseGroupManager; import org.olat.course.nodes.bigbluebutton.BigBlueButtonEditController; +import org.olat.course.nodes.bigbluebutton.BigBlueButtonPeekViewController; import org.olat.course.run.navigation.NodeRunConstructionResult; import org.olat.course.run.userview.NodeEvaluation; import org.olat.course.run.userview.UserCourseEnvironment; @@ -115,6 +116,12 @@ public class BigBlueButtonCourseNode extends AbstractAccessableCourseNode { return new NodeRunConstructionResult(ctrl); } + @Override + public Controller createPeekViewRunController(UserRequest ureq, WindowControl wControl, UserCourseEnvironment userCourseEnv, + NodeEvaluation ne) { + return new BigBlueButtonPeekViewController(ureq, wControl, userCourseEnv.getCourseEnvironment(), this); + } + @Override public StatusDescription isConfigValid() { if (oneClickStatusCache != null) { return oneClickStatusCache[0]; } diff --git a/src/main/java/org/olat/course/nodes/bigbluebutton/BigBlueButtonConfigForm.java b/src/main/java/org/olat/course/nodes/bigbluebutton/BigBlueButtonConfigForm.java index 2dadf712c05..8b50b719ca7 100644 --- a/src/main/java/org/olat/course/nodes/bigbluebutton/BigBlueButtonConfigForm.java +++ b/src/main/java/org/olat/course/nodes/bigbluebutton/BigBlueButtonConfigForm.java @@ -75,8 +75,7 @@ public class BigBlueButtonConfigForm extends FormBasicController { @Override protected void formOK(UserRequest ureq) { Collection<String> selectedKeys = accessEl.getSelectedKeys(); - config.setBooleanEntry(BigBlueButtonEditController.ACCESS_BY_DATES, selectedKeys.contains(accessKeys[0])); - config.setBooleanEntry(BigBlueButtonEditController.GUEST_ACCESS_ALLOWED, !selectedKeys.contains(accessKeys[1])); + config.setBooleanEntry(BigBlueButtonEditController.MODERATOR_START_MEETING, !selectedKeys.contains(accessKeys[0])); fireEvent(ureq, NodeEditController.NODECONFIG_CHANGED_EVENT); } } diff --git a/src/main/java/org/olat/course/nodes/bigbluebutton/BigBlueButtonPeekViewController.java b/src/main/java/org/olat/course/nodes/bigbluebutton/BigBlueButtonPeekViewController.java index 310ab4e0083..1b4c00c092a 100644 --- a/src/main/java/org/olat/course/nodes/bigbluebutton/BigBlueButtonPeekViewController.java +++ b/src/main/java/org/olat/course/nodes/bigbluebutton/BigBlueButtonPeekViewController.java @@ -19,11 +19,29 @@ */ package org.olat.course.nodes.bigbluebutton; +import java.util.List; + +import org.olat.core.commons.persistence.SortKey; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.form.flexible.FormItemContainer; +import org.olat.core.gui.components.form.flexible.elements.FlexiTableElement; +import org.olat.core.gui.components.form.flexible.elements.FlexiTableSortOptions; import org.olat.core.gui.components.form.flexible.impl.FormBasicController; +import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiColumnModel; +import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel; +import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataModelFactory; import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.WindowControl; +import org.olat.core.util.Util; +import org.olat.course.nodes.BigBlueButtonCourseNode; +import org.olat.course.run.environment.CourseEnvironment; +import org.olat.modules.bigbluebutton.BigBlueButtonManager; +import org.olat.modules.bigbluebutton.BigBlueButtonMeeting; +import org.olat.modules.bigbluebutton.ui.BigBlueButtonMeetingTableModel; +import org.olat.modules.bigbluebutton.ui.BigBlueButtonMeetingTableModel.BMeetingsCols; +import org.olat.modules.bigbluebutton.ui.BigBlueButtonRunController; +import org.olat.repository.RepositoryEntry; +import org.springframework.beans.factory.annotation.Autowired; /** * @@ -32,16 +50,48 @@ import org.olat.core.gui.control.WindowControl; * */ public class BigBlueButtonPeekViewController extends FormBasicController { + + private FlexiTableElement upcomingTableEl; + private BigBlueButtonMeetingTableModel upcomingTableModel; - public BigBlueButtonPeekViewController(UserRequest ureq, WindowControl wControl) { - super(ureq, wControl); + private final String subIdent; + private final RepositoryEntry courseEntry; + + @Autowired + private BigBlueButtonManager bigBlueButtonManager; + + public BigBlueButtonPeekViewController(UserRequest ureq, WindowControl wControl, + CourseEnvironment courseEnv, BigBlueButtonCourseNode courseNode) { + super(ureq, wControl, "peekview", Util.createPackageTranslator(BigBlueButtonRunController.class, ureq.getLocale())); + courseEntry = courseEnv.getCourseGroupManager().getCourseEntry(); + subIdent = courseNode.getIdent(); initForm(ureq); + loadModel(); } @Override protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { - // + FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel(); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(BMeetingsCols.name)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(BMeetingsCols.start)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(BMeetingsCols.end)); + + upcomingTableModel = new BigBlueButtonMeetingTableModel(columnsModel, getLocale()); + upcomingTableEl = uifactory.addTableElement(getWindowControl(), "upcomingMeetings", upcomingTableModel, + 5, false, getTranslator(), formLayout); + upcomingTableEl.setEmtpyTableMessageKey("no.upcoming.meetings"); + upcomingTableEl.setCustomizeColumns(false); + upcomingTableEl.setNumOfRowsEnabled(false); + + FlexiTableSortOptions sortOptions = new FlexiTableSortOptions(); + sortOptions.setDefaultOrderBy(new SortKey(BMeetingsCols.start.name(), true)); + upcomingTableEl.setSortSettings(sortOptions); + } + + private void loadModel() { + List<BigBlueButtonMeeting> meetings = bigBlueButtonManager.getUpcomingsMeetings(courseEntry, subIdent, 5); + upcomingTableModel.setObjects(meetings); } @Override diff --git a/src/main/java/org/olat/course/nodes/bigbluebutton/_content/peekview.html b/src/main/java/org/olat/course/nodes/bigbluebutton/_content/peekview.html new file mode 100644 index 00000000000..40b7a437fab --- /dev/null +++ b/src/main/java/org/olat/course/nodes/bigbluebutton/_content/peekview.html @@ -0,0 +1 @@ +$r.render("upcomingMeetings") diff --git a/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonManager.java b/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonManager.java index 2f417ea429a..b7c32977de8 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonManager.java +++ b/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonManager.java @@ -19,12 +19,14 @@ */ package org.olat.modules.bigbluebutton; +import java.util.Date; import java.util.List; import org.olat.core.id.Identity; import org.olat.group.BusinessGroup; import org.olat.modules.bigbluebutton.model.BigBlueButtonErrors; import org.olat.repository.RepositoryEntry; +import org.olat.repository.RepositoryEntryRef; /** * @@ -45,6 +47,18 @@ public interface BigBlueButtonManager { * @return A meeting with some default values */ public BigBlueButtonMeeting createAndPersistMeeting(String name, RepositoryEntry entry, String subIdent, BusinessGroup businessGroup); + + /** + * Is there a server available. + * + * @param template The selected template + * @param start Start date + * @param leadTime Lead time + * @param end End date + * @param followupTime Follow-up time + * @return true if the meeting can be reserved + */ + public boolean isSlotAvailable(BigBlueButtonMeetingTemplate template, Date start, long leadTime, Date end, long followupTime); public BigBlueButtonMeeting getMeeting(BigBlueButtonMeeting meeting); @@ -62,9 +76,17 @@ public interface BigBlueButtonManager { public boolean isTemplateInUse(BigBlueButtonMeetingTemplate template); - public List<BigBlueButtonMeeting> getMeetings(RepositoryEntry entry, String subIdent, BusinessGroup businessGroup); + public List<BigBlueButtonMeeting> getMeetings(RepositoryEntryRef entry, String subIdent, BusinessGroup businessGroup); + + /** + * Return the list of upcoming meetings, without any permanent one. + * + * @param entry The course / resource entry + * @param subIdent The sub identifier + * @return + */ + public List<BigBlueButtonMeeting> getUpcomingsMeetings(RepositoryEntryRef entry, String subIdent, int maxResults); - public List<BigBlueButtonMeeting> getAllMeetings(); public String join(BigBlueButtonMeeting meeting, Identity identity, boolean moderator, boolean guest, BigBlueButtonErrors errors); diff --git a/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonMeetingTemplate.java b/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonMeetingTemplate.java index d1b3edff097..7e3f5f72bcf 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonMeetingTemplate.java +++ b/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonMeetingTemplate.java @@ -44,6 +44,10 @@ public interface BigBlueButtonMeetingTemplate extends ModifiedInfo, CreateInfo { public void setDescription(String description); + public Integer getMaxConcurrentMeetings(); + + public void setMaxConcurrentMeetings(Integer maxConcurrentMeetings); + public Integer getMaxParticipants(); public void setMaxParticipants(Integer maxParticipants); diff --git a/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonModule.java b/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonModule.java index 157865ab772..232c98d797b 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonModule.java +++ b/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonModule.java @@ -51,6 +51,9 @@ public class BigBlueButtonModule extends AbstractSpringModule implements ConfigO private static final String PROP_PORT = "vc.bigbluebutton.port"; private static final String PROP_BASEURL = "vc.bigbluebutton.baseurl"; private static final String PROP_CONTEXTPATH = "vc.bigbluebutton.contextpath"; + private static final String PROP_PERMANENT_MEETING = "vc.bigbluebutton.permanent.meeting"; + private static final String PROP_ADHOC_MEETING = "vc.bigbluebutton.adhoc.meeting"; + private static final String PROP_USER_BANDWIDTH_REQUIREMENT = "vc.bigbluebutton.user.bandwidth.requirement"; @Value("${vc.bigbluebutton.enabled}") private boolean enabled; @@ -77,6 +80,13 @@ public class BigBlueButtonModule extends AbstractSpringModule implements ConfigO @Value("${vc.bigbluebutton.shared.secret}") private String sharedSecret; + @Value("${vc.bigbluebutton.permanent.meeting:false}") + private String permanentMeetingEnabled; + @Value("${vc.bigbluebutton.adhoc.meeting:true}") + private String adhocMeetingEnabled; + @Value("${vc.bigbluebutton.user.bandwidth.requirement:0.4}") + private Double userBandwidhtRequirement; + @Autowired public BigBlueButtonModule(CoordinatorManager coordinatorManager) { @@ -101,6 +111,11 @@ public class BigBlueButtonModule extends AbstractSpringModule implements ConfigO daysToKeep = getStringPropertyValue(PROP_DAYS_TO_KEEP, daysToKeep); secret = getStringPropertyValue(PROP_SECRET, secret); sharedSecret = getStringPropertyValue(PROP_SHARED_SECRET, sharedSecret); + + String bandwidthReqObj = getStringPropertyValue(PROP_USER_BANDWIDTH_REQUIREMENT, true); + if(StringHelper.containsNonWhitespace(bandwidthReqObj)) { + userBandwidhtRequirement = Double.parseDouble(bandwidthReqObj); + } } @Override @@ -267,4 +282,30 @@ public class BigBlueButtonModule extends AbstractSpringModule implements ConfigO } + public Double getUserBandwidhtRequirement() { + return userBandwidhtRequirement; + } + + public void setUserBandwidhtRequirement(Double userBandwidhtRequirement) { + this.userBandwidhtRequirement = userBandwidhtRequirement; + setStringProperty(PROP_USER_BANDWIDTH_REQUIREMENT, userBandwidhtRequirement.toString(), true); + } + + public boolean isPermanentMeetingEnabled() { + return "true".equals(permanentMeetingEnabled); + } + + public void setPermanentMeetingEnabled(boolean permanentMeetingEnabled) { + this.permanentMeetingEnabled = Boolean.toString(permanentMeetingEnabled); + setStringProperty(PROP_PERMANENT_MEETING, this.permanentMeetingEnabled, true); + } + + public boolean isAdhocMeetingEnabled() { + return "true".equals(adhocMeetingEnabled); + } + + public void setAdhocMeetingEnabled(boolean adhocMeetingEnabled) { + this.adhocMeetingEnabled = Boolean.toString(adhocMeetingEnabled); + setStringProperty(PROP_ADHOC_MEETING, this.adhocMeetingEnabled, true); + } } diff --git a/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonManagerImpl.java b/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonManagerImpl.java index 8d6a29d7bb0..40bddc823c2 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonManagerImpl.java +++ b/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonManagerImpl.java @@ -20,6 +20,7 @@ package org.olat.modules.bigbluebutton.manager; import java.net.URI; +import java.util.Date; import java.util.List; import org.apache.http.client.methods.CloseableHttpResponse; @@ -51,6 +52,7 @@ import org.olat.modules.bigbluebutton.model.BigBlueButtonError; import org.olat.modules.bigbluebutton.model.BigBlueButtonErrorCodes; import org.olat.modules.bigbluebutton.model.BigBlueButtonErrors; import org.olat.repository.RepositoryEntry; +import org.olat.repository.RepositoryEntryRef; import org.olat.repository.manager.RepositoryEntryDAO; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; @@ -84,32 +86,32 @@ public class BigBlueButtonManagerImpl implements BigBlueButtonManager, Initializ List<BigBlueButtonMeetingTemplate> templates = bigBlueButtonMeetingTemplateDao.getTemplates(); // Web conferen - defaultTemplate("web-conference", "Web conference", 100, + defaultTemplate("sys-meetings", "Meetings", 5, 5, Boolean.FALSE, Boolean.FALSE, Boolean.TRUE, // recording - Boolean.TRUE, Boolean.TRUE, // webcams, unmute - Boolean.TRUE, Boolean.TRUE, // cam, mic + Boolean.FALSE, Boolean.TRUE, // webcams moderator only, unmute + Boolean.FALSE, Boolean.FALSE, // cam, mic Boolean.FALSE, Boolean.TRUE, // chat Boolean.FALSE, Boolean.FALSE, // node, layout - GuestPolicyEnum.ALWAYS_ACCEPT, templates); + GuestPolicyEnum.ALWAYS_DENY, templates); - defaultTemplate("web-classe", "Classes / Klasse", 25, + defaultTemplate("sys-classes", "Classes", 20, 30, Boolean.FALSE, Boolean.FALSE, Boolean.TRUE, // recording - Boolean.FALSE, Boolean.TRUE, // webcams, unmute - Boolean.FALSE, Boolean.FALSE, // cam, mic + Boolean.TRUE, Boolean.TRUE, // webcamsmoderator only, unmute + Boolean.TRUE, Boolean.TRUE, // cam, mic Boolean.FALSE, Boolean.FALSE, // chat Boolean.FALSE, Boolean.FALSE, // node, layout GuestPolicyEnum.ALWAYS_DENY, templates); - defaultTemplate("web-one-to-one", "One to one", 2, + defaultTemplate("sys-cafe", "Cafe", 10, 10, Boolean.FALSE, Boolean.FALSE, Boolean.TRUE, // recording - Boolean.FALSE, Boolean.TRUE, // webcams, unmute + Boolean.FALSE, Boolean.TRUE, // webcams moderator only, unmute Boolean.FALSE, Boolean.FALSE, // cam, mic Boolean.TRUE, Boolean.FALSE, // chat Boolean.FALSE, Boolean.FALSE, // node, layout GuestPolicyEnum.ALWAYS_DENY, templates); } - private void defaultTemplate(String externalId, String name, Integer maxParticipants, + private void defaultTemplate(String externalId, String name, Integer maxConcurrentMeetings, Integer maxParticipants, Boolean muteOnStart, Boolean autoStartRecording, Boolean allowStartStopRecording, Boolean webcamsOnlyForModerator, Boolean allowModsToUnmuteUsers, Boolean lockSettingsDisableCam, Boolean lockSettingsDisableMic, @@ -120,9 +122,12 @@ public class BigBlueButtonManagerImpl implements BigBlueButtonManager, Initializ BigBlueButtonMeetingTemplate template = templates.stream() .filter(tpl -> externalId.equals(tpl.getExternalId())) .findFirst().orElse(null); - if(template == null) { - template = bigBlueButtonMeetingTemplateDao.createTemplate(name, externalId, true); + if(template != null) { + return; } + + template = bigBlueButtonMeetingTemplateDao.createTemplate(name, externalId, true); + template.setMaxConcurrentMeetings(maxConcurrentMeetings); template.setMaxParticipants(maxParticipants); template.setMuteOnStart(muteOnStart); template.setAutoStartRecording(autoStartRecording); @@ -144,6 +149,18 @@ public class BigBlueButtonManagerImpl implements BigBlueButtonManager, Initializ return bigBlueButtonMeetingDao.createAndPersistMeeting(name, entry, subIdent, businessGroup); } + @Override + public boolean isSlotAvailable(BigBlueButtonMeetingTemplate template, Date startDate, long leadTime, Date endDate, long followupTime) { + if(template == null) return false; // template are mandatory + if(template.getMaxConcurrentMeetings() == null) { + return true; + } + Date start = bigBlueButtonMeetingDao.calculateStartWithLeadTime(startDate, leadTime); + Date end = bigBlueButtonMeetingDao.calculateEndWithFollowupTime(endDate, followupTime); + int numOfCurrentMeetings = bigBlueButtonMeetingDao.getConcurrentMeetings(template, start, end); + return numOfCurrentMeetings < template.getMaxConcurrentMeetings().intValue(); + } + @Override public BigBlueButtonMeeting getMeeting(BigBlueButtonMeeting meeting) { return bigBlueButtonMeetingDao.loadByKey(meeting.getKey()); @@ -186,10 +203,15 @@ public class BigBlueButtonManagerImpl implements BigBlueButtonManager, Initializ } @Override - public List<BigBlueButtonMeeting> getMeetings(RepositoryEntry entry, String subIdent, BusinessGroup businessGroup) { + public List<BigBlueButtonMeeting> getMeetings(RepositoryEntryRef entry, String subIdent, BusinessGroup businessGroup) { return bigBlueButtonMeetingDao.getMeetings(entry, subIdent, businessGroup); } + @Override + public List<BigBlueButtonMeeting> getUpcomingsMeetings(RepositoryEntryRef entry, String subIdent, int maxResults) { + return bigBlueButtonMeetingDao.getUpcomingMeetings(entry, subIdent, maxResults); + } + @Override public boolean deleteMeeting(BigBlueButtonMeeting meeting, BigBlueButtonErrors errors) { BigBlueButtonMeeting reloadedMeeting = bigBlueButtonMeetingDao.loadByKey(meeting.getKey()); @@ -346,10 +368,11 @@ public class BigBlueButtonManagerImpl implements BigBlueButtonManager, Initializ .optionalParameter("moderatorPW", meeting.getModeratorPassword()) .optionalParameter("logoutURL", getBusinessPath(meeting)); if(meeting.getStartWithLeadTime() != null && meeting.getEndWithFollowupTime() != null) { - long start = meeting.getStartWithLeadTime().getTime(); + long now = new Date().getTime(); + long start = Math.max(now, meeting.getStartWithLeadTime().getTime()); long end = meeting.getEndWithFollowupTime().getTime(); - long duration = (end - start) / (60l * 1000l); - uriBuilder.optionalParameter("duration", Long.toString(duration + 1));// + 1 for rounding error + long duration = 1 + (Math.abs(end - start) / (60l * 1000l));// + 1 to compensate rounding error + uriBuilder.optionalParameter("duration", Long.toString(duration)); } if(template != null) { @@ -377,6 +400,16 @@ public class BigBlueButtonManagerImpl implements BigBlueButtonManager, Initializ return BigBlueButtonUtils.checkSuccess(doc, errors); } + public void getBigBlueButtonDefaultConfigXml() { + BigBlueButtonUriBuilder uriBuilder = getUriBuilder(); + uriBuilder + .operation("getDefaultConfigXML"); + BigBlueButtonErrors errors = new BigBlueButtonErrors(); + Document doc = sendRequest(uriBuilder, errors); + BigBlueButtonUtils.print(doc); + BigBlueButtonUtils.checkSuccess(doc, errors); + } + @Override public boolean checkConnection(String url, String sharedSecret, BigBlueButtonErrors errors) { BigBlueButtonUriBuilder uriBuilder = BigBlueButtonUriBuilder.fromUri(URI.create(url), sharedSecret); diff --git a/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonMeetingDAO.java b/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonMeetingDAO.java index 94540374f49..58fbf824e11 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonMeetingDAO.java +++ b/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonMeetingDAO.java @@ -31,8 +31,10 @@ import org.olat.core.commons.persistence.QueryBuilder; import org.olat.core.util.StringHelper; import org.olat.group.BusinessGroup; import org.olat.modules.bigbluebutton.BigBlueButtonMeeting; +import org.olat.modules.bigbluebutton.BigBlueButtonMeetingTemplate; import org.olat.modules.bigbluebutton.model.BigBlueButtonMeetingImpl; import org.olat.repository.RepositoryEntry; +import org.olat.repository.RepositoryEntryRef; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -93,19 +95,14 @@ public class BigBlueButtonMeetingDAO { } private void updateDates(BigBlueButtonMeetingImpl meet, Date start, long leadTime, Date end, long followupTime) { - Calendar cal = Calendar.getInstance(); if(start == null) { meet.setStartDate(null); meet.setLeadTime(0); meet.setStartWithLeadTime(null); } else { - start = cleanDate(start); - if(leadTime > 0) { - cal.add(Calendar.MINUTE, -(int)leadTime); - } meet.setStartDate(start); meet.setLeadTime(leadTime); - meet.setStartWithLeadTime(cal.getTime()); + meet.setStartWithLeadTime(calculateStartWithLeadTime(start, leadTime)); } if(end == null) { @@ -113,15 +110,30 @@ public class BigBlueButtonMeetingDAO { meet.setFollowupTime(0); meet.setEndWithFollowupTime(null); } else { - end = cleanDate(end); - cal.setTime(end); - if(followupTime > 0) { - cal.add(Calendar.MINUTE, (int)followupTime); - } meet.setEndDate(end); meet.setFollowupTime(followupTime); - meet.setEndWithFollowupTime(cal.getTime()); + meet.setEndWithFollowupTime(calculateEndWithFollowupTime(end, followupTime)); + } + } + + protected Date calculateStartWithLeadTime(Date start, long leadTime) { + start = cleanDate(start); + Calendar cal = Calendar.getInstance(); + cal.setTime(start); + if(leadTime > 0) { + cal.add(Calendar.MINUTE, -(int)leadTime); } + return cal.getTime(); + } + + protected Date calculateEndWithFollowupTime(Date end, long followupTime) { + end = cleanDate(end); + Calendar cal = Calendar.getInstance(); + cal.setTime(end); + if(followupTime > 0) { + cal.add(Calendar.MINUTE, (int)followupTime); + } + return cal.getTime(); } /** @@ -149,7 +161,30 @@ public class BigBlueButtonMeetingDAO { .getResultList(); } - public List<BigBlueButtonMeeting> getMeetings(RepositoryEntry entry, String subIdent, BusinessGroup businessGroup) { + public int getConcurrentMeetings(BigBlueButtonMeetingTemplate template, Date start, Date end) { + QueryBuilder sb = new QueryBuilder(); + sb.append("select count(distinct meeting.key) from bigbluebuttonmeeting as meeting") + .append(" inner join meeting.template as template") + .append(" where template.key=:templateKey") + .append(" and (") + .append(" (meeting.startWithLeadTime>=:startDate and meeting.startWithLeadTime<=:endDate)") + .append(" or") + .append(" (meeting.endWithFollowupTime>=:startDate and meeting.endWithFollowupTime<=:endDate)") + .append(" or") + .append(" (meeting.startWithLeadTime>=:startDate and meeting.endWithFollowupTime<=:endDate)") + .append(" or") + .append(" (meeting.startWithLeadTime<=:startDate and meeting.endWithFollowupTime>=:endDate)") + .append(")"); + List<Long> count = dbInstance.getCurrentEntityManager() + .createQuery(sb.toString(), Long.class) + .setParameter("templateKey", template.getKey()) + .setParameter("startDate", start) + .setParameter("endDate", end) + .getResultList(); + return count == null || count.isEmpty() || count.get(0) == null ? 0 : count.get(0).intValue(); + } + + public List<BigBlueButtonMeeting> getMeetings(RepositoryEntryRef entry, String subIdent, BusinessGroup businessGroup) { QueryBuilder sb = new QueryBuilder(); sb.append("select meeting from bigbluebuttonmeeting as meeting") .append(" left join fetch meeting.template as template"); @@ -168,7 +203,6 @@ public class BigBlueButtonMeetingDAO { if(entry != null) { query.setParameter("entryKey", entry.getKey()); - sb.and().append("meeting.entry.key=:entryKey"); if(StringHelper.containsNonWhitespace(subIdent)) { query.setParameter("subIdent", subIdent); } @@ -178,5 +212,29 @@ public class BigBlueButtonMeetingDAO { } return query.getResultList(); } + + public List<BigBlueButtonMeeting> getUpcomingMeetings(RepositoryEntryRef entry, String subIdent, int maxResults) { + QueryBuilder sb = new QueryBuilder(); + sb.append("select meeting from bigbluebuttonmeeting as meeting") + .append(" left join fetch meeting.template as template") + .append(" where meeting.entry.key=:entryKey and meeting.permanent=false") + .append(" and meeting.startDate is not null and meeting.endDate is not null"); + if(StringHelper.containsNonWhitespace(subIdent)) { + sb.append(" and meeting.subIdent=:subIdent"); + } + sb.append(" and meeting.endDate>=:now") + .append(" order by meeting.startDate asc"); + + TypedQuery<BigBlueButtonMeeting> query = dbInstance.getCurrentEntityManager() + .createQuery(sb.toString(), BigBlueButtonMeeting.class) + .setFirstResult(0) + .setMaxResults(maxResults) + .setParameter("entryKey", entry.getKey()) + .setParameter("now", new Date()); + if(StringHelper.containsNonWhitespace(subIdent)) { + query.setParameter("subIdent", subIdent); + } + return query.getResultList(); + } } diff --git a/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonUtils.java b/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonUtils.java index e6010d3a5c6..1d45442fac6 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonUtils.java +++ b/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonUtils.java @@ -131,5 +131,4 @@ public class BigBlueButtonUtils { } } } - } diff --git a/src/main/java/org/olat/modules/bigbluebutton/model/BigBlueButtonMeetingTemplateImpl.java b/src/main/java/org/olat/modules/bigbluebutton/model/BigBlueButtonMeetingTemplateImpl.java index 4c97189530b..ccef9cdc0b2 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/model/BigBlueButtonMeetingTemplateImpl.java +++ b/src/main/java/org/olat/modules/bigbluebutton/model/BigBlueButtonMeetingTemplateImpl.java @@ -68,6 +68,9 @@ public class BigBlueButtonMeetingTemplateImpl implements Persistable, BigBlueBut @Column(name="b_external_id", nullable=true, insertable=true, updatable=true) private String externalId; + @Column(name="b_max_concurrent_meetings", nullable=true, insertable=true, updatable=true) + private Integer maxConcurrentMeetings; + @Column(name="b_max_participants", nullable=true, insertable=true, updatable=true) private Integer maxParticipants; @@ -165,6 +168,16 @@ public class BigBlueButtonMeetingTemplateImpl implements Persistable, BigBlueBut this.externalId = externalId; } + @Override + public Integer getMaxConcurrentMeetings() { + return maxConcurrentMeetings; + } + + @Override + public void setMaxConcurrentMeetings(Integer maxConcurrentMeetings) { + this.maxConcurrentMeetings = maxConcurrentMeetings; + } + @Override public Integer getMaxParticipants() { return maxParticipants; diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonAdminController.java b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonAdminController.java index 868fe75fa49..1a94e6f04a2 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonAdminController.java +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonAdminController.java @@ -33,6 +33,7 @@ import org.olat.core.gui.control.Event; import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.control.controller.BasicController; import org.olat.core.gui.control.generic.dtabs.Activateable2; +import org.olat.core.id.Roles; import org.olat.core.id.context.ContextEntry; import org.olat.core.id.context.StateEntry; import org.olat.core.util.resource.OresHelper; @@ -45,12 +46,14 @@ import org.olat.core.util.resource.OresHelper; */ public class BigBlueButtonAdminController extends BasicController implements Activateable2 { + private Link configurationLink; private final Link meetingsLink; private final Link templatesLink; - private final Link configurationLink; private final SegmentViewComponent segmentView; private final VelocityContainer mainVC; + private final boolean configurationReadOnly; + private BigBlueButtonConfigurationController configCtrl; private BigBlueButtonAdminMeetingsController meetingsCtrl; private BigBlueButtonAdminTemplatesController templatesCtrl; @@ -58,17 +61,27 @@ public class BigBlueButtonAdminController extends BasicController implements Act public BigBlueButtonAdminController(UserRequest ureq, WindowControl wControl) { super(ureq, wControl); + Roles roles = ureq.getUserSession().getRoles(); + configurationReadOnly = !roles.isAdministrator() && !roles.isSystemAdmin(); + mainVC = createVelocityContainer("bbb_admin"); - segmentView = SegmentViewFactory.createSegmentView("segments", mainVC, this); - configurationLink = LinkFactory.createLink("account.configuration", mainVC, this); - segmentView.addSegment(configurationLink, true); + segmentView = SegmentViewFactory.createSegmentView("segments", mainVC, this); + if(!configurationReadOnly) { + configurationLink = LinkFactory.createLink("account.configuration", mainVC, this); + segmentView.addSegment(configurationLink, true); + } templatesLink = LinkFactory.createLink("templates.title", mainVC, this); segmentView.addSegment(templatesLink, false); meetingsLink = LinkFactory.createLink("meetings.title", mainVC, this); segmentView.addSegment(meetingsLink, false); - doOpenConfiguration(ureq); + if(configurationReadOnly) { + doOpenMeetings(ureq); + segmentView.select(meetingsLink); + } else { + doOpenConfiguration(ureq); + } putInitialPanel(mainVC); } @@ -83,7 +96,7 @@ public class BigBlueButtonAdminController extends BasicController implements Act if(entries == null || entries.isEmpty()) return; String type = entries.get(0).getOLATResourceable().getResourceableTypeName(); - if("Configuration".equalsIgnoreCase(type)) { + if("Configuration".equalsIgnoreCase(type) && !configurationReadOnly) { doOpenConfiguration(ureq); segmentView.select(configurationLink); } else if("Templates".equalsIgnoreCase(type)) { @@ -127,7 +140,7 @@ public class BigBlueButtonAdminController extends BasicController implements Act private void doOpenTemplates(UserRequest ureq) { if(templatesCtrl == null) { WindowControl bwControl = addToHistory(ureq, OresHelper.createOLATResourceableInstance("Templates", 0l), null); - templatesCtrl = new BigBlueButtonAdminTemplatesController(ureq, bwControl); + templatesCtrl = new BigBlueButtonAdminTemplatesController(ureq, bwControl, configurationReadOnly); listenTo(templatesCtrl); } else { addToHistory(ureq, templatesCtrl); diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonAdminMeetingsController.java b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonAdminMeetingsController.java index 071ff0065b4..b7270325c89 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonAdminMeetingsController.java +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonAdminMeetingsController.java @@ -45,6 +45,7 @@ import org.olat.core.gui.control.generic.modal.DialogBoxUIFactory; import org.olat.core.util.StringHelper; import org.olat.modules.bigbluebutton.BigBlueButtonManager; import org.olat.modules.bigbluebutton.BigBlueButtonMeeting; +import org.olat.modules.bigbluebutton.BigBlueButtonModule; import org.olat.modules.bigbluebutton.model.BigBlueButtonErrors; import org.olat.modules.bigbluebutton.ui.BigBlueButtonMeetingTableModel.BMeetingsCols; import org.olat.modules.gotomeeting.ui.GoToMeetingTableModel.MeetingsCols; @@ -62,7 +63,9 @@ public class BigBlueButtonAdminMeetingsController extends FormBasicController { private BigBlueButtonMeetingTableModel tableModel; private DialogBoxController confirmDelete; - + + @Autowired + private BigBlueButtonModule bigBlueButtonModule; @Autowired private BigBlueButtonManager bigBlueButtonManager; @@ -77,9 +80,10 @@ public class BigBlueButtonAdminMeetingsController extends FormBasicController { protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel(); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(BMeetingsCols.name)); - columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(BMeetingsCols.permanent)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(bigBlueButtonModule.isPermanentMeetingEnabled(), BMeetingsCols.permanent)); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(BMeetingsCols.start)); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(BMeetingsCols.end)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(BMeetingsCols.template)); FlexiCellRenderer renderer = new StaticFlexiCellRenderer("resource", new TextFlexiCellRenderer()); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(BMeetingsCols.resource.i18nHeaderKey(), BMeetingsCols.resource.ordinal(), "resource", true, BMeetingsCols.resource.name(), renderer)); diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonAdminTemplatesController.java b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonAdminTemplatesController.java index 5f1b083e3bc..58a22151c9e 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonAdminTemplatesController.java +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonAdminTemplatesController.java @@ -62,11 +62,14 @@ public class BigBlueButtonAdminTemplatesController extends FormBasicController { private DialogBoxController confirmDelete; private EditBigBlueButtonTemplateController editTemplateCtlr; + private final boolean readOnly; + @Autowired private BigBlueButtonManager bigBlueButtonManager; - public BigBlueButtonAdminTemplatesController(UserRequest ureq, WindowControl wControl) { + public BigBlueButtonAdminTemplatesController(UserRequest ureq, WindowControl wControl, boolean readOnly) { super(ureq, wControl, "templates_admin"); + this.readOnly = readOnly; initForm(ureq); updateModel(); } @@ -74,14 +77,22 @@ public class BigBlueButtonAdminTemplatesController extends FormBasicController { @Override protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { addTemplateButton = uifactory.addFormLink("add.template", formLayout, Link.BUTTON); + addTemplateButton.setVisible(!readOnly); //add the table FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel(); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(BTemplatesCols.name)); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(BTemplatesCols.system)); - columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel("edit", translate("edit"), "edit")); - columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel("delete", BTemplatesCols.system.ordinal(), "delete", - new BooleanCellRenderer(null, new StaticFlexiCellRenderer(translate("delete"), "delete")))); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(BTemplatesCols.maxConcurrentMeetings)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(BTemplatesCols.maxParticipants)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(BTemplatesCols.webcamsOnlyForModerator)); + if(readOnly) { + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel("view", translate("view"), "view")); + } else { + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel("edit", translate("edit"), "edit")); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel("delete", BTemplatesCols.system.ordinal(), "delete", + new BooleanCellRenderer(null, new StaticFlexiCellRenderer(translate("delete"), "delete")))); + } tableModel = new BigBlueButtonTemplateTableModel(columnsModel, getLocale()); tableEl = uifactory.addTableElement(getWindowControl(), "templates", tableModel, getTranslator(), formLayout); @@ -141,7 +152,7 @@ public class BigBlueButtonAdminTemplatesController extends FormBasicController { } else if(tableEl == source) { if(event instanceof SelectionEvent) { SelectionEvent se = (SelectionEvent)event; - if("edit".equals(se.getCommand())) { + if("edit".equals(se.getCommand()) || "view".equals(se.getCommand())) { doEditTemplate(ureq, tableModel.getObject(se.getIndex())); } else if("delete".equals(se.getCommand())) { doConfirmDelete(ureq, tableModel.getObject(se.getIndex())); @@ -167,11 +178,16 @@ public class BigBlueButtonAdminTemplatesController extends FormBasicController { private void doEditTemplate(UserRequest ureq, BigBlueButtonMeetingTemplate template) { if(guardModalController(editTemplateCtlr)) return; - editTemplateCtlr = new EditBigBlueButtonTemplateController(ureq, getWindowControl(), template); + editTemplateCtlr = new EditBigBlueButtonTemplateController(ureq, getWindowControl(), template, readOnly); listenTo(editTemplateCtlr); - cmc = new CloseableModalController(getWindowControl(), "close", editTemplateCtlr.getInitialComponent(), - true, translate("edit.template", new String[] { template.getName() })); + String title; + if(readOnly) { + title = translate("view.template", new String[] { template.getName() }); + } else { + title = translate("edit.template", new String[] { template.getName() }); + } + cmc = new CloseableModalController(getWindowControl(), "close", editTemplateCtlr.getInitialComponent(), true, title); cmc.activate(); listenTo(cmc); } diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonConfigurationController.java b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonConfigurationController.java index 122d4c104e8..c3fde6fb9a7 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonConfigurationController.java +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonConfigurationController.java @@ -64,6 +64,8 @@ public class BigBlueButtonConfigurationController extends FormBasicController { private SingleSelection cleanMeetingsEl; private MultipleSelectionElement moduleEnabled; private MultipleSelectionElement enabledForEl; + private MultipleSelectionElement permanentForEl; + private MultipleSelectionElement adhocForEl; private static final String[] enabledKeys = new String[]{"on"}; private final String[] enabledValues; @@ -98,7 +100,13 @@ public class BigBlueButtonConfigurationController extends FormBasicController { enabledForEl = uifactory.addCheckboxesVertical("bigbluebutton.module.enabled.for", formLayout, FOR_KEYS, forValues, 1); enabledForEl.select(FOR_KEYS[0], bigBlueButtonModule.isCoursesEnabled()); enabledForEl.select(FOR_KEYS[1], bigBlueButtonModule.isGroupsEnabled()); - + + permanentForEl = uifactory.addCheckboxesHorizontal("enable.permanent.meeting", formLayout, enabledKeys, enabledValues); + permanentForEl.select(enabledKeys[0], bigBlueButtonModule.isPermanentMeetingEnabled()); + + adhocForEl = uifactory.addCheckboxesHorizontal("enable.adhoc.meeting", formLayout, enabledKeys, enabledValues); + adhocForEl.select(enabledKeys[0], bigBlueButtonModule.isAdhocMeetingEnabled()); + //spacer spacerEl = uifactory.addSpacerElement("spacer", formLayout, false); @@ -237,6 +245,9 @@ public class BigBlueButtonConfigurationController extends FormBasicController { bigBlueButtonModule.setBigBlueButtonURI(new URI(url)); bigBlueButtonModule.setCoursesEnabled(enabledForEl.isSelected(0)); bigBlueButtonModule.setGroupsEnabled(enabledForEl.isSelected(1)); + bigBlueButtonModule.setPermanentMeetingEnabled(permanentForEl.isAtLeastSelected(1)); + bigBlueButtonModule.setAdhocMeetingEnabled(adhocForEl.isAtLeastSelected(1)); + if(cleanMeetingsEl.isSelected(0)) { bigBlueButtonModule.setCleanupMeetings(false); bigBlueButtonModule.setDaysToKeep(null); diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonEditMeetingsController.java b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonEditMeetingsController.java index f75a5486f1a..ffaf19c6c2f 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonEditMeetingsController.java +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonEditMeetingsController.java @@ -44,6 +44,7 @@ import org.olat.core.gui.control.generic.modal.DialogBoxUIFactory; import org.olat.group.BusinessGroup; import org.olat.modules.bigbluebutton.BigBlueButtonManager; import org.olat.modules.bigbluebutton.BigBlueButtonMeeting; +import org.olat.modules.bigbluebutton.BigBlueButtonModule; import org.olat.modules.bigbluebutton.model.BigBlueButtonErrors; import org.olat.modules.bigbluebutton.ui.BigBlueButtonMeetingTableModel.BMeetingsCols; import org.olat.modules.gotomeeting.ui.GoToMeetingTableModel.MeetingsCols; @@ -70,7 +71,9 @@ public class BigBlueButtonEditMeetingsController extends FormBasicController { private final String subIdent; private final RepositoryEntry entry; private final BusinessGroup businessGroup; - + + @Autowired + private BigBlueButtonModule bigBlueButtonModule; @Autowired private BigBlueButtonManager bigBlueButtonManager; @@ -94,9 +97,12 @@ public class BigBlueButtonEditMeetingsController extends FormBasicController { //add the table FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel(); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(BMeetingsCols.name)); - columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(BMeetingsCols.permanent)); + if(bigBlueButtonModule.isPermanentMeetingEnabled()) { + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(BMeetingsCols.permanent)); + } columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(BMeetingsCols.start)); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(BMeetingsCols.end)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(BMeetingsCols.template)); if(!readOnly) { columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel("edit", translate("edit"), "edit")); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel("delete", translate("delete"), "delete")); diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonMeetingController.java b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonMeetingController.java index 4ed37f2d105..90e7dd17927 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonMeetingController.java +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonMeetingController.java @@ -41,6 +41,7 @@ import org.olat.core.util.event.GenericEventListener; import org.olat.core.util.resource.OresHelper; import org.olat.modules.bigbluebutton.BigBlueButtonManager; import org.olat.modules.bigbluebutton.BigBlueButtonMeeting; +import org.olat.modules.bigbluebutton.BigBlueButtonModule; import org.olat.modules.bigbluebutton.model.BigBlueButtonErrors; import org.springframework.beans.factory.annotation.Autowired; @@ -57,12 +58,13 @@ public class BigBlueButtonMeetingController extends FormBasicController implemen private final boolean administrator; private BigBlueButtonMeeting meeting; - private final boolean userMeetingsDates; private final boolean moderatorStartMeeting; private final OLATResourceable meetingOres; private Link joinButton; + @Autowired + private BigBlueButtonModule bigBlueButtonModule; @Autowired private BigBlueButtonManager bigBlueButtonManager; @@ -76,12 +78,9 @@ public class BigBlueButtonMeetingController extends FormBasicController implemen this.administrator = administrator; meetingOres = OresHelper.createOLATResourceableInstance(BigBlueButtonMeeting.class.getSimpleName(), meeting.getKey()); CoordinatorManager.getInstance().getCoordinator().getEventBus().registerFor(this, getIdentity(), meetingOres); - - userMeetingsDates = !meeting.isPermanent(); moderatorStartMeeting = configuration.isModeratorStartMeeting(); initForm(ureq); - updateButtonsAndStatus(); } @@ -106,21 +105,21 @@ public class BigBlueButtonMeetingController extends FormBasicController implemen joinButton = LinkFactory.createButtonLarge("meeting.join.button", flc.getFormItemComponent(), this); joinButton.setTarget("_blank"); - joinButton.setVisible(!ended || moderator || administrator); + joinButton.setVisible(!ended); } private boolean isEnded() { return meeting != null && meeting.getEndDate() != null && new Date().after(meeting.getEndDate()); } - private boolean isValidDates() { - if(!userMeetingsDates) { - return true; + private boolean isAccessible() { + if(meeting.isPermanent()) { + return bigBlueButtonModule.isPermanentMeetingEnabled(); } + Date now = new Date(); Date start = meeting.getStartWithLeadTime(); Date end = meeting.getEndWithFollowupTime(); - return !((start != null && start.compareTo(now) >= 0) || (end != null && end.compareTo(now) <= 0)); } @@ -130,42 +129,31 @@ public class BigBlueButtonMeetingController extends FormBasicController implemen } private void updateButtonsAndStatus() { - boolean meetingsExists = StringHelper.containsNonWhitespace(meeting.getMeetingId()); boolean isEnded = isEnded(); - - flc.contextPut("meetingsExists", Boolean.valueOf(meetingsExists)); + boolean accessible = isAccessible(); flc.contextPut("ended", Boolean.valueOf(isEnded)); - - boolean accessible = !isEnded() || administrator || moderator; - boolean running = bigBlueButtonManager.isMeetingRunning(meeting); - if(moderator || administrator) { - joinButton.setVisible(accessible); - joinButton.setEnabled(!readOnly); + flc.contextPut("notStarted", Boolean.TRUE); + joinButton.setVisible(accessible); + joinButton.setEnabled(!readOnly); - if(!running && moderatorStartMeeting) { - joinButton.setCustomDisplayText(translate("meeting.start.button")); - } else if(isValidDates()) { - joinButton.setCustomDisplayText(translate("meeting.join.button")); - } else { - joinButton.setCustomDisplayText(translate("meeting.go.button")); - } - } else { - boolean validDates = isValidDates(); - - joinButton.setVisible(accessible); - if(!running && moderatorStartMeeting) { + if(accessible) { + boolean running = bigBlueButtonManager.isMeetingRunning(meeting); + if(moderator || administrator) { + flc.contextPut("notStarted", Boolean.FALSE); + if(!running && moderatorStartMeeting) { + joinButton.setCustomDisplayText(translate("meeting.start.button")); + } else { + joinButton.setCustomDisplayText(translate("meeting.join.button")); + } + } else if(!running && moderatorStartMeeting) { + flc.contextPut("notStarted", Boolean.TRUE); joinButton.setEnabled(false); } else { - joinButton.setEnabled(!readOnly && validDates); - } - - if(validDates && !running && moderatorStartMeeting) { - flc.contextPut("notStarted", Boolean.TRUE); - } else if(validDates || isEnded) { flc.contextPut("notStarted", Boolean.FALSE); - } else { - flc.contextPut("notStarted", Boolean.TRUE); + joinButton.setEnabled(!readOnly); } + } else if(isEnded) { + flc.contextPut("notStarted", Boolean.FALSE); } } diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonMeetingTableModel.java b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonMeetingTableModel.java index 983104ad20e..cb1f53b8174 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonMeetingTableModel.java +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonMeetingTableModel.java @@ -29,6 +29,7 @@ import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTable import org.olat.core.gui.components.form.flexible.impl.elements.table.SortableFlexiTableDataModel; import org.olat.core.gui.components.form.flexible.impl.elements.table.SortableFlexiTableModelDelegate; import org.olat.modules.bigbluebutton.BigBlueButtonMeeting; +import org.olat.modules.bigbluebutton.BigBlueButtonMeetingTemplate; /** * @@ -69,11 +70,17 @@ implements SortableFlexiTableDataModel<BigBlueButtonMeeting> { case permanent: return Boolean.valueOf(row.isPermanent()); case start: return row.getStartDate(); case end: return row.getEndDate(); + case template: return getTemplate(row); case resource: return getResourceName(row); default: return "ERROR"; } } + private String getTemplate(BigBlueButtonMeeting row) { + BigBlueButtonMeetingTemplate template = row.getTemplate(); + return template == null ? null: template.getName(); + } + private String getResourceName(BigBlueButtonMeeting row) { String displayName = null; if(row.getEntry() != null) { @@ -95,6 +102,7 @@ implements SortableFlexiTableDataModel<BigBlueButtonMeeting> { permanent("table.header.permanent"), start("meeting.start"), end("meeting.end"), + template("table.header.template"), resource("meeting.resource"); private final String i18nHeaderKey; diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonMeetingsController.java b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonMeetingsController.java index 3dec4134a52..0d9daf74c94 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonMeetingsController.java +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonMeetingsController.java @@ -41,6 +41,7 @@ import org.olat.core.gui.control.WindowControl; import org.olat.group.BusinessGroup; import org.olat.modules.bigbluebutton.BigBlueButtonManager; import org.olat.modules.bigbluebutton.BigBlueButtonMeeting; +import org.olat.modules.bigbluebutton.BigBlueButtonModule; import org.olat.modules.bigbluebutton.ui.BigBlueButtonMeetingTableModel.BMeetingsCols; import org.olat.repository.RepositoryEntry; import org.springframework.beans.factory.annotation.Autowired; @@ -62,7 +63,9 @@ public class BigBlueButtonMeetingsController extends FormBasicController { private final String subIdent; private final BusinessGroup businessGroup; private final boolean showPermanentCol; - + + @Autowired + private BigBlueButtonModule bigBlueButtonModule; @Autowired private BigBlueButtonManager bigBlueButtonManager; @@ -72,7 +75,7 @@ public class BigBlueButtonMeetingsController extends FormBasicController { this.entry = entry; this.subIdent = subIdent; this.businessGroup = businessGroup; - showPermanentCol = (administrator || moderator); + showPermanentCol = (administrator || moderator) && bigBlueButtonModule.isPermanentMeetingEnabled(); initForm(ureq); updateModel(); @@ -95,7 +98,7 @@ public class BigBlueButtonMeetingsController extends FormBasicController { upcomingTableEl.setEmtpyTableMessageKey("no.upcoming.meetings"); FlexiTableSortOptions sortOptions = new FlexiTableSortOptions(); - sortOptions.setDefaultOrderBy(new SortKey(BMeetingsCols.start.name(), false)); + sortOptions.setDefaultOrderBy(new SortKey(BMeetingsCols.start.name(), true)); upcomingTableEl.setSortSettings(sortOptions); upcomingTableEl.setAndLoadPersistedPreferences(ureq, "big-blue-button-upcoming-meetings-list"); @@ -113,7 +116,7 @@ public class BigBlueButtonMeetingsController extends FormBasicController { pastTableEl = uifactory.addTableElement(getWindowControl(), "pastMeetings", pastTableModel, getTranslator(), formLayout); FlexiTableSortOptions pastSortOptions = new FlexiTableSortOptions(); - pastSortOptions.setDefaultOrderBy(new SortKey(BMeetingsCols.start.name(), false)); + pastSortOptions.setDefaultOrderBy(new SortKey(BMeetingsCols.start.name(), true)); pastTableEl.setSortSettings(pastSortOptions); pastTableEl.setAndLoadPersistedPreferences(ureq, "big-blue-button-past-meetings-list"); } diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonTemplateTableModel.java b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonTemplateTableModel.java index dd1cdc551d7..65f46b2dadc 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonTemplateTableModel.java +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonTemplateTableModel.java @@ -67,6 +67,9 @@ implements SortableFlexiTableDataModel<BigBlueButtonMeetingTemplate> { switch(COLS[col]) { case name: return row.getName(); case system: return Boolean.valueOf(row.isSystem()); + case maxConcurrentMeetings: return row.getMaxConcurrentMeetings(); + case maxParticipants: return row.getMaxParticipants(); + case webcamsOnlyForModerator: return row.getWebcamsOnlyForModerator(); default: return "ERROR"; } } @@ -79,7 +82,10 @@ implements SortableFlexiTableDataModel<BigBlueButtonMeetingTemplate> { public enum BTemplatesCols implements FlexiSortableColumnDef { name("meeting.name"), - system("table.header.system"); + system("table.header.system"), + maxConcurrentMeetings("table.header.max.concurrent.meetings"), + maxParticipants("table.header.max.participants"), + webcamsOnlyForModerator("table.header.webcams.only.moderator"); private final String i18nHeaderKey; diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/EditBigBlueButtonMeetingController.java b/src/main/java/org/olat/modules/bigbluebutton/ui/EditBigBlueButtonMeetingController.java index 3396fa973ec..6d0bbd2a586 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/EditBigBlueButtonMeetingController.java +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/EditBigBlueButtonMeetingController.java @@ -41,6 +41,7 @@ import org.olat.group.BusinessGroup; import org.olat.modules.bigbluebutton.BigBlueButtonManager; import org.olat.modules.bigbluebutton.BigBlueButtonMeeting; import org.olat.modules.bigbluebutton.BigBlueButtonMeetingTemplate; +import org.olat.modules.bigbluebutton.BigBlueButtonModule; import org.olat.repository.RepositoryEntry; import org.springframework.beans.factory.annotation.Autowired; @@ -70,6 +71,8 @@ public class EditBigBlueButtonMeetingController extends FormBasicController { private BigBlueButtonMeeting meeting; private List<BigBlueButtonMeetingTemplate> templates; + @Autowired + private BigBlueButtonModule bigBlueButtonModule; @Autowired private BigBlueButtonManager bigBlueButtonManager; @@ -113,13 +116,13 @@ public class EditBigBlueButtonMeetingController extends FormBasicController { welcomeEl = uifactory.addRichTextElementForStringDataMinimalistic("meeting.welcome", "meeting.welcome", welcome, 8, 60, formLayout, getWindowControl()); KeyValues templatesKeyValues = new KeyValues(); - templatesKeyValues.add(KeyValues.entry("-", translate("meeting.no.template"))); for(BigBlueButtonMeetingTemplate template:templates) { templatesKeyValues.add(KeyValues.entry(template.getKey().toString(), template.getName())); } String[] templatesKeys = templatesKeyValues.keys(); templateEl = uifactory.addDropdownSingleselect("meeting.template", "meeting.template", formLayout, templatesKeys, templatesKeyValues.values()); + templateEl.addActionListener(FormEvent.ONCHANGE); templateEl.setMandatory(true); boolean templateSelected = false; if(meeting != null && meeting.getTemplate() != null) { @@ -134,13 +137,15 @@ public class EditBigBlueButtonMeetingController extends FormBasicController { if(!templateSelected) { templateEl.select(templatesKeys[0], true); } + updateTemplateInformations(); String[] permValues = new String[] { translate("meeting.permanent.on") }; permanentEl = uifactory.addCheckboxesHorizontal("meeting.permanent", formLayout, permKeys, permValues); permanentEl.addActionListener(FormEvent.ONCHANGE); - boolean permanent = meeting == null ? false : meeting.isPermanent(); + boolean permanent = meeting != null && bigBlueButtonModule.isPermanentMeetingEnabled() && meeting.isPermanent(); permanentEl.select(permKeys[0], permanent); permanentEl.setHelpTextKey("meeting.permanent.explain", null); + permanentEl.setVisible(bigBlueButtonModule.isPermanentMeetingEnabled()); Date startDate = meeting == null ? null : meeting.getStartDate(); startDateEl = uifactory.addDateChooser("meeting.start", "meeting.start", startDate, formLayout); @@ -173,6 +178,21 @@ public class EditBigBlueButtonMeetingController extends FormBasicController { followupTimeEl.setVisible(!permanent); } + private void updateTemplateInformations() { + templateEl.setExampleKey(null, null); + if(templateEl.isOneSelected()) { + BigBlueButtonMeetingTemplate template = getSelectedTemplate(); + if(template != null && template.getMaxParticipants() != null) { + String[] args = new String[] { template.getMaxParticipants().toString() }; + if(template.getWebcamsOnlyForModerator() != null && template.getWebcamsOnlyForModerator().booleanValue()) { + templateEl.setExampleKey("template.explain.max.participants.with.webcams.mod", args); + } else { + templateEl.setExampleKey("template.explain.max.participants", args); + } + } + } + } + @Override protected void doDispose() { // @@ -181,19 +201,10 @@ public class EditBigBlueButtonMeetingController extends FormBasicController { @Override protected boolean validateFormLogic(UserRequest ureq) { boolean allOk = super.validateFormLogic(ureq); - - nameEl.clearError(); - if(!StringHelper.containsNonWhitespace(nameEl.getValue())) { - nameEl.setErrorKey("form.legende.mandatory", null); - allOk &= false; - } else if (nameEl.getValue().contains("&")) { - nameEl.setErrorKey("form.invalidchar.noamp", null); - allOk &= false; - } - + startDateEl.clearError(); endDateEl.clearError(); - if(!permanentEl.isAtLeastSelected(1)) { + if(!permanentEl.isVisible() || !permanentEl.isAtLeastSelected(1)) { if(startDateEl.getDate() == null) { startDateEl.setErrorKey("form.legende.mandatory", null); allOk &= false; @@ -210,11 +221,41 @@ public class EditBigBlueButtonMeetingController extends FormBasicController { endDateEl.setErrorKey("error.start.after.end", null); allOk &= false; } + + Date now = new Date(); + if(end.before(now)) { + endDateEl.setErrorKey("error.end.past", null); + allOk &= false; + } } } allOk &= validateTime(leadTimeEl); allOk &= validateTime(followupTimeEl); + + templateEl.clearError(); + if(!templateEl.isOneSelected()) { + endDateEl.setErrorKey("form.legende.mandatory", null); + allOk &= false; + } + + // dates ok + if(allOk && (!permanentEl.isVisible() || !permanentEl.isAtLeastSelected(1))) { + boolean canMeeting = validateSlot(); + if(!canMeeting) { + startDateEl.setErrorKey("server.overloaded", null); + allOk &= false; + } + } + + nameEl.clearError(); + if(!StringHelper.containsNonWhitespace(nameEl.getValue())) { + nameEl.setErrorKey("form.legende.mandatory", null); + allOk &= false; + } else if (nameEl.getValue().contains("&")) { + nameEl.setErrorKey("form.invalidchar.noamp", null); + allOk &= false; + } return allOk; } @@ -227,11 +268,43 @@ public class EditBigBlueButtonMeetingController extends FormBasicController { } return allOk; } + + private boolean validateSlot() { + BigBlueButtonMeetingTemplate template = getSelectedTemplate(); + return bigBlueButtonManager.isSlotAvailable(template, + startDateEl.getDate(), getLeadTime(), endDateEl.getDate(), getFollowupTime()); + } + + private BigBlueButtonMeetingTemplate getSelectedTemplate() { + String selectedTemplateId = templateEl.getSelectedKey(); + return templates.stream() + .filter(tpl -> selectedTemplateId.equals(tpl.getKey().toString())) + .findFirst() + .orElse(null); + } + + public long getLeadTime() { + long leadTime = 0; + if(leadTimeEl.isVisible() && StringHelper.isLong(leadTimeEl.getValue())) { + leadTime = Long.valueOf(leadTimeEl.getValue()); + } + return leadTime; + } + + private long getFollowupTime() { + long followupTime = 0; + if(followupTimeEl.isVisible() && StringHelper.isLong(followupTimeEl.getValue())) { + followupTime = Long.valueOf(followupTimeEl.getValue()); + } + return followupTime; + } @Override protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { if(permanentEl == source) { updateUI(); + } else if(templateEl == source) { + updateTemplateInformations(); } super.formInnerEvent(ureq, source, event); } @@ -247,22 +320,10 @@ public class EditBigBlueButtonMeetingController extends FormBasicController { meeting.setDescription(descriptionEl.getValue()); meeting.setWelcome(welcomeEl.getValue()); - BigBlueButtonMeetingTemplate template = null; - if(templateEl.isOneSelected() && !"-".equals(templateEl.getSelectedKey())) { - String selectedTemplateId = templateEl.getSelectedKey(); - template = templates.stream() - .filter(tpl -> selectedTemplateId.equals(tpl.getKey().toString())) - .findFirst() - .orElse(null); - } + BigBlueButtonMeetingTemplate template = getSelectedTemplate(); meeting.setTemplate(template); - boolean permanent; - if(permanentEl.isVisible()) { - permanent = permanentEl.isAtLeastSelected(1); - } else { - permanent = false; - } + boolean permanent = permanentEl.isVisible() && permanentEl.isAtLeastSelected(1); meeting.setPermanent(permanent); if(permanent) { meeting.setStartDate(null); @@ -271,20 +332,12 @@ public class EditBigBlueButtonMeetingController extends FormBasicController { meeting.setFollowupTime(0l); } else { Date startDate = startDateEl.getDate(); - Date endDate = endDateEl.getDate(); meeting.setStartDate(startDate); + Date endDate = endDateEl.getDate(); meeting.setEndDate(endDate); - - long leadTime = 0; - if(leadTimeEl.isVisible() && StringHelper.isLong(leadTimeEl.getValue())) { - leadTime = Long.valueOf(leadTimeEl.getValue()); - } + long leadTime = getLeadTime(); meeting.setLeadTime(leadTime); - - long followupTime = 0; - if(followupTimeEl.isVisible() && StringHelper.isLong(followupTimeEl.getValue())) { - followupTime = Long.valueOf(followupTimeEl.getValue()); - } + long followupTime = getFollowupTime(); meeting.setFollowupTime(followupTime); } diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/EditBigBlueButtonTemplateController.java b/src/main/java/org/olat/modules/bigbluebutton/ui/EditBigBlueButtonTemplateController.java index 224e4c90afc..ad3e171277d 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/EditBigBlueButtonTemplateController.java +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/EditBigBlueButtonTemplateController.java @@ -44,13 +44,14 @@ import org.springframework.beans.factory.annotation.Autowired; public class EditBigBlueButtonTemplateController extends FormBasicController { private static final String[] maxParticipantsKeys = new String[] { "2", "5", "10", "25", "50", "100" }; - private static final String[] onKeys = new String[] { "yes", "no", "default" }; + private static final String[] onKeys = new String[] { "yes", "no" }; private static final String[] guestPolicyKeys = new String[] { GuestPolicyEnum.ALWAYS_DENY.name(), GuestPolicyEnum.ASK_MODERATOR.name(), GuestPolicyEnum.ALWAYS_ACCEPT.name() }; private TextElement nameEl; private TextElement descriptionEl; + private TextElement maxConcurrentMeetingsEl; private SingleSelection muteOnStartEl; private SingleSelection maxParticipantsEl; private SingleSelection autoStartRecordingEl; @@ -66,6 +67,7 @@ public class EditBigBlueButtonTemplateController extends FormBasicController { private SingleSelection guestPolicyEl; + private final boolean readOnly; private BigBlueButtonMeetingTemplate template; @Autowired @@ -73,13 +75,15 @@ public class EditBigBlueButtonTemplateController extends FormBasicController { public EditBigBlueButtonTemplateController(UserRequest ureq, WindowControl wControl) { super(ureq, wControl); + readOnly = false; initForm(ureq); } - public EditBigBlueButtonTemplateController(UserRequest ureq, WindowControl wControl, BigBlueButtonMeetingTemplate template) { + public EditBigBlueButtonTemplateController(UserRequest ureq, WindowControl wControl, + BigBlueButtonMeetingTemplate template, boolean readOnly) { super(ureq, wControl); + this.readOnly = readOnly; this.template = template; - initForm(ureq); } @@ -95,9 +99,11 @@ public class EditBigBlueButtonTemplateController extends FormBasicController { String description = template == null ? "" : template.getDescription(); descriptionEl = uifactory.addTextAreaElement("template.description", "template.description", 2000, 4, 72, false, false, description, formLayout); - String maxParticipants = template == null || template.getMaxParticipants() == null ? "-" : template.getMaxParticipants().toString(); + String maxConcurrentMeetings = template == null || template.getMaxConcurrentMeetings() == null ? "" : template.getMaxConcurrentMeetings().toString(); + maxConcurrentMeetingsEl = uifactory.addTextElement("template.max.concurrent.meetings", "template.max.concurrent.meetings", 8, maxConcurrentMeetings, formLayout); + + String maxParticipants = template == null || template.getMaxParticipants() == null ? "5" : template.getMaxParticipants().toString(); KeyValues maxParticipantsKeyValues = new KeyValues(); - maxParticipantsKeyValues.add(KeyValues.entry("-", translate("template.maxParticipants.default"))); for(String maxParticipantsKey:maxParticipantsKeys) { maxParticipantsKeyValues.add(KeyValues.entry(maxParticipantsKey, maxParticipantsKey)); } @@ -108,51 +114,51 @@ public class EditBigBlueButtonTemplateController extends FormBasicController { maxParticipantsKeyValues.keys(), maxParticipantsKeyValues.values()); maxParticipantsEl.select(maxParticipants, true); - String[] onValues = new String[] { "Yes", "No", "Default" }; + String[] onValues = new String[] { translate("yes"), translate("no") }; Boolean muteOnStart = template == null ? null : template.getMuteOnStart(); muteOnStartEl = uifactory.addRadiosHorizontal("template.muteOnStart", formLayout, onKeys, onValues); - select(muteOnStart, muteOnStartEl); + select(muteOnStart, muteOnStartEl, false); Boolean autoStartRecording = template == null ? null : template.getAutoStartRecording(); autoStartRecordingEl = uifactory.addRadiosHorizontal("template.autoStartRecording", formLayout, onKeys, onValues); - select(autoStartRecording, autoStartRecordingEl); + select(autoStartRecording, autoStartRecordingEl, false); Boolean allowStartStopRecording = template == null ? null : template.getAllowStartStopRecording(); allowStartStopRecordingEl = uifactory.addRadiosHorizontal("template.allowStartStopRecording", formLayout, onKeys, onValues); - select(allowStartStopRecording, allowStartStopRecordingEl); + select(allowStartStopRecording, allowStartStopRecordingEl, true); Boolean webcamsOnlyForModerator = template == null ? null : template.getWebcamsOnlyForModerator(); webcamsOnlyForModeratorEl = uifactory.addRadiosHorizontal("template.webcamsOnlyForModerator", formLayout, onKeys, onValues); - select(webcamsOnlyForModerator, webcamsOnlyForModeratorEl); + select(webcamsOnlyForModerator, webcamsOnlyForModeratorEl, true); Boolean allowModsToUnmuteUsers = template == null ? null : template.getAllowModsToUnmuteUsers(); allowModsToUnmuteUsersEl = uifactory.addRadiosHorizontal("template.allowModsToUnmuteUsers", formLayout, onKeys, onValues); - select(allowModsToUnmuteUsers, allowModsToUnmuteUsersEl); + select(allowModsToUnmuteUsers, allowModsToUnmuteUsersEl, false); Boolean lockSettingsDisableCam = template == null ? null : template.getLockSettingsDisableCam(); lockSettingsDisableCamEl = uifactory.addRadiosHorizontal("template.lockSettingsDisableCam", formLayout, onKeys, onValues); - select(lockSettingsDisableCam, lockSettingsDisableCamEl); + select(lockSettingsDisableCam, lockSettingsDisableCamEl, false); Boolean lockSettingsDisableMic = template == null ? null : template.getLockSettingsDisableMic(); lockSettingsDisableMicEl = uifactory.addRadiosHorizontal("template.lockSettingsDisableMic", formLayout, onKeys, onValues); - select(lockSettingsDisableMic, lockSettingsDisableMicEl); + select(lockSettingsDisableMic, lockSettingsDisableMicEl, false); Boolean lockSettingsDisablePrivateChat = template == null ? null : template.getLockSettingsDisablePrivateChat(); lockSettingsDisablePrivateChatEl = uifactory.addRadiosHorizontal("template.lockSettingsDisablePrivateChat", formLayout, onKeys, onValues); - select(lockSettingsDisablePrivateChat, lockSettingsDisablePrivateChatEl); + select(lockSettingsDisablePrivateChat, lockSettingsDisablePrivateChatEl, false); Boolean lockSettingsDisablePublicChat = template == null ? null : template.getLockSettingsDisablePublicChat(); lockSettingsDisablePublicChatEl = uifactory.addRadiosHorizontal("template.lockSettingsDisablePublicChat", formLayout, onKeys, onValues); - select(lockSettingsDisablePublicChat, lockSettingsDisablePublicChatEl); + select(lockSettingsDisablePublicChat, lockSettingsDisablePublicChatEl, false); Boolean lockSettingsDisableNote = template == null ? null : template.getLockSettingsDisableNote(); lockSettingsDisableNoteEl = uifactory.addRadiosHorizontal("template.lockSettingsDisableNote", formLayout, onKeys, onValues); - select(lockSettingsDisableNote, lockSettingsDisableNoteEl); + select(lockSettingsDisableNote, lockSettingsDisableNoteEl, false); Boolean lockSettingsLockedLayout = template == null ? null : template.getLockSettingsLockedLayout(); lockSettingsLockedLayoutEl = uifactory.addRadiosHorizontal("template.lockSettingsLockedLayout", formLayout, onKeys, onValues); - select(lockSettingsLockedLayout, lockSettingsLockedLayoutEl); + select(lockSettingsLockedLayout, lockSettingsLockedLayoutEl, false); GuestPolicyEnum guestPolicy = template == null ? GuestPolicyEnum.ALWAYS_DENY : template.getGuestPolicyEnum(); String[] guestPolicyValues = new String[] { @@ -166,10 +172,10 @@ public class EditBigBlueButtonTemplateController extends FormBasicController { formLayout.add("buttons", buttonLayout); uifactory.addFormCancelButton("cancel", buttonLayout, ureq, getWindowControl()); - if(template == null || !template.isSystem()) { - uifactory.addFormSubmitButton("save", buttonLayout); - } else { + if(readOnly) { disableSystemTemplate(); + } else { + uifactory.addFormSubmitButton("save", buttonLayout); } } @@ -177,6 +183,7 @@ public class EditBigBlueButtonTemplateController extends FormBasicController { nameEl.setEnabled(false); descriptionEl.setEnabled(false); muteOnStartEl.setEnabled(false); + maxConcurrentMeetingsEl.setEnabled(false); maxParticipantsEl.setEnabled(false); autoStartRecordingEl.setEnabled(false); allowStartStopRecordingEl.setEnabled(false); @@ -191,10 +198,11 @@ public class EditBigBlueButtonTemplateController extends FormBasicController { guestPolicyEl.setEnabled(false); } - private void select(Boolean val, SingleSelection selectEl) { + private void select(Boolean val, SingleSelection selectEl, boolean defaultValue) { if(val == null) { - selectEl.select(onKeys[2], true); - } else if(val.booleanValue()) { + val = Boolean.valueOf(defaultValue); + } + if(val.booleanValue()) { selectEl.select(onKeys[0], true); } else { selectEl.select(onKeys[1], true); @@ -221,7 +229,15 @@ public class EditBigBlueButtonTemplateController extends FormBasicController { descriptionEl.clearError(); if(!StringHelper.containsNonWhitespace(descriptionEl.getValue()) && descriptionEl.getValue().length() > 2000) { - nameEl.setErrorKey("form.error.toolong", new String[] { "2000" }); + descriptionEl.setErrorKey("form.error.toolong", new String[] { "2000" }); + allOk &= false; + } + + maxConcurrentMeetingsEl.clearError(); + if(StringHelper.containsNonWhitespace(maxConcurrentMeetingsEl.getValue()) + && (!StringHelper.isLong(maxConcurrentMeetingsEl.getValue()) + || Long.valueOf(maxConcurrentMeetingsEl.getValue()).longValue() <= 0)) { + maxConcurrentMeetingsEl.setErrorKey("form.error.nointeger", null); allOk &= false; } @@ -237,6 +253,13 @@ public class EditBigBlueButtonTemplateController extends FormBasicController { } template.setDescription(descriptionEl.getValue()); + if(StringHelper.containsNonWhitespace(maxConcurrentMeetingsEl.getValue()) + && StringHelper.isLong(maxConcurrentMeetingsEl.getValue())) { + template.setMaxConcurrentMeetings(Long.valueOf(maxConcurrentMeetingsEl.getValue()).intValue()); + } else { + template.setMaxConcurrentMeetings(null); + } + if(maxParticipantsEl.isOneSelected() && !"-".equals(maxParticipantsEl.getSelectedKey())) { template.setMaxParticipants(Integer.parseInt(maxParticipantsEl.getSelectedKey())); } else { @@ -263,14 +286,10 @@ public class EditBigBlueButtonTemplateController extends FormBasicController { } private Boolean getSelected(SingleSelection selectEl) { - Boolean val = null; + Boolean val = Boolean.FALSE; if(selectEl.isOneSelected()) { String selected = selectEl.getSelectedKey(); - if("yes".equals(selected)) { - val = Boolean.TRUE; - } else if("no".equals(selected)) { - val = Boolean.FALSE; - } + val = Boolean.valueOf("yes".equals(selected)); } return val; } diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/_content/meeting.html b/src/main/java/org/olat/modules/bigbluebutton/ui/_content/meeting.html index 897c719b205..b81c8189e33 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/_content/meeting.html +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/_content/meeting.html @@ -12,7 +12,7 @@ <div class="o_block_large o_warning">$r.translate("meeting.ended")</div> #end -#if($r.isFalse($meetingsExists) || $r.isTrue($notStarted)) +#if($r.isTrue($notStarted)) <div class="o_block_large o_info">$r.translate("meeting.create.intro")</div> #end diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/_content/meetings.html b/src/main/java/org/olat/modules/bigbluebutton/ui/_content/meetings.html index ea913fe6bd8..6a9f6a139c1 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/_content/meetings.html +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/_content/meetings.html @@ -1,6 +1,8 @@ <h3>$r.translate("meetings.upcoming")</h3> $r.render("upcomingMeetings") #if($r.visible("pastMeetings")) +<div class="o_block_large"> <h3>$r.translate("meetings.past")</h3> $r.render("pastMeetings") +</div> #end \ No newline at end of file diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/_content/run_admin.html b/src/main/java/org/olat/modules/bigbluebutton/ui/_content/run_admin.html index 49cfbf886d4..0dcce15ec31 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/_content/run_admin.html +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/_content/run_admin.html @@ -3,7 +3,7 @@ $r.render("backLink") $r.render("meeting") #else - $r.render("segments")<br> + $r.render("segments") #if($r.available("segmentCmp")) $r.render("segmentCmp") #end diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/modules/bigbluebutton/ui/_i18n/LocalStrings_de.properties index 9d53db5c503..afd1385db88 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/_i18n/LocalStrings_de.properties @@ -4,7 +4,7 @@ add.meeting=Meeting hinzuf\u00FCgen add.template=Vorlage erstellen admin.menu.title=BigBlueButton admin.menu.title.alt=BigBlueButton -bigbluebutton.intro=Intro +bigbluebutton.intro=Konfiguration von BigBlueButton Web Conferencing bigbluebutton.module.enabled=Modul "BigBlueButton" bigbluebutton.module.enabled.for=Einschalten f\u00FCr bigbluebutton.module.enabled.for.courses=Kurse @@ -18,6 +18,9 @@ confirm.delete.template.title=Vorlage "{0}" l\u00F6schen connection.failed=Login fehlgeschlagen. connection.successful=Login erfolgreich\! edit.template=Vorlage "{0}" bearbeiten +enable.permanent.meeting=Dauernd Meeting einschalten +enable.adhoc.meeting=Adhoc Meeting einschalten +error.end.past=Meeting kann nicht in Vergangenheit geplant werden. error.prefix=Ein Fehler ist aufgetreten\: error.server.raw={1} <small>Schl\u00FCssel\: {0}</small> error.start.after.end=Das Datum f\u00FCr das Ende des Meetings darf nicht vor dem Beginn Datum sein. @@ -55,12 +58,19 @@ option.bigbluebutton.secret=Secret option.bigbluebutton.shared.secret=Shared secret option.clean.meetings=Meetings aufra\u00FCmen (Tage) option.dont.clean.meetings=Nie +server.overloaded=Es gibt kein Platz verf\u00FCgbar auf dem Server an den gew\u00E4hlten Datum. table.header.permanent=Dauernd table.header.system=System +table.header.max.concurrent.meetings=Gleichseitig Meetings +table.header.max.participants=Max. Teilnehmer +table.header.template=Typ +table.header.webcams.only.moderator=Webcams Moderatoren template.allowModsToUnmuteUsers=Allow moderators to unmute users template.allowStartStopRecording=Allow to start / stop recording template.autoStartRecording=Auto start recording template.description=Beschreibung +template.explain.max.participants=Max. Anzhal von Teilnehmer: {0} +template.explain.max.participants.with.webcams.mod=Max. Anzhal von Teilnehmer: {0}, Webcams f\u00FCr Moderatoren template.guestPolicy=Guest policy template.lockSettingsDisableCam=Lock settings disable camera template.lockSettingsDisableMic=Lock settings disable micro @@ -70,8 +80,11 @@ template.lockSettingsDisablePublicChat=Lock settings disable public chat template.lockSettingsLockedLayout=Lock settings locked layout template.maxParticipants=Max. Teilnehmer template.maxParticipants.default=Standard +template.max.concurrent.meetings=Max. gleichzeitige Meetings template.muteOnStart=Mute on start template.name=Name template.webcamsOnlyForModerator=Webcams only for moderators templates.title=Vorlagen +view=Ansehen +view.template=Vorlage "{0}" warning.template.in.use=Die Vorlage kann nicht gel\u00F6scht werden weil sie nocht benutzt wird. diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/modules/bigbluebutton/ui/_i18n/LocalStrings_en.properties index 63767caff0e..5d4a4e88929 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/_i18n/LocalStrings_en.properties @@ -4,7 +4,7 @@ add.meeting=Add meeting add.template=New template admin.menu.title=BigBlueButton admin.menu.title.alt=BigBlueButton -bigbluebutton.intro=Intro +bigbluebutton.intro=Configuration of BigBlueButton Web Conferencing bigbluebutton.module.enabled=Module "BigBlueButton" bigbluebutton.module.enabled.for=Enable for bigbluebutton.module.enabled.for.courses=Courses @@ -18,6 +18,9 @@ confirm.delete.template.title=Delete template "{0}" connection.failed=Login failed. connection.successful=Login successful\! edit.template=Edit template "{0}" +enable.permanent.meeting=Enable permanent meeting +enable.adhoc.meeting=Enable adhoc meeting +error.end.past=Meeting cannot be planned in the past. error.prefix=An error happened\: error.server.raw={1} <small>Key {0}</small> error.start.after.end=The end date of the meeting must not be before the start date. @@ -55,12 +58,19 @@ option.bigbluebutton.secret=Secret option.bigbluebutton.shared.secret=Shared secret option.clean.meetings=Clean-up meetings (days) option.dont.clean.meetings=Never +server.overloaded=There is no place available on the server for the choosen dates. table.header.permanent=Permanent table.header.system=System +table.header.max.concurrent.meetings=Concurrent meetings +table.header.max.participants=Max. participants +table.header.template=Type +table.header.webcams.only.moderator=Webcams moderators template.allowModsToUnmuteUsers=Allow moderators to unmute users template.allowStartStopRecording=Allow to start / stop recording template.autoStartRecording=Auto start recording template.description=Description +template.explain.max.participants=Max. number of participants: {0} +template.explain.max.participants.with.webcams.mod=Max. number of participants: {0}, webcams for moderators template.guestPolicy=Guest policy template.lockSettingsDisableCam=Lock settings disable camera template.lockSettingsDisableMic=Lock settings disable micro @@ -70,9 +80,12 @@ template.lockSettingsDisablePublicChat=Lock settings disable public chat template.lockSettingsLockedLayout=Lock settings locked layout template.maxParticipants=Max. participants template.maxParticipants.default=Default +template.max.concurrent.meetings=Max. concurrent meetings template.muteOnStart=Mute on start template.name=Name template.webcamsOnlyForModerator=Webcams only for moderators templates.title=Templates +view=View +view.template=Template "{0}" warning.template.in.use=The template cannot be deleted. It's still used. diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/_i18n/LocalStrings_fr.properties b/src/main/java/org/olat/modules/bigbluebutton/ui/_i18n/LocalStrings_fr.properties index 271a5b4d3ba..b34fdf6d533 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/_i18n/LocalStrings_fr.properties +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/_i18n/LocalStrings_fr.properties @@ -4,7 +4,7 @@ add.meeting=Ajouter un meeting add.template=Ajouter un mod\u00E8le admin.menu.title=BigBlueButton admin.menu.title.alt=BigBlueButton -bigbluebutton.intro=Intro +bigbluebutton.intro=Configuration de BigBlueButton, solution de conférences en ligne bigbluebutton.module.enabled=Module "BigBlueButton" bigbluebutton.module.enabled.for=Activer pour bigbluebutton.module.enabled.for.courses=Cours diff --git a/src/main/resources/database/mysql/alter_14_2_x_to_14_2_5.sql b/src/main/resources/database/mysql/alter_14_2_x_to_14_2_5.sql index 5f7ca8b2ecd..8ee52760d87 100644 --- a/src/main/resources/database/mysql/alter_14_2_x_to_14_2_5.sql +++ b/src/main/resources/database/mysql/alter_14_2_x_to_14_2_5.sql @@ -6,6 +6,7 @@ create table o_bbb_template ( b_description varchar(2000) default null, b_system bool default false not null, b_external_id varchar(255) default null, + b_max_concurrent_meetings int default null, b_max_participants int default null, b_mute_on_start bool default null, b_auto_start_recording bool default null, diff --git a/src/main/resources/database/mysql/setupDatabase.sql b/src/main/resources/database/mysql/setupDatabase.sql index 5d690dd8173..e86cafd52be 100644 --- a/src/main/resources/database/mysql/setupDatabase.sql +++ b/src/main/resources/database/mysql/setupDatabase.sql @@ -1226,6 +1226,7 @@ create table o_bbb_template ( b_description varchar(2000) default null, b_system bool default false not null, b_external_id varchar(255) default null, + b_max_concurrent_meetings int default null, b_max_participants int default null, b_mute_on_start bool default null, b_auto_start_recording bool default null, diff --git a/src/main/resources/database/oracle/alter_14_2_x_to_14_2_5.sql b/src/main/resources/database/oracle/alter_14_2_x_to_14_2_5.sql index b9a23fcb51c..54f3de70ad5 100644 --- a/src/main/resources/database/oracle/alter_14_2_x_to_14_2_5.sql +++ b/src/main/resources/database/oracle/alter_14_2_x_to_14_2_5.sql @@ -6,6 +6,7 @@ create table o_bbb_template ( b_description varchar(2000) default null, b_system number default 0 not null, b_external_id varchar(255) default null, + b_max_concurrent_meetings int default null, b_max_participants int default null, b_mute_on_start number default null, b_auto_start_recording number default null, diff --git a/src/main/resources/database/oracle/setupDatabase.sql b/src/main/resources/database/oracle/setupDatabase.sql index 23ea493ed59..98c4c3d7eef 100644 --- a/src/main/resources/database/oracle/setupDatabase.sql +++ b/src/main/resources/database/oracle/setupDatabase.sql @@ -1292,6 +1292,7 @@ create table o_bbb_template ( b_description varchar(2000) default null, b_system number default 0 not null, b_external_id varchar(255) default null, + b_max_concurrent_meetings int default null, b_max_participants int default null, b_mute_on_start number default null, b_auto_start_recording number default null, diff --git a/src/main/resources/database/postgresql/alter_14_2_x_to_14_2_5.sql b/src/main/resources/database/postgresql/alter_14_2_x_to_14_2_5.sql index 150c4e68f4e..ea80879cd60 100644 --- a/src/main/resources/database/postgresql/alter_14_2_x_to_14_2_5.sql +++ b/src/main/resources/database/postgresql/alter_14_2_x_to_14_2_5.sql @@ -6,7 +6,8 @@ create table o_bbb_template ( b_description varchar(2000) default null, b_system bool default false not null, b_external_id varchar(255) default null, - b_max_participants int default null, + b_max_concurrent_meetings int default null, + b_max_participants int8 default null, b_mute_on_start bool default null, b_auto_start_recording bool default null, b_allow_start_stop_recording bool default null, diff --git a/src/main/resources/database/postgresql/setupDatabase.sql b/src/main/resources/database/postgresql/setupDatabase.sql index f0f5d3d5716..8aa8395f4db 100644 --- a/src/main/resources/database/postgresql/setupDatabase.sql +++ b/src/main/resources/database/postgresql/setupDatabase.sql @@ -1251,6 +1251,7 @@ create table o_bbb_template ( b_description varchar(2000) default null, b_system bool default false not null, b_external_id varchar(255) default null, + b_max_concurrent_meetings int default null, b_max_participants int default null, b_mute_on_start bool default null, b_auto_start_recording bool default null, diff --git a/src/test/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonMeetingDAOTest.java b/src/test/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonMeetingDAOTest.java index 2d775381127..8adeda0b061 100644 --- a/src/test/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonMeetingDAOTest.java +++ b/src/test/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonMeetingDAOTest.java @@ -19,14 +19,19 @@ */ package org.olat.modules.bigbluebutton.manager; +import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.UUID; import org.junit.Assert; import org.junit.Test; +import org.olat.commons.calendar.CalendarUtils; import org.olat.core.commons.persistence.DB; +import org.olat.group.BusinessGroup; +import org.olat.group.manager.BusinessGroupDAO; import org.olat.modules.bigbluebutton.BigBlueButtonMeeting; +import org.olat.modules.bigbluebutton.BigBlueButtonMeetingTemplate; import org.olat.repository.RepositoryEntry; import org.olat.test.JunitTestHelper; import org.olat.test.OlatTestCase; @@ -43,7 +48,11 @@ public class BigBlueButtonMeetingDAOTest extends OlatTestCase { @Autowired private DB dbInstance; @Autowired + private BusinessGroupDAO businessGroupDao; + @Autowired private BigBlueButtonMeetingDAO bigBlueButtonMeetingDao; + @Autowired + private BigBlueButtonMeetingTemplateDAO bigBlueButtonMeetingTemplateDao; @Test public void createMeetingForRepositoryEntry() { @@ -123,5 +132,64 @@ public class BigBlueButtonMeetingDAOTest extends OlatTestCase { Assert.assertEquals(1, meetings.size()); Assert.assertTrue(meetings.contains(meeting)); } + + @Test + public void getAllMeetings() { + String name = "BigBlueButton - 3"; + BusinessGroup group = businessGroupDao.createAndPersist(null, "BBB group", "bbb-desc", -1, -1, false, false, false, false, false); + BigBlueButtonMeeting meeting = bigBlueButtonMeetingDao.createAndPersistMeeting(name, null, null, group); + dbInstance.commit(); + + List<BigBlueButtonMeeting> meetings = bigBlueButtonMeetingDao.getAllMeetings(); + Assert.assertNotNull(meetings); + Assert.assertEquals(1, meetings.size()); + Assert.assertTrue(meetings.contains(meeting)); + } + + @Test + public void getConcurrentMeetings() { + String externalId = UUID.randomUUID().toString(); + BigBlueButtonMeetingTemplate template = bigBlueButtonMeetingTemplateDao.createTemplate("A new template", externalId, false); + template.setMaxConcurrentMeetings(2); + template = bigBlueButtonMeetingTemplateDao.updateTemplate(template); + dbInstance.commit(); + + BusinessGroup group = businessGroupDao.createAndPersist(null, "BBB group", "bbb-desc", -1, -1, false, false, false, false, false); + createMeeting("BigBlueButton - 4", date(1, 12), 15, date(1, 14), 15, template, group); + createMeeting("BigBlueButton - 5", date(1, 10), 120, date(1, 18), 120, template, group); + createMeeting("BigBlueButton - 6", date(1, 14), 0, date(1, 19), 0, template, group); + createMeeting("BigBlueButton - 7", date(2, 12), 15, date(2, 15), 15, template, group); + dbInstance.commit(); + int concurrent = bigBlueButtonMeetingDao.getConcurrentMeetings(template, date(1, 15), date(1, 19)); + Assert.assertEquals(2, concurrent); + + int concurrentFollowup = bigBlueButtonMeetingDao.getConcurrentMeetings(template, date(1, 20), date(1, 21)); + Assert.assertEquals(1, concurrentFollowup); + + int concurrentWidePeriod = bigBlueButtonMeetingDao.getConcurrentMeetings(template, date(0, 10), date(3, 21)); + Assert.assertEquals(4, concurrentWidePeriod); + + int concurrentWithin = bigBlueButtonMeetingDao.getConcurrentMeetings(template, date(2, 13), date(2, 14)); + Assert.assertEquals(1, concurrentWithin); + } + + private BigBlueButtonMeeting createMeeting(String name, Date start, int leadTime, Date end, int followupTime, + BigBlueButtonMeetingTemplate template, BusinessGroup group) { + BigBlueButtonMeeting meeting = bigBlueButtonMeetingDao.createAndPersistMeeting(name, null, null, group); + meeting.setStartDate(start); + meeting.setLeadTime(leadTime); + meeting.setEndDate(end); + meeting.setFollowupTime(followupTime); + meeting.setTemplate(template); + return bigBlueButtonMeetingDao.updateMeeting(meeting); + } + + private Date date(int addDays, int hour) { + Calendar cal = Calendar.getInstance(); + cal = CalendarUtils.getStartOfDay(cal); + cal.add(Calendar.DATE, addDays); + cal.set(Calendar.HOUR, hour); + return cal.getTime(); + } } -- GitLab