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;