diff --git a/src/main/java/org/olat/core/commons/modules/bc/meta/_content/external_url.html b/src/main/java/org/olat/core/commons/modules/bc/meta/_content/external_url.html index 247750d5f24c7780901db413030337679b6e6081..9afbc241bc92035cf824e6c8cca17a57400bacae 100644 --- a/src/main/java/org/olat/core/commons/modules/bc/meta/_content/external_url.html +++ b/src/main/java/org/olat/core/commons/modules/bc/meta/_content/external_url.html @@ -1,8 +1,6 @@ <div class="o_copy_code o_nowrap form-control-static"><a href="javascript:;" id="o_extlink"><i class="o_icon o_icon-lg o_icon-fw o_icon_qrcode"> </i></a><input type="text" value="$resourceUrl" onclick="this.select()"/></div> <script> -/* <![CDATA[ */ - jQuery(function() { - o_QRCodePopup('o_extlink', '$resourceUrl', 'right'); - }); -/* ]]> */ +jQuery(function() { + o_QRCodePopup('o_extlink', '$resourceUrl', 'right'); +}); </script> \ No newline at end of file diff --git a/src/main/java/org/olat/core/commons/services/taskexecutor/TaskExecutorManager.java b/src/main/java/org/olat/core/commons/services/taskexecutor/TaskExecutorManager.java index b38be44b61c0fc4b890cb2877dbf7cb3fbbac220..ad38f144512ab526ed571acc724b68714576640f 100644 --- a/src/main/java/org/olat/core/commons/services/taskexecutor/TaskExecutorManager.java +++ b/src/main/java/org/olat/core/commons/services/taskexecutor/TaskExecutorManager.java @@ -27,6 +27,7 @@ package org.olat.core.commons.services.taskexecutor; import java.util.Date; import java.util.List; +import java.util.TimerTask; import java.util.concurrent.Executor; import org.olat.core.id.Identity; @@ -101,5 +102,16 @@ public interface TaskExecutorManager extends Executor { * @param resSubPath The sub path (cannot be null) */ public void delete(OLATResource resource, String resSubPath); + + /** + * This is a light weight, not clustered way to delay a task + * a few seconds. Don't abuse of it, only delay of a few seconds + * is acceptable because the tasks are serialized and the task is + * hold in memory. + * + * @param task + * @param delay + */ + public void schedule(TimerTask task, long delay); } diff --git a/src/main/java/org/olat/core/commons/services/taskexecutor/manager/TaskExecutorManagerImpl.java b/src/main/java/org/olat/core/commons/services/taskexecutor/manager/TaskExecutorManagerImpl.java index 68a6839efb14d3d75a0c02ab932938c35ff198eb..03b4c3221b25b850a57d9d00f4a5485abdf66ab4 100644 --- a/src/main/java/org/olat/core/commons/services/taskexecutor/manager/TaskExecutorManagerImpl.java +++ b/src/main/java/org/olat/core/commons/services/taskexecutor/manager/TaskExecutorManagerImpl.java @@ -28,10 +28,13 @@ package org.olat.core.commons.services.taskexecutor.manager; import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Timer; +import java.util.TimerTask; import java.util.UUID; import java.util.concurrent.ExecutorService; import java.util.concurrent.RejectedExecutionException; +import org.apache.logging.log4j.Logger; import org.olat.core.commons.persistence.DB; import org.olat.core.commons.services.taskexecutor.LongRunnable; import org.olat.core.commons.services.taskexecutor.LowPriorityRunnable; @@ -45,7 +48,6 @@ import org.olat.core.commons.services.taskexecutor.model.PersistentTask; import org.olat.core.commons.services.taskexecutor.model.PersistentTaskRunnable; import org.olat.core.id.Identity; import org.olat.core.logging.AssertException; -import org.apache.logging.log4j.Logger; import org.olat.core.logging.Tracing; import org.olat.resource.OLATResource; import org.quartz.JobKey; @@ -73,6 +75,8 @@ public class TaskExecutorManagerImpl implements TaskExecutorManager { private DB dbInstance; private Scheduler scheduler; private PersistentTaskDAO persistentTaskDao; + + private Timer timer = new Timer(); /** * [used by spring] @@ -238,6 +242,11 @@ public class TaskExecutorManagerImpl implements TaskExecutorManager { public void delete(OLATResource resource, String resSubPath) { persistentTaskDao.delete(resource, resSubPath); } + + @Override + public void schedule(TimerTask task, long delay) { + timer.schedule(task, delay); + } public enum Queue { sequential, diff --git a/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonDispatcher.java b/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonDispatcher.java index a6d0c05b18e85a9537eec3fa831e3d693b286676..614884501aff6c2019e02622bd0a548e22aad863 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonDispatcher.java +++ b/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonDispatcher.java @@ -58,9 +58,9 @@ public class BigBlueButtonDispatcher implements Dispatcher { private static final Logger log = Tracing.createLoggerFor(BigBlueButtonDispatcher.class); - private static final String BIGBLUEBUTTON_PATH = "survey"; + private static final String BIGBLUEBUTTON_PATH = "bigbluebutton"; - public static final String getExecutionUrl(String identifier) { + public static final String getMeetingUrl(String identifier) { return new StringBuilder() .append(Settings.getServerContextPathURI()) .append("/") @@ -72,8 +72,6 @@ public class BigBlueButtonDispatcher implements Dispatcher { @Override public void execute(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - - UserRequest ureq = null; final String pathInfo = request.getPathInfo(); String uriPrefix = DispatcherModule.getLegacyUriPrefix(request); diff --git a/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonManager.java b/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonManager.java index 4c9b1d777ce4059fdb13ba2e8617b9854278ac2c..fe714ec51fd3558fe6c725238104a42ff3618a8a 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonManager.java +++ b/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonManager.java @@ -80,6 +80,13 @@ public interface BigBlueButtonManager { public BigBlueButtonMeeting getMeeting(BigBlueButtonMeeting meeting); + /** + * Return the first meeting which matches the specified identifier + * as the meeting's identifier or readable identifier. + * + * @param identifier The identifier + * @return A meeting + */ public BigBlueButtonMeeting getMeeting(String identifier); public BigBlueButtonMeeting updateMeeting(BigBlueButtonMeeting meeting); @@ -87,6 +94,8 @@ public interface BigBlueButtonManager { public boolean deleteMeeting(BigBlueButtonMeeting meeting, BigBlueButtonErrors errors); public BigBlueButtonMeetingTemplate createAndPersistTemplate(String name); + + public boolean isIdentifierInUse(String identifier, BigBlueButtonMeeting reference); public List<BigBlueButtonMeetingTemplate> getTemplates(); diff --git a/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonMeetingTemplate.java b/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonMeetingTemplate.java index 78f4bd411681e456ab071f50dcb26d169de35440..03fbaf10eea6ac007ebc8734b639ec6d6bace0f5 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonMeetingTemplate.java +++ b/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonMeetingTemplate.java @@ -50,6 +50,10 @@ public interface BigBlueButtonMeetingTemplate extends ModifiedInfo, CreateInfo { public void setDescription(String description); + public boolean isExternalUsersAllowed(); + + public void setExternalUsersAllowed(boolean external); + public Integer getMaxConcurrentMeetings(); public void setMaxConcurrentMeetings(Integer maxConcurrentMeetings); 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 3ff2815531637a04d88712d53ca86ddf4af3e51e..730b7962f0e4ab9c8134f87a070c6fe95f43739c 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonManagerImpl.java +++ b/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonManagerImpl.java @@ -336,6 +336,14 @@ public class BigBlueButtonManagerImpl implements BigBlueButtonManager, return null; } + @Override + public boolean isIdentifierInUse(String identifier, BigBlueButtonMeeting reference) { + if(StringHelper.containsNonWhitespace(identifier)) { + return bigBlueButtonMeetingDao.isIdentifierInUse(identifier, reference); + } + return false; + } + @Override public BigBlueButtonMeeting updateMeeting(BigBlueButtonMeeting meeting) { updateCalendarEvent(meeting); 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 b98b2de52839b7d17738a8cf871407fc9183ad2b..8f3d37fe97c8f799788a05c570066b25c130d088 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonMeetingDAO.java +++ b/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonMeetingDAO.java @@ -99,7 +99,7 @@ public class BigBlueButtonMeetingDAO { .append(" left join fetch meeting.businessGroup as businessGroup") .append(" left join fetch meeting.template as template") .append(" left join fetch meeting.server as server") - .append(" where meeting.identifier=:identifier"); + .append(" where meeting.identifier=:identifier or meeting.readableIdentifier=:identifier"); List<BigBlueButtonMeeting> meetings = dbInstance.getCurrentEntityManager() .createQuery(sb.toString(), BigBlueButtonMeeting.class) @@ -108,6 +108,27 @@ public class BigBlueButtonMeetingDAO { return meetings == null || meetings.isEmpty() ? null : meetings.get(0); } + public boolean isIdentifierInUse(String identifier, BigBlueButtonMeeting reference) { + QueryBuilder sb = new QueryBuilder(); + sb.append("select meeting.key from bigbluebuttonmeeting as meeting") + .append(" where (meeting.identifier=:identifier or meeting.readableIdentifier=:identifier)"); + if(reference != null) { + sb.append(" and meeting.key<>:referenceKey"); + } + + TypedQuery<Long> query = dbInstance.getCurrentEntityManager() + .createQuery(sb.toString(), Long.class) + .setParameter("identifier", identifier) + .setFirstResult(0) + .setMaxResults(1); + if(reference != null) { + query.setParameter("referenceKey", reference.getKey()); + } + + List<Long> otherKeys = query.getResultList(); + return otherKeys != null && !otherKeys.isEmpty() && otherKeys.get(0) != null && otherKeys.get(0).intValue() > 0; + } + public List<String> getMeetingsIds(Date from, Date to ) { QueryBuilder sb = new QueryBuilder(); sb.append("select meeting.meetingId from bigbluebuttonmeeting as meeting") 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 fa8019d77dac130f55ebd53ecd0f478fb1975f65..98bddddff713e76d3439f0f595d5600e041544b8 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/model/BigBlueButtonMeetingTemplateImpl.java +++ b/src/main/java/org/olat/modules/bigbluebutton/model/BigBlueButtonMeetingTemplateImpl.java @@ -75,6 +75,9 @@ public class BigBlueButtonMeetingTemplateImpl implements Persistable, BigBlueBut @Column(name="b_max_concurrent_meetings", nullable=true, insertable=true, updatable=true) private Integer maxConcurrentMeetings; + + @Column(name="b_external_users", nullable=false, insertable=true, updatable=true) + private boolean externalAllowed; @Column(name="b_max_participants", nullable=true, insertable=true, updatable=true) private Integer maxParticipants; @@ -200,6 +203,16 @@ public class BigBlueButtonMeetingTemplateImpl implements Persistable, BigBlueBut this.externalId = externalId; } + @Override + public boolean isExternalUsersAllowed() { + return externalAllowed; + } + + @Override + public void setExternalUsersAllowed(boolean externalAllowed) { + this.externalAllowed = externalAllowed; + } + @Override public Integer getMaxConcurrentMeetings() { return maxConcurrentMeetings; 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 2924526e8a6bc4acd054224ab069e8e4d2b4299a..dd39809d3f2b7f9618ba471751eafb11441d6f45 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonAdminTemplatesController.java +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonAdminTemplatesController.java @@ -89,6 +89,7 @@ public class BigBlueButtonAdminTemplatesController extends FormBasicController { columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(BTemplatesCols.maxDuration, new TemplateMinuteCellRenderer(getTranslator()))); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(BTemplatesCols.webcamsOnlyForModerator)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(BTemplatesCols.externalUsers)); if(readOnly) { columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel("view", translate("view"), "view")); } else { diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonGuestJoinController.java b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonGuestJoinController.java index 0927881cd5e7c4d3e3752f8e18afaec55eb66b00..28755a74d9f7fb674da9782daf52355c176513c5 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonGuestJoinController.java +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonGuestJoinController.java @@ -28,14 +28,16 @@ import org.olat.core.gui.components.form.flexible.elements.FormLink; import org.olat.core.gui.components.form.flexible.elements.TextElement; import org.olat.core.gui.components.form.flexible.impl.FormBasicController; import org.olat.core.gui.components.form.flexible.impl.FormEvent; +import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer; import org.olat.core.gui.components.link.Link; import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.Event; import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.media.MediaResource; import org.olat.core.gui.media.RedirectMediaResource; +import org.olat.core.id.IdentityEnvironment; import org.olat.core.id.OLATResourceable; -import org.olat.core.id.Roles; +import org.olat.core.util.Formatter; import org.olat.core.util.StringHelper; import org.olat.core.util.UserSession; import org.olat.core.util.coordinate.CoordinatorManager; @@ -57,6 +59,7 @@ import org.olat.modules.bigbluebutton.BigBlueButtonModule; import org.olat.modules.bigbluebutton.model.BigBlueButtonErrors; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryEntrySecurity; +import org.olat.repository.RepositoryEntryStatusEnum; import org.olat.repository.RepositoryManager; import org.springframework.beans.factory.annotation.Autowired; @@ -77,7 +80,7 @@ public class BigBlueButtonGuestJoinController extends FormBasicController implem private boolean moderatorStartMeeting; private final boolean allowedToMeet; private BigBlueButtonMeeting meeting; - private final OLATResourceable meetingOres; + private OLATResourceable meetingOres; @Autowired private NodeAccessService nodeAccessService; @@ -98,21 +101,42 @@ public class BigBlueButtonGuestJoinController extends FormBasicController implem initForm(ureq); updateButtonsAndStatus(); - meetingOres = OresHelper.createOLATResourceableInstance(BigBlueButtonMeeting.class.getSimpleName(), meeting.getKey()); - CoordinatorManager.getInstance().getCoordinator().getEventBus().registerFor(this, getIdentity(), meetingOres); + if(meeting != null) { + meetingOres = OresHelper.createOLATResourceableInstance(BigBlueButtonMeeting.class.getSimpleName(), meeting.getKey()); + CoordinatorManager.getInstance().getCoordinator().getEventBus().registerFor(this, getIdentity(), meetingOres); + } } @Override protected void doDispose() { - CoordinatorManager.getInstance().getCoordinator().getEventBus().deregisterFor(this, meetingOres); + if(meetingOres != null) { + CoordinatorManager.getInstance().getCoordinator().getEventBus().deregisterFor(this, meetingOres); + } } @Override protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { - boolean end = isEnded(); + + if(formLayout instanceof FormLayoutContainer && meeting != null + && !Boolean.TRUE.equals(ureq.getUserSession().getEntry("meeting-" + meeting.getKey()))) { + FormLayoutContainer layoutCont = (FormLayoutContainer)formLayout; + layoutCont.contextPut("title", meeting.getName()); + if(StringHelper.containsNonWhitespace(meeting.getDescription())) { + layoutCont.contextPut("description", meeting.getDescription()); + } + if(meeting.getStartDate() != null) { + String start = Formatter.getInstance(getLocale()).formatDateAndTime(meeting.getStartDate()); + layoutCont.contextPut("start", start); + } + if(meeting.getEndDate() != null) { + String end = Formatter.getInstance(getLocale()).formatDateAndTime(meeting.getEndDate()); + layoutCont.contextPut("end", end); + } + } nameEl = uifactory.addTextElement("meeting.guest.pseudo", 128, "", formLayout); + boolean end = isEnded(); joinButton = uifactory.addFormLink("meeting.join.button", formLayout, Link.BUTTON_LARGE); joinButton.setElementCssClass("o_sel_bbb_guest_join"); joinButton.setVisible(!end); @@ -156,16 +180,23 @@ public class BigBlueButtonGuestJoinController extends FormBasicController implem private boolean isAllowedToMeet(UserRequest ureq) { if(meeting == null) return false; - if(meeting.getEntry() != null) { - UserSession usess = ureq.getUserSession(); - Roles roles = usess.getRoles(); - RepositoryEntrySecurity reSecurity = repositoryManager.isAllowed(getIdentity(), roles, meeting.getEntry()); + UserSession usess = ureq.getUserSession(); + IdentityEnvironment identEnv = usess.getIdentityEnvironment(); + if(identEnv.getRoles() == null && identEnv.getIdentity() == null) { + boolean externalUsersAllowed = StringHelper.containsNonWhitespace(meeting.getReadableIdentifier()); + if(meeting.getEntry() != null) { + RepositoryEntry re = meeting.getEntry(); + externalUsersAllowed &= re.getEntryStatus() == RepositoryEntryStatusEnum.published; + } + return externalUsersAllowed; + } else if(meeting.getEntry() != null) { + RepositoryEntrySecurity reSecurity = repositoryManager.isAllowed(getIdentity(), identEnv.getRoles(), meeting.getEntry()); if(reSecurity.canLaunch()) { readOnly = reSecurity.isReadOnly(); if(StringHelper.containsNonWhitespace(meeting.getSubIdent())) { RepositoryEntry entry = repositoryManager.lookupRepositoryEntry(meeting.getEntry().getKey()); ICourse course = CourseFactory.loadCourse(entry); - UserCourseEnvironmentImpl uce = new UserCourseEnvironmentImpl(usess.getIdentityEnvironment(), course.getCourseEnvironment()); + UserCourseEnvironmentImpl uce = new UserCourseEnvironmentImpl(identEnv, course.getCourseEnvironment()); CourseTreeNode courseTreeNode = (CourseTreeNode)nodeAccessService.getCourseTreeModelBuilder(uce) .withFilter(AccessibleFilter.create()) .build() @@ -181,6 +212,7 @@ public class BigBlueButtonGuestJoinController extends FormBasicController implem } private boolean isModeratorStartMeeting() { + if(meeting == null) return true; if(meeting.getEntry() != null) { RepositoryEntry entry = repositoryManager.lookupRepositoryEntry(meeting.getEntry().getKey()); ICourse course = CourseFactory.loadCourse(entry); 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 e0b26b50322e48bf4ca30e00eded6afda1d57a98..26606b0fe476a3922c95f54dca7cd4a213ad3b2f 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonMeetingController.java +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonMeetingController.java @@ -21,7 +21,9 @@ package org.olat.modules.bigbluebutton.ui; import java.util.Date; import java.util.List; +import java.util.TimerTask; +import org.olat.core.commons.services.taskexecutor.TaskExecutorManager; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; import org.olat.core.gui.components.form.flexible.FormItem; @@ -48,9 +50,11 @@ import org.olat.core.helpers.Settings; import org.olat.core.id.OLATResourceable; import org.olat.core.util.Formatter; import org.olat.core.util.StringHelper; +import org.olat.core.util.UserSession; import org.olat.core.util.coordinate.CoordinatorManager; import org.olat.core.util.event.GenericEventListener; import org.olat.core.util.resource.OresHelper; +import org.olat.modules.bigbluebutton.BigBlueButtonDispatcher; import org.olat.modules.bigbluebutton.BigBlueButtonManager; import org.olat.modules.bigbluebutton.BigBlueButtonMeeting; import org.olat.modules.bigbluebutton.BigBlueButtonModule; @@ -83,6 +87,8 @@ public class BigBlueButtonMeetingController extends FormBasicController implemen private DialogBoxController confirmDeleteRecordingDialog; + @Autowired + private TaskExecutorManager taskExecutorManager; @Autowired private BigBlueButtonModule bigBlueButtonModule; @Autowired @@ -96,7 +102,8 @@ public class BigBlueButtonMeetingController extends FormBasicController implemen this.readOnly = readOnly; this.moderator = moderator; this.administrator = administrator; - guest = ureq.getUserSession().getRoles().isGuestOnly(); + UserSession usess = ureq.getUserSession(); + guest = usess.getRoles().isGuestOnly(); meetingOres = OresHelper.createOLATResourceableInstance(BigBlueButtonMeeting.class.getSimpleName(), meeting.getKey()); CoordinatorManager.getInstance().getCoordinator().getEventBus().registerFor(this, getIdentity(), meetingOres); moderatorStartMeeting = configuration.isModeratorStartMeeting(); @@ -104,6 +111,10 @@ public class BigBlueButtonMeetingController extends FormBasicController implemen initForm(ureq); updateButtonsAndStatus(); loadRecordingsModel(); + + if(guest) { + usess.putEntryInNonClearedStore("meeting-" + meeting.getKey(), Boolean.TRUE); + } } @Override @@ -128,6 +139,11 @@ public class BigBlueButtonMeetingController extends FormBasicController implemen String end = Formatter.getInstance(getLocale()).formatDateAndTime(meeting.getEndDate()); layoutCont.contextPut("end", end); } + + if((administrator || moderator) && StringHelper.containsNonWhitespace(meeting.getReadableIdentifier())) { + String url = BigBlueButtonDispatcher.getMeetingUrl(meeting.getReadableIdentifier()); + layoutCont.contextPut("externalUrl", url); + } } joinButton = LinkFactory.createButtonLarge("meeting.join.button", flc.getFormItemComponent(), this); @@ -308,8 +324,7 @@ public class BigBlueButtonMeetingController extends FormBasicController implemen BigBlueButtonErrors errors = new BigBlueButtonErrors(); if(moderator || administrator) { meetingUrl = bigBlueButtonManager.join(meeting, getIdentity(), null, (administrator || moderator), false, null, errors); - BigBlueButtonEvent openEvent = new BigBlueButtonEvent(meeting.getKey(), getIdentity().getKey()); - CoordinatorManager.getInstance().getCoordinator().getEventBus().fireEventToListenersOf(openEvent, meetingOres); + delayEvent(new BigBlueButtonEvent(meeting.getKey(), getIdentity().getKey())); } else if(!moderatorStartMeeting) { meetingUrl = bigBlueButtonManager.join(meeting, getIdentity(), null, false, guest, null, errors); } else if(bigBlueButtonManager.isMeetingRunning(meeting)) { @@ -344,4 +359,25 @@ public class BigBlueButtonMeetingController extends FormBasicController implemen } loadRecordingsModel(); } + + private void delayEvent(BigBlueButtonEvent openEvent) { + final EventTask task = new EventTask(openEvent, meetingOres); + taskExecutorManager.schedule(task , 10000); + } + + private static class EventTask extends TimerTask { + + private final BigBlueButtonEvent event; + private final OLATResourceable ores; + + public EventTask(BigBlueButtonEvent event, OLATResourceable ores) { + this.event = event; + this.ores = OresHelper.clone(ores); + } + + @Override + public void run() { + CoordinatorManager.getInstance().getCoordinator().getEventBus().fireEventToListenersOf(event, ores); + } + } } 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 eb48b4f28b991c5c71d22067ca6fa86627b9f7ca..47146431ce4ef8a254d8d420daa4da6e1a7960f9 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonTemplateTableModel.java +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonTemplateTableModel.java @@ -72,6 +72,7 @@ implements SortableFlexiTableDataModel<BigBlueButtonMeetingTemplate> { case maxParticipants: return row.getMaxParticipants(); case maxDuration: return row.getMaxDuration(); case webcamsOnlyForModerator: return row.getWebcamsOnlyForModerator(); + case externalUsers: return row.isExternalUsersAllowed(); default: return "ERROR"; } } @@ -89,7 +90,8 @@ implements SortableFlexiTableDataModel<BigBlueButtonMeetingTemplate> { maxConcurrentMeetings("table.header.max.concurrent.meetings"), maxParticipants("table.header.max.participants"), maxDuration("table.header.max.duration"), - webcamsOnlyForModerator("table.header.webcams.only.moderator"); + webcamsOnlyForModerator("table.header.webcams.only.moderator"), + externalUsers("table.header.external.users"); 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 d66adcba1fe18db93513b1c75d6a9140c9360169..8ecb297cc79cea9b1c5cef1a7109e72ff7010803 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/EditBigBlueButtonMeetingController.java +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/EditBigBlueButtonMeetingController.java @@ -19,6 +19,7 @@ */ package org.olat.modules.bigbluebutton.ui; +import java.net.URI; import java.util.Calendar; import java.util.Date; import java.util.List; @@ -41,6 +42,7 @@ import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController; import org.olat.core.util.StringHelper; import org.olat.group.BusinessGroup; +import org.olat.modules.bigbluebutton.BigBlueButtonDispatcher; import org.olat.modules.bigbluebutton.BigBlueButtonManager; import org.olat.modules.bigbluebutton.BigBlueButtonMeeting; import org.olat.modules.bigbluebutton.BigBlueButtonMeetingLayoutEnum; @@ -71,6 +73,7 @@ public class EditBigBlueButtonMeetingController extends FormBasicController { private SingleSelection templateEl; private SingleSelection layoutEl; private MultipleSelectionElement guestEl; + private TextElement externalLinkEl; private final Mode mode; private final String subIdent; @@ -201,7 +204,10 @@ public class EditBigBlueButtonMeetingController extends FormBasicController { String[] guestValues = new String[] { translate("meeting.guest.on") }; guestEl = uifactory.addCheckboxesHorizontal("meeting.guest", formLayout, onKeys, guestValues); guestEl.setVisible(entry != null && entry.isGuests()); - + + String externalLink = meeting == null ? null : meeting.getReadableIdentifier(); + externalLinkEl = uifactory.addTextElement("meeting.external.users", 64, externalLink, formLayout); + openCalLink = uifactory.addFormLink("calendar.open", formLayout); openCalLink.setIconLeftCSS("o_icon o_icon-fw o_icon_calendar"); updateTemplateInformations(); @@ -269,6 +275,9 @@ public class EditBigBlueButtonMeetingController extends FormBasicController { templateEl.setExampleKey("template.explain.max.participants", args); } } + externalLinkEl.setVisible(template != null && template.isExternalUsersAllowed()); + } else { + externalLinkEl.setVisible(false); } } @@ -312,6 +321,8 @@ public class EditBigBlueButtonMeetingController extends FormBasicController { @Override public boolean validateFormLogic(UserRequest ureq) { boolean allOk = super.validateFormLogic(ureq); + + allOk &= validateReadableIdentifier(); if(mode == Mode.dates) { startDateEl.clearError(); @@ -371,6 +382,32 @@ public class EditBigBlueButtonMeetingController extends FormBasicController { return allOk; } + private boolean validateReadableIdentifier() { + boolean allOk = true; + + externalLinkEl.clearError(); + if(externalLinkEl.isVisible() && StringHelper.containsNonWhitespace(externalLinkEl.getValue())) { + String identifier = externalLinkEl.getValue(); + if(identifier.length() > 64) { + externalLinkEl.setErrorKey("form.error.toolong", new String[] { "64" }); + allOk &= false; + } else if(bigBlueButtonManager.isIdentifierInUse(identifier, meeting)) { + externalLinkEl.setErrorKey("error.identifier.in.use", null); + allOk &= false; + } else { + try { + URI uri = new URI(BigBlueButtonDispatcher.getMeetingUrl(identifier)); + uri.normalize(); + } catch(Exception e) { + externalLinkEl.setErrorKey("error.identifier.url.not.valid", new String[] { e.getMessage() }); + allOk &= false; + } + } + } + + return allOk; + } + private boolean validateTime(TextElement el, long maxValue) { boolean allOk = true; el.clearError(); @@ -505,6 +542,13 @@ public class EditBigBlueButtonMeetingController extends FormBasicController { BigBlueButtonMeetingTemplate template = getSelectedTemplate(); meeting.setTemplate(template); + if(template != null && template.isExternalUsersAllowed() + && externalLinkEl.isVisible() && StringHelper.containsNonWhitespace(externalLinkEl.getValue())) { + meeting.setReadableIdentifier(externalLinkEl.getValue()); + } else { + meeting.setReadableIdentifier(null); + } + meeting.setPermanent(mode == Mode.permanent); if(mode == Mode.permanent) { meeting.setStartDate(null); 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 1989a106e5f6a87c6b492ad0617ef995dc9859c4..70742cba60e8f0ef2ad313c4dd2d77132d0f2fbd 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/EditBigBlueButtonTemplateController.java +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/EditBigBlueButtonTemplateController.java @@ -59,6 +59,7 @@ public class EditBigBlueButtonTemplateController extends FormBasicController { private MultipleSelectionElement enableEl; private MultipleSelectionElement rolesEl; + private MultipleSelectionElement externalEl; private TextElement maxConcurrentMeetingsEl; private TextElement maxParticipantsEl; @@ -141,6 +142,10 @@ public class EditBigBlueButtonTemplateController extends FormBasicController { String maxConcurrentMeetings = template == null || template.getMaxConcurrentMeetings() == null ? "" : template.getMaxConcurrentMeetings().toString(); maxConcurrentMeetingsEl = uifactory.addTextElement("template.max.concurrent.meetings", "template.max.concurrent.meetings", 8, maxConcurrentMeetings, formLayout); + boolean external = template != null && template.isExternalUsersAllowed(); + externalEl = uifactory.addCheckboxesHorizontal("template.external.enabled", "template.external.enabled", formLayout, onKeys, new String[] { "" }); + externalEl.select(onKeys[0], external); + KeyValues rolesKeyValues = new KeyValues(); for(BigBlueButtonTemplatePermissions role:BigBlueButtonTemplatePermissions.values()) { rolesKeyValues.add(KeyValues.entry(role.name(), translate("role.".concat(role.name())))); @@ -353,6 +358,7 @@ public class EditBigBlueButtonTemplateController extends FormBasicController { } template.setDescription(descriptionEl.getValue()); template.setEnabled(enableEl.isAtLeastSelected(1)); + template.setExternalUsersAllowed(externalEl.isAtLeastSelected(1)); List<BigBlueButtonTemplatePermissions> roles = rolesEl.getSelectedKeys().stream() .map(BigBlueButtonTemplatePermissions::valueOf).collect(Collectors.toList()); diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/_content/guest_join.html b/src/main/java/org/olat/modules/bigbluebutton/ui/_content/guest_join.html index 3945a0ec6c2fa976fe19d8a15a0fe92a83b880c0..ccc3dbc5c0db37e60f42cd300873c81dae7d8041 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/_content/guest_join.html +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/_content/guest_join.html @@ -1,5 +1,14 @@ <div class="o_bbb_guest_join_box"> #if($r.isTrue($allowedToMeet)) + <h3>$r.escapeHtml($title)</h3> + + #if($r.isNotNull($start) || $r.isNotNull($end)) + <div><i class="o_icon o_icon_lifecycle_date"> </i> #if($r.isNotNull($start))$start#end - #if($r.isNotNull($end))$end#end</div> + #end + #if($description && !${description.isEmpty()}) + <div class="o_block_large o_info">$r.xssScan($description)</div> + #end + #if($r.isTrue($ended)) <div class="o_block_large o_warning">$r.translate("meeting.ended")</div> #end 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 98d442c248c96eb2fec1f07dc5323d8e22218a74..6fd809d69e88f68301495b85e9aa9e969bbf3353 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 @@ -9,6 +9,18 @@ #if($description && !${description.isEmpty()}) <div class="o_block_large o_info">$r.xssScan($description)</div> #end +#if($r.isNotEmpty($externalUrl) && $r.isFalse($ended)) +<div class="o_block_large"> + <label forid="externalusersmeetingurl">$r.translate("meeting.url.external.users")</label> + <div class="o_copy_code o_nowrap form-control-static"><a href="javascript:;" id="o_extmeetingurl"><i class="o_icon o_icon-lg o_icon-fw o_icon_qrcode"> </i></a><input id="externalusersmeetingurl" type="text" value="$externalUrl" onclick="this.select()"/></div> + <script> + jQuery(function() { + o_QRCodePopup('o_extmeetingurl', '$externalUrl', 'right'); + }); + </script> +</div> +#end + #if($r.isTrue($ended)) <div class="o_block_large o_warning">$r.translate("meeting.ended")</div> #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 569fbb0f2fdc7731bdae27022a123fd9918c55f6..a6e2c0e5f88d8eefeca154e0992d139d97b31d65 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 @@ -46,6 +46,8 @@ error.date.in.past=Der Termin kann sich nicht in der Vergangenheit befinden. 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.identifier.in.use=Name ist schon verwendet. Bitte ein anders w\u00E4hlen. +error.identifier.url.not.valid=Das URL wird nicht g\u00FCltig. Bitte Sonderzeichen wie $, ?, Leerschl\u00E4ge entfernen. error.prefix=Ein Fehler ist aufgetreten\: error.same.day=Sie haben schon ein Meeting an diesem Tag geplant. error.server.exists=Ein Server mit diesem URL existiert schon. @@ -64,6 +66,7 @@ meeting.deleted=Das Meeting wurde erfolgreich gel\u00F6scht. meeting.description=Beschreibung meeting.end=Ende meeting.ended=Der Online-Termin wurde bereits beendet. +meeting.external.users=F\u00FCr externe Benutzer meeting.followupTime=Nachlaufzeit (Min.) meeting.go.button=Zum Online-Termin Raum meeting.guest.on=erlauben @@ -84,6 +87,7 @@ meeting.resource=Kontext meeting.start=Beginn meeting.start.button=Online-Termin starten meeting.template=Raumvorlage +meeting.url.external.users=Link f\u00FCr externe Benutzer meeting.welcome=Begr\u00FCssungstext meetings.admin.title=Terminverwaltung meetings.past=Abgelaufene Online-Termine @@ -122,6 +126,7 @@ table.header.breakout.recording.meetings=\# Breakout Recording table.header.capacity.factor=Kapazit\u00E4t table.header.day.week=Tag table.header.enabled=Aktiv +table.header.external.users=Externe Benutzer table.header.listener.count=\# Listeners table.header.load=Load table.header.max.concurrent.meetings=R\u00E4ume @@ -156,6 +161,7 @@ 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.external.enabled=Offen f\u00FCr externe Benutzer template.lock=F\u00FCr gesperrte Teilnehmer... template.lockSettingsDisableCam=Kamera ausschalten template.lockSettingsDisableMic=Mikrofon ausschalten 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 a30cc2fdbca731d4dc578d09c6ed3329912bbb51..1890e205a8ea7e1ca425453c958d638c21fa2ff9 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 @@ -46,6 +46,8 @@ 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.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.identifier.in.use=Name is already used. Please choose an other one. +error.identifier.url.not.valid=The URL will not be valid. Please remove special characters like $, ? and spaces. error.prefix=An error happened\: error.same.day=You already have a meeting planed at this date. error.server.exists=A server with this URL already exists. @@ -64,6 +66,7 @@ meeting.deleted=The meeting was successfully deleted. meeting.description=Description meeting.end=End date meeting.ended=The online-meeting has already ended. +meeting.external.users=For external users meeting.followupTime=Follow-up (min.) meeting.go.button=Go to the onlin-meeting room meeting.guest.on=allowed @@ -84,6 +87,7 @@ meeting.resource=Context meeting.start=Start date meeting.start.button=Start the online-meeting meeting.template=Room-template +meeting.url.external.users=Link for external users meeting.welcome=Welcome message meetings.admin.title=Meeting management meetings.past=Past online-meetings @@ -122,6 +126,7 @@ table.header.breakout.recording.meetings=\# Breakout Recording table.header.capacity.factor=Capacity table.header.day.week=Day table.header.enabled=Enabled +table.header.external.users=External users table.header.listener.count=\# Listeners table.header.load=Load table.header.max.concurrent.meetings=Rooms @@ -156,6 +161,7 @@ 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.external.enabled=Open for external users template.lock=For locked participants... template.lockSettingsDisableCam=disable webcam template.lockSettingsDisableMic=disable microphone diff --git a/src/main/resources/database/mysql/alter_15_0_x_to_15_1_0.sql b/src/main/resources/database/mysql/alter_15_0_x_to_15_1_0.sql index 3c16149d635d46a7c1e00f7f9984a0372591e165..954ab5e31cbbccffb77ca4c800b45c0fc620e7ce 100644 --- a/src/main/resources/database/mysql/alter_15_0_x_to_15_1_0.sql +++ b/src/main/resources/database/mysql/alter_15_0_x_to_15_1_0.sql @@ -10,6 +10,8 @@ alter table o_bbb_meeting add column b_guest bool default false not null; alter table o_bbb_meeting add column b_identifier varchar(64); alter table o_bbb_meeting add column b_read_identifier varchar(64); +alter table o_bbb_template add column b_external_users bool default false not null; + -- Appointments create table o_ap_topic ( diff --git a/src/main/resources/database/mysql/setupDatabase.sql b/src/main/resources/database/mysql/setupDatabase.sql index 2c16d49a5dc921ab5cb6d2e4253bee3ff1f5542e..0df19af4bab0422e8cd98cff89bf0231eb04da28 100644 --- a/src/main/resources/database/mysql/setupDatabase.sql +++ b/src/main/resources/database/mysql/setupDatabase.sql @@ -1157,6 +1157,7 @@ create table o_bbb_template ( b_system bool default false not null, b_enabled bool default true not null, b_external_id varchar(255) default null, + b_external_users bool default false not null, b_max_concurrent_meetings int default null, b_max_participants int default null, b_max_duration bigint default null, diff --git a/src/main/resources/database/oracle/alter_15_0_x_to_15_1_0.sql b/src/main/resources/database/oracle/alter_15_0_x_to_15_1_0.sql index f8b2267f45ec9b1932326b45102a85d38012359e..355cffdd8f83359ae12111fcb0fcc70dc412bd3b 100644 --- a/src/main/resources/database/oracle/alter_15_0_x_to_15_1_0.sql +++ b/src/main/resources/database/oracle/alter_15_0_x_to_15_1_0.sql @@ -10,6 +10,8 @@ alter table o_bbb_meeting add b_guest number default 0 not null; alter table o_bbb_meeting add b_identifier varchar2(64); alter table o_bbb_meeting add b_read_identifier varchar2(64); +alter table o_bbb_template add b_external_users number default 0 not null; + -- Appointments create table o_ap_topic ( diff --git a/src/main/resources/database/oracle/setupDatabase.sql b/src/main/resources/database/oracle/setupDatabase.sql index 7cebcd4e31b4c2e2ff979721a91408c7f6e808d6..5768914b3d1dcfcbbc51233f91b934db56573564 100644 --- a/src/main/resources/database/oracle/setupDatabase.sql +++ b/src/main/resources/database/oracle/setupDatabase.sql @@ -1220,6 +1220,7 @@ create table o_bbb_template ( b_system number default 0 not null, b_enabled number default 1 not null, b_external_id varchar(255) default null, + b_external_users number default 0 not null, b_max_concurrent_meetings int default null, b_max_participants int default null, b_max_duration number default null, diff --git a/src/main/resources/database/postgresql/alter_15_0_x_to_15_1_0.sql b/src/main/resources/database/postgresql/alter_15_0_x_to_15_1_0.sql index ba13dbfd7b36baac5de690b3719a2972dca476ba..8173f9912ae22ff17aec1bd184e48f261698bbe4 100644 --- a/src/main/resources/database/postgresql/alter_15_0_x_to_15_1_0.sql +++ b/src/main/resources/database/postgresql/alter_15_0_x_to_15_1_0.sql @@ -10,6 +10,8 @@ alter table o_bbb_meeting add column b_guest bool default false not null; alter table o_bbb_meeting add column b_identifier varchar(64); alter table o_bbb_meeting add column b_read_identifier varchar(64); +alter table o_bbb_template add column b_external_users bool default false not null; + -- Appointments create table o_ap_topic ( diff --git a/src/main/resources/database/postgresql/setupDatabase.sql b/src/main/resources/database/postgresql/setupDatabase.sql index 6c13a5a3ca847139dced3280a405e83988265f6e..34c492ca80d283c07c3f54ddb13f6119fe57e682 100644 --- a/src/main/resources/database/postgresql/setupDatabase.sql +++ b/src/main/resources/database/postgresql/setupDatabase.sql @@ -1179,6 +1179,7 @@ create table o_bbb_template ( b_system bool default false not null, b_enabled bool default true not null, b_external_id varchar(255) default null, + b_external_users bool default false not null, b_max_concurrent_meetings int default null, b_max_participants int8 default null, b_max_duration int8 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 d6acba287ba776cbcfac2167f9fd262d489ef251..c0d9f00cd894b0d40f356814bce8d8ab1f08edd8 100644 --- a/src/test/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonMeetingDAOTest.java +++ b/src/test/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonMeetingDAOTest.java @@ -124,9 +124,8 @@ public class BigBlueButtonMeetingDAOTest extends OlatTestCase { @Test public void loadByIdentifier() { - String name = "BigBlueButton - 8"; BusinessGroup group = businessGroupDao.createAndPersist(null, "BBB 8 group", "bbb-desc", -1, -1, false, false, false, false, false); - BigBlueButtonMeeting meeting = bigBlueButtonMeetingDao.createAndPersistMeeting(name, null, null, group); + BigBlueButtonMeeting meeting = bigBlueButtonMeetingDao.createAndPersistMeeting("BigBlueButton - 8", null, null, group); dbInstance.commitAndCloseSession(); BigBlueButtonMeeting loadedMeeting = bigBlueButtonMeetingDao.loadByIdentifier(meeting.getIdentifier()); @@ -134,6 +133,25 @@ public class BigBlueButtonMeetingDAOTest extends OlatTestCase { Assert.assertEquals(meeting, loadedMeeting); } + @Test + public void isIdentifierInUse() { + BusinessGroup group = businessGroupDao.createAndPersist(null, "BBB 12 group", "bbb-desc", -1, -1, false, false, false, false, false); + BigBlueButtonMeeting meeting = bigBlueButtonMeetingDao.createAndPersistMeeting("BigBlueButton - 12", null, null, group); + BigBlueButtonMeeting meetingOther = bigBlueButtonMeetingDao.createAndPersistMeeting("BigBlueButton - 13", null, null, group); + dbInstance.commit(); + String identifier = UUID.randomUUID().toString(); + meeting.setReadableIdentifier(identifier); + meeting = bigBlueButtonMeetingDao.updateMeeting(meeting); + dbInstance.commitAndCloseSession(); + + boolean inUseItself = bigBlueButtonMeetingDao.isIdentifierInUse(identifier, meeting); + Assert.assertFalse(inUseItself); + boolean inUseAll = bigBlueButtonMeetingDao.isIdentifierInUse(identifier, null); + Assert.assertTrue(inUseAll); + boolean inUseOther = bigBlueButtonMeetingDao.isIdentifierInUse(identifier, meetingOther); + Assert.assertTrue(inUseOther); + } + @Test public void loadByKey() { String name = "BigBlueButton - 9";