From ce39bcf7ef5d6c89590f162c9cbe2c01f89b74c1 Mon Sep 17 00:00:00 2001 From: srosse <stephane.rosse@frentix.com> Date: Wed, 8 Apr 2020 20:43:40 +0200 Subject: [PATCH] OO-4626: add panel with statistics of BigBlueButton servers --- .../bigbluebutton/BigBlueButtonManager.java | 3 + .../manager/BigBlueButtonManagerImpl.java | 60 +++++----- .../manager/BigBlueButtonUtils.java | 12 +- .../model/BigBlueButtonMeetingInfos.java | 47 +++++++- .../model/BigBlueButtonServerInfos.java | 107 ++++++++++++++++++ .../ui/BigBlueButtonAdminController.java | 30 +++-- .../BigBlueButtonAdminServersController.java | 55 ++++++++- .../BigBlueButtonAdminServersTableModel.java | 71 ++++++++++-- .../ui/BigBlueButtonServerRow.java | 89 +++++++++++++++ .../ui/CapacityFactorCellRenderer.java | 51 +++++++++ .../ui/_content/servers_admin.html | 1 + .../ui/_i18n/LocalStrings_de.properties | 56 +++++---- .../ui/_i18n/LocalStrings_en.properties | 50 +++++--- 13 files changed, 542 insertions(+), 90 deletions(-) create mode 100644 src/main/java/org/olat/modules/bigbluebutton/model/BigBlueButtonServerInfos.java create mode 100644 src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonServerRow.java create mode 100644 src/main/java/org/olat/modules/bigbluebutton/ui/CapacityFactorCellRenderer.java create mode 100644 src/main/java/org/olat/modules/bigbluebutton/ui/_content/servers_admin.html diff --git a/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonManager.java b/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonManager.java index 8e18ffd750a..4547dcc8c36 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonManager.java +++ b/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonManager.java @@ -26,6 +26,7 @@ import org.olat.core.id.Identity; import org.olat.core.id.Roles; import org.olat.group.BusinessGroup; import org.olat.modules.bigbluebutton.model.BigBlueButtonErrors; +import org.olat.modules.bigbluebutton.model.BigBlueButtonServerInfos; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryEntryRef; @@ -45,6 +46,8 @@ public interface BigBlueButtonManager { public List<BigBlueButtonServer> getServers(); + public List<BigBlueButtonServerInfos> getServersInfos(); + public void deleteServer(BigBlueButtonServer server, BigBlueButtonErrors errors); 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 a05e56c3412..43d08f1e58f 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonManagerImpl.java +++ b/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonManagerImpl.java @@ -22,6 +22,7 @@ package org.olat.modules.bigbluebutton.manager; import java.net.URI; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.Date; import java.util.List; import java.util.concurrent.CountDownLatch; @@ -63,6 +64,7 @@ import org.olat.modules.bigbluebutton.model.BigBlueButtonErrorCodes; import org.olat.modules.bigbluebutton.model.BigBlueButtonErrors; import org.olat.modules.bigbluebutton.model.BigBlueButtonMeetingImpl; import org.olat.modules.bigbluebutton.model.BigBlueButtonMeetingInfos; +import org.olat.modules.bigbluebutton.model.BigBlueButtonServerInfos; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryEntryRef; import org.olat.repository.RepositoryManager; @@ -225,6 +227,12 @@ public class BigBlueButtonManagerImpl implements BigBlueButtonManager, Initializ return bigBlueButtonServerDao.getServers(); } + @Override + public List<BigBlueButtonServerInfos> getServersInfos() { + List<BigBlueButtonServer> servers = getServers(); + return getServersInfos(servers); + } + @Override public void deleteServer(BigBlueButtonServer server, BigBlueButtonErrors errors) { List<BigBlueButtonMeeting> meetings = bigBlueButtonMeetingDao.getMeetings(server); @@ -349,6 +357,15 @@ public class BigBlueButtonManagerImpl implements BigBlueButtonManager, Initializ } private BigBlueButtonServer getBigBlueButtonServer(List<BigBlueButtonServer> servers) { + List<BigBlueButtonServerInfos> serversInfos = getServersInfos(servers); + if(serversInfos.isEmpty()) { + return null; + } + Collections.sort(serversInfos, new ServerLoadComparator()); + return serversInfos.get(0).getServer(); + } + + private List<BigBlueButtonServerInfos> getServersInfos(List<BigBlueButtonServer> servers) { CountDownLatch serverLatch = new CountDownLatch(servers.size()); List<MeetingInfosThread> threads = new ArrayList<>(); @@ -363,21 +380,16 @@ public class BigBlueButtonManagerImpl implements BigBlueButtonManager, Initializ } catch (InterruptedException e) { log.error("", e); } - - List<ServerLoad> serversLoad = threads.stream() - .filter(MeetingInfosThread::isExecuted) - .filter(thread -> !thread.hasErrors()) - .map(thread -> calculateLoad(thread.getServer(), thread.getMeetingsInfos())) - .collect(Collectors.toList()); - - if(serversLoad.isEmpty()) { - return null; - } - Collections.sort(serversLoad); - return serversLoad.get(0).getServer(); + + return threads.stream() + .filter(MeetingInfosThread::isExecuted) + .filter(thread -> !thread.hasErrors()) + .map(thread -> new BigBlueButtonServerInfos(thread.getServer(), thread.getMeetingsInfos(), + calculateLoad(thread.getServer(), thread.getMeetingsInfos()))) + .collect(Collectors.toList()); } - private ServerLoad calculateLoad(BigBlueButtonServer server, List<BigBlueButtonMeetingInfos> meetingsInfos) { + private double calculateLoad(BigBlueButtonServer server, List<BigBlueButtonMeetingInfos> meetingsInfos) { double load = 0.0d; for(BigBlueButtonMeetingInfos meetingInfos:meetingsInfos) { load += meetingInfos.getListenerCount() * 1.0d; @@ -389,7 +401,7 @@ public class BigBlueButtonManagerImpl implements BigBlueButtonManager, Initializ && server.getCapacityFactory().doubleValue() > 1.0) { load = load / server.getCapacityFactory().doubleValue(); } - return new ServerLoad(server, load); + return load; } private List<BigBlueButtonMeetingInfos> getMeetingInfos(BigBlueButtonServer server, BigBlueButtonErrors errors) { @@ -750,23 +762,13 @@ public class BigBlueButtonManagerImpl implements BigBlueButtonManager, Initializ } } - private static class ServerLoad implements Comparable<ServerLoad> { - - private final double load; - private final BigBlueButtonServer server; - - public ServerLoad(BigBlueButtonServer server, double load) { - this.server = server; - this.load = load; - } - - public BigBlueButtonServer getServer() { - return server; - } + private static class ServerLoadComparator implements Comparator<BigBlueButtonServerInfos> { @Override - public int compareTo(ServerLoad o) { - return Double.compare(load, o.load); + public int compare(BigBlueButtonServerInfos o1, BigBlueButtonServerInfos o2) { + double l1 = o1.getLoad(); + double l2 = o2.getLoad(); + return Double.compare(l1, l2); } } 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 9c5cb30fee6..e8c854043ba 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonUtils.java +++ b/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonUtils.java @@ -135,7 +135,9 @@ public class BigBlueButtonUtils { for(int i=meetingList.getLength(); i-->0; ) { Element meetingEl = (Element)meetingList.item(i); String meetingId = getFirstElementValue(meetingEl, "meetingID"); - BigBlueButtonMeetingInfos meeting = new BigBlueButtonMeetingInfos(meetingId); + String running = getFirstElementValue(meetingEl, "running"); + + BigBlueButtonMeetingInfos meeting = new BigBlueButtonMeetingInfos(meetingId, "true".equals(running)); meetings.add(meeting); String videoCount = getFirstElementValue(meetingEl, "videoCount"); @@ -149,6 +151,14 @@ public class BigBlueButtonUtils { meeting.setParticipantCount(toLong(participantCount)); String moderatorCount = getFirstElementValue(meetingEl, "moderatorCount"); meeting.setModeratorCount(toLong(moderatorCount)); + + String recording = getFirstElementValue(meetingEl, "recording"); + meeting.setRecording("true".equals(recording)); + String isBreakout = getFirstElementValue(meetingEl, "isBreakout"); + meeting.setBreakout("true".equals(isBreakout)); + + String maxUsers = getFirstElementValue(meetingEl, "maxUsers"); + meeting.setMaxUsers(toLong(maxUsers)); } return meetings; } diff --git a/src/main/java/org/olat/modules/bigbluebutton/model/BigBlueButtonMeetingInfos.java b/src/main/java/org/olat/modules/bigbluebutton/model/BigBlueButtonMeetingInfos.java index 5f0fea5e1fa..7dd1ea3da07 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/model/BigBlueButtonMeetingInfos.java +++ b/src/main/java/org/olat/modules/bigbluebutton/model/BigBlueButtonMeetingInfos.java @@ -28,6 +28,7 @@ package org.olat.modules.bigbluebutton.model; public class BigBlueButtonMeetingInfos { private final String meetingId; + private final boolean running; private long videoCount; private long listenerCount; @@ -36,13 +37,25 @@ public class BigBlueButtonMeetingInfos { private long participantCount; private long moderatorCount; - public BigBlueButtonMeetingInfos(String meetingId) { + private boolean recording; + private boolean breakout; + + private long maxUsers; + + private double load; + + public BigBlueButtonMeetingInfos(String meetingId, boolean running) { this.meetingId = meetingId; + this.running = running; } public String getMeetingId() { return meetingId; } + + public boolean isRunning() { + return running; + } public long getVideoCount() { return videoCount; @@ -84,6 +97,38 @@ public class BigBlueButtonMeetingInfos { this.moderatorCount = moderatorCount; } + public boolean isRecording() { + return recording; + } + + public void setRecording(boolean recording) { + this.recording = recording; + } + + public boolean isBreakout() { + return breakout; + } + + public void setBreakout(boolean breakout) { + this.breakout = breakout; + } + + public long getMaxUsers() { + return maxUsers; + } + + public void setMaxUsers(long maxUsers) { + this.maxUsers = maxUsers; + } + + public double getLoad() { + return load; + } + + public void setLoad(double load) { + this.load = load; + } + @Override public int hashCode() { return meetingId == null ? 127846 : meetingId.hashCode(); diff --git a/src/main/java/org/olat/modules/bigbluebutton/model/BigBlueButtonServerInfos.java b/src/main/java/org/olat/modules/bigbluebutton/model/BigBlueButtonServerInfos.java new file mode 100644 index 00000000000..d0162ffd385 --- /dev/null +++ b/src/main/java/org/olat/modules/bigbluebutton/model/BigBlueButtonServerInfos.java @@ -0,0 +1,107 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.modules.bigbluebutton.model; + +import java.util.List; +import java.util.stream.Collectors; + +import org.olat.modules.bigbluebutton.BigBlueButtonServer; + +/** + * + * Initial date: 8 avr. 2020<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class BigBlueButtonServerInfos { + + private double load; + private final BigBlueButtonServer server; + private final List<BigBlueButtonMeetingInfos> meetingsInfos; + + public BigBlueButtonServerInfos(BigBlueButtonServer server, List<BigBlueButtonMeetingInfos> meetingsInfos, double load) { + this.load = load; + this.server = server; + this.meetingsInfos = meetingsInfos; + } + + public double getLoad() { + return load; + } + + public BigBlueButtonServer getServer() { + return server; + } + + public List<BigBlueButtonMeetingInfos> getMeetingsInfos() { + return meetingsInfos; + } + + public Long getModeratorCount() { + return meetingsInfos.stream() + .filter(BigBlueButtonMeetingInfos::isRunning) + .collect(Collectors.summingLong(BigBlueButtonMeetingInfos::getModeratorCount)); + } + + public Long getParticipantCount() { + return meetingsInfos.stream() + .filter(BigBlueButtonMeetingInfos::isRunning) + .collect(Collectors.summingLong(BigBlueButtonMeetingInfos::getParticipantCount)); + } + + public Long getListenerCount() { + return meetingsInfos.stream() + .filter(BigBlueButtonMeetingInfos::isRunning) + .collect(Collectors.summingLong(BigBlueButtonMeetingInfos::getListenerCount)); + } + + public Long getVoiceParticipantCount() { + return meetingsInfos.stream() + .filter(BigBlueButtonMeetingInfos::isRunning) + .collect(Collectors.summingLong(BigBlueButtonMeetingInfos::getVoiceParticipantCount)); + } + + public Long getVideoCount() { + return meetingsInfos.stream() + .filter(BigBlueButtonMeetingInfos::isRunning) + .collect(Collectors.summingLong(BigBlueButtonMeetingInfos::getVideoCount)); + } + + public Long getMaxUsers() { + return meetingsInfos.stream() + .filter(BigBlueButtonMeetingInfos::isRunning) + .collect(Collectors.summingLong(BigBlueButtonMeetingInfos::getMaxUsers)); + } + + public long getRecordingMeetings() { + return meetingsInfos.stream() + .filter(BigBlueButtonMeetingInfos::isRunning) + .filter(BigBlueButtonMeetingInfos::isRecording) + .count(); + } + + public long getBreakoutRecordingMeetings() { + return meetingsInfos.stream() + .filter(BigBlueButtonMeetingInfos::isRunning) + .filter(BigBlueButtonMeetingInfos::isRecording) + .filter(BigBlueButtonMeetingInfos::isBreakout) + .count(); + } +} 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 c3dd16119d4..4ac6053ca0e 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonAdminController.java +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonAdminController.java @@ -47,7 +47,7 @@ import org.olat.core.util.resource.OresHelper; public class BigBlueButtonAdminController extends BasicController implements Activateable2 { private Link configurationLink; - //private final Link serversLink; + private final Link serversLink; private final Link meetingsLink; private final Link templatesLink; private final Link calendarLink; @@ -55,7 +55,8 @@ public class BigBlueButtonAdminController extends BasicController implements Act private final VelocityContainer mainVC; private final boolean configurationReadOnly; - + + private BigBlueButtonAdminServersController serversCtrl; private BigBlueButtonConfigurationController configCtrl; private BigBlueButtonAdminMeetingsController meetingsCtrl; private BigBlueButtonAdminTemplatesController templatesCtrl; @@ -74,8 +75,8 @@ public class BigBlueButtonAdminController extends BasicController implements Act configurationLink = LinkFactory.createLink("account.configuration", mainVC, this); segmentView.addSegment(configurationLink, true); } - //serversLink = LinkFactory.createLink("servers.title", mainVC, this); - //segmentView.addSegment(serversLink, false); + serversLink = LinkFactory.createLink("servers.title", mainVC, this); + segmentView.addSegment(serversLink, false); templatesLink = LinkFactory.createLink("templates.title", mainVC, this); segmentView.addSegment(templatesLink, false); meetingsLink = LinkFactory.createLink("meetings.title", mainVC, this); @@ -115,6 +116,9 @@ public class BigBlueButtonAdminController extends BasicController implements Act } else if("Calendar".equalsIgnoreCase(type)) { doOpenCalendar(ureq); segmentView.select(calendarLink); + } else if("Servers".equalsIgnoreCase(type)) { + doOpenServers(ureq); + segmentView.select(serversLink); } } @@ -127,12 +131,14 @@ public class BigBlueButtonAdminController extends BasicController implements Act Component clickedLink = mainVC.getComponent(segmentCName); if (clickedLink == configurationLink) { doOpenConfiguration(ureq); - } else if (clickedLink == templatesLink){ + } else if (clickedLink == templatesLink) { doOpenTemplates(ureq); - } else if (clickedLink == meetingsLink){ + } else if (clickedLink == meetingsLink) { doOpenMeetings(ureq); - } else if (clickedLink == calendarLink){ + } else if (clickedLink == calendarLink) { doOpenCalendar(ureq); + } else if (clickedLink == serversLink) { + doOpenServers(ureq); } } } @@ -182,4 +188,14 @@ public class BigBlueButtonAdminController extends BasicController implements Act mainVC.put("segmentCmp", calendarsCtrl.getInitialComponent()); } + private void doOpenServers(UserRequest ureq) { + if(serversCtrl == null) { + WindowControl bwControl = addToHistory(ureq, OresHelper.createOLATResourceableInstance("Servers", 0l), null); + serversCtrl = new BigBlueButtonAdminServersController(ureq, bwControl); + listenTo(serversCtrl); + } else { + addToHistory(ureq, serversCtrl); + } + mainVC.put("segmentCmp", serversCtrl.getInitialComponent()); + } } diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonAdminServersController.java b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonAdminServersController.java index b9d9fa5ccf5..62285767ad8 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonAdminServersController.java +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonAdminServersController.java @@ -19,11 +19,24 @@ */ package org.olat.modules.bigbluebutton.ui; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + 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.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.modules.bigbluebutton.BigBlueButtonManager; +import org.olat.modules.bigbluebutton.BigBlueButtonServer; +import org.olat.modules.bigbluebutton.model.BigBlueButtonServerInfos; +import org.olat.modules.bigbluebutton.ui.BigBlueButtonAdminServersTableModel.ServersCols; +import org.springframework.beans.factory.annotation.Autowired; /** * @@ -33,21 +46,59 @@ import org.olat.core.gui.control.WindowControl; */ public class BigBlueButtonAdminServersController extends FormBasicController { + private FlexiTableElement serversTableEl; + private BigBlueButtonAdminServersTableModel serversTableModel; + + @Autowired + private BigBlueButtonManager bigBlueButtonManager; + public BigBlueButtonAdminServersController(UserRequest ureq, WindowControl wControl) { - super(ureq, wControl); + super(ureq, wControl, "servers_admin"); initForm(ureq); + loadModel(); } @Override protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { - // + + FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel(); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ServersCols.enabled)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ServersCols.url)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ServersCols.capacityFactor, new CapacityFactorCellRenderer())); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ServersCols.moderatorCount)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ServersCols.participantCount)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ServersCols.listenerCount)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ServersCols.voiceParticipantCount)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ServersCols.videoCount)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ServersCols.maxUsers)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ServersCols.recordingMeetings)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ServersCols.breakoutRecordingMeetings)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ServersCols.load, new CapacityFactorCellRenderer())); + + serversTableModel = new BigBlueButtonAdminServersTableModel(columnsModel, getLocale()); + + serversTableEl = uifactory.addTableElement(getWindowControl(), "servers", serversTableModel, 10, false, getTranslator(), formLayout); + serversTableEl.setCustomizeColumns(true); + serversTableEl.setEmtpyTableMessageKey("bigbluebutton.servers.empty"); } @Override protected void doDispose() { // } + + private void loadModel() { + List<BigBlueButtonServer> servers = bigBlueButtonManager.getServers(); + List<BigBlueButtonServerInfos> serversInfos = bigBlueButtonManager.getServersInfos(); + Map<BigBlueButtonServer, BigBlueButtonServerInfos> serversToInfos = serversInfos.stream() + .collect(Collectors.toMap(BigBlueButtonServerInfos::getServer, infos -> infos, (u, v) -> u)); + List<BigBlueButtonServerRow> rows = servers.stream() + .map(server -> new BigBlueButtonServerRow(server, serversToInfos.get(server))) + .collect(Collectors.toList()); + serversTableModel.setObjects(rows); + serversTableEl.reset(true, true, true); + } @Override protected void formOK(UserRequest ureq) { diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonAdminServersTableModel.java b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonAdminServersTableModel.java index 39c4538ad4e..3abcef753e2 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonAdminServersTableModel.java +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonAdminServersTableModel.java @@ -23,9 +23,9 @@ import java.util.Locale; import org.olat.core.commons.persistence.SortKey; import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiTableDataModel; +import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiSortableColumnDef; import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel; import org.olat.core.gui.components.form.flexible.impl.elements.table.SortableFlexiTableDataModel; -import org.olat.modules.bigbluebutton.BigBlueButtonServer; /** * @@ -33,8 +33,10 @@ import org.olat.modules.bigbluebutton.BigBlueButtonServer; * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ -public class BigBlueButtonAdminServersTableModel extends DefaultFlexiTableDataModel<BigBlueButtonServer> -implements SortableFlexiTableDataModel<BigBlueButtonServer> { +public class BigBlueButtonAdminServersTableModel extends DefaultFlexiTableDataModel<BigBlueButtonServerRow> +implements SortableFlexiTableDataModel<BigBlueButtonServerRow> { + + private static final ServersCols[] COLS = ServersCols.values(); private final Locale locale; @@ -50,20 +52,69 @@ implements SortableFlexiTableDataModel<BigBlueButtonServer> { @Override public Object getValueAt(int row, int col) { - // TODO Auto-generated method stub - return null; + BigBlueButtonServerRow server = getObject(row); + return getValueAt(server, col); } @Override - public Object getValueAt(BigBlueButtonServer row, int col) { - return null; + public Object getValueAt(BigBlueButtonServerRow row, int col) { + switch(COLS[col]) { + case url: return row.getUrl(); + case enabled: return row.isEnabled(); + case capacityFactor: return row.getCapacityFactor(); + case moderatorCount: return row.getModeratorCount(); + case participantCount: return row.getParticipantCount(); + case listenerCount: return row.getListenerCount(); + case voiceParticipantCount: return row.getVoiceParticipantCount(); + case videoCount: return row.getVideoCount(); + case maxUsers: return row.getMaxUsers(); + case recordingMeetings: return row.getRecordingMeetings(); + case breakoutRecordingMeetings: return row.getBreakoutRecordingMeetings(); + case load: return row.getLoad(); + default: return "ERROR"; + } } @Override - public DefaultFlexiTableDataModel<BigBlueButtonServer> createCopyWithEmptyList() { - return null; + public BigBlueButtonAdminServersTableModel createCopyWithEmptyList() { + return new BigBlueButtonAdminServersTableModel(getTableColumnModel(), locale); } - + public enum ServersCols implements FlexiSortableColumnDef { + + url("table.header.server.url"), + enabled("table.header.server.enabled"), + capacityFactor("table.header.capacity.factor"), + moderatorCount("table.header.moderator.count"), + participantCount("table.header.participant.count"), + listenerCount("table.header.listener.count"), + voiceParticipantCount("table.header.voice.participant.count"), + videoCount("table.header.video.count"), + maxUsers("table.header.max.users"), + recordingMeetings("table.header.recording.meetings"), + breakoutRecordingMeetings("table.header.breakout.recording.meetings"), + load("table.header.load"); + + private final String i18nHeaderKey; + + private ServersCols(String i18nHeaderKey) { + this.i18nHeaderKey = i18nHeaderKey; + } + + @Override + public boolean sortable() { + return true; + } + + @Override + public String sortKey() { + return name(); + } + + @Override + public String i18nHeaderKey() { + return i18nHeaderKey; + } + } } diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonServerRow.java b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonServerRow.java new file mode 100644 index 00000000000..e2f3cf0517e --- /dev/null +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonServerRow.java @@ -0,0 +1,89 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.modules.bigbluebutton.ui; + +import org.olat.modules.bigbluebutton.BigBlueButtonServer; +import org.olat.modules.bigbluebutton.model.BigBlueButtonServerInfos; + +/** + * + * Initial date: 8 avr. 2020<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class BigBlueButtonServerRow { + + private final BigBlueButtonServer server; + private final BigBlueButtonServerInfos serverInfos; + + public BigBlueButtonServerRow(BigBlueButtonServer server, BigBlueButtonServerInfos serverInfos) { + this.server = server; + this.serverInfos = serverInfos; + } + + public String getUrl() { + return server.getUrl(); + } + + public boolean isEnabled() { + return server.isEnabled(); + } + + public Double getCapacityFactor() { + return server.getCapacityFactory(); + } + + public double getLoad() { + return serverInfos.getLoad(); + } + + public Long getModeratorCount() { + return serverInfos.getModeratorCount(); + } + + public Long getParticipantCount() { + return serverInfos.getParticipantCount(); + } + + public Long getListenerCount() { + return serverInfos.getListenerCount(); + } + + public Long getVoiceParticipantCount() { + return serverInfos.getVoiceParticipantCount(); + } + + public Long getVideoCount() { + return serverInfos.getVideoCount(); + } + + public Long getMaxUsers() { + return serverInfos.getMaxUsers(); + } + + public Long getRecordingMeetings() { + return serverInfos.getRecordingMeetings(); + } + + public Long getBreakoutRecordingMeetings() { + return serverInfos.getBreakoutRecordingMeetings(); + } + +} diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/CapacityFactorCellRenderer.java b/src/main/java/org/olat/modules/bigbluebutton/ui/CapacityFactorCellRenderer.java new file mode 100644 index 00000000000..ac392ff2caf --- /dev/null +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/CapacityFactorCellRenderer.java @@ -0,0 +1,51 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.modules.bigbluebutton.ui; + +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.Locale; + +import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiCellRenderer; +import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableComponent; +import org.olat.core.gui.render.Renderer; +import org.olat.core.gui.render.StringOutput; +import org.olat.core.gui.render.URLBuilder; +import org.olat.core.gui.translator.Translator; + +/** + * + * Initial date: 8 avr. 2020<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class CapacityFactorCellRenderer implements FlexiCellRenderer { + + private final DecimalFormat doubleFormat = new DecimalFormat("#0.#", new DecimalFormatSymbols(Locale.ENGLISH)); + + @Override + public void render(Renderer renderer, StringOutput target, Object cellValue, int row, FlexiTableComponent source, + URLBuilder ubu, Translator translator) { + if(cellValue instanceof Double) { + Double val = (Double)cellValue; + target.append(doubleFormat.format(val.doubleValue())); + } + } +} diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/_content/servers_admin.html b/src/main/java/org/olat/modules/bigbluebutton/ui/_content/servers_admin.html new file mode 100644 index 00000000000..abaade57c0a --- /dev/null +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/_content/servers_admin.html @@ -0,0 +1 @@ +$r.render("servers") \ No newline at end of file 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 6e2fa664a0c..7049cc84d66 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 @@ -1,4 +1,4 @@ -#Thu Mar 19 21:29:32 CET 2020 +#Wed Apr 08 20:40:23 CEST 2020 account.configuration=Konfiguration add.daily.meeting=T\u00E4glich wiederkehrende Online-Termine hinzuf\u00FCgen add.meeting=Online-Termin hinzuf\u00FCgen @@ -9,7 +9,7 @@ add.template=Raumvorlage erstellen add.weekly.meeting=W\u00F6chentlich wiederkehrende Online-Termine hinzuf\u00FCgen admin.menu.title=BigBlueButton admin.menu.title.alt=BigBlueButton Web Conferencing -bigbluebutton.intro=BigBlueButton ist ein Open Source Web Conferencing System speziell f\u00fcr eLearning. Weitere Informationen zu BigBlueButton findet sich auf der <a href='https://bigbluebutton.org' target='_blank'>BigBlueButton Webseite</a> und dem <a href='https://github.com/bigbluebutton' target='_blank'>BigBlueButton GitHub Repository</a> +bigbluebutton.intro=BigBlueButton ist ein Open Source Web Conferencing System speziell f\u00FCr eLearning. Weitere Informationen zu BigBlueButton findet sich auf der <a href\='https\://bigbluebutton.org' target\='_blank'>BigBlueButton Webseite</a> und dem <a href\='https\://github.com/bigbluebutton' target\='_blank'>BigBlueButton GitHub Repository</a> bigbluebutton.module.enabled=Modul "BigBlueButton" bigbluebutton.module.enabled.for=Aktivieren f\u00FCr bigbluebutton.module.enabled.for.courses=Kurse @@ -32,12 +32,12 @@ connection.failed=BigBlueButton API Login fehlgeschlagen. connection.successful=BigBlueButton API Login erfolgreich\! edit.server=Server "{0}" bearbeiten edit.template=Raumvorlage "{0}" bearbeiten -enable.permanent.meeting=Online-Termine ohne Datum enable.adhoc.meeting=Adhoc Online-Termin +enable.permanent.meeting=Online-Termine ohne Datum error.capacity.factory=Ein Nummer zwischen 1.0 und 100.0 -error.connectionValidationFailed=Die Verbindungspr\u00fcfung ist fehlgeschlagen: <pre>{0}</pre> +error.connectionValidationFailed=Die Verbindungspr\u00FCfung ist fehlgeschlagen\: <pre>{0}</pre> error.date.in.past=Der Termin kann sich nicht in der Vergangenheit befinden. -error.duration=Termindauer \u00fcberschritten. Maximal Dauer: {0} Minuten. +error.duration=Termindauer \u00FCberschritten. Maximal Dauer\: {0} Minuten. error.end.past=Der Online-Termin kann nicht in Vergangenheit geplant werden. error.first.date.in.past=Der erste Termin kann sich nicht in der Vergangenheit befinden. error.prefix=Ein Fehler ist aufgetreten\: @@ -46,11 +46,11 @@ error.server.exists=Ein Server mit diesem URL existiert schon. error.server.raw={1} <small>Schl\u00FCssel\: {0}</small> error.start.after.end=Das Enddatum darf nicht vor dem Beginndatum sein. error.too.long.time=Zeit ist zu lang. Es sind maximal {0} Minuten erlaubt. -error.url.invalid=Ung\u00fcltige Serveradresse -meeting.create.intro=Der Online-Termin wurde vom Betreuer noch nicht er\u00F6ffnet. Teilnehmer k\u00F6nnen den Raum f\u00fcr noch nicht betreten. +error.url.invalid=Ung\u00FCltige Serveradresse +meeting.create.intro=Der Online-Termin wurde vom Betreuer noch nicht er\u00F6ffnet. Teilnehmer k\u00F6nnen den Raum f\u00FCr noch nicht betreten. meeting.day=Datum des Meetings -meeting.description=Beschreibung meeting.deleted=Das Meeting wurde erfolgreich gel\u00F6scht. +meeting.description=Beschreibung meeting.end=Ende meeting.ended=Der Online-Termin wurde bereits beendet. meeting.followupTime=Nachlaufzeit (Min.) @@ -73,45 +73,57 @@ meetings.title=Online-Termine meetings.upcoming=Aktuelle und zuk\u00FCnftige Online-Termine minutes={0} Min. no.meeting.configured=Zur Zeit sind keine Online-Termine vorhanden. -no.template.configured=Es sind keine aktiven Raumvorlagen vorhanden. no.recordings=Es ist zur Zeit noch keine Aufzeichnung f\u00FCr diesen Online-Termin vorhanden. +no.template.configured=Es sind keine aktiven Raumvorlagen vorhanden. no.upcoming.meetings=Zur Zeit sind keine aktuellen oder zuk\u00FCnftigen Online-Termine vorhanden. option.baseurl=BigBlueButton API URL option.baseurl.example=https\://bigbluebutton.openolat.com/bigbluebutton/ option.bigbluebutton.secret=Secret option.bigbluebutton.shared.secret=Shared secret -option.capacity.factory=Capacity factor option.capacity.factor.example=Ein Nummer zwischen 1 und 100 (zum Beispiel 1.23) +option.capacity.factory=Capacity factor option.enabled.server=Server aktivieren option.recordingurl=Aufzeichnung URL -recordings=Aufzeichnungen recording.browser.infos=Aufzeichnungen k\u00F6nnen nur mit Google Chrome und Firefox gesehen werden. recording.type.podcast=Podcast recording.type.presentation=Pr\u00E4sentation +recordings=Aufzeichnungen role.administrator=Administrator role.author=Autor role.coach=Betreuer -role.owner=Kursbesitzer role.group=Gruppenmitglied +role.owner=Kursbesitzer server.overloaded=F\u00FCr das gew\u00E4hlten Datum/Uhrzeit ist kein Raum verf\u00FCgbar. W\u00E4hlen Sie ein anderes Datum/Uhrzeit oder eine andere Raumvorlage. servers.title=Server -table.header.day.week=Tag table.header.available=Verf\u00FCgbarkeit +table.header.breakout.meetings=\# Breakout +table.header.breakout.recording.meetings=\# Breakout Recording +table.header.capacity.factor=Kapazit\u00E4t +table.header.day.week=Tag table.header.enabled=Aktiv -table.header.permanent=Ohne Datum -table.header.system=System +table.header.listener.count=\# Listeners +table.header.load=Load table.header.max.concurrent.meetings=R\u00E4ume table.header.max.duration=Dauer table.header.max.participants=Teilnehmer +table.header.max.users=Max. Benutzer +table.header.moderator.count=\# Moderatoren +table.header.number.meetings=\# Meetings +table.header.participant.count=\# Teilnehmer +table.header.permanent=Ohne Datum +table.header.recording.end=Ende +table.header.recording.meetings=\# Recordings table.header.recording.name=Name -table.header.recording.type=Typ table.header.recording.open=\u00D6ffnen table.header.recording.start=Beginn -table.header.recording.end=Ende +table.header.recording.type=Typ table.header.server.enabled=Eingeschaltet table.header.server.recording=Aufzeichnungen URL table.header.server.url=URL +table.header.system=System table.header.template=Raumvorlage +table.header.video.count=\# Video +table.header.voice.participant.count=\# Voices table.header.webcams.only.moderator=Nur Moderatorenkamera template.allowModsToUnmuteUsers=Moderatoren d\u00FCrfen Teilnehmer-Mikrofon aktiveren template.allowStartStopRecording=Starten und Stoppen von Aufzeichnungen zulassen @@ -119,21 +131,21 @@ template.autoStartRecording=Aufzeichnung automatisch starten template.breakout=Breakout-R\u00E4me zulassen template.description=Beschreibung template.enabled=Raumvorlage aktivieren -template.explain.max.participants=Max. Anzahl Teilnehmer: {0} ({1} R\u00E4ume verf\u00FCgbar) -template.explain.max.participants.with.webcams.mod=Max. Anzahl Teilnehmer: {0}, nur Moderatorenkamera ({1} R\u00E4ume verf\u00FCgbar) +template.explain.max.participants=Max. Anzahl Teilnehmer\: {0} ({1} R\u00E4ume verf\u00FCgbar) +template.explain.max.participants.with.webcams.mod=Max. Anzahl Teilnehmer\: {0}, nur Moderatorenkamera ({1} R\u00E4ume verf\u00FCgbar) template.lock=F\u00FCr gesperrte Teilnehmer... template.lockSettingsDisableCam=Kamera ausschalten template.lockSettingsDisableMic=Mikrofon ausschalten template.lockSettingsDisableNote=Gmeinsame Notizen ausschalten template.lockSettingsDisablePrivateChat=Privater Chat ausschalten -template.lockSettingsDisablePublicChat=Öffentlicher Chat ausschalten -template.lockSettingsLockedLayout=Layoutanpassung ausschalten +template.lockSettingsDisablePublicChat=\u00D6ffentlicher Chat ausschalten template.lockSettingsHideUserList=Teilnehmerliste ausschalten template.lockSettingsLockOnJoin=Teilnehmer bei Eintritt automatisch sperren template.lockSettingsLockOnJoinConfigurable=Ausschalten der automatischen Sperre durch Moderator verhindern +template.lockSettingsLockedLayout=Layoutanpassung ausschalten +template.max.concurrent.meetings=Anzahl R\u00E4ume template.maxDuration=Dauer (Minuten) template.maxParticipants=Anzahl Teilnehmer -template.max.concurrent.meetings=Anzahl R\u00E4ume template.muteOnStart=Teilnehmer-Mikrofon beim Eintritt deaktivieren template.name=Raumname template.record=Aufzeichnungen des Online-Termins zulassen 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 a7ff2b03d5e..0a56e87a859 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 @@ -1,14 +1,15 @@ -#Thu Mar 19 21:29:07 CET 2020 +#Wed Apr 08 20:38:54 CEST 2020 account.configuration=Configuration add.daily.meeting=Add daily recurring meeting add.meeting=Add online-meeting add.permanent.meeting=Add permanent meeting room +add.server=Add server add.single.meeting=Add single meeting add.template=New room template add.weekly.meeting=Add weekly recurring meeting admin.menu.title=BigBlueButton admin.menu.title.alt=BigBlueButton Web Conferencing -bigbluebutton.intro=BigBlueButton is an Open Source Web Conferencing System designed for learning. More information about BigBlueButton can be found on the <a href='https://bigbluebutton.org' target='_blank'>BigBlueButton web page</a> and the <a href='https://github.com/bigbluebutton' target='_blank'>BigBlueButton GitHub repository</a> +bigbluebutton.intro=BigBlueButton is an Open Source Web Conferencing System designed for learning. More information about BigBlueButton can be found on the <a href\='https\://bigbluebutton.org' target\='_blank'>BigBlueButton web page</a> and the <a href\='https\://github.com/bigbluebutton' target\='_blank'>BigBlueButton GitHub repository</a> bigbluebutton.module.enabled=Module "BigBlueButton" bigbluebutton.module.enabled.for=Activate for bigbluebutton.module.enabled.for.courses=Courses @@ -31,12 +32,12 @@ connection.failed=BigBlueButton API login failed. connection.successful=BigBlueButton API login successful\! edit.server=Edit server "{0}" edit.template=Edit room-template "{0}" -enable.permanent.meeting=Online-Meetings without date enable.adhoc.meeting=Adhoc online-meeting +enable.permanent.meeting=Online-Meetings without date error.capacity.factory=A number between 1.0 and 100.0 -error.connectionValidationFailed=The connection validation failed: <pre>{0}</pre> +error.connectionValidationFailed=The connection validation failed\: <pre>{0}</pre> error.date.in.past=The meeting date can not be in the past. -error.duration=Meeting duration is too long. Maximal duration: {0} minutes. +error.duration=Meeting duration is too long. Maximal duration\: {0} minutes. error.end.past=Online-meeting cannot be planned in the past. error.first.date.in.past=The first meeting date can not be in the past. error.prefix=An error happened\: @@ -48,8 +49,8 @@ error.too.long.time=Time is too long. It is limited to {0} minutes. error.url.invalid=Invalid server URL meeting.create.intro=The meeting has not yet been started by the coach. Participants are not able to enter the classroom. meeting.day=Date of the meeting -meeting.description=Description meeting.deleted=The meeting was successfully deleted. +meeting.description=Description meeting.end=End date meeting.ended=The online-meeting has already ended. meeting.followupTime=Follow-up (min.) @@ -79,37 +80,50 @@ option.baseurl=BigBlueButton API URL option.baseurl.example=https\://bigbluebutton.openolat.com/bigbluebutton/ option.bigbluebutton.secret=Secret option.bigbluebutton.shared.secret=Shared secret +option.capacity.factor.example=A number between 1 and 100 (for example\: 1.23) option.capacity.factory=Capacity factor -option.capacity.factor.example=A number between 1 and 100 (for example: 1.23) option.enabled.server=Activate server option.recordingurl=Recording URL -recordings=Recordings recording.browser.infos=Recordings can only be viewed with Google Chrome or Firefox. recording.type.podcast=Podcast recording.type.presentation=Presentation +recordings=Recordings role.administrator=Administrator role.author=Author role.coach=Coach -role.owner=Course owner role.group=Group user +role.owner=Course owner server.overloaded=There is no room available for the choosen date/time. Choose another date/time or another room template. servers.title=Servers +table.header.available=Availability +table.header.breakout.meetings=\# Breakout +table.header.breakout.recording.meetings=\# Breakout Recording +table.header.capacity.factor=Capacity table.header.day.week=Day table.header.enabled=Enabled -table.header.permanent=Withour date -table.header.system=System +table.header.listener.count=\# Listeners +table.header.load=Load table.header.max.concurrent.meetings=Rooms table.header.max.duration=Duration table.header.max.participants=Participants +table.header.max.users=Max. users +table.header.moderator.count=\# Moderator +table.header.number.meetings=\# Meetings +table.header.participant.count=\# Participants +table.header.permanent=Withour date +table.header.recording.end=End +table.header.recording.meetings=\# Recordings table.header.recording.name=Name -table.header.recording.type=Type table.header.recording.open=Open table.header.recording.start=Start -table.header.recording.end=End +table.header.recording.type=Type table.header.server.enabled=Enabled table.header.server.recording=Recording URL table.header.server.url=URL +table.header.system=System table.header.template=Room-template +table.header.video.count=\# Video +table.header.voice.participant.count=\# Voices table.header.webcams.only.moderator=Webcams only for moderators template.allowModsToUnmuteUsers=Allow moderators to activate the participants microphone template.allowStartStopRecording=Allow to start / stop of recordings @@ -117,21 +131,21 @@ template.autoStartRecording=Automatically start recording template.breakout=Allow breakout rooms template.description=Description template.enabled=Enable room-template -template.explain.max.participants=Max. number of participants: {0} ({1} rooms available) -template.explain.max.participants.with.webcams.mod=Max. number of participants: {0}, only moderator webcam ({1} rooms available) +template.explain.max.participants=Max. number of participants\: {0} ({1} rooms available) +template.explain.max.participants.with.webcams.mod=Max. number of participants\: {0}, only moderator webcam ({1} rooms available) template.lock=For locked participants... template.lockSettingsDisableCam=disable webcam template.lockSettingsDisableMic=disable microphone template.lockSettingsDisableNote=disable shared-notes template.lockSettingsDisablePrivateChat=disable private chat template.lockSettingsDisablePublicChat=disable public chat -template.lockSettingsLockedLayout=disable layout changes template.lockSettingsHideUserList=disable participant list template.lockSettingsLockOnJoin=Put participants into lock mode on join template.lockSettingsLockOnJoinConfigurable=Prevent disabling of the automatic locking by the moderator +template.lockSettingsLockedLayout=disable layout changes +template.max.concurrent.meetings=Number of rooms template.maxDuration=Duration (minutes) template.maxParticipants=Number of participants -template.max.concurrent.meetings=Number of rooms template.muteOnStart=Disable participant microphone on join template.name=Room name template.record=Allow meeting recording @@ -141,7 +155,7 @@ templates.title=Room-templates undelete=Reactivate view=View view.template=Room-template "{0}" +warning.at.least.one.meeting=You must select at least one meeting. warning.template.in.use=The room-template cannot be deleted because it is used by online-meetings. Delete the corresponding online-meeting or de-activate the room-template. wizard.dates.title=Dates wizard.meeting.title=Configuration - -- GitLab