diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9bd57e816d8f17f31c92e8bbb37099fb257712d4..1aad7567d23f97a666f77d2f80a091e1976b9b90 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ### Added
 
 ### Changed
+ - Course editions not created in Queue will now not be displayed until the teacher 'unhides' them. [@rwbackx](https://gitlab.ewi.tudelft.nl/rwbackx)
 
 ### Fixed
 
diff --git a/src/main/java/nl/tudelft/queue/controller/EditionController.java b/src/main/java/nl/tudelft/queue/controller/EditionController.java
index f370d8bbd8d785afc17382fe0d1912a023b35d2b..226dce53e8f4b53c75c6ab4379126a2bd790d8b5 100644
--- a/src/main/java/nl/tudelft/queue/controller/EditionController.java
+++ b/src/main/java/nl/tudelft/queue/controller/EditionController.java
@@ -18,7 +18,6 @@
 package nl.tudelft.queue.controller;
 
 import static java.time.LocalDateTime.now;
-import static nl.tudelft.labracore.lib.LabracoreApiUtil.fromPageable;
 import static nl.tudelft.queue.PageUtil.toPage;
 
 import java.io.IOException;
@@ -29,13 +28,16 @@ import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 import javax.servlet.http.HttpServletResponse;
+import javax.transaction.Transactional;
 import javax.validation.Valid;
 
 import nl.tudelft.labracore.api.*;
 import nl.tudelft.labracore.api.dto.*;
 import nl.tudelft.labracore.lib.security.user.AuthenticatedPerson;
+import nl.tudelft.labracore.lib.security.user.DefaultRole;
 import nl.tudelft.labracore.lib.security.user.Person;
 import nl.tudelft.librador.dto.view.View;
+import nl.tudelft.queue.PageUtil;
 import nl.tudelft.queue.cache.*;
 import nl.tudelft.queue.csv.EmptyCsvException;
 import nl.tudelft.queue.csv.InvalidCsvException;
@@ -43,17 +45,19 @@ import nl.tudelft.queue.dto.create.CourseRequestCreateDTO;
 import nl.tudelft.queue.dto.create.QueueEditionCreateDTO;
 import nl.tudelft.queue.dto.create.QueueRoleCreateDTO;
 import nl.tudelft.queue.dto.util.EditionFilterDTO;
+import nl.tudelft.queue.dto.view.QueueEditionDetailsDTO;
 import nl.tudelft.queue.dto.view.QueueSessionSummaryDTO;
 import nl.tudelft.queue.model.LabRequest;
+import nl.tudelft.queue.model.QueueEdition;
 import nl.tudelft.queue.model.enums.QueueSessionType;
 import nl.tudelft.queue.model.labs.Lab;
+import nl.tudelft.queue.repository.QueueEditionRepository;
 import nl.tudelft.queue.repository.QueueSessionRepository;
 import nl.tudelft.queue.service.*;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.core.io.Resource;
-import org.springframework.data.domain.PageImpl;
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.domain.Sort;
 import org.springframework.data.web.PageableDefault;
@@ -138,6 +142,9 @@ public class EditionController {
 	@Autowired
 	private QuestionService qs;
 
+	@Autowired
+	private QueueEditionRepository qer;
+
 	@Autowired
 	@Lazy
 	private LabService ls;
@@ -159,16 +166,18 @@ public class EditionController {
 		PersonDetailsDTO pd = Objects.requireNonNull(pApi.getPersonById(person.getId()).block());
 
 		var filter = es.getFilter("/editions");
-		var editions = eApi
-				.getEditionsPageActiveOrTaughtBy(person.getId(), fromPageable(pageable), filter.getPrograms(),
-						filter.getNameSearch())
+		List<EditionDetailsDTO> lcEditions = eApi.getAllEditionsActiveOrTaughtBy(person.getId()).collectList()
 				.block();
+		eCache.register(lcEditions);
+		erCache.getAndIgnoreMissing(lcEditions.stream().map(EditionDetailsDTO::getId));
 
-		eCache.register(editions.getContent());
-		erCache.getAndIgnoreMissing(editions.getContent().stream().map(EditionDetailsDTO::getId));
+		var editions = es.queueEditionDTO(lcEditions, QueueEditionDetailsDTO.class);
+		if (person.getDefaultRole() != DefaultRole.ADMIN) {
+			editions = editions.stream().filter(e -> !e.getHidden()).toList();
+		}
+		var page = PageUtil.toPage(pageable, editions);
 
-		model.addAttribute("editions",
-				new PageImpl<>(editions.getContent(), pageable, editions.getTotalElements()));
+		model.addAttribute("editions", page);
 		model.addAttribute("programs", cCache.getAll()
 				.stream().map(CourseDetailsDTO::getProgram).distinct()
 				.sorted(Comparator.comparing(ProgramSummaryDTO::getName))
@@ -257,7 +266,8 @@ public class EditionController {
 	 */
 	@GetMapping("/edition/{editionId}")
 	public String getEditionView(@PathVariable Long editionId, Model model) {
-		EditionDetailsDTO edition = eCache.getRequired(editionId);
+		QueueEditionDetailsDTO edition = es.queueEditionDTO(eCache.getRequired(editionId),
+				QueueEditionDetailsDTO.class);
 
 		model.addAttribute("edition", edition);
 		model.addAttribute("assignments",
@@ -288,7 +298,7 @@ public class EditionController {
 			students = es.studentsMatchingFilter(students, studentSearch);
 		}
 
-		model.addAttribute("edition", edition);
+		model.addAttribute("edition", es.queueEditionDTO(edition, QueueEditionDetailsDTO.class));
 		model.addAttribute("assignments",
 				mCache.getAndIgnoreMissing(edition.getModules().stream().map(ModuleSummaryDTO::getId))
 						.stream()
@@ -322,7 +332,7 @@ public class EditionController {
 		sgCache.getAndIgnoreMissing(modules.stream()
 				.flatMap(m -> m.getGroups().stream().map(StudentGroupSmallSummaryDTO::getId)));
 
-		model.addAttribute("edition", edition);
+		model.addAttribute("edition", es.queueEditionDTO(edition, QueueEditionDetailsDTO.class));
 		model.addAttribute("modules", modules);
 		model.addAttribute("assignments",
 				modules.stream().flatMap(m -> m.getAssignments().stream()).toList());
@@ -363,7 +373,7 @@ public class EditionController {
 		// Sort all labs
 		labs = es.sortLabs(labs);
 
-		model.addAttribute("edition", edition);
+		model.addAttribute("edition", es.queueEditionDTO(edition, QueueEditionDetailsDTO.class));
 		model.addAttribute("labs", labs);
 		model.addAttribute("allLabTypes", QueueSessionType.values());
 		model.addAttribute("queueSessionTypes", queueSessionTypes);
@@ -389,7 +399,7 @@ public class EditionController {
 	public String getEditionQuestions(@PathVariable Long editionId, Model model) {
 		EditionDetailsDTO edition = eCache.getRequired(editionId);
 
-		model.addAttribute("edition", edition);
+		model.addAttribute("edition", es.queueEditionDTO(edition, QueueEditionDetailsDTO.class));
 		model.addAttribute("assignments",
 				mCache.getAndIgnoreMissing(edition.getModules().stream().map(ModuleSummaryDTO::getId))
 						.stream()
@@ -411,7 +421,7 @@ public class EditionController {
 	public String getAddParticipantPage(@PathVariable Long editionId, Model model) {
 		EditionDetailsDTO edition = eCache.getRequired(editionId);
 
-		model.addAttribute("edition", edition);
+		model.addAttribute("edition", es.queueEditionDTO(edition, QueueEditionDetailsDTO.class));
 		model.addAttribute("assignments",
 				mCache.getAndIgnoreMissing(edition.getModules().stream().map(ModuleSummaryDTO::getId))
 						.stream()
@@ -439,7 +449,7 @@ public class EditionController {
 		if (dto.hasErrors()) {
 			EditionDetailsDTO edition = eCache.getRequired(editionId);
 
-			model.addAttribute("edition", edition);
+			model.addAttribute("edition", es.queueEditionDTO(edition, QueueEditionDetailsDTO.class));
 			model.addAttribute("assignments",
 					mCache.getAndIgnoreMissing(edition.getModules().stream().map(ModuleSummaryDTO::getId))
 							.stream()
@@ -505,7 +515,7 @@ public class EditionController {
 			Model model) {
 		EditionDetailsDTO edition = eCache.getRequired(editionId);
 
-		model.addAttribute("edition", edition);
+		model.addAttribute("edition", es.queueEditionDTO(edition, QueueEditionDetailsDTO.class));
 		model.addAttribute("assignments",
 				mCache.getAndIgnoreMissing(edition.getModules().stream().map(ModuleSummaryDTO::getId))
 						.stream()
@@ -558,7 +568,7 @@ public class EditionController {
 	public String getParticipantLeavePage(@PathVariable Long editionId,
 			Model model) {
 		EditionDetailsDTO edition = eCache.getRequired(editionId);
-		model.addAttribute("edition", edition);
+		model.addAttribute("edition", es.queueEditionDTO(edition, QueueEditionDetailsDTO.class));
 		model.addAttribute("assignments",
 				mCache.getAndIgnoreMissing(edition.getModules().stream().map(ModuleSummaryDTO::getId))
 						.stream()
@@ -600,7 +610,7 @@ public class EditionController {
 		var sessions = sCache.getAndHandleAll(edition.getSessions().stream().map(SessionSummaryDTO::getId),
 				ls.deleteSessionsByIds());
 
-		model.addAttribute("edition", edition);
+		model.addAttribute("edition", es.queueEditionDTO(edition, QueueEditionDetailsDTO.class));
 		model.addAttribute("assignments",
 				mCache.getAndIgnoreMissing(edition.getModules().stream().map(ModuleSummaryDTO::getId))
 						.stream()
@@ -672,6 +682,7 @@ public class EditionController {
 		EditionCreateDTO edition = create.apply();
 
 		Long id = eApi.addEdition(edition).block();
+		qer.save(QueueEdition.builder().id(id).hidden(false).build());
 
 		return "redirect:/edition/" + id;
 	}
@@ -718,7 +729,7 @@ public class EditionController {
 			Model model) {
 		EditionDetailsDTO edition = eCache.getRequired(editionId);
 
-		model.addAttribute("edition", edition);
+		model.addAttribute("edition", es.queueEditionDTO(edition, QueueEditionDetailsDTO.class));
 		model.addAttribute("assignments",
 				mCache.getAndIgnoreMissing(edition.getModules().stream().map(ModuleSummaryDTO::getId))
 						.stream()
@@ -764,7 +775,7 @@ public class EditionController {
 			Model model) {
 		EditionDetailsDTO edition = eCache.getRequired(editionId);
 
-		model.addAttribute("edition", edition);
+		model.addAttribute("edition", es.queueEditionDTO(edition, QueueEditionDetailsDTO.class));
 		model.addAttribute("assignments",
 				mCache.getAndIgnoreMissing(edition.getModules().stream().map(ModuleSummaryDTO::getId))
 						.stream()
@@ -842,4 +853,20 @@ public class EditionController {
 				QueueSessionSummaryDTO.class);
 	}
 
+	/**
+	 * Toggles the visibility of an edition in Queue.
+	 *
+	 * @param  editionId The id of the edition
+	 * @return           A redirect to the edition page
+	 */
+	@Transactional
+	@PostMapping("/edition/{editionId}/visibility")
+	@PreAuthorize("@permissionService.canManageEdition(#editionId)")
+	public String toggleVisibility(@PathVariable Long editionId) {
+		QueueEdition qEdition = es.getOrCreateQueueEdition(editionId);
+		qEdition.setHidden(!qEdition.getHidden());
+		qer.save(qEdition);
+		return "redirect:/edition/{editionId}";
+	}
+
 }
diff --git a/src/main/java/nl/tudelft/queue/controller/HomeController.java b/src/main/java/nl/tudelft/queue/controller/HomeController.java
index 20766ace181932bc2e824bee04a908a51916fc96..2383940db42a7af9440d7f056183132a85e3b9c6 100644
--- a/src/main/java/nl/tudelft/queue/controller/HomeController.java
+++ b/src/main/java/nl/tudelft/queue/controller/HomeController.java
@@ -23,7 +23,6 @@ import static nl.tudelft.labracore.api.dto.RoleEditionDetailsDTO.TypeEnum.TEACHE
 
 import java.time.LocalDateTime;
 import java.util.*;
-import java.util.function.Function;
 import java.util.stream.Collectors;
 
 import nl.tudelft.labracore.api.EditionControllerApi;
@@ -38,6 +37,7 @@ import nl.tudelft.queue.cache.EditionCollectionCacheManager;
 import nl.tudelft.queue.cache.PersonCacheManager;
 import nl.tudelft.queue.cache.SessionCacheManager;
 import nl.tudelft.queue.dto.view.FeedbackViewDTO;
+import nl.tudelft.queue.dto.view.QueueEditionDetailsDTO;
 import nl.tudelft.queue.dto.view.QueueSessionSummaryDTO;
 import nl.tudelft.queue.model.Feedback;
 import nl.tudelft.queue.repository.FeedbackRepository;
@@ -173,7 +173,8 @@ public class HomeController {
 
 			var editions = eCache.getAndIgnoreMissing(roles.stream().map(r -> r.getEdition().getId()))
 					.stream()
-					.collect(Collectors.toMap(EditionDetailsDTO::getId, Function.identity()));
+					.collect(Collectors.toMap(EditionDetailsDTO::getId,
+							e -> es.queueEditionDTO(e, QueueEditionDetailsDTO.class)));
 
 			Set<SessionDetailsDTO> sessions = new HashSet<>(sCache.getAndHandleAll(editions.values().stream()
 					.flatMap(e -> e.getSessions().stream())
diff --git a/src/main/java/nl/tudelft/queue/controller/LabController.java b/src/main/java/nl/tudelft/queue/controller/LabController.java
index 773c15859e3dacef9e774fd926798cc6f54b6895..b38f40e375b065a300eaf4736f8070b2c53fd013 100644
--- a/src/main/java/nl/tudelft/queue/controller/LabController.java
+++ b/src/main/java/nl/tudelft/queue/controller/LabController.java
@@ -87,6 +87,9 @@ public class LabController {
 	@Autowired
 	private LabService ls;
 
+	@Autowired
+	private EditionService es;
+
 	@Autowired
 	private RequestTableService rts;
 
diff --git a/src/main/java/nl/tudelft/queue/dto/view/QueueEditionDetailsDTO.java b/src/main/java/nl/tudelft/queue/dto/view/QueueEditionDetailsDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..05cea4e6b4cd4649f8f780b4e9d124af3c1bdaa5
--- /dev/null
+++ b/src/main/java/nl/tudelft/queue/dto/view/QueueEditionDetailsDTO.java
@@ -0,0 +1,35 @@
+/*
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2021  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.queue.dto.view;
+
+import javax.validation.constraints.NotNull;
+
+import lombok.*;
+import nl.tudelft.labracore.api.dto.*;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class QueueEditionDetailsDTO extends EditionDetailsDTO implements QueueEditionViewDTO {
+
+	@NotNull
+	private Boolean hidden;
+
+}
diff --git a/src/main/java/nl/tudelft/queue/dto/view/QueueEditionSummaryDTO.java b/src/main/java/nl/tudelft/queue/dto/view/QueueEditionSummaryDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..3f6d65b0a6ae1c69ce36e7a4e72d80404ed7dffe
--- /dev/null
+++ b/src/main/java/nl/tudelft/queue/dto/view/QueueEditionSummaryDTO.java
@@ -0,0 +1,35 @@
+/*
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2021  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.queue.dto.view;
+
+import javax.validation.constraints.NotNull;
+
+import lombok.*;
+import nl.tudelft.labracore.api.dto.EditionSummaryDTO;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class QueueEditionSummaryDTO extends EditionSummaryDTO implements QueueEditionViewDTO {
+
+	@NotNull
+	private Boolean hidden;
+
+}
diff --git a/src/main/java/nl/tudelft/queue/dto/view/QueueEditionViewDTO.java b/src/main/java/nl/tudelft/queue/dto/view/QueueEditionViewDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..d2dfb3ce04e76e4160b1dc8b431fa6712cbc0268
--- /dev/null
+++ b/src/main/java/nl/tudelft/queue/dto/view/QueueEditionViewDTO.java
@@ -0,0 +1,26 @@
+/*
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2021  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.queue.dto.view;
+
+public interface QueueEditionViewDTO {
+
+	Long getId();
+
+	void setHidden(Boolean hidden);
+
+}
diff --git a/src/main/java/nl/tudelft/queue/model/QueueEdition.java b/src/main/java/nl/tudelft/queue/model/QueueEdition.java
new file mode 100644
index 0000000000000000000000000000000000000000..797bb9b20aa9ac28a0b633f916ff7b088976f329
--- /dev/null
+++ b/src/main/java/nl/tudelft/queue/model/QueueEdition.java
@@ -0,0 +1,43 @@
+/*
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2021  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.queue.model;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.validation.constraints.NotNull;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@Entity
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class QueueEdition {
+
+	@Id
+	private Long id;
+
+	@NotNull
+	@Builder.Default
+	private Boolean hidden = true;
+
+}
diff --git a/src/main/java/nl/tudelft/queue/repository/QueueEditionRepository.java b/src/main/java/nl/tudelft/queue/repository/QueueEditionRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..88b254458d98cd5821d5740996e315c8b6b4c3bc
--- /dev/null
+++ b/src/main/java/nl/tudelft/queue/repository/QueueEditionRepository.java
@@ -0,0 +1,28 @@
+/*
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2021  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.queue.repository;
+
+import nl.tudelft.queue.model.QueueEdition;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.querydsl.QuerydslPredicateExecutor;
+
+public interface QueueEditionRepository
+		extends JpaRepository<QueueEdition, Long>, QuerydslPredicateExecutor<QueueEdition> {
+
+}
diff --git a/src/main/java/nl/tudelft/queue/service/EditionService.java b/src/main/java/nl/tudelft/queue/service/EditionService.java
index 0fe80542f26985df1d7315e11d052588b54acbc9..64a48bb7c3db34de442c668d840ce7380235d82b 100644
--- a/src/main/java/nl/tudelft/queue/service/EditionService.java
+++ b/src/main/java/nl/tudelft/queue/service/EditionService.java
@@ -30,6 +30,7 @@ import java.util.zip.ZipOutputStream;
 
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
+import javax.transaction.Transactional;
 
 import nl.tudelft.labracore.api.EditionControllerApi;
 import nl.tudelft.labracore.api.PersonControllerApi;
@@ -41,16 +42,20 @@ import nl.tudelft.queue.csv.EmptyCsvException;
 import nl.tudelft.queue.csv.InvalidCsvException;
 import nl.tudelft.queue.csv.UserCsvHelper;
 import nl.tudelft.queue.dto.util.EditionFilterDTO;
+import nl.tudelft.queue.dto.view.QueueEditionViewDTO;
 import nl.tudelft.queue.dto.view.QueueSessionSummaryDTO;
 import nl.tudelft.queue.model.LabRequest;
+import nl.tudelft.queue.model.QueueEdition;
 import nl.tudelft.queue.model.QueueSession;
 import nl.tudelft.queue.model.embeddables.AllowedRequest;
 import nl.tudelft.queue.model.enums.QueueSessionType;
 import nl.tudelft.queue.model.enums.RequestType;
 import nl.tudelft.queue.model.labs.Lab;
+import nl.tudelft.queue.repository.QueueEditionRepository;
 import nl.tudelft.queue.repository.QueueSessionRepository;
 
 import org.apache.commons.lang3.StringUtils;
+import org.modelmapper.ModelMapper;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.core.io.Resource;
@@ -61,6 +66,8 @@ import org.springframework.web.multipart.MultipartFile;
 @Service
 public class EditionService {
 
+	private static final ModelMapper mapper = new ModelMapper();
+
 	@Autowired
 	private HttpSession session;
 
@@ -76,6 +83,9 @@ public class EditionService {
 	@Autowired
 	private EditionControllerApi eca;
 
+	@Autowired
+	private QueueEditionRepository qer;
+
 	@Autowired
 	@Lazy
 	private LabService ls;
@@ -86,6 +96,45 @@ public class EditionService {
 	@Autowired
 	private AssignmentCacheManager acm;
 
+	/**
+	 * Converts any kind of EditionDTO to a QueueEditionDTO.
+	 *
+	 * @param  dto    The DTO to convert
+	 * @param  qClass The class of QueueEditionDTO to convert to
+	 * @return        The converted DTO
+	 */
+	public <DTO, QDTO extends QueueEditionViewDTO> QDTO queueEditionDTO(DTO dto, Class<QDTO> qClass) {
+		return queueEditionDTO(List.of(dto), qClass).get(0);
+	}
+
+	/**
+	 * Converts a list of any kind of EditionDTOs to QueueEditionDTOs.
+	 *
+	 * @param  dtos   The DTOs to convert
+	 * @param  qClass The class of QueueEditionDTO to convert to
+	 * @return        The converted list of DTOs
+	 */
+	public <DTO, QDTO extends QueueEditionViewDTO> List<QDTO> queueEditionDTO(List<DTO> dtos,
+			Class<QDTO> qClass) {
+		return dtos.stream().map(dto -> {
+			QDTO qDto = mapper.map(dto, qClass);
+			QueueEdition qEdition = getOrCreateQueueEdition(qDto.getId());
+			qDto.setHidden(qEdition.getHidden());
+			return qDto;
+		}).toList();
+	}
+
+	/**
+	 * Gets a queue edition from the database or creates a default one if it does not exist;
+	 *
+	 * @param  id The id of the edition
+	 * @return    The Queue Edition
+	 */
+	@Transactional
+	public QueueEdition getOrCreateQueueEdition(Long id) {
+		return qer.findById(id).orElseGet(() -> qer.save(QueueEdition.builder().id(id).build()));
+	}
+
 	/**
 	 * Filters a list of people on their username, display name and student number. If any of the
 	 * aforementioned values match the given search term, the person is included. If not, the person is
diff --git a/src/main/java/nl/tudelft/queue/service/LabService.java b/src/main/java/nl/tudelft/queue/service/LabService.java
index 79eea8c1d5f22d102d726949917ec18d5c81f22f..784da1d1bc1b7853aac005f9d897892fc9009730 100644
--- a/src/main/java/nl/tudelft/queue/service/LabService.java
+++ b/src/main/java/nl/tudelft/queue/service/LabService.java
@@ -44,6 +44,7 @@ import nl.tudelft.queue.dto.create.labs.AbstractSlottedLabCreateDTO;
 import nl.tudelft.queue.dto.patch.QueueSessionPatchDTO;
 import nl.tudelft.queue.dto.patch.labs.AbstractSlottedLabPatchDTO;
 import nl.tudelft.queue.dto.view.CalendarEntryViewDTO;
+import nl.tudelft.queue.dto.view.QueueEditionDetailsDTO;
 import nl.tudelft.queue.dto.view.requests.LabRequestViewDTO;
 import nl.tudelft.queue.dto.view.requests.SelectionRequestViewDTO;
 import nl.tudelft.queue.misc.QueueSessionStatus;
@@ -90,6 +91,9 @@ public class LabService {
 	@Autowired
 	private SessionCacheManager sCache;
 
+	@Autowired
+	private EditionService es;
+
 	@Autowired
 	private EditionControllerApi eApi;
 
@@ -146,7 +150,10 @@ public class LabService {
 	 */
 	public void setOrganizationInModel(SessionDetailsDTO session, Model model) {
 		model.addAttribute("edition",
-				(session.getEdition() != null) ? eCache.getRequired(session.getEdition().getId()) : null);
+				(session.getEdition() != null)
+						? es.queueEditionDTO(eCache.getRequired(session.getEdition().getId()),
+								QueueEditionDetailsDTO.class)
+						: null);
 		model.addAttribute("ec",
 				(session.getEditionCollection() != null)
 						? ecCache.getRequired(session.getEditionCollection().getId())
diff --git a/src/main/java/nl/tudelft/queue/startup/UnhideEditionsService.java b/src/main/java/nl/tudelft/queue/startup/UnhideEditionsService.java
new file mode 100644
index 0000000000000000000000000000000000000000..fed78bb7e5b83ad8607c8ed688b5b175526407ea
--- /dev/null
+++ b/src/main/java/nl/tudelft/queue/startup/UnhideEditionsService.java
@@ -0,0 +1,78 @@
+/*
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2021  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.queue.startup;
+
+import javax.transaction.Transactional;
+
+import nl.tudelft.labracore.api.CourseControllerApi;
+import nl.tudelft.labracore.api.EditionControllerApi;
+import nl.tudelft.labracore.api.ProgramControllerApi;
+import nl.tudelft.labracore.api.dto.*;
+import nl.tudelft.queue.model.QueueEdition;
+import nl.tudelft.queue.repository.QueueEditionRepository;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
+import org.springframework.boot.context.event.ApplicationReadyEvent;
+import org.springframework.context.event.EventListener;
+import org.springframework.stereotype.Service;
+
+/**
+ * A service that runs during startup. This startup procedure goes through all editions and unhides them in
+ * Queue.
+ */
+@Service
+@ConditionalOnExpression("#{'${queue.startup.unhide-all-editions}' == 'true'}")
+public class UnhideEditionsService {
+	private static final Logger logger = LoggerFactory.getLogger(UnhideEditionsService.class);
+
+	@Autowired
+	private QueueEditionRepository qer;
+
+	@Autowired
+	private EditionControllerApi eApi;
+
+	@Autowired
+	private CourseControllerApi cApi;
+
+	@Autowired
+	private ProgramControllerApi pApi;
+
+	@Transactional
+	@EventListener(ApplicationReadyEvent.class)
+	public void unhideAllEditions() {
+		// Going by programme > course > edition to not request to much data
+		for (ProgramSummaryDTO programme : pApi.getAllPrograms().collectList().block()) {
+			logger.info("Unhiding courses of programme: " + programme.getName());
+			for (CourseSummaryDTO course : cApi.getAllCoursesByProgram(programme.getId()).collectList()
+					.block()) {
+				for (EditionDetailsDTO edition : eApi.getAllEditionsByCourse(course.getId()).collectList()
+						.block()) {
+					qer.findById(edition.getId()).ifPresentOrElse(
+							e -> {
+								e.setHidden(false);
+								qer.save(e);
+							},
+							() -> qer.save(QueueEdition.builder().id(edition.getId()).hidden(false).build()));
+				}
+			}
+		}
+	}
+}
diff --git a/src/main/resources/application.yml.template b/src/main/resources/application.yml.template
index 60370803bebb7308e34e18369cb55cf3cf0cbbed..12b9e635f8ab9ad902f3bbf3acd45a26bd4d183d 100644
--- a/src/main/resources/application.yml.template
+++ b/src/main/resources/application.yml.template
@@ -73,6 +73,7 @@ queue:
     logo: /img/tudelft-logo.png
   startup:
     repair-requests: false
+    unhide-all-editions: false
   mail:
     enabled: true
     support-email: root
diff --git a/src/main/resources/migrations.yml b/src/main/resources/migrations.yml
index 958db173e9bbbf78e769c38cc6d46cd9ee669fea..eb73594fccaa2d94dd95d572a96897f929791a03 100644
--- a/src/main/resources/migrations.yml
+++ b/src/main/resources/migrations.yml
@@ -1045,7 +1045,26 @@ databaseChangeLog:
             tableName: slotted_lab
             columnName: previous_empty_allowed_threshold
             columnDataType: INTEGER
-
-
+  # Hide non-queue editions by default
+  - changeSet:
+      id: 1684835006964-1
+      author: ruben (generated)
+      changes:
+        - createTable:
+            columns:
+              - column:
+                  constraints:
+                    nullable: false
+                    primaryKey: true
+                    primaryKeyName: CONSTRAINT_D
+                  name: id
+                  type: BIGINT
+              - column:
+                  constraints:
+                    nullable: false
+                  name: hidden
+                  type: BOOLEAN
+                  defaultValueBoolean: true
+            tableName: queue_edition
 
 
diff --git a/src/main/resources/templates/edition/view.html b/src/main/resources/templates/edition/view.html
index dbaf72e4da9c498023bae261be5a4477658342fc..cfccb53b1fdbaa1c209c3002a422403e046dc710 100644
--- a/src/main/resources/templates/edition/view.html
+++ b/src/main/resources/templates/edition/view.html
@@ -44,6 +44,21 @@
         </nav>
     </section>
 
+    <th:block th:if="${ec == null && (edition instanceof T(nl.tudelft.queue.dto.view.QueueEditionViewDTO)) && edition.hidden && @permissionService.canManageEdition(edition.id)}">
+        <div id="edition-hidden-banner" style="cursor: pointer;" class="alert alert-danger mt-md-3" role="alert">
+            <span>This edition is not visible to students in Queue. Click on this banner to unhide this edition.</span>
+        </div>
+        <form id="edition-unhide-form" method="post" th:action="@{/edition/{editionId}/visibility(editionId=${edition.id})}"></form>
+        <script th:inline="javascript">
+            document.addEventListener("DOMContentLoaded", function () {
+                const editionUnhideForm = document.getElementById("edition-unhide-form");
+                const editionHiddenBanner = document.getElementById("edition-hidden-banner");
+                editionHiddenBanner.addEventListener("click", function () {
+                    editionUnhideForm.submit();
+                });
+            });
+        </script>
+    </th:block>
     <div class="alert alert-danger mt-md-3" role="alert" th:if="${ec == null && #lists.isEmpty(edition.modules)}">
         <span>This edition does not have any modules, create a module first to be able to create sessions.</span>
     </div>
diff --git a/src/main/resources/templates/edition/view/info.html b/src/main/resources/templates/edition/view/info.html
index 7db5cbcf8c5bfbe207aa4362f486ea92557d5aa0..c78099e884e716122bef296ae6fe55559c84a04c 100644
--- a/src/main/resources/templates/edition/view/info.html
+++ b/src/main/resources/templates/edition/view/info.html
@@ -125,6 +125,11 @@
             <h3>Options</h3>
         </div>
 
+        <form class="d-inline" th:action="@{/edition/{editionId}/visibility(editionId=${edition.id})}" method="post">
+            <button class="btn btn-primary">
+                <span th:if="${edition.hidden}">Unh</span><span th:unless="${edition.hidden}">H</span><span>ide in Queue</span>
+            </button>
+        </form>
         <a href="#" th:href="@{/edition/{id}/requests/export(id=${edition.id})}" class="btn btn-primary">Export all labs</a>
         <a href="#" th:href="@{/edition/{id}/requests/signofflist.csv(id=${edition.id})}"
            class="btn btn-secondary">Export latest status of student submission requests</a>
diff --git a/src/main/resources/templates/home/dashboard.html b/src/main/resources/templates/home/dashboard.html
index 5a7587764e700f8f5eccb4b4bd3a94f096bb809c..222999ef4827cd8ac9d7551efccded00969bc7b9 100644
--- a/src/main/resources/templates/home/dashboard.html
+++ b/src/main/resources/templates/home/dashboard.html
@@ -140,10 +140,14 @@
             </div>
 
             <div class="tab-pane fade show active" id="overview" role="tabpanel"
-                 aria-labelledby="overview-tabs">
+                 aria-labelledby="overview-tabs"
+                 th:with="visibleActive = ${activeRoles.?[#this.type.name() == 'TEACHER' or not (#root.editions.get(#this.edition.id).hidden)]},
+                          visibleUpcoming = ${upcomingRoles.?[#this.type.name() == 'TEACHER' or not (#root.editions.get(#this.edition.id).hidden)]},
+                          visibleFinished = ${finishedRoles.?[not (#root.editions.get(#this.edition.id).hidden)]},
+                          visibleArchived = ${archivedRoles.?[not (#root.editions.get(#this.edition.id).hidden)]}">
                 <div class="boxed-group">
                     <h3>Active courses you participate in</h3>
-                    <div class="boxed-group-inner" th:if="${#lists.isEmpty(activeRoles)}">
+                    <div class="boxed-group-inner" th:if="${#lists.isEmpty(visibleActive)}">
                         <span th:if="${user.defaultRole.name() == 'STUDENT'}">
                             You do not participate in any courses. Why don't you <a th:href="@{/editions}">enrol for your first course</a>?
                         </span>
@@ -153,11 +157,13 @@
                     </div>
 
                     <ul class="list-group">
-                        <li class="list-group-item" th:each="role : ${activeRoles}"
-                            th:with="edition = ${editions.get(role.edition.id)}">
+                        <li class="list-group-item" th:each="role : ${visibleActive}"
+                            th:with="edition = ${editions.get(role.edition.id)}"
+                            th:if="${role.type.name() == 'TEACHER'} or not ${editions[role.edition.id].hidden}">
                             <a th:href="@{/edition/{id}(id=${edition.id})}"
                                th:text="|${edition.course.name} (${edition.name})|"></a>
                             <span th:text="${'(' + @roleDTOService.typeDisplayName(role.type.toString()) + ')'}"></span>
+                            <span class="badge badge-pill badge-info text-white" th:if="${edition.hidden}">Hidden</span>
                             <ul th:unless="${#lists.isEmpty(labs)}">
                                 <th:block th:each="sess : ${edition.sessions}">
                                     <li th:each="lab : ${labs[sess.id]}" th:unless="${lab.isShared}" th:classappend="${lab.slot.open()} ? 'lab-open' : 'lab-closed'">
@@ -216,24 +222,25 @@
                     </ul>
                 </div>
 
-                <div class="boxed-group" th:if="${!#lists.isEmpty(upcomingRoles)}">
+                <div class="boxed-group" th:if="${!#lists.isEmpty(visibleUpcoming)}">
                     <h3>Upcoming courses</h3>
 
                     <ul class="list-group">
-                        <li class="list-group-item" th:each="role : ${upcomingRoles}"
+                        <li class="list-group-item" th:each="role : ${visibleUpcoming}"
                             th:with="edition = ${editions.get(role.edition.id)}">
                             <a th:href="@{/edition/{id}(id=${edition.id})}"
                                th:text="|${edition.course.name} (${edition.name})|"></a>
                             <span th:text="${'(' + @roleDTOService.typeDisplayName(role.type.toString()) + ')'}"></span>
+                            <span class="badge badge-pill badge-info text-white" th:if="${edition.hidden}">Hidden</span>
                         </li>
                     </ul>
                 </div>
 
-                <div class="boxed-group" th:if="${!#lists.isEmpty(finishedRoles)}">
+                <div class="boxed-group" th:if="${!#lists.isEmpty(visibleFinished)}">
                     <h3>Finished courses</h3>
 
                     <ul class="list-group">
-                        <li class="list-group-item" th:each="role : ${finishedRoles}"
+                        <li class="list-group-item" th:each="role : ${visibleFinished}"
                             th:with="edition = ${editions.get(role.edition.id)}">
                             <a th:href="@{/edition/{id}(id=${edition.id})}"
                                th:text="|${edition.course.name} (${edition.name})|"></a>
@@ -242,11 +249,11 @@
                     </ul>
                 </div>
 
-                <div class="boxed-group" th:if="${!#lists.isEmpty(archivedRoles)}">
+                <div class="boxed-group" th:if="${!#lists.isEmpty(visibleArchived)}">
                     <h3>Archived courses</h3>
 
                     <ul class="list-group">
-                        <th:block th:each="role : ${archivedRoles}" th:with="edition = ${editions.get(role.edition.id)}">
+                        <th:block th:each="role : ${visibleArchived}" th:with="edition = ${editions.get(role.edition.id)}">
                             <li class="list-group-item">
                                 <a th:href="@{/edition/{id}(id=${edition.id})}"
                                    th:text="|${edition.course.name} (${edition.name})|"></a>
diff --git a/src/test/java/nl/tudelft/queue/controller/EditionControllerTest.java b/src/test/java/nl/tudelft/queue/controller/EditionControllerTest.java
index a5598ca589e939e7b3a36a743bfa053ab8ce755d..260ba0e1bb03b0475ed75c057753823d2e1fb8c3 100644
--- a/src/test/java/nl/tudelft/queue/controller/EditionControllerTest.java
+++ b/src/test/java/nl/tudelft/queue/controller/EditionControllerTest.java
@@ -17,6 +17,7 @@
  */
 package nl.tudelft.queue.controller;
 
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -33,12 +34,14 @@ import nl.tudelft.labracore.api.EditionControllerApi;
 import nl.tudelft.labracore.api.dto.EditionDetailsDTO;
 import nl.tudelft.labracore.api.dto.EditionSummaryDTO;
 import nl.tudelft.labracore.api.dto.Id;
-import nl.tudelft.labracore.api.dto.PageEditionDetailsDTO;
 import nl.tudelft.labracore.api.dto.PersonSummaryDTO;
 import nl.tudelft.labracore.api.dto.RoleDetailsDTO;
 import nl.tudelft.labracore.api.dto.SessionDetailsDTO;
 import nl.tudelft.queue.dto.util.EditionFilterDTO;
+import nl.tudelft.queue.dto.view.QueueEditionDetailsDTO;
 import nl.tudelft.queue.model.labs.Lab;
+import nl.tudelft.queue.repository.QueueEditionRepository;
+import nl.tudelft.queue.service.EditionService;
 
 import org.hamcrest.core.StringContains;
 import org.junit.jupiter.api.BeforeEach;
@@ -99,6 +102,12 @@ public class EditionControllerTest {
 	@Autowired
 	private StudentGroupApiMocker sgApiMocker;
 
+	@Autowired
+	private QueueEditionRepository qer;
+
+	@Autowired
+	private EditionService es;
+
 	private EditionDetailsDTO oopNow;
 	private SessionDetailsDTO session1;
 	private Lab lab1;
@@ -128,8 +137,8 @@ public class EditionControllerTest {
 	@Test
 	@WithUserDetails("teacher0")
 	void getEmptyCourseListContainsModelAttributes() throws Exception {
-		when(eApi.getEditionsPageActiveOrTaughtBy(any(), any(), any(), any()))
-				.thenReturn(Mono.just(new PageEditionDetailsDTO().content(List.of()).totalElements(0L)));
+		when(eApi.getAllEditionsActiveOrTaughtBy(any()))
+				.thenReturn(Flux.just());
 
 		when(eApi.getAllEditionsActiveDuringPeriod(any())).thenReturn(Flux.just());
 
@@ -156,8 +165,8 @@ public class EditionControllerTest {
 	@Test
 	@WithUserDetails("teacher0")
 	void submitFiltersRemembersFilter() throws Exception {
-		when(eApi.getEditionsPageActiveOrTaughtBy(any(), any(), any(), any()))
-				.thenReturn(Mono.just(new PageEditionDetailsDTO().content(List.of()).totalElements(0L)));
+		when(eApi.getAllEditionsActiveOrTaughtBy(any()))
+				.thenReturn(Flux.just());
 
 		when(eApi.getAllEditionsActiveDuringPeriod(any())).thenReturn(Flux.just());
 
@@ -185,8 +194,8 @@ public class EditionControllerTest {
 	@Test
 	@WithUserDetails("student155")
 	void allEditionsNotExposedToStudent() throws Exception {
-		when(eApi.getEditionsPageActiveOrTaughtBy(any(), any(), any(), any()))
-				.thenReturn(Mono.just(new PageEditionDetailsDTO().content(List.of()).totalElements(0L)));
+		when(eApi.getAllEditionsActiveOrTaughtBy(any()))
+				.thenReturn(Flux.just());
 
 		when(eApi.getAllEditionsActiveDuringPeriod(any())).thenReturn(Flux.just());
 
@@ -245,7 +254,8 @@ public class EditionControllerTest {
 	void everyoneShouldBeAbleToSeeEditionInfo() throws Exception {
 		mvc.perform(get("/edition/{id}", oopNow.getId()))
 				.andExpect(status().isOk())
-				.andExpect(model().attribute("edition", oopNow));
+				.andExpect(model().attribute("edition",
+						es.queueEditionDTO(oopNow, QueueEditionDetailsDTO.class)));
 	}
 
 	@Test
@@ -309,6 +319,17 @@ public class EditionControllerTest {
 				.andExpect(flash().attributeExists("error"));
 	}
 
+	@Test
+	@WithUserDetails("admin")
+	void toggleEdition() throws Exception {
+		mvc.perform(post("/edition/1/visibility").with(csrf()))
+				.andExpect(status().is3xxRedirection());
+		assertThat(qer.findById(1L)).isPresent().hasValueSatisfying(e -> assertThat(e.getHidden()).isFalse());
+		mvc.perform(post("/edition/1/visibility").with(csrf()))
+				.andExpect(status().is3xxRedirection());
+		assertThat(qer.findById(1L)).isPresent().hasValueSatisfying(e -> assertThat(e.getHidden()).isTrue());
+	}
+
 	@ParameterizedTest
 	@MethodSource(value = "protectedEndpoints")
 	void testWithoutUserDetailsIsForbidden(MockHttpServletRequestBuilder request) throws Exception {
@@ -335,6 +356,7 @@ public class EditionControllerTest {
 				post("/edition/1/participants/1/block"),
 				get("/edition/1/leave"),
 				post("/edition/1/leave"),
+				post("/edition/1/toggle"),
 				get("/edition/1/status"));
 	}
 }