diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/FormUIFactory.java b/src/main/java/org/olat/core/gui/components/form/flexible/FormUIFactory.java index 7a3bbc0e9c39aa02fd05cd6eb4d3b57ab1115c9a..bd0eab00b9a336b770ef950d70d62b6367883be2 100644 --- a/src/main/java/org/olat/core/gui/components/form/flexible/FormUIFactory.java +++ b/src/main/java/org/olat/core/gui/components/form/flexible/FormUIFactory.java @@ -1093,7 +1093,11 @@ public class FormUIFactory { } public FormLink addFormLink(String name, String cmd, String i18nLink, FlexiTableElement table) { - FormLinkImpl fte = new FormLinkImpl(name, cmd, i18nLink, Link.LINK); + return addFormLink(name, cmd, i18nLink, table, Link.LINK); + } + + public FormLink addFormLink(String name, String cmd, String i18nLink, FlexiTableElement table, int presentation) { + FormLinkImpl fte = new FormLinkImpl(name, cmd, i18nLink, presentation); fte.setI18nKey(i18nLink); setLabelIfNotNull(null, fte); diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiTableComponent.java b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiTableComponent.java index 146115233fd412f92498c60140792a9bd3a8a2d0..6d8b4990ce2142c6fd969947a978d07d4ffdbe1d 100644 --- a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiTableComponent.java +++ b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiTableComponent.java @@ -77,12 +77,15 @@ public class FlexiTableComponent extends FormBaseComponentImpl implements Compon @Override public Iterable<Component> getComponents() { - List<Component> cmp = new ArrayList<>(); + List<Component> cmps = new ArrayList<>(); for(FormItem item:element.getFormItems()) { - cmp.add(item.getComponent()); + Component cmp = item.getComponent(); + if(cmp != null) {// it's possible that not used form links as a null component + cmps.add(cmp); + } } - cmp.addAll(components.values()); - return cmp; + cmps.addAll(components.values()); + return cmps; } @Override diff --git a/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonManager.java b/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonManager.java index 59d1c688f5bef8e26ffdeb02dec85cb7d8265d21..bb9c4752cb199de704bec365e702d67fadd03041 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonManager.java +++ b/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonManager.java @@ -191,6 +191,8 @@ public interface BigBlueButtonManager { * @return */ public List<BigBlueButtonRecordingReference> getRecordingReferences(Collection<BigBlueButtonMeeting> meetings); + + public BigBlueButtonRecordingReference getRecordingReference(BigBlueButtonRecordingReference reference); public BigBlueButtonRecordingReference updateRecordingReference(BigBlueButtonRecordingReference reference); diff --git a/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonModule.java b/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonModule.java index 1bf627cf0494a874aaba2b5660bcf69ab58dacf6..38981b8a3c35e66b4bade69b367844ba502dd5dd 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonModule.java +++ b/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonModule.java @@ -57,6 +57,7 @@ public class BigBlueButtonModule extends AbstractSpringModule implements ConfigO private static final String PROP_USER_BANDWIDTH_REQUIREMENT = "vc.bigbluebutton.user.bandwidth.requirement"; private static final String PROP_RECORDING_HANDLER_ID = "vc.bigbluebutton.recording.handler.id"; private static final String PROP_MAX_UPLOAD_SIZE = "vc.bigbluebutton.max.upload.size"; + private static final String PROP_RECORDINGS_DEF_PERMANENT = "vc.bigbluebutton.recordings.permanent"; public static final Set<String> SLIDES_MIME_TYPES = Set.of("image/jpg", "image/jpeg", "image/png", "application/pdf", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", @@ -105,6 +106,8 @@ public class BigBlueButtonModule extends AbstractSpringModule implements ConfigO @Value("${vc.bigbluebutton.recording.handler.id:native}") private String recordingHandlerId; + @Value("${vc.bigbluebutton.recordings.permanent:false}") + private String recordingsPermanent; @Autowired public BigBlueButtonModule(CoordinatorManager coordinatorManager) { @@ -145,6 +148,7 @@ public class BigBlueButtonModule extends AbstractSpringModule implements ConfigO } recordingHandlerId = getStringPropertyValue(PROP_RECORDING_HANDLER_ID, recordingHandlerId); + recordingsPermanent = getStringPropertyValue(PROP_RECORDINGS_DEF_PERMANENT, recordingsPermanent); } @Override @@ -349,6 +353,20 @@ public class BigBlueButtonModule extends AbstractSpringModule implements ConfigO setStringProperty(PROP_RECORDING_HANDLER_ID, recordingHandlerId, true); } + /** + * Default value for the recordings references. + * + * @return true if the recordings must be held indefinitely + */ + public boolean isRecordingsPermanent() { + return "true".equals(recordingsPermanent); + } + + public void setRecordingsPermanent(boolean permanent) { + recordingsPermanent = permanent ? "true" : "false"; + setStringProperty(PROP_RECORDINGS_DEF_PERMANENT, recordingsPermanent, true); + } + public int getHttpConnectTimeout() { return httpConnectTimeout; } diff --git a/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonRecordingReference.java b/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonRecordingReference.java index bdc1c64a48b74c9fad2df6c66d04d11a48d4802e..5ca94df581d3c97b27504c5f5335b58ecd9f0bb8 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonRecordingReference.java +++ b/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonRecordingReference.java @@ -32,6 +32,8 @@ import org.olat.core.id.ModifiedInfo; */ public interface BigBlueButtonRecordingReference extends ModifiedInfo, CreateInfo { + public Long getKey(); + public String getRecordingId(); public Date getStartDate(); @@ -42,6 +44,10 @@ public interface BigBlueButtonRecordingReference extends ModifiedInfo, CreateInf public String getType(); + public Boolean getPermanent(); + + public void setPermanent(Boolean permanent); + public BigBlueButtonRecordingsPublishedRoles[] getPublishToEnum(); public void setPublishToEnum(BigBlueButtonRecordingsPublishedRoles[] publishTo); diff --git a/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonRecordingsHandler.java b/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonRecordingsHandler.java index fa37d666a9220dd3fccfd6b6455bed471cad318d..bce48e8a7832727d29da176416a5465afb52c06d 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonRecordingsHandler.java +++ b/src/main/java/org/olat/modules/bigbluebutton/BigBlueButtonRecordingsHandler.java @@ -40,6 +40,12 @@ public interface BigBlueButtonRecordingsHandler { public boolean canDeleteRecordings(); + /** + * + * @return true if the back-end can held the recordings indefinitely. + */ + public boolean allowPermanentRecordings(); + public String getRecordingInfo(Locale locale); public List<BigBlueButtonRecording> getRecordings(BigBlueButtonMeeting meeting, 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 2cf4168ed10042528e8d610e728dc18841b7d056..6b8408d1c01a1afa2635c0d3f799c3a179c75753 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonManagerImpl.java +++ b/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonManagerImpl.java @@ -691,7 +691,13 @@ public class BigBlueButtonManagerImpl implements BigBlueButtonManager, private void deleteRecordings(BigBlueButtonMeeting meeting, BigBlueButtonErrors errors) { List<BigBlueButtonRecordingWithReference> recordingsAndRefs = getRecordingAndReferences(meeting, errors); if(recordingsAndRefs != null && !recordingsAndRefs.isEmpty()) { + BigBlueButtonRecordingsHandler recordingsHanlder = getRecordingsHandler(); + boolean defaultPermanent = recordingsHanlder.allowPermanentRecordings() + && bigBlueButtonModule.isRecordingsPermanent(); + List<BigBlueButtonRecording> recordings = recordingsAndRefs.stream() + .filter(r -> (r.getReference().getPermanent() == null && !defaultPermanent) + || (r.getReference().getPermanent() != null && !r.getReference().getPermanent().booleanValue())) .map(BigBlueButtonRecordingWithReference::getRecording) .collect(Collectors.toList()); getRecordingsHandler().deleteRecordings(recordings, meeting, errors); @@ -1146,6 +1152,14 @@ public class BigBlueButtonManagerImpl implements BigBlueButtonManager, return bigBlueButtonRecordingReferenceDao.getRecordingReferences(meetings); } + @Override + public BigBlueButtonRecordingReference getRecordingReference(BigBlueButtonRecordingReference reference) { + if(reference == null || reference.getKey() == null) { + return null; + } + return bigBlueButtonRecordingReferenceDao.loadRecordingReferenceByKey(reference.getKey()); + } + @Override public BigBlueButtonRecordingReference updateRecordingReference(BigBlueButtonRecordingReference reference) { return bigBlueButtonRecordingReferenceDao.updateRecordingReference(reference); diff --git a/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonNativeRecordingsHandler.java b/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonNativeRecordingsHandler.java index 4d83240243882eadfb45d2b663c6fe5ee35bfe77..7dd89022ac6eb9b91a5522202cc75592ae3d9bde 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonNativeRecordingsHandler.java +++ b/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonNativeRecordingsHandler.java @@ -87,6 +87,11 @@ public class BigBlueButtonNativeRecordingsHandler implements BigBlueButtonRecord return true; } + @Override + public boolean allowPermanentRecordings() { + return false; + } + @Override public List<BigBlueButtonRecording> getRecordings(BigBlueButtonMeeting meeting, BigBlueButtonErrors errors) { if(meeting == null || meeting.getServer() == null || !meeting.getServer().isEnabled()) { diff --git a/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonOpenCastRecordingsHandler.java b/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonOpenCastRecordingsHandler.java index dcd246878247b03c8b4c09e3c4a1532fb0b68e9b..3ec8426a67c9d5fda92a846a93c4ebc746477d1a 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonOpenCastRecordingsHandler.java +++ b/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonOpenCastRecordingsHandler.java @@ -96,6 +96,11 @@ public class BigBlueButtonOpenCastRecordingsHandler implements BigBlueButtonReco public boolean canDeleteRecordings() { return true; } + + @Override + public boolean allowPermanentRecordings() { + return true; + } @Override public List<BigBlueButtonRecording> getRecordings(BigBlueButtonMeeting meeting, BigBlueButtonErrors errors) { diff --git a/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonRecordingReferenceDAO.java b/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonRecordingReferenceDAO.java index b26bb13586da4d3f53fdf250f710cae7a62e224a..2220023b563d21986b0b2d3aedfeb707ba0b86ca 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonRecordingReferenceDAO.java +++ b/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonRecordingReferenceDAO.java @@ -67,7 +67,7 @@ public class BigBlueButtonRecordingReferenceDAO { } public List<BigBlueButtonRecordingReference> getRecordingReferences(BigBlueButtonMeeting meeting) { - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new StringBuilder(128); sb.append("select record from bigbluebuttonrecording as record") .append(" where record.meeting.key=:meetingKey"); @@ -77,6 +77,18 @@ public class BigBlueButtonRecordingReferenceDAO { .getResultList(); } + public BigBlueButtonRecordingReference loadRecordingReferenceByKey(Long referenceKey) { + StringBuilder sb = new StringBuilder(128); + sb.append("select record from bigbluebuttonrecording as record") + .append(" where record.key=:referenceKey"); + + List<BigBlueButtonRecordingReference> refs = dbInstance.getCurrentEntityManager() + .createQuery(sb.toString(), BigBlueButtonRecordingReference.class) + .setParameter("referenceKey", referenceKey) + .getResultList(); + return refs == null || refs.isEmpty() ? null : refs.get(0); + } + public List<BigBlueButtonRecordingReference> getRecordingReferences(Collection<BigBlueButtonMeeting> meetings) { StringBuilder sb = new StringBuilder(); sb.append("select record from bigbluebuttonrecording as record") diff --git a/src/main/java/org/olat/modules/bigbluebutton/model/BigBlueButtonRecordingReferenceImpl.java b/src/main/java/org/olat/modules/bigbluebutton/model/BigBlueButtonRecordingReferenceImpl.java index 6ac0a18717c489317565519ed3b81694a112a35e..666c251ff00c4ce40359619ab85b0378ce3fa1fd 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/model/BigBlueButtonRecordingReferenceImpl.java +++ b/src/main/java/org/olat/modules/bigbluebutton/model/BigBlueButtonRecordingReferenceImpl.java @@ -75,6 +75,9 @@ public class BigBlueButtonRecordingReferenceImpl implements Persistable, BigBlue @Column(name="b_type", nullable=true, insertable=true, updatable=true) private String type; + @Column(name="b_permanent", nullable=true, insertable=true, updatable=true) + private Boolean permanent; + @Column(name="b_publish_to", nullable=true, insertable=true, updatable=true) private String publishTo; @@ -126,6 +129,16 @@ public class BigBlueButtonRecordingReferenceImpl implements Persistable, BigBlue public void setPublishTo(String publishTo) { this.publishTo = publishTo; } + + @Override + public Boolean getPermanent() { + return permanent; + } + + @Override + public void setPermanent(Boolean permanent) { + this.permanent = permanent; + } @Override public BigBlueButtonRecordingsPublishedRoles[] getPublishToEnum() { diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonConfigurationController.java b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonConfigurationController.java index d39d4c26a21faf0a5cc56265ffd113da725b8259..76107441150292449286315d92d53cc287d6586d 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonConfigurationController.java +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonConfigurationController.java @@ -67,6 +67,7 @@ public class BigBlueButtonConfigurationController extends FormBasicController { private MultipleSelectionElement enabledForEl; private MultipleSelectionElement permanentForEl; private SingleSelection recordingsHandlerEl; + private MultipleSelectionElement recordingPermanentEl; private TextElement slidesUploadLimitEl; private FormLink addServerButton; @@ -137,6 +138,7 @@ public class BigBlueButtonConfigurationController extends FormBasicController { } recordingsHandlerEl = uifactory.addDropdownSingleselect("bigbluebutton.recording.handler", formLayout, handlersKeyPairs.keys(), handlersKeyPairs.values()); + recordingsHandlerEl.addActionListener(FormEvent.ONCHANGE); String selectedHandlerId = bigBlueButtonModule.getRecordingHandlerId(); if(handlersKeyPairs.containsKey(selectedHandlerId)) { recordingsHandlerEl.select(selectedHandlerId, true); @@ -144,6 +146,12 @@ public class BigBlueButtonConfigurationController extends FormBasicController { recordingsHandlerEl.select(BigBlueButtonNativeRecordingsHandler.NATIVE_RECORDING_HANDLER_ID, true); } + recordingPermanentEl = uifactory.addCheckboxesHorizontal("permanent.rec", "bigbluebutton.recording.permanent", formLayout, + ENABLED_KEY, new String[] { "" }); + if(bigBlueButtonModule.isRecordingsPermanent()) { + recordingPermanentEl.select(ENABLED_KEY[0], true); + } + Integer maxSize = bigBlueButtonModule.getMaxUploadSize(); String maxSizeStr = maxSize == null ? null : maxSize.toString(); slidesUploadLimitEl = uifactory.addTextElement("slides.upload.limit", 8, maxSizeStr, formLayout); @@ -166,6 +174,16 @@ public class BigBlueButtonConfigurationController extends FormBasicController { enabledForEl.setVisible(enabled); serversTableEl.setVisible(enabled); addServerButton.setVisible(enabled); + recordingsHandlerEl.setVisible(enabled); + slidesUploadLimitEl.setVisible(enabled); + + boolean allowPermanentRecordings = false; + for(BigBlueButtonRecordingsHandler recordingsHandler:recordingsHandlers) { + if(recordingsHandler.getId().equals(recordingsHandlerEl.getSelectedKey())) { + allowPermanentRecordings = recordingsHandler.allowPermanentRecordings(); + } + } + recordingPermanentEl.setVisible(enabled && allowPermanentRecordings); } private void loadModel() { @@ -234,7 +252,7 @@ public class BigBlueButtonConfigurationController extends FormBasicController { @Override protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { - if(source == moduleEnabled) { + if(source == moduleEnabled || recordingsHandlerEl == source) { updateUI(); } else if(addServerButton == source) { addServer(ureq); @@ -263,6 +281,7 @@ public class BigBlueButtonConfigurationController extends FormBasicController { bigBlueButtonModule.setPermanentMeetingEnabled(permanentForEl.isAtLeastSelected(1)); bigBlueButtonModule.setRecordingHandlerId(recordingsHandlerEl.getSelectedKey()); bigBlueButtonModule.setMaxUploadSize(Integer.valueOf(slidesUploadLimitEl.getValue())); + bigBlueButtonModule.setRecordingsPermanent(recordingPermanentEl.isVisible() && recordingPermanentEl.isAtLeastSelected(1)); } CollaborationToolsFactory.getInstance().initAvailableTools(); } 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 333e292bcf8c2fb2eab8c2afc62d88d4199f358c..858f9b2615b55a991a76c355493e5ec59074b2c5 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonMeetingController.java +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonMeetingController.java @@ -27,6 +27,7 @@ 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; import org.olat.core.gui.components.form.flexible.FormItemContainer; import org.olat.core.gui.components.form.flexible.elements.FlexiTableElement; @@ -42,10 +43,13 @@ import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTable import org.olat.core.gui.components.form.flexible.impl.elements.table.SelectionEvent; import org.olat.core.gui.components.form.flexible.impl.elements.table.StaticFlexiCellRenderer; import org.olat.core.gui.components.link.Link; +import org.olat.core.gui.components.link.LinkFactory; import org.olat.core.gui.components.util.KeyValues; +import org.olat.core.gui.components.velocity.VelocityContainer; 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.control.controller.BasicController; import org.olat.core.gui.control.generic.closablewrapper.CloseableCalloutWindowController; import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController; import org.olat.core.gui.control.generic.modal.DialogBoxController; @@ -71,6 +75,7 @@ import org.olat.modules.bigbluebutton.BigBlueButtonMeeting; import org.olat.modules.bigbluebutton.BigBlueButtonModule; import org.olat.modules.bigbluebutton.BigBlueButtonRecording; import org.olat.modules.bigbluebutton.BigBlueButtonRecordingReference; +import org.olat.modules.bigbluebutton.BigBlueButtonRecordingsHandler; import org.olat.modules.bigbluebutton.BigBlueButtonRecordingsPublishedRoles; import org.olat.modules.bigbluebutton.manager.SlidesContainerMapper; import org.olat.modules.bigbluebutton.model.BigBlueButtonErrors; @@ -105,10 +110,12 @@ public class BigBlueButtonMeetingController extends FormBasicController implemen private SlidesContainerMapper slidesMapper; + private ToolsController toolsCtrl; private CloseableModalController cmc; private SlideUploadController uploadSlideCtrl; private PublishRecordingController publishCtrl; private DialogBoxController confirmDeleteRecordingDialog; + private CloseableCalloutWindowController toolsCalloutCtrl; private CloseableCalloutWindowController publishCalloutCtrl; @Autowired @@ -239,21 +246,31 @@ public class BigBlueButtonMeetingController extends FormBasicController implemen } private void initRecordings(FormItemContainer formLayout) { + BigBlueButtonRecordingsHandler recordingsHandler = bigBlueButtonManager.getRecordingsHandler(); + FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel(); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(BRecordingsCols.name)); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(BRecordingsCols.type, new RecordingTypeCellRenderer(getTranslator()))); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(BRecordingsCols.start)); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(BRecordingsCols.end)); + if(administrator && recordingsHandler.canDeleteRecordings() && recordingsHandler.allowPermanentRecordings()) { + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(BRecordingsCols.permanent)); + } columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel("table.header.recording.open", BRecordingsCols.open.ordinal(), "open-recording", new BooleanCellRenderer(new StaticFlexiCellRenderer(translate("table.header.recording.open"), "open-recording", true, true), null))); + if(administrator) { columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(BRecordingsCols.publish)); - if(bigBlueButtonManager.getRecordingsHandler().canDeleteRecordings()) { + if(recordingsHandler.canDeleteRecordings() && recordingsHandler.allowPermanentRecordings()) { + DefaultFlexiColumnModel toolsCol = new DefaultFlexiColumnModel(BRecordingsCols.tools); + toolsCol.setIconHeader("o_icon o_icon_actions o_icon-lg"); + columnsModel.addFlexiColumnModel(toolsCol); + } else if(recordingsHandler.canDeleteRecordings()) { columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel("delete", translate("delete"), "delete")); } } - recordingTableModel = new BigBlueButtonRecordingTableModel(columnsModel, getLocale()); + recordingTableModel = new BigBlueButtonRecordingTableModel(columnsModel, bigBlueButtonModule.isRecordingsPermanent(), getLocale()); tableEl = uifactory.addTableElement(getWindowControl(), "recordings", recordingTableModel, 24, false, getTranslator(), formLayout); tableEl.setEmtpyTableMessageKey("no.recordings"); tableEl.setNumOfRowsEnabled(false); @@ -267,10 +284,7 @@ public class BigBlueButtonMeetingController extends FormBasicController implemen List<BigBlueButtonRecordingWithReference> recordings = bigBlueButtonManager.getRecordingAndReferences(meeting, errors); List<BigBlueButtonRecordingRow> rows = new ArrayList<>(recordings.size()); for(BigBlueButtonRecordingWithReference recording:recordings) { - BigBlueButtonRecordingRow row = forgeRow(recording, attendee); - if(row != null) { - rows.add(row); - } + rows.add(forgeRow(recording, attendee)); } recordingTableModel.setObjects(rows); tableEl.reset(true, true, true); @@ -283,10 +297,18 @@ public class BigBlueButtonMeetingController extends FormBasicController implemen boolean pusblished = isPublishedForMe(recording.getReference(), attendee); BigBlueButtonRecordingRow row = new BigBlueButtonRecordingRow(recording, pusblished); if(administrator || moderator) { - FormLink publishLink = uifactory.addFormLink("publish-" + recording.getRecording().getRecordId(), + String recId = recording.getRecording().getRecordId().toString(); + FormLink publishLink = uifactory.addFormLink("publish-".concat(recId), "publish", "publish.recording", tableEl); row.setPublishLink(publishLink); publishLink.setUserObject(row); + + FormLink toolsLink = uifactory.addFormLink("tools-".concat(recId), + "tools", "", tableEl, Link.LINK | Link.NONTRANSLATED); + toolsLink.setAriaLabel(translate("table.header.actions")); + toolsLink.setIconRightCSS("o_icon o_icon_actions o_icon-lg"); + toolsLink.setUserObject(row); + row.setToolsLink(toolsLink); } return row; } @@ -440,7 +462,10 @@ public class BigBlueButtonMeetingController extends FormBasicController implemen } cmc.deactivate(); cleanUp(); - } else if(publishCalloutCtrl == source || cmc == source) { + } else if(toolsCtrl == source) { + toolsCalloutCtrl.deactivate(); + cleanUp(); + } else if(publishCalloutCtrl == source || toolsCalloutCtrl == source || cmc == source) { cleanUp(); } super.event(ureq, source, event); @@ -449,11 +474,13 @@ public class BigBlueButtonMeetingController extends FormBasicController implemen private void cleanUp() { removeAsListenerAndDispose(confirmDeleteRecordingDialog); removeAsListenerAndDispose(publishCalloutCtrl); + removeAsListenerAndDispose(toolsCalloutCtrl); removeAsListenerAndDispose(uploadSlideCtrl); removeAsListenerAndDispose(publishCtrl); removeAsListenerAndDispose(cmc); confirmDeleteRecordingDialog = null; publishCalloutCtrl = null; + toolsCalloutCtrl = null; uploadSlideCtrl = null; publishCtrl = null; cmc = null; @@ -496,6 +523,8 @@ public class BigBlueButtonMeetingController extends FormBasicController implemen FormLink link = (FormLink)source; if("publish".equals(link.getCmd()) && link.getUserObject() instanceof BigBlueButtonRecordingRow) { doPublish(ureq, link, (BigBlueButtonRecordingRow)link.getUserObject()); + } else if("tools".equals(link.getCmd()) && link.getUserObject() instanceof BigBlueButtonRecordingRow) { + doOpenTools(ureq, link, (BigBlueButtonRecordingRow)link.getUserObject()); } else if("delete".equals(link.getCmd()) && link.getUserObject() instanceof SlideWrapper) { doDeleteSlide((SlideWrapper)link.getUserObject()); loadSlides(flc); @@ -538,6 +567,26 @@ public class BigBlueButtonMeetingController extends FormBasicController implemen publishCalloutCtrl.activate(); } + private void doOpenTools(UserRequest ureq, FormLink link, BigBlueButtonRecordingRow row) { + toolsCtrl = new ToolsController(ureq, getWindowControl(), row); + listenTo(toolsCtrl); + + toolsCalloutCtrl = new CloseableCalloutWindowController(ureq, getWindowControl(), + toolsCtrl.getInitialComponent(), link.getFormDispatchId(), "", true, ""); + listenTo(toolsCalloutCtrl); + toolsCalloutCtrl.activate(); + } + + private void doTogglePermanent(BigBlueButtonRecordingRow row) { + BigBlueButtonRecordingReference ref = bigBlueButtonManager.getRecordingReference(row.getReference()); + if(ref != null) { + boolean flag = ref.getPermanent() == null || !ref.getPermanent().booleanValue(); + ref.setPermanent(Boolean.valueOf(flag)); + bigBlueButtonManager.updateRecordingReference(ref); + } + loadRecordingsModel(); + } + private void doGuestJoin(UserRequest ureq) { if(!validateFormLogic(ureq)) { getWindowControl().getWindowBackOffice().sendCommandTo(CommandFactory.createNewWindowCancelRedirectTo()); @@ -634,4 +683,45 @@ public class BigBlueButtonMeetingController extends FormBasicController implemen CoordinatorManager.getInstance().getCoordinator().getEventBus().fireEventToListenersOf(event, ores); } } + + private class ToolsController extends BasicController { + + private final Link deleteLink; + private final Link permanentLink; + + private final BigBlueButtonRecordingRow recordingRow; + + public ToolsController(UserRequest ureq, WindowControl wControl, BigBlueButtonRecordingRow recordingRow) { + super(ureq, wControl); + this.recordingRow = recordingRow; + + VelocityContainer mainVC = createVelocityContainer("recording_tools"); + + Boolean permanent = recordingRow.getReference().getPermanent(); + boolean flagged = permanent == null || !permanent.booleanValue(); + String permanentI18nKey = flagged ? "mark.as.permanent" : "mark.as.not.permanent"; + permanentLink = LinkFactory.createLink(permanentI18nKey, "permanent", getTranslator(), mainVC, this, Link.LINK); + permanentLink.setIconLeftCSS("o_icon o_icon-fw o_icon_copy"); + + deleteLink = LinkFactory.createLink("delete", "delete", getTranslator(), mainVC, this, Link.LINK); + deleteLink.setIconLeftCSS("o_icon o_icon-fw o_icon_delete_item"); + + putInitialPanel(mainVC); + } + + @Override + protected void doDispose() { + // + } + + @Override + protected void event(UserRequest ureq, Component source, Event event) { + fireEvent(ureq, Event.DONE_EVENT); + if(permanentLink == source) { + doTogglePermanent(recordingRow); + } else if(deleteLink == source) { + doConfirmDeleteRecording(ureq, recordingRow.getRecording()); + } + } + } } diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonRecordingRow.java b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonRecordingRow.java index 06e67c8c04a4c3008d6f27280a5502b50296b8fb..2c746b5188213a01e5e7c7169d866bfb1b5f80b5 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonRecordingRow.java +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonRecordingRow.java @@ -34,6 +34,7 @@ import org.olat.modules.bigbluebutton.model.BigBlueButtonRecordingWithReference; */ public class BigBlueButtonRecordingRow { + private FormLink toolsLink; private FormLink publishLink; private final boolean published; private BigBlueButtonRecordingWithReference recordingReference; @@ -78,4 +79,12 @@ public class BigBlueButtonRecordingRow { public void setPublishLink(FormLink publishLink) { this.publishLink = publishLink; } + + public FormLink getToolsLink() { + return toolsLink; + } + + public void setToolsLink(FormLink toolsLink) { + this.toolsLink = toolsLink; + } } diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonRecordingTableModel.java b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonRecordingTableModel.java index dcdb7f9a565f34ab7392095ed838afc3d25976dc..35f158bcf7276779c9410619530c37d4d5f7bc73 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonRecordingTableModel.java +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonRecordingTableModel.java @@ -41,10 +41,12 @@ implements SortableFlexiTableDataModel<BigBlueButtonRecordingRow> { private static final BRecordingsCols[] COLS = BRecordingsCols.values(); private final Locale locale; + private final Boolean defaultPermanentRecording; - public BigBlueButtonRecordingTableModel(FlexiTableColumnModel columnsModel, Locale locale) { + public BigBlueButtonRecordingTableModel(FlexiTableColumnModel columnsModel, Boolean defaultPermanentRecording, Locale locale) { super(columnsModel); this.locale = locale; + this.defaultPermanentRecording = defaultPermanentRecording; } @Override @@ -68,15 +70,25 @@ implements SortableFlexiTableDataModel<BigBlueButtonRecordingRow> { case type: return row.getType(); case start: return row.getStart(); case end: return row.getEnd(); + case permanent: return getPermanent(row); case open: return row.isPublished(); case publish: return row.getPublishLink(); + case tools: return row.getToolsLink(); default: return "ERROR"; } } + + private Boolean getPermanent(BigBlueButtonRecordingRow row) { + Boolean permanent = row.getReference().getPermanent(); + if(permanent == null) { + permanent = defaultPermanentRecording; + } + return permanent; + } @Override public DefaultFlexiTableDataModel<BigBlueButtonRecordingRow> createCopyWithEmptyList() { - return new BigBlueButtonRecordingTableModel(getTableColumnModel(), locale); + return new BigBlueButtonRecordingTableModel(getTableColumnModel(), defaultPermanentRecording, locale); } public enum BRecordingsCols implements FlexiSortableColumnDef { @@ -86,7 +98,9 @@ implements SortableFlexiTableDataModel<BigBlueButtonRecordingRow> { start("table.header.recording.start"), end("table.header.recording.end"), open("table.header.recording.open"), - publish("table.header.publish"); + publish("table.header.publish"), + permanent("table.header.recording.permanent"), + tools("table.header.actions"); private final String i18nHeaderKey; diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/_content/recording_tools.html b/src/main/java/org/olat/modules/bigbluebutton/ui/_content/recording_tools.html new file mode 100644 index 0000000000000000000000000000000000000000..ccbd995d00774d9f197b17611263e2161662042e --- /dev/null +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/_content/recording_tools.html @@ -0,0 +1,11 @@ +<ul class="o_dropdown list-unstyled"> +#if($r.available("mark.as.permanent")) + <li>$r.render("mark.as.permanent")</li> +#end +#if($r.available("mark.as.not.permanent")) + <li>$r.render("mark.as.not.permanent")</li> +#end +#if($r.available("delete")) + <li>$r.render("delete")</li> +#end +</ul> \ 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 68086c2ade883caf00e895a9fb17eda7ecd3890c..5d1f745756302b6ac4178aee93c85027530fbd0a 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 @@ -16,6 +16,7 @@ bigbluebutton.module.enabled.for.appointments=Kursbaustein "$org.olat.course.nod bigbluebutton.module.enabled.for.courses=Kursbaustein "$org.olat.course.nodes.bigbluebutton\:title_vc" bigbluebutton.module.enabled.for.groups=Gruppen bigbluebutton.recording.handler=Aufzeichnungen Handler +bigbluebutton.recording.permanent=Aufzeichnungen nie l\u00f6schen bigbluebutton.servers=Server bigbluebutton.servers.empty=Sie haben noch kein Server konfiguriert bigbluebutton.title=Konfiguration BigBlueButton Web Conferencing Service @@ -65,6 +66,8 @@ filter.all.instances=Alle OpenOlats filter.this.instance=Dieses OpenOlat layout.standard=Standard layout.webcam=Webcam Termin +mark.as.not.permanent=Aufzeichnung löschbar +mark.as.permanent=Aufzeichnung nicht löschbar meeting.acknowledge.recording.agree=Ich bin einverstanden meeting.acknowledge.recording.explain=Dieses Meeting wird mit allen Videos, Audios, Chat-Nachrichten und Whiteboard-Eintr\u00e4gen aufgezeichnet. Die Aufzeichnung kann nach dem Meeting ver\u00f6ffentlicht werden. meeting.acknowledge.recording.explain.title=Aufzeichnungen @@ -154,6 +157,7 @@ server.status.available=Verf\u00fcgbar server.status.offline=Scheint offline zu sein server.status.disabled=Abgeschaltet slides.upload.limit=Max. Grösse den Pr\u00e4sentationsfolien (in MB) +table.header.actions=Aktionen table.header.available=Verf\u00fcgbarkeit table.header.breakout.meetings=\# Breakout table.header.breakout.recording.meetings=\# Breakout Recording @@ -176,6 +180,7 @@ table.header.recording.end=Ende table.header.recording.meetings=\# Recordings table.header.recording.name=Name table.header.recording.open=\u00d6ffnen +table.header.recording.permanent=Dauernd table.header.recording.start=Beginn table.header.recording.type=Typ table.header.server=Server 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 0b4d4ada9d358c0efae56e491dfbd8cbc568f714..cd6cbbeae8b7c7cf83b46d0886574d9d6b798a36 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 @@ -16,6 +16,7 @@ bigbluebutton.module.enabled.for.appointments=Course element "$org.olat.course.n bigbluebutton.module.enabled.for.courses=Course element "$org.olat.course.nodes.bigbluebutton\:title_vc" bigbluebutton.module.enabled.for.groups=Groups bigbluebutton.recording.handler=Recording handler +bigbluebutton.recording.permanent=Never delete recordings bigbluebutton.servers=Servers bigbluebutton.servers.empty=You don't have configured a server. bigbluebutton.title=Configuration of BigBlueButton Web Conferencing service @@ -65,6 +66,9 @@ filter.all.instances=All OpenOlats filter.this.instance=This OpenOlat layout.standard=Standard layout.webcam=Webcam meeting +mark.as.permanent=As permanent +mark.as.not.permanent=Recording deletable +mark.as.permanent=Recording as not deletable meeting.acknowledge.recording.agree=I agree meeting.acknowledge.recording.explain=This meeting including all video, audio, chat message and whiteboard annotations will be recorded. The recording can be published after the meeting. meeting.acknowledge.recording.explain.title=Meeting recording @@ -154,6 +158,7 @@ server.status.offline=Seems to be offline server.status.disabled=Disabled servers.title=Servers slides.upload.limit=Max. size of slides (in MB) +table.header.actions=Aktionen table.header.available=Availability table.header.breakout.meetings=\# Breakout table.header.breakout.recording.meetings=\# Breakout Recording @@ -176,6 +181,7 @@ table.header.recording.end=End table.header.recording.meetings=\# Recordings table.header.recording.name=Name table.header.recording.open=Open +table.header.recording.permanent=Permanent table.header.recording.start=Start table.header.recording.type=Type table.header.server=Server diff --git a/src/main/resources/database/mysql/alter_15_3_x_to_15_3_8.sql b/src/main/resources/database/mysql/alter_15_3_x_to_15_3_8.sql index fe8e49f1257efa4418577d68757ffa24b5ba80ea..537e1c033af4b8e8c9e4ec1b9b41d3130f9db2ff 100644 --- a/src/main/resources/database/mysql/alter_15_3_x_to_15_3_8.sql +++ b/src/main/resources/database/mysql/alter_15_3_x_to_15_3_8.sql @@ -3,6 +3,8 @@ alter table o_bbb_meeting add column b_password varchar(64) default null; alter table o_bbb_meeting add column b_directory varchar(64) default null; alter table o_bbb_meeting add constraint bbb_dir_idx unique (b_directory); +alter table o_bbb_recording add column b_permanent bool default null; + -- Livestream create table o_livestream_url_template ( id bigint not null auto_increment, diff --git a/src/main/resources/database/mysql/setupDatabase.sql b/src/main/resources/database/mysql/setupDatabase.sql index 5dc82b07ff45075654d3e57ed96b84af7046188d..1e00f56f33ad08556adc96faad8a58222f7f7c3b 100644 --- a/src/main/resources/database/mysql/setupDatabase.sql +++ b/src/main/resources/database/mysql/setupDatabase.sql @@ -1255,6 +1255,7 @@ create table o_bbb_recording ( lastmodified datetime not null, b_recording_id varchar(255) not null, b_publish_to varchar(128), + b_permanent bool default null, b_start_date datetime default null, b_end_date datetime default null, b_url varchar(1024), diff --git a/src/main/resources/database/oracle/alter_15_3_x_to_15_3_8.sql b/src/main/resources/database/oracle/alter_15_3_x_to_15_3_8.sql index 8bc9812d3be2f5a7e24fd9a767d9d8a997040ceb..e99f24c59b2feeb5f36a193ed7b8c2a0328fe56a 100644 --- a/src/main/resources/database/oracle/alter_15_3_x_to_15_3_8.sql +++ b/src/main/resources/database/oracle/alter_15_3_x_to_15_3_8.sql @@ -3,6 +3,8 @@ alter table o_bbb_meeting add b_password varchar2(64) default null; alter table o_bbb_meeting add b_directory varchar2(64) default null; alter table o_bbb_meeting add constraint bbb_dir_idx unique (b_directory); +alter table o_bbb_recording add b_permanent number default null; + -- Livestream create table o_livestream_url_template ( id bigserial, diff --git a/src/main/resources/database/oracle/setupDatabase.sql b/src/main/resources/database/oracle/setupDatabase.sql index fd1c199bcbff3fe22c1cefe0fe730c91052006ab..4a46e019dbc0e0adc4058889ed92b3653d53e491 100644 --- a/src/main/resources/database/oracle/setupDatabase.sql +++ b/src/main/resources/database/oracle/setupDatabase.sql @@ -1319,6 +1319,7 @@ create table o_bbb_recording ( lastmodified date not null, b_recording_id varchar(255) not null, b_publish_to varchar(128), + b_permanent number default null, b_start_date date default null, b_end_date date default null, b_url varchar(1024), diff --git a/src/main/resources/database/postgresql/alter_15_3_x_to_15_3_8.sql b/src/main/resources/database/postgresql/alter_15_3_x_to_15_3_8.sql index 282bb14f8a5950e127e73715a7ba7b53b92bb4c1..68f2b7ab0cecd535a9cefc04bf351240ab2d6ec4 100644 --- a/src/main/resources/database/postgresql/alter_15_3_x_to_15_3_8.sql +++ b/src/main/resources/database/postgresql/alter_15_3_x_to_15_3_8.sql @@ -3,6 +3,8 @@ alter table o_bbb_meeting add column b_password varchar(64) default null; alter table o_bbb_meeting add column b_directory varchar(64) default null; alter table o_bbb_meeting add constraint bbb_dir_idx unique (b_directory); +alter table o_bbb_recording add column b_permanent bool default null; + -- Livestream create table o_livestream_url_template ( id bigserial, diff --git a/src/main/resources/database/postgresql/setupDatabase.sql b/src/main/resources/database/postgresql/setupDatabase.sql index 6fb5eb0d84569d55720609e383d7a56193793767..2042315aa4a59d5d9c8e1b4064624ef3e1d93e34 100644 --- a/src/main/resources/database/postgresql/setupDatabase.sql +++ b/src/main/resources/database/postgresql/setupDatabase.sql @@ -1277,6 +1277,7 @@ create table o_bbb_recording ( lastmodified timestamp not null, b_recording_id varchar(255) not null, b_publish_to varchar(128), + b_permanent bool default null, b_start_date timestamp default null, b_end_date timestamp default null, b_url varchar(1024), diff --git a/src/main/resources/serviceconfig/olat.properties b/src/main/resources/serviceconfig/olat.properties index b90193e0ab381a65d07886f7fef4cbd67c772718..8cd008e4b54b451e6209c344631e343d68a0b936 100644 --- a/src/main/resources/serviceconfig/olat.properties +++ b/src/main/resources/serviceconfig/olat.properties @@ -1646,6 +1646,9 @@ vc.bigbluebutton.secret= vc.bigbluebutton.shared.secret= vc.bigbluebutton.recording.handler.id=native vc.bigbluebutton.recording.handler.id.values=native,opencast +# Prevent deletion of recordings along the meetings +vc.bigbluebutton.recordings.permanent=false +vc.bigbluebutton.recordings.permanent.values=true,false # HTTP connection generic settings in milliseconds vc.http.connect.timeout=30000 diff --git a/src/test/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonManagerTest.java b/src/test/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonManagerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9d9b8c5931e93ec8728f174f3dd023002d5e9680 --- /dev/null +++ b/src/test/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonManagerTest.java @@ -0,0 +1,180 @@ +/** + * <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.manager; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Date; +import java.util.List; +import java.util.UUID; + +import org.junit.Assert; +import org.junit.Test; +import org.olat.core.commons.persistence.DB; +import org.olat.core.id.Identity; +import org.olat.group.BusinessGroup; +import org.olat.group.manager.BusinessGroupDAO; +import org.olat.modules.bigbluebutton.BigBlueButtonManager; +import org.olat.modules.bigbluebutton.BigBlueButtonMeeting; +import org.olat.modules.bigbluebutton.BigBlueButtonModule; +import org.olat.modules.bigbluebutton.BigBlueButtonRecording; +import org.olat.modules.bigbluebutton.BigBlueButtonRecordingReference; +import org.olat.modules.bigbluebutton.BigBlueButtonRecordingsHandler; +import org.olat.modules.bigbluebutton.model.BigBlueButtonErrors; +import org.olat.modules.bigbluebutton.model.BigBlueButtonRecordingImpl; +import org.olat.test.JunitTestHelper; +import org.olat.test.OlatTestCase; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * + * Initial date: 6 janv. 2021<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class BigBlueButtonManagerTest extends OlatTestCase { + + @Autowired + private DB dbInstance; + @Autowired + private BusinessGroupDAO businessGroupDao; + @Autowired + private BigBlueButtonModule bigBlueButtonModule; + @Autowired + private BigBlueButtonManager bigBlueButtonManager; + @Autowired + private BigBlueButtonMeetingDAO bigBlueButtonMeetingDao; + @Autowired + private BigBlueButtonUnitTestRecordingsHandler unitTestRecordingsHandler; + @Autowired + private BigBlueButtonRecordingReferenceDAO bigBlueButtonRecordingReferenceDao; + + @Test + public void checkUnitTestsRecordingsHandler() { + String currentHandlerId = bigBlueButtonModule.getRecordingHandlerId(); + bigBlueButtonModule.setRecordingHandlerId("unittests"); + + BigBlueButtonRecordingsHandler recordingsHandler = bigBlueButtonManager.getRecordingsHandler(); + Assert.assertEquals(unitTestRecordingsHandler, recordingsHandler); + List<BigBlueButtonRecordingsHandler> recordingHandlers = bigBlueButtonManager.getRecordingsHandlers(); + Assert.assertEquals(3, recordingHandlers.size()); + + bigBlueButtonModule.setRecordingHandlerId(currentHandlerId); + } + + @Test + public void deleteMeetingsPermanentRecording() { + String currentHandlerId = bigBlueButtonModule.getRecordingHandlerId(); + bigBlueButtonModule.setRecordingsPermanent(true); + bigBlueButtonModule.setRecordingHandlerId("unittests"); + + Identity id = JunitTestHelper.createAndPersistIdentityAsRndUser("bbb-recording-1"); + BusinessGroup group = businessGroupDao.createAndPersist(null, "BBB Recording 1", "Delete recording or not", -1, -1, false, false, false, false, false); + BigBlueButtonMeeting meeting = bigBlueButtonMeetingDao.createAndPersistMeeting("Recording saved - 1", null, null, group, id); + dbInstance.commit(); + + // default + BigBlueButtonRecording recordingDef = BigBlueButtonRecordingImpl.valueOf(UUID.randomUUID().toString(), "Recorded", UUID.randomUUID().toString(), + new Date(), new Date(), "http://button.openolat.com/1", "presentation"); + bigBlueButtonRecordingReferenceDao.createReference(recordingDef, meeting, null); + + // flagged explicitly as not permanent + BigBlueButtonRecording recordingNot = BigBlueButtonRecordingImpl.valueOf(UUID.randomUUID().toString(), "Recorded", UUID.randomUUID().toString(), + new Date(), new Date(), "http://button.openolat.com/2", "presentation"); + BigBlueButtonRecordingReference referenceNot = bigBlueButtonRecordingReferenceDao.createReference(recordingNot, meeting, null); + referenceNot.setPermanent(Boolean.FALSE); + referenceNot = bigBlueButtonRecordingReferenceDao.updateRecordingReference(referenceNot); + + // flagged explicitly as permanent + BigBlueButtonRecording recordingPermanent = BigBlueButtonRecordingImpl.valueOf(UUID.randomUUID().toString(), "Recorded", UUID.randomUUID().toString(), + new Date(), new Date(), "http://button.openolat.com/3", "presentation"); + BigBlueButtonRecordingReference referencePermanent = bigBlueButtonRecordingReferenceDao.createReference(recordingPermanent, meeting, null); + referencePermanent.setPermanent(Boolean.TRUE); + referencePermanent = bigBlueButtonRecordingReferenceDao.updateRecordingReference(referencePermanent); + + unitTestRecordingsHandler.setRecordingsList(List.of(recordingDef, recordingNot, recordingPermanent)); + dbInstance.commitAndCloseSession(); + + BigBlueButtonErrors errors = new BigBlueButtonErrors(); + bigBlueButtonManager.deleteMeeting(meeting, errors); + dbInstance.commitAndCloseSession(); + + List<BigBlueButtonRecording> recordings = unitTestRecordingsHandler.getRecordingsList(); + assertThat(recordings) + .isNotNull() + .contains(recordingDef, recordingPermanent) + .doesNotContain(recordingNot); + + List<BigBlueButtonRecordingReference> deletedReferences = bigBlueButtonRecordingReferenceDao.getRecordingReferences(meeting); + Assert.assertTrue(deletedReferences.isEmpty()); + + bigBlueButtonModule.setRecordingHandlerId(currentHandlerId); + } + + @Test + public void deleteMeetingsNotPermanentRecording() { + String currentHandlerId = bigBlueButtonModule.getRecordingHandlerId(); + bigBlueButtonModule.setRecordingsPermanent(false); + bigBlueButtonModule.setRecordingHandlerId("unittests"); + + Identity id = JunitTestHelper.createAndPersistIdentityAsRndUser("bbb-recording-2"); + BusinessGroup group = businessGroupDao.createAndPersist(null, "BBB Recording 2", "Not hold recordings", -1, -1, false, false, false, false, false); + BigBlueButtonMeeting meeting = bigBlueButtonMeetingDao.createAndPersistMeeting("Record all - 2", null, null, group, id); + dbInstance.commit(); + + // default + BigBlueButtonRecording recordingDef = BigBlueButtonRecordingImpl.valueOf(UUID.randomUUID().toString(), "Recorded", UUID.randomUUID().toString(), + new Date(), new Date(), "http://button.openolat.com/1", "presentation"); + bigBlueButtonRecordingReferenceDao.createReference(recordingDef, meeting, null); + + // flagged explicitly as not permanent + BigBlueButtonRecording recordingNot = BigBlueButtonRecordingImpl.valueOf(UUID.randomUUID().toString(), "Recorded", UUID.randomUUID().toString(), + new Date(), new Date(), "http://button.openolat.com/2", "presentation"); + BigBlueButtonRecordingReference referenceNot = bigBlueButtonRecordingReferenceDao.createReference(recordingNot, meeting, null); + referenceNot.setPermanent(Boolean.FALSE); + referenceNot = bigBlueButtonRecordingReferenceDao.updateRecordingReference(referenceNot); + + // flagged explicitly as permanent + BigBlueButtonRecording recordingPermanent = BigBlueButtonRecordingImpl.valueOf(UUID.randomUUID().toString(), "Recorded", UUID.randomUUID().toString(), + new Date(), new Date(), "http://button.openolat.com/3", "presentation"); + BigBlueButtonRecordingReference referencePermanent = bigBlueButtonRecordingReferenceDao.createReference(recordingPermanent, meeting, null); + referencePermanent.setPermanent(Boolean.TRUE); + referencePermanent = bigBlueButtonRecordingReferenceDao.updateRecordingReference(referencePermanent); + + unitTestRecordingsHandler.setRecordingsList(List.of(recordingDef, recordingNot, recordingPermanent)); + dbInstance.commitAndCloseSession(); + + BigBlueButtonErrors errors = new BigBlueButtonErrors(); + bigBlueButtonManager.deleteMeeting(meeting, errors); + dbInstance.commitAndCloseSession(); + + List<BigBlueButtonRecording> recordings = unitTestRecordingsHandler.getRecordingsList(); + assertThat(recordings) + .isNotNull() + .contains(recordingPermanent) + .doesNotContain(recordingDef, recordingNot); + + List<BigBlueButtonRecordingReference> deletedReferences = bigBlueButtonRecordingReferenceDao.getRecordingReferences(meeting); + Assert.assertTrue(deletedReferences.isEmpty()); + + bigBlueButtonModule.setRecordingHandlerId(currentHandlerId); + } + +} diff --git a/src/test/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonRecordingReferenceDAOTest.java b/src/test/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonRecordingReferenceDAOTest.java index c3272fdcde1cefa561c16461281eb6d0c075738e..57990c53c4ab7f6b29ea68bddd5ba4cc522e9fe0 100644 --- a/src/test/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonRecordingReferenceDAOTest.java +++ b/src/test/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonRecordingReferenceDAOTest.java @@ -33,6 +33,7 @@ import org.olat.group.manager.BusinessGroupDAO; import org.olat.modules.bigbluebutton.BigBlueButtonMeeting; import org.olat.modules.bigbluebutton.BigBlueButtonRecording; import org.olat.modules.bigbluebutton.BigBlueButtonRecordingReference; +import org.olat.modules.bigbluebutton.BigBlueButtonRecordingsPublishedRoles; import org.olat.modules.bigbluebutton.model.BigBlueButtonRecordingImpl; import org.olat.test.JunitTestHelper; import org.olat.test.OlatTestCase; @@ -132,4 +133,47 @@ public class BigBlueButtonRecordingReferenceDAOTest extends OlatTestCase { Assert.assertTrue(recordingReferences.contains(reference21)); Assert.assertFalse(recordingReferences.contains(reference31)); } + + @Test + public void loadRecordingReferenceByKey() { + Identity id = JunitTestHelper.createAndPersistIdentityAsRndUser("bbb-record-4"); + BusinessGroup group = businessGroupDao.createAndPersist(null, "BBB Recording 4", "A description", -1, -1, false, false, true, true, false); + BigBlueButtonMeeting meeting = bigBlueButtonMeetingDao.createAndPersistMeeting("Recording - 4", null, null, group, id); + + BigBlueButtonRecording recording = BigBlueButtonRecordingImpl.valueOf(UUID.randomUUID().toString(), "Recorded always", UUID.randomUUID().toString(), + new Date(), new Date(), "http://button.openolat.com/2", "presentation"); + BigBlueButtonRecordingReference reference = bigBlueButtonRecordingReferenceDao.createReference(recording, meeting, + new BigBlueButtonRecordingsPublishedRoles[] { BigBlueButtonRecordingsPublishedRoles.all }); + dbInstance.commitAndCloseSession(); + + BigBlueButtonRecordingReference reloadReference = bigBlueButtonRecordingReferenceDao.loadRecordingReferenceByKey(reference.getKey()); + Assert.assertEquals(reference, reloadReference); + } + + @Test + public void deleteRecordingReferences() { + Identity id = JunitTestHelper.createAndPersistIdentityAsRndUser("bbb-record-5"); + BusinessGroup group = businessGroupDao.createAndPersist(null, "BBB Recording 5", "Several recordings", -1, -1, false, false, false, false, false); + BigBlueButtonMeeting meeting1 = bigBlueButtonMeetingDao.createAndPersistMeeting("Recording - 5.1", null, null, group, id); + BigBlueButtonMeeting meeting2 = bigBlueButtonMeetingDao.createAndPersistMeeting("Recording - 5.2", null, null, group, id); + + BigBlueButtonRecording recording11 = BigBlueButtonRecordingImpl.valueOf(UUID.randomUUID().toString(), "Recorded A", UUID.randomUUID().toString(), + new Date(), new Date(), "http://button.openolat.com/5", "presentation"); + BigBlueButtonRecordingReference reference11 = bigBlueButtonRecordingReferenceDao.createReference(recording11, meeting1, null); + BigBlueButtonRecording recording12 = BigBlueButtonRecordingImpl.valueOf(UUID.randomUUID().toString(), "Recorded B", UUID.randomUUID().toString(), + new Date(), new Date(), "http://button.openolat.com/6", "presentation"); + BigBlueButtonRecordingReference reference12 = bigBlueButtonRecordingReferenceDao.createReference(recording12, meeting1, null); + BigBlueButtonRecording recording21 = BigBlueButtonRecordingImpl.valueOf(UUID.randomUUID().toString(), "Recorded C", UUID.randomUUID().toString(), + new Date(), new Date(), "http://button.openolat.com/7", "presentation"); + BigBlueButtonRecordingReference reference21 = bigBlueButtonRecordingReferenceDao.createReference(recording21, meeting2, null); + dbInstance.commitAndCloseSession(); + + bigBlueButtonRecordingReferenceDao.deleteRecordingReferences(meeting1); + dbInstance.commitAndCloseSession(); + + List<BigBlueButtonRecordingReference> recordingReferences = bigBlueButtonRecordingReferenceDao.getRecordingReferences(Arrays.asList(meeting1, meeting2)); + Assert.assertFalse(recordingReferences.contains(reference11)); + Assert.assertFalse(recordingReferences.contains(reference12)); + Assert.assertTrue(recordingReferences.contains(reference21)); + } } diff --git a/src/test/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonUnitTestRecordingsHandler.java b/src/test/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonUnitTestRecordingsHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..9b2c250853af5ed96901ed635d74c039e9a80b66 --- /dev/null +++ b/src/test/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonUnitTestRecordingsHandler.java @@ -0,0 +1,111 @@ +/** + * <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.manager; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import org.olat.core.gui.translator.Translator; +import org.olat.core.util.UserSession; +import org.olat.core.util.Util; +import org.olat.modules.bigbluebutton.BigBlueButtonMeeting; +import org.olat.modules.bigbluebutton.BigBlueButtonRecording; +import org.olat.modules.bigbluebutton.BigBlueButtonRecordingsHandler; +import org.olat.modules.bigbluebutton.model.BigBlueButtonErrors; +import org.olat.modules.bigbluebutton.ui.BigBlueButtonAdminController; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; + +/** + * This is a recordings handler used as mock handler for unit tests but + * not created using mockito to extra check database queries. + * + * Initial date: 6 janv. 2021<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +@Service +@Qualifier("unittests") +public class BigBlueButtonUnitTestRecordingsHandler implements BigBlueButtonRecordingsHandler { + + public static final String UNITTESTS_RECORDING_HANDLER_ID = "unittests"; + + private List<BigBlueButtonRecording> recordingsList; + + public List<BigBlueButtonRecording> getRecordingsList() { + return recordingsList; + } + + public void setRecordingsList(List<BigBlueButtonRecording> recordingsList) { + this.recordingsList = new ArrayList<>(recordingsList); + } + + @Override + public String getId() { + return UNITTESTS_RECORDING_HANDLER_ID; + } + + @Override + public String getName(Locale locale) { + Translator translator = Util.createPackageTranslator(BigBlueButtonAdminController.class, locale); + return translator.translate("native.recording.handler"); + } + + @Override + public String getRecordingInfo(Locale locale) { + Translator translator = Util.createPackageTranslator(BigBlueButtonAdminController.class, locale); + return translator.translate("recording.browser.infos"); + } + + @Override + public boolean canDeleteRecordings() { + return true; + } + + @Override + public boolean allowPermanentRecordings() { + return true; + } + + @Override + public List<BigBlueButtonRecording> getRecordings(BigBlueButtonMeeting meeting, BigBlueButtonErrors errors) { + return recordingsList; + } + + @Override + public String getRecordingURL(UserSession usess, BigBlueButtonRecording recording) { + return recording.getUrl(); + } + + @Override + public void appendMetadata(BigBlueButtonUriBuilder uriBuilder, BigBlueButtonMeeting meeting) { + // Title of the episode + uriBuilder.optionalParameter("meta_dc-title", meeting.getName()); + // Media package and event identifier + uriBuilder.optionalParameter("meta_dc-identifier", meeting.getMeetingId()); + } + + @Override + public boolean deleteRecordings(List<BigBlueButtonRecording> recordings, BigBlueButtonMeeting meeting, BigBlueButtonErrors errors) { + return recordingsList.removeAll(recordings); + } + +} diff --git a/src/test/java/org/olat/test/AllTestsJunit4.java b/src/test/java/org/olat/test/AllTestsJunit4.java index 4ad19ccf9542a8fbae2d0c5483e5fc238df1678f..7d9c4e9c363e30fe5e366d46ae2ff987685adbd2 100644 --- a/src/test/java/org/olat/test/AllTestsJunit4.java +++ b/src/test/java/org/olat/test/AllTestsJunit4.java @@ -220,6 +220,7 @@ import org.junit.runners.Suite; org.olat.modules.bigbluebutton.manager.BigBlueButtonMeetingTemplateDAOTest.class, org.olat.modules.bigbluebutton.manager.BigBlueButtonRecordingReferenceDAOTest.class, org.olat.modules.bigbluebutton.manager.BigBlueButtonUriBuilderTest.class, + org.olat.modules.bigbluebutton.manager.BigBlueButtonManagerTest.class, org.olat.modules.contacttracing.manager.ContactTracingLocationDAOTest.class, org.olat.modules.contacttracing.manager.ContactTracingRegistrationDAOTest.class, org.olat.modules.dcompensation.manager.DisadvantageCompensationDAOTest.class, diff --git a/src/test/java/org/olat/test/JunitTestHelper.java b/src/test/java/org/olat/test/JunitTestHelper.java index 8a1999184caa780feec378c1557a860ff5e5e718..0954704205357a0e6a12d38a37513cac9f676f46 100644 --- a/src/test/java/org/olat/test/JunitTestHelper.java +++ b/src/test/java/org/olat/test/JunitTestHelper.java @@ -108,7 +108,7 @@ public class JunitTestHelper { public static final OLATResource createRandomResource() { String resName = UUID.randomUUID().toString().replace("-", ""); long resId = randomResId.nextInt(Integer.MAX_VALUE - 10) + 1; - OLATResourceable ores = OresHelper.createOLATResourceableInstance(resName, new Long(resId)); + OLATResourceable ores = OresHelper.createOLATResourceableInstance(resName, Long.valueOf(resId)); OLATResource resource = OLATResourceManager.getInstance().createOLATResourceInstance(ores); OLATResourceManager.getInstance().saveOLATResource(resource); return resource;