diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3c9842dde6af1e1341a4100cd444acf6b4b1e346..171cc4d115127a5cfc150aca4da63931f82ffc3e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,17 +5,19 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
-## [Unreleased]
+## [2.1.6]
 
 ### Added
 - Add pagination to raise requests, all declarations, and all applications. @toberhuber
+- Ability to select multiple applications to reject/offer. @dsavvidi 
 
 ### Changed
 - Applications will now be removed from the 'My applications' page after the edition has ended. @rwbackx
-- Raise request PDFs are now embedded so do not need to be downloade anymore. @rwbackx
+- Raise request PDFs are now embedded so do not need to be downloaded anymore. @rwbackx
 
 ### Fixed
  - FlexDelft export did not always work when generating the export took too long. @rwbackx
+ - Contract requests would not show up for head TAs. @rwbackx
 
 ## [2.1.5]
 
diff --git a/build.gradle.kts b/build.gradle.kts
index 8a1389c044a55db8ba90685f9e8917012e5b642c..414b472d85f2717d5f6d84c6032a6b951d68e80d 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -4,7 +4,7 @@ import nl.javadude.gradle.plugins.license.DownloadLicensesExtension
 import nl.javadude.gradle.plugins.license.LicenseExtension
 
 group = "nl.tudelft.tam"
-version = "2.1.5"
+version = "2.1.6"
 
 val javaVersion = JavaVersion.VERSION_17
 
@@ -63,7 +63,7 @@ plugins {
     id("io.spring.dependency-management").version("1.1.3")
 
     // Spotless plugin for checking style of Java code.
-    id("com.diffplug.spotless").version("6.19.0")
+    id("com.diffplug.spotless").version("6.21.0")
 
     // Plugin for checking license headers within our code and files.
     id("com.github.hierynomus.license").version("0.16.1")
@@ -123,7 +123,7 @@ configure<SpotlessExtension> {
     format("frontend") {
         target("src/main/resources/**/*.html", "src/main/resources/**/*.js", "src/main/resources/scss/**/*.scss")
 
-        prettier("2.6").config(mapOf(
+        prettier("3.0.3").config(mapOf(
                 "tabWidth" to 4, "semi" to true,
                 "printWidth" to 100,
                 "bracketSameLine" to true,
diff --git a/src/main/java/nl/tudelft/tam/cache/EditionRolesCacheManager.java b/src/main/java/nl/tudelft/tam/cache/EditionRolesCacheManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..4ef46ea26f70844a75f153e2ccb314203c5fdfb6
--- /dev/null
+++ b/src/main/java/nl/tudelft/tam/cache/EditionRolesCacheManager.java
@@ -0,0 +1,82 @@
+/*
+ * TAM
+ * Copyright (C) 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.tam.cache;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.annotation.RequestScope;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import nl.tudelft.labracore.api.EditionControllerApi;
+import nl.tudelft.labracore.api.dto.RolePersonDetailsDTO;
+import nl.tudelft.librador.cache.CoreCacheManager;
+
+@Component
+@RequestScope
+public class EditionRolesCacheManager extends CoreCacheManager<Long, EditionRolesCacheManager.RoleHolder> {
+	/**
+	 * Holds a list of roles cached by the ID of the edition that was to be looked up.
+	 */
+	@Data
+	@AllArgsConstructor
+	public static class RoleHolder {
+		private Long id;
+		private List<RolePersonDetailsDTO> roles;
+	}
+
+	private final EditionControllerApi eApi;
+
+	public EditionRolesCacheManager(@Autowired EditionControllerApi eApi) {
+		this.eApi = eApi;
+	}
+
+	/**
+	 * Gets the role details of a person in a given edition
+	 *
+	 * @param  personId  the person to get the role details of
+	 * @param  editionId the edition to get the role details in
+	 * @return           the role edition details
+	 */
+	public RoleHolder getRolesInEdition(Long personId, Long editionId) {
+		return new RoleHolder(editionId, eApi.getEditionParticipants(editionId)
+				.filter(x -> x.getPerson().getId().equals(personId))
+				.collectList().block());
+	}
+
+	@Override
+	protected List<RoleHolder> fetch(List<Long> ids) {
+		return ids.stream()
+				.map(id -> new RoleHolder(id, eApi.getEditionParticipants(id).collectList().block()))
+				.collect(Collectors.toList());
+	}
+
+	@Override
+	protected Long getId(RoleHolder roleHolder) {
+		return roleHolder.id;
+	}
+
+	@Override
+	protected int batchSize() {
+		// Set batch size to MAX because we already do batches of n=1 in fetch.
+		return Integer.MAX_VALUE;
+	}
+}
diff --git a/src/main/java/nl/tudelft/tam/controller/ApplicationController.java b/src/main/java/nl/tudelft/tam/controller/ApplicationController.java
index c173968ff504bda53bda21cee375e8b171e3ca53..a833c1da7f916e44c50e6a1ce69e23ac64be22db 100644
--- a/src/main/java/nl/tudelft/tam/controller/ApplicationController.java
+++ b/src/main/java/nl/tudelft/tam/controller/ApplicationController.java
@@ -71,7 +71,7 @@ public class ApplicationController {
 	 *
 	 * @param  person  The authenticated person
 	 * @param  program The program to see the applications for
-	 * @param  since   The applcations changed since this time
+	 * @param  since   The applications changed since this time
 	 * @param  model   The model to add data to
 	 * @return         The coordinator page
 	 */
@@ -133,6 +133,34 @@ public class ApplicationController {
 
 	// region manipulate data
 
+	/**
+	 * Promotes/Changes role to head TA (promote) for an already accepted student TA for that job offer
+	 *
+	 * @param  personId The student TA who will be promoted
+	 * @param  offerId  The job offer for which the student will be promoted
+	 * @return          The job offer page (i.e. from where the promotion was made)
+	 */
+	@PostMapping("promote/{personId}/{offerId}")
+	@PreAuthorize("@authorisationService.isManagerOfAny()")
+	public String promote(@PathVariable Long personId, @PathVariable Long offerId) {
+		applicationService.promote(personId, offerId);
+		return "redirect:/job-offer/" + offerId;
+	}
+
+	/**
+	 * Changes role of person to TA (demote) for the job offer
+	 *
+	 * @param  personId The student TA whose role will change
+	 * @param  offerId  The job offer for which the student's role will change
+	 * @return          The job offer page (i.e. from where the action was taken to change)
+	 */
+	@PostMapping("demote/{personId}/{offerId}")
+	@PreAuthorize("@authorisationService.isManagerOfAny()")
+	public String demote(@PathVariable Long personId, @PathVariable Long offerId) {
+		applicationService.demote(personId, offerId);
+		return "redirect:/job-offer/" + offerId;
+	}
+
 	/**
 	 * Submits an application for a student to a specific job offer.
 	 *
@@ -188,7 +216,7 @@ public class ApplicationController {
 	 */
 	@PostMapping("reject/{personId}/{offerId}")
 	@PreAuthorize("@authorisationService.isManagerOfAny()")
-	public String rejectApplication(@AuthenticatedPerson Person person, @PathVariable Long personId,
+	public String reject(@AuthenticatedPerson Person person, @PathVariable Long personId,
 			@PathVariable Long offerId) {
 		applicationService.reject(personId, offerId, person.getId());
 		return "redirect:/job-offer/" + offerId;
@@ -237,6 +265,21 @@ public class ApplicationController {
 		return "redirect:/job-offer/" + offerId;
 	}
 
+	/**
+	 * Rejects a group of people for a position
+	 *
+	 * @param  offerId        The ID of the position
+	 * @param  identifiersAll Identifiers for the people rejected
+	 * @return                The job offer page
+	 */
+	@PostMapping("reject/{offerId}")
+	@PreAuthorize("@authorisationService.isManagerOfJob(#offerId)")
+	public String bulkRejectApplication(@AuthenticatedPerson Person person, @PathVariable Long offerId,
+			@RequestParam String identifiersAll) {
+		applicationService.bulkReject(offerId, identifiersAll, person.getId());
+		return "redirect:/job-offer/" + offerId;
+	}
+
 	/**
 	 * Accept a job offer
 	 *
diff --git a/src/main/java/nl/tudelft/tam/controller/DeclarationController.java b/src/main/java/nl/tudelft/tam/controller/DeclarationController.java
index e7c9da9def875ec3d663c1f9843d2c34f1cf1e08..6a396ccd7d0add1f5173a4b751ab03ce666acc14 100644
--- a/src/main/java/nl/tudelft/tam/controller/DeclarationController.java
+++ b/src/main/java/nl/tudelft/tam/controller/DeclarationController.java
@@ -112,6 +112,7 @@ public class DeclarationController {
 	 * @return           The my declarations page or the previous page if provided
 	 */
 	@PostMapping("submit")
+	@PreAuthorize("@authorisationService.canDeclareHours(#createDTO.workId)")
 	public String submitDeclaration(@AuthenticatedPerson Person person,
 			@ModelAttribute DeclarationCreateDTO createDTO,
 			@RequestParam(required = false) String page) {
diff --git a/src/main/java/nl/tudelft/tam/controller/ExtraWorkController.java b/src/main/java/nl/tudelft/tam/controller/ExtraWorkController.java
index a90eaf47a6fe2d65443b2546b0d9ca5006df4e12..b9bcca8e963a58ba7b9e637c003e215679c140a2 100644
--- a/src/main/java/nl/tudelft/tam/controller/ExtraWorkController.java
+++ b/src/main/java/nl/tudelft/tam/controller/ExtraWorkController.java
@@ -123,7 +123,8 @@ public class ExtraWorkController {
 
 		List<Long> hiredEditionIds = personService.getRolesForPerson(person.getId())
 				.stream()
-				.filter(x -> x.getType().equals(RoleEditionDetailsDTO.TypeEnum.TA))
+				.filter(x -> Set.of(RoleEditionDetailsDTO.TypeEnum.TA, RoleEditionDetailsDTO.TypeEnum.HEAD_TA)
+						.contains(x.getType()))
 				.map(x -> Objects.requireNonNull(x.getEdition()).getId())
 				.collect(Collectors.toList());
 
diff --git a/src/main/java/nl/tudelft/tam/controller/JobOfferController.java b/src/main/java/nl/tudelft/tam/controller/JobOfferController.java
index 76a1519702f882c9415102b778c52c1c00dc44f7..2a9be5b25ac44266265eca822b288199abb81ffe 100644
--- a/src/main/java/nl/tudelft/tam/controller/JobOfferController.java
+++ b/src/main/java/nl/tudelft/tam/controller/JobOfferController.java
@@ -85,9 +85,6 @@ public class JobOfferController {
 	@Autowired
 	private CSVService csvService;
 
-	@Autowired
-	private PersonService personService;
-
 	@Autowired
 	private AuthorisationService authorisationService;
 
@@ -139,7 +136,6 @@ public class JobOfferController {
 			@RequestParam(required = false) String q, Model model) {
 		List<ApplicationDetailsJobOfferDTO> applications = applicationService
 				.getFilteredApplicationsForPerson(person.getId(), q);
-
 		List<JobOfferDetailsDTO> offerDetails = jobOfferService.getFilteredOpen(q);
 		List<ProgramSummaryDTO> programsWithJobOffers = courseService.getCoursesById(
 				offerDetails.stream().map(o -> o.getEdition().getCourse().getId()).distinct().toList())
diff --git a/src/main/java/nl/tudelft/tam/controller/PersonController.java b/src/main/java/nl/tudelft/tam/controller/PersonController.java
index 87d37ebc7dd71667b6cb56d476509e7bfdc83411..ecf2cd977a18145ee199006cdf044eda7cd8c1b1 100644
--- a/src/main/java/nl/tudelft/tam/controller/PersonController.java
+++ b/src/main/java/nl/tudelft/tam/controller/PersonController.java
@@ -69,6 +69,27 @@ public class PersonController {
 		return "redirect:/job-offer/" + offerId;
 	}
 
+	/**
+	 * Search for candidate to reject for a job offer based on one or a list of identifiers.
+	 *
+	 * @param  offerId       The job offer number for which the search is being conducted
+	 * @param  identifiers   The identifiers to identify the people searched for
+	 * @param  redirectAttrs The model attributes to keep when redirecting
+	 * @return               A redirect to the specific job offer page
+	 */
+	@PostMapping("find-candidate-to-reject")
+	@PreAuthorize("@authorisationService.isManagerOfAny()")
+	public String findCandidatesToReject(@RequestParam Long offerId,
+			@RequestParam String identifiers,
+			RedirectAttributes redirectAttrs) {
+		List<PersonSummaryDTO> candidates = personService.searchForPeople(identifiers);
+
+		redirectAttrs.addFlashAttribute("specificRejectCandidates", candidates);
+		redirectAttrs.addFlashAttribute("specificRejectQuery", identifiers);
+
+		return "redirect:/job-offer/" + offerId;
+	}
+
 	/**
 	 * Search for trainees based on one or a list of identifiers.
 	 *
diff --git a/src/main/java/nl/tudelft/tam/security/AuthorisationService.java b/src/main/java/nl/tudelft/tam/security/AuthorisationService.java
index 5d963e8e7f64824ae2164043dd9b04956954102d..64b58c4c382f417fd8f78e8e0b8c0e6a4f4fd1a6 100644
--- a/src/main/java/nl/tudelft/tam/security/AuthorisationService.java
+++ b/src/main/java/nl/tudelft/tam/security/AuthorisationService.java
@@ -306,4 +306,16 @@ public class AuthorisationService {
 	public boolean canCreateEdition(Long courseId) {
 		return isManagerOfCourse(courseId);
 	}
+
+	/**
+	 * Checks whether the authenticated user can declare hours for extra work.
+	 *
+	 * @param  workId The id of the extra work
+	 * @return        True iff the user can declare hours
+	 */
+	public boolean canDeclareHours(Long workId) {
+		Long editionId = extraWorkService.getById(workId).getEdition().getId();
+		return isAdmin() || roleService.hasAnyTARole(editionId, getAuthPerson().getId());
+	}
+
 }
diff --git a/src/main/java/nl/tudelft/tam/service/ApplicationService.java b/src/main/java/nl/tudelft/tam/service/ApplicationService.java
index 3f4a90d2922f6d64c07b7f08a05a290c58b5717a..3fb4b5e3574e0dc2ca562987ab71281e6afd34cd 100644
--- a/src/main/java/nl/tudelft/tam/service/ApplicationService.java
+++ b/src/main/java/nl/tudelft/tam/service/ApplicationService.java
@@ -217,7 +217,7 @@ public class ApplicationService {
 				setStatus(a, Status.REJECTED_BY_TEACHER);
 				roleService.removeRole(a.getId().getPersonId(), a.getId().getJobOfferId());
 
-				// Send notification to student
+				// Send notification to the student
 				String rejectionMessage = jobOffer.getRejectMessage();
 				sendApplicationRejectedNotification(a.getId().getPersonId(), jobOffer.getName(),
 						edition.getCourse().getName(), edition.getCourse().getCode(), edition.getName(),
@@ -226,6 +226,19 @@ public class ApplicationService {
 		});
 	}
 
+	/**
+	 * Finds the IDs of people the position is rejected for; Rejects them for the job.
+	 *
+	 * @param offerId     ID of job offer
+	 * @param identifiers Identifiers of people for which the job is rejected
+	 * @param handler     id of the person rejecting (ie. teacher)
+	 */
+	@Transactional
+	public void bulkReject(Long offerId, String identifiers, Long handler) {
+		List<PersonSummaryDTO> persons = personService.searchForPeople(identifiers);
+		persons.forEach(p -> reject(p.getId(), offerId, handler));
+	}
+
 	/**
 	 * Propose an offer. Change status to offered. PersonID not validated.
 	 *
@@ -271,7 +284,7 @@ public class ApplicationService {
 	}
 
 	/**
-	 * Finds the IDs of people the position is offerred to; Makes offer to everyone for the job.
+	 * Finds the IDs of people the position is offered to; Makes offer to everyone for the job.
 	 *
 	 * @param offerId     ID of job offer
 	 * @param identifiers Identifiers of people the job is offered to
@@ -343,12 +356,48 @@ public class ApplicationService {
 		roleService.changeToTa(personId, offerId);
 	}
 
+	/**
+	 * Promote student to head TA.
+	 *
+	 * @param  personId                   id of the person
+	 * @param  offerId                    id of the job offer
+	 * @throws ApplicationStatusException if status is not "accepted"
+	 */
+	@Transactional
+	public void promote(Long personId, Long offerId) {
+		Application.AppId id = new Application.AppId(personId, offerId);
+		Application app = applicationRepository.findByIdOrThrow(id);
+		if (!app.getStatus().equals(Status.ACCEPTED))
+			throw new IllegalStateException(
+					"Trying to change application from status:[" + app.getStatus() + "] to promoted.");
+
+		roleService.changeToHeadTa(personId, offerId);
+	}
+
+	/**
+	 * Demote student to TA.
+	 *
+	 * @param  personId                   id of the person
+	 * @param  offerId                    id of the job offer
+	 * @throws ApplicationStatusException if status is not "accepted"
+	 */
+	@Transactional
+	public void demote(Long personId, Long offerId) {
+		Application.AppId id = new Application.AppId(personId, offerId);
+		Application app = applicationRepository.findByIdOrThrow(id);
+		if (!app.getStatus().equals(Status.ACCEPTED))
+			throw new IllegalStateException(
+					"Trying to change application from status:[" + app.getStatus() + "] to demoted.");
+
+		roleService.changeToTa(personId, offerId);
+	}
+
 	// endregion
 
 	// region checkers
 
 	/**
-	 * Checks if applications exists for a person in job offer.
+	 * Checks if application exists for a person in job offer.
 	 *
 	 * @param  personId id of the person
 	 * @param  offerId  id of the person
@@ -535,7 +584,7 @@ public class ApplicationService {
 	}
 
 	/**
-	 * Counts the total number of applications , offers, accepted offers, and rejected offers.
+	 * Counts the total number of applications, offers, accepted offers, and rejected offers.
 	 *
 	 * @param  applications the applications to be counted
 	 * @return              an array of three integers, representing the total number of applications, offers,
@@ -586,7 +635,7 @@ public class ApplicationService {
 
 	/**
 	 * Retrieves applications for person and offers. Returns a map from offer id to a tuple containing a
-	 * boolean indicating whether application exists and is active, and the application status.
+	 * boolean indicating whether the application exists and is active, and the application status.
 	 *
 	 * @param  personId The id of the person
 	 * @param  offerIds The list of ids of job offers
@@ -838,7 +887,7 @@ public class ApplicationService {
 	// region notifications
 
 	/**
-	 * Send a application successfully received notification
+	 * Send an application successfully received notification
 	 *
 	 * @param personId   The ID of the person to send the notification to
 	 * @param position   The position that the application was for
@@ -865,7 +914,7 @@ public class ApplicationService {
 	}
 
 	/**
-	 * Send a application offered notification
+	 * Send an application offered notification
 	 *
 	 * @param personId      The ID of the person to send the notification to
 	 * @param position      The position that the application was for
@@ -899,7 +948,7 @@ public class ApplicationService {
 	}
 
 	/**
-	 * Send a application rejected notification
+	 * Send an application rejected notification
 	 *
 	 * @param personId         The ID of the person to send the notification to
 	 * @param position         The position that the application was for
diff --git a/src/main/java/nl/tudelft/tam/service/RoleService.java b/src/main/java/nl/tudelft/tam/service/RoleService.java
index 0a55b6ac352c2f88242fe22b08ab5584aab04ed0..c3cbf61eb75be0bc8ae59ca483f17c62252e7f3c 100644
--- a/src/main/java/nl/tudelft/tam/service/RoleService.java
+++ b/src/main/java/nl/tudelft/tam/service/RoleService.java
@@ -17,21 +17,22 @@
  */
 package nl.tudelft.tam.service;
 
+import static java.util.Objects.requireNonNull;
 import static nl.tudelft.labracore.api.dto.RoleEditionDetailsDTO.TypeEnum.HEAD_TA;
 import static nl.tudelft.labracore.api.dto.RoleEditionDetailsDTO.TypeEnum.TA;
 
 import java.util.HashSet;
 import java.util.List;
-import java.util.Objects;
 import java.util.Set;
-import java.util.stream.Collectors;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import nl.tudelft.labracore.api.EditionControllerApi;
 import nl.tudelft.labracore.api.PersonControllerApi;
 import nl.tudelft.labracore.api.RoleControllerApi;
 import nl.tudelft.labracore.api.dto.*;
+import nl.tudelft.tam.cache.EditionRolesCacheManager;
 
 @Service
 public class RoleService {
@@ -48,9 +49,15 @@ public class RoleService {
 	@Autowired
 	RoleControllerApi roleControllerApi;
 
+	@Autowired
+	EditionControllerApi editionControllerApi;
+
 	@Autowired
 	PersonControllerApi personApi;
 
+	@Autowired
+	EditionRolesCacheManager editionRolesCache;
+
 	/**
 	 * Get all the TA and Head TA roles for a person.
 	 *
@@ -74,8 +81,38 @@ public class RoleService {
 
 		assert listOfRoles != null;
 
-		return listOfRoles.stream().map(x -> Objects.requireNonNull(x.getEdition()).getId())
-				.collect(Collectors.toList()).contains(editionId);
+		return listOfRoles.stream().map(x -> requireNonNull(x.getEdition()).getId())
+				.toList().contains(editionId);
+	}
+
+	/**
+	 * Checks if a person has a TA or head TA role in an edition
+	 *
+	 * @param  personId  The person for whom to check
+	 * @param  editionId The edition to check in
+	 * @return           True if the person has a TA or head TA role in that edition
+	 */
+	public boolean hasAnyTARole(Long personId, Long editionId) {
+		List<RolePersonDetailsDTO> listOfRoles = editionRolesCache.getRolesInEdition(personId, editionId)
+				.getRoles();
+		return requireNonNull(listOfRoles).stream().map(RolePersonDetailsDTO::getType)
+				.toList().contains(RolePersonDetailsDTO.TypeEnum.HEAD_TA);
+	}
+
+	/**
+	 * Checks if a person has a head TA role in an edition
+	 *
+	 * @param  personId  The person for whom to check
+	 * @param  editionId The edition to check in
+	 * @return           True if the person has a head TA role in that edition
+	 */
+	public boolean hasHeadTARole(Long personId, Long editionId) {
+		List<RolePersonDetailsDTO> listOfRoles = editionRolesCache.getRolesInEdition(personId, editionId)
+				.getRoles();
+
+		assert listOfRoles != null;
+		return listOfRoles.stream().map(RolePersonDetailsDTO::getType)
+				.toList().contains(RolePersonDetailsDTO.TypeEnum.HEAD_TA);
 	}
 
 	/**
@@ -113,6 +150,27 @@ public class RoleService {
 		}
 	}
 
+	/**
+	 * Change the role of a person in the edition of the provided job offer to Head TA
+	 *
+	 * @param personId The person whose role to change
+	 * @param offerId  The job offer whose edition to change the role in
+	 */
+	public void changeToHeadTa(Long personId, Long offerId) {
+		Long editionId = jobOfferService.getEditionFromJobOffer(offerId);
+
+		if (hasRole(personId, editionId)) {
+			roleControllerApi
+					.patchRole(new RolePatchDTO().type(RolePatchDTO.TypeEnum.HEAD_TA), personId, editionId)
+					.block();
+		} else {
+			roleControllerApi.addRole(new RoleCreateDTO()
+					.edition(new EditionIdDTO().id(editionId))
+					.person(new PersonIdDTO().id(personId))
+					.type(RoleCreateDTO.TypeEnum.HEAD_TA)).block();
+		}
+	}
+
 	/**
 	 * Change the role of a person in the edition of the provided job offer to Teacher
 	 *
diff --git a/src/main/resources/email/html/application_offered.html b/src/main/resources/email/html/application_offered.html
index 550ccd1c83fc8e22650e63eb8c3e687e4fee0564..2cff7fe167715e2615f5956cf48b1228ca94de35 100644
--- a/src/main/resources/email/html/application_offered.html
+++ b/src/main/resources/email/html/application_offered.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html lang="en" xmlns:th="http://www.thymeleaf.org" style="font-family: sans-serif">
     <head>
         <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
diff --git a/src/main/resources/email/html/application_rejected.html b/src/main/resources/email/html/application_rejected.html
index 0562dc54f490ed260438b457367cbb4f908078f1..0fdece97bad851464b659f66965e41bfcf5890c0 100644
--- a/src/main/resources/email/html/application_rejected.html
+++ b/src/main/resources/email/html/application_rejected.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html lang="en" xmlns:th="http://www.thymeleaf.org" style="font-family: sans-serif">
     <head>
         <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
diff --git a/src/main/resources/email/html/application_submitted.html b/src/main/resources/email/html/application_submitted.html
index a4f2309d7648cbfd00f6d94991b17ff527b40c09..60e88b9f49edc5485eee65f61ba4a8495d42ae3d 100644
--- a/src/main/resources/email/html/application_submitted.html
+++ b/src/main/resources/email/html/application_submitted.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html lang="en" xmlns:th="http://www.thymeleaf.org" style="font-family: sans-serif">
     <head>
         <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
diff --git a/src/main/resources/email/html/declaration_approved.html b/src/main/resources/email/html/declaration_approved.html
index 675a6016a29526b19b6a6037843be311ba04d5f7..4e01c3aa4b49053443133795a1e92cccdc135a1d 100644
--- a/src/main/resources/email/html/declaration_approved.html
+++ b/src/main/resources/email/html/declaration_approved.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html lang="en" xmlns:th="http://www.thymeleaf.org" style="font-family: sans-serif">
     <head>
         <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
diff --git a/src/main/resources/email/html/declaration_rejected.html b/src/main/resources/email/html/declaration_rejected.html
index 86a8e6206e21ce7725995a649f5f6234fa1668a0..a0bc173e6b19c4242db5e8b940b66359b6516d19 100644
--- a/src/main/resources/email/html/declaration_rejected.html
+++ b/src/main/resources/email/html/declaration_rejected.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html lang="en" xmlns:th="http://www.thymeleaf.org" style="font-family: sans-serif">
     <head>
         <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties
index 87b0d73079552e4a6df4547edcebbe2aed861ad9..93e67521613a0b82a1917b8039f031a56818c393 100644
--- a/src/main/resources/messages.properties
+++ b/src/main/resources/messages.properties
@@ -102,12 +102,19 @@ jobOffer.search.people.label = Search for Candidates
 jobOffer.apply = Apply
 jobOffer.accept = Accept
 jobOffer.reject = Reject
+jobOffer.promote = Promote
+jobOffer.promote.confirm = Confirm Promotion
+jobOffer.promote.confirm.desc = Are you sure you would like to promote the following people:
+jobOffer.demote = Demote
+jobOffer.demote.confirm = Confirm Demote
+jobOffer.demote.confirm.desc = Are you sure you would like to demote the following people:
 jobOffer.reject.confirm = Confirm Rejection
 jobOffer.reject.confirm.desc = Are you sure you would like to reject the following people:
 jobOffer.offer = Send Offer
 jobOffer.offer.confirm = Confirm Offer
 jobOffer.offer.confirm.desc = Are you sure you would like to offer a position to the following people:
 jobOffer.offerToAll = Send Offer to All
+jobOffer.offerToSelected = Send Offers to Selected
 jobOffer.retract = Retract
 jobOffer.retract.confirm = Confirm Retraction
 jobOffer.retract.confirm.desc = Are you sure you would like to retract your application for the following positions:
@@ -160,7 +167,11 @@ jobOffer.edit = Edit Job Offer
 jobOffer.closeDeadline = Close New Applications
 jobOffer.closeDeadline.desc = Are you sure you want to close new applications? No other people will be able to apply to this job offer anymore.
 jobOffer.rejectAll = Reject All Open Applications
-jobOffer.rejectAll.desc = Are you sure you want to reject all applications? All submitted and offered applications will be rejected, only accepted applications will remain.
+jobOffer.selectAll = Select All
+jobOffer.rejectSelectedButton = Reject Selected
+jobOffer.rejectSelected = Reject Selected Applications
+jobOffer.rejectAll.desc = Are you sure you want to reject all open applications? All applications with status "submitted" will be rejected. Applications with status "offered" will remain in the offered state. Check the rejection message before clicking!
+jobOffer.rejectSelected.desc = Are you sure you want to reject all selected applications?
 jobOffer.addOffer = Offer Positions
 jobOffer.addOffer.desc = Search for student(s) using their NetID, student number, or name, with each separate student on a new line.
 jobOffer.addOffer.notFound = No students matching this query found. Try checking the capitalization since the search is CaSe SenSitIve.
@@ -291,6 +302,7 @@ status.jobOffer.rejected_by_student = Offer Rejected
 status.jobOffer.rejected_by_teacher = Rejected
 status.jobOffer.offered = Offered
 status.jobOffer.accepted = Accepted
+status.jobOffer.promoted = Promoted
 
 status.extraWork.submitted = Submitted
 status.extraWork.rejected_by_student = Retracted
@@ -302,6 +314,7 @@ manager.empty = Currently no managed courses.
 manager.managed = Managed Courses
 manager.coordinator = Coordinating Courses
 
+person.select = Select
 person.name = Full Name
 person.username = NetID
 person.number = Student Number
@@ -423,4 +436,5 @@ email.application.rejected.rejectionMessage = The following message was provided
 email.declaration.rejected.title = Contract Request Rejected
 email.declaration.rejected.message = Sadly, your contract request of {0} hours for {1} in the {2} ({3}) course was rejected. For more information please contact the course staff.
 email.declaration.approved.title = Contract Request Approved
-email.declaration.approved.message = Your contract request of {0} hours for {1} in the {2} ({3}) course was approved! You will be able to declare these hours in FlexDelft soon. For more information please contact the course staff.
\ No newline at end of file
+email.declaration.approved.message = Your contract request of {0} hours for {1} in the {2} ({3}) course was approved! You will be able to declare these hours in FlexDelft soon. For more information please contact the course staff.
+
diff --git a/src/main/resources/static/js/main.js b/src/main/resources/static/js/main.js
index a3f1613e57571bca5226c73607bba3da1ffd334d..62acbdfaf755ec5694bcd1618bbf26a6bda11346 100644
--- a/src/main/resources/static/js/main.js
+++ b/src/main/resources/static/js/main.js
@@ -119,10 +119,10 @@ function newDeclarationOverlay(workId) {
     if (workId !== null) {
         document.getElementById("new-declaration-id").value = workId;
         document.getElementById("new-declaration-name").value = document.getElementById(
-            "work-name-" + workId
+            "work-name-" + workId,
         ).innerText;
         document.getElementById("new-declaration-remaining-time").value = document.getElementById(
-            "work-remaining-time-" + workId
+            "work-remaining-time-" + workId,
         ).innerText;
     } else {
         document.getElementById("new-declaration-id").value = null;
@@ -173,10 +173,10 @@ function newOfferOverlay(type, editionId) {
     if (editionId !== null) {
         document.getElementById("new-" + type + "-edition-id").value = editionId;
         document.getElementById("new-" + type + "-edition-name").value = document.getElementById(
-            "edition-" + editionId
+            "edition-" + editionId,
         ).innerText;
         document.getElementById("new-" + type + "-course-name").value = document.getElementById(
-            "course-" + editionId
+            "course-" + editionId,
         ).innerText;
         if (type === "extra-work") {
             if (
@@ -188,7 +188,7 @@ function newOfferOverlay(type, editionId) {
             }
             if (document.getElementById("default-baanCode-extra-work-" + editionId).value !== "") {
                 document.getElementById("new-extra-work-baan-code").value = document.getElementById(
-                    "default-baanCode-extra-work-" + editionId
+                    "default-baanCode-extra-work-" + editionId,
                 ).value;
             } else {
                 // No Default Baan-Code
@@ -201,7 +201,7 @@ function newOfferOverlay(type, editionId) {
             ) {
                 toggleDatePickerWith(
                     "new-extra-work-deadline",
-                    "default-declaration-deadline-extra-work-" + editionId
+                    "default-declaration-deadline-extra-work-" + editionId,
                 );
                 document.getElementById("new-extra-work-deadline-bool").checked = true;
             }
@@ -212,7 +212,7 @@ function newOfferOverlay(type, editionId) {
             ) {
                 toggleDatePickerWith(
                     "new-job-offer-deadline",
-                    "default-application-deadline-job-offer-" + editionId
+                    "default-application-deadline-job-offer-" + editionId,
                 );
                 document.getElementById("new-job-offer-deadline-bool").checked = true;
             }
@@ -224,7 +224,7 @@ function newOfferOverlay(type, editionId) {
             }
             if (document.getElementById("default-baanCode-job-offer-" + editionId).value !== "") {
                 document.getElementById("new-job-offer-baan-code").value = document.getElementById(
-                    "default-baanCode-job-offer-" + editionId
+                    "default-baanCode-job-offer-" + editionId,
                 ).value;
             } else {
                 // No Default Baan-Code
@@ -237,7 +237,7 @@ function newOfferOverlay(type, editionId) {
             ) {
                 document.getElementById("new-job-offer-contract-start").value =
                     document.getElementById(
-                        "default-contract-startDate-job-offer-" + editionId
+                        "default-contract-startDate-job-offer-" + editionId,
                     ).value;
             }
             if (
@@ -246,7 +246,7 @@ function newOfferOverlay(type, editionId) {
             ) {
                 document.getElementById("new-job-offer-contract-end").value =
                     document.getElementById(
-                        "default-contract-endDate-job-offer-" + editionId
+                        "default-contract-endDate-job-offer-" + editionId,
                     ).value;
             }
             if (
@@ -341,7 +341,7 @@ function declarationSubmit() {
 function showHideEmailNotificationFrequency(emailNotificationsEnabledInput) {
     const emailNotificationFrequencyInput = document.getElementById("emailNotificationFrequency");
     const emailNotificationFrequencyLabel = document.getElementById(
-        "labelForEmailNotificationFrequency"
+        "labelForEmailNotificationFrequency",
     );
 
     if (emailNotificationsEnabledInput.checked) {
diff --git a/src/main/resources/templates/application/all.html b/src/main/resources/templates/application/all.html
index c895caf99999f0539eb02c91333fa1273c93cc07..7354d1cbc7635e86a30fb595d3940c7de12a4adf 100644
--- a/src/main/resources/templates/application/all.html
+++ b/src/main/resources/templates/application/all.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
@@ -154,7 +154,7 @@
                         const batch = $("#export-batch-number").val();
                         window.open(
                             `/application/flex-delft-export?program=${program}&since=${updatedSince.val()}&batch=${batch}`,
-                            "_blank"
+                            "_blank",
                         );
                         let url = new URL(window.location);
                         url.searchParams.set("since", moment().format("yyyy-MM-DDTHH:mm:ss"));
@@ -168,7 +168,7 @@
                     updatedSince
                         .change(function () {
                             $("#export-batch-number").val(
-                                $(this).children(":selected").data("batch") + 1
+                                $(this).children(":selected").data("batch") + 1,
                             );
                         })
                         .change(function () {
@@ -178,7 +178,7 @@
                         });
 
                     $("#export-batch-number").val(
-                        updatedSince.children(":selected").data("batch") + 1
+                        updatedSince.children(":selected").data("batch") + 1,
                     );
                 });
             </script>
diff --git a/src/main/resources/templates/application/view_many.html b/src/main/resources/templates/application/view_many.html
index 68d718c488e50b31a3c2423d7af5595652dfb959..a96273d329b3b7f2b004a533a47a45381d1bebd4 100644
--- a/src/main/resources/templates/application/view_many.html
+++ b/src/main/resources/templates/application/view_many.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/application/view_response.html b/src/main/resources/templates/application/view_response.html
index d17dc46a18219c6d5c67457ead244136f8dd5633..a64da78b00497a05cb5f98a56c299d847d18a8af 100644
--- a/src/main/resources/templates/application/view_response.html
+++ b/src/main/resources/templates/application/view_response.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/container.html b/src/main/resources/templates/container.html
index 77f4a58f896aa560487de94e0d48da9f5448cdbd..794772e4e0b4194db737d6ee88654b109d9db9cc 100644
--- a/src/main/resources/templates/container.html
+++ b/src/main/resources/templates/container.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/coordinator/confirm_training_type_removal.html b/src/main/resources/templates/coordinator/confirm_training_type_removal.html
index 5ad4dbacc81b223d49201ee3793b5fe3976fc6f6..a12595c9b120553808c3270729bec8fcf80c8655 100644
--- a/src/main/resources/templates/coordinator/confirm_training_type_removal.html
+++ b/src/main/resources/templates/coordinator/confirm_training_type_removal.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/coordinator/create_training_type.html b/src/main/resources/templates/coordinator/create_training_type.html
index 4dd24bdf5f8b1e0c277484bf3bca36ef0221232c..a35119508a2eacaabea3964a725a6971bd788c78 100644
--- a/src/main/resources/templates/coordinator/create_training_type.html
+++ b/src/main/resources/templates/coordinator/create_training_type.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/coordinator/defaults.html b/src/main/resources/templates/coordinator/defaults.html
index 73fd564c1b6e4038473669c6dfb37f50ddbfaffc..fd3ef4abcccd7be476aea4c88d61f97a89d607ca 100644
--- a/src/main/resources/templates/coordinator/defaults.html
+++ b/src/main/resources/templates/coordinator/defaults.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
@@ -367,7 +367,7 @@
                         document.querySelectorAll("[data-remove-ad]").forEach(e =>
                             e.addEventListener("click", function () {
                                 sendRemoveAd(e.getAttribute("data-remove-ad"));
-                            })
+                            }),
                         );
                     });
                 </script>
@@ -382,7 +382,7 @@
                             if (!saveButton.innerHTML.endsWith("*")) {
                                 saveButton.innerHTML += "*";
                             }
-                        })
+                        }),
                     );
                     saveButton.addEventListener("click", function () {
                         window.onbeforeunload = undefined;
diff --git a/src/main/resources/templates/coordinator/edit_training_type.html b/src/main/resources/templates/coordinator/edit_training_type.html
index 51f51e8950a3739392b9b6b03955745a56b6c0b7..aad753cd36199321019db503b23bd73ae2310f64 100644
--- a/src/main/resources/templates/coordinator/edit_training_type.html
+++ b/src/main/resources/templates/coordinator/edit_training_type.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/coordinator/manage.html b/src/main/resources/templates/coordinator/manage.html
index c67a06345ead062bdf4d6b4d6eb70c5795ef2feb..f067ac72a934a27ed199d540c086f6aa35b83298 100644
--- a/src/main/resources/templates/coordinator/manage.html
+++ b/src/main/resources/templates/coordinator/manage.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/coordinator/manage_coordinators.html b/src/main/resources/templates/coordinator/manage_coordinators.html
index d555a965ef22d3d86b1a8490966965a9935bcf66..4f929ceecf0d6ac8a8a147bb5ef7f6dfd89c320b 100644
--- a/src/main/resources/templates/coordinator/manage_coordinators.html
+++ b/src/main/resources/templates/coordinator/manage_coordinators.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/coordinator/training.html b/src/main/resources/templates/coordinator/training.html
index 31aa04115c6f4d34b04e52a140edac12f2a44a7b..fb855d769db8ccb33b1e6f915c0df44ea6ad0869 100644
--- a/src/main/resources/templates/coordinator/training.html
+++ b/src/main/resources/templates/coordinator/training.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/declaration/all.html b/src/main/resources/templates/declaration/all.html
index 41a03d9cc3436cb0150b745a6257a12cb0fb7e06..e5c3200f70c05023ca2d0d8fe3dcebd68c3ae3ab 100644
--- a/src/main/resources/templates/declaration/all.html
+++ b/src/main/resources/templates/declaration/all.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/declaration/view_reason.html b/src/main/resources/templates/declaration/view_reason.html
index f16624e38cfa9bef65f6511a214983ecf3062434..6d9a0a7bb11d02358f21f76a114c0c56798bf552 100644
--- a/src/main/resources/templates/declaration/view_reason.html
+++ b/src/main/resources/templates/declaration/view_reason.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/edition/create.html b/src/main/resources/templates/edition/create.html
index 31cd7c5b659bf30a1067fbf93c9f41bf55af5afb..6cb1b4a432955e107d3ca22ac01e41ceb2f2344d 100644
--- a/src/main/resources/templates/edition/create.html
+++ b/src/main/resources/templates/edition/create.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/error.html b/src/main/resources/templates/error.html
index 5ed41ad19a36e331ae0580f31920dc99872d9eb0..987495ce386ca64f0dbb33424f1e66bae825c044 100644
--- a/src/main/resources/templates/error.html
+++ b/src/main/resources/templates/error.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/extra_work/confirm_approve.html b/src/main/resources/templates/extra_work/confirm_approve.html
index 7b01e3d16bfc4255c55a47f8e202516184203343..943aa9a17e18786ad6f570171956e700357085d7 100644
--- a/src/main/resources/templates/extra_work/confirm_approve.html
+++ b/src/main/resources/templates/extra_work/confirm_approve.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/extra_work/confirm_approve_dec.html b/src/main/resources/templates/extra_work/confirm_approve_dec.html
index 3b51144d7f61e78086ca248314b7bed51362bf98..a41e3b814c95f3c0a547e037e7c40dbc922b5e05 100644
--- a/src/main/resources/templates/extra_work/confirm_approve_dec.html
+++ b/src/main/resources/templates/extra_work/confirm_approve_dec.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/extra_work/confirm_close.html b/src/main/resources/templates/extra_work/confirm_close.html
index bf08442276a34a8255aea8df1a450d6896c1a45d..95ad99aaa3ee5c6e4e90392366cf73a4580f320a 100644
--- a/src/main/resources/templates/extra_work/confirm_close.html
+++ b/src/main/resources/templates/extra_work/confirm_close.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/extra_work/confirm_reject.html b/src/main/resources/templates/extra_work/confirm_reject.html
index 946fc9f25b7a4b824663dbc671b50640c16da7d0..725524c4628d75eb5851ce4d9540478336126764 100644
--- a/src/main/resources/templates/extra_work/confirm_reject.html
+++ b/src/main/resources/templates/extra_work/confirm_reject.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/extra_work/confirm_reject_dec.html b/src/main/resources/templates/extra_work/confirm_reject_dec.html
index 1e3e6f321adf7526fd007a4dd4ad14cb31a0ea95..36b818fbd313c4fd4892572ef47222050b6be835 100644
--- a/src/main/resources/templates/extra_work/confirm_reject_dec.html
+++ b/src/main/resources/templates/extra_work/confirm_reject_dec.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/extra_work/confirm_retract_dec.html b/src/main/resources/templates/extra_work/confirm_retract_dec.html
index a04fa95d7eb4b51c1ae0b0e7e6b91fcfb44f7f2b..f080b482fc1a733c9bfb592ead6d8e066364ab42 100644
--- a/src/main/resources/templates/extra_work/confirm_retract_dec.html
+++ b/src/main/resources/templates/extra_work/confirm_retract_dec.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/extra_work/create.html b/src/main/resources/templates/extra_work/create.html
index e8ca78f4929d5a95412592abd415d9e04c155fff..c27f7e9c3355603dc85a982a6a3020a4b2553449 100644
--- a/src/main/resources/templates/extra_work/create.html
+++ b/src/main/resources/templates/extra_work/create.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/extra_work/edit.html b/src/main/resources/templates/extra_work/edit.html
index 9b19e687c688cdbae02e612242f9b106c10bf7ea..34fec6cd43681bfe68bff23d1446ccae7a6221a4 100644
--- a/src/main/resources/templates/extra_work/edit.html
+++ b/src/main/resources/templates/extra_work/edit.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/extra_work/import.html b/src/main/resources/templates/extra_work/import.html
index d116e061c592cf21adc1de3a7b1b102463a0c50a..eb4c0af95c37b8512c9f949c982a1df82dd6a43f 100644
--- a/src/main/resources/templates/extra_work/import.html
+++ b/src/main/resources/templates/extra_work/import.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/extra_work/new_declaration.html b/src/main/resources/templates/extra_work/new_declaration.html
index f52ae108c602db71a1df6b6464a97680f7fde1b7..c54c74a4a3de94ac45d1ddff9034549576c49f1d 100644
--- a/src/main/resources/templates/extra_work/new_declaration.html
+++ b/src/main/resources/templates/extra_work/new_declaration.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/extra_work/view_many.html b/src/main/resources/templates/extra_work/view_many.html
index a16b6d5fdad729fc7d19ad60857e218c59c06aab..a43578a73a7cb047cd9c924793543c017f59cf5e 100644
--- a/src/main/resources/templates/extra_work/view_many.html
+++ b/src/main/resources/templates/extra_work/view_many.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/extra_work/view_one.html b/src/main/resources/templates/extra_work/view_one.html
index 3aa5cab718dd9191f47393f1cdb826a064fdd787..d1d7c8d9bfec7ec648c8fcb28e7d524a1b70abb9 100644
--- a/src/main/resources/templates/extra_work/view_one.html
+++ b/src/main/resources/templates/extra_work/view_one.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html
index 1667a13d6ae4454c6e6b60e50435bfe97f33e43d..d417c92ea81fcb48eb508552f69cd95b62dd4538 100644
--- a/src/main/resources/templates/index.html
+++ b/src/main/resources/templates/index.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/job_offer/add_offer.html b/src/main/resources/templates/job_offer/add_offer.html
index b4a8a5a40bd1b61c14beb9c87939ef2c886b485e..cfabf4a5f722023193691dd87e58d6e5844b7d3d 100644
--- a/src/main/resources/templates/job_offer/add_offer.html
+++ b/src/main/resources/templates/job_offer/add_offer.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/job_offer/apply.html b/src/main/resources/templates/job_offer/apply.html
index 32cae5f01876cbc25303b4b172acfb5b87da0f75..0991634ecb1bdc1cdf9c26fc3e77b3b25c66a0d4 100644
--- a/src/main/resources/templates/job_offer/apply.html
+++ b/src/main/resources/templates/job_offer/apply.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/job_offer/confirm_close.html b/src/main/resources/templates/job_offer/confirm_close.html
index 9ff2e2a7edef1e9b6873f41c84021bf0181aebca..a454d4e711e09785c66b497143cb78ce6aab8218 100644
--- a/src/main/resources/templates/job_offer/confirm_close.html
+++ b/src/main/resources/templates/job_offer/confirm_close.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/job_offer/confirm_demote_app.html b/src/main/resources/templates/job_offer/confirm_demote_app.html
new file mode 100644
index 0000000000000000000000000000000000000000..443128bb9a0f766791ee1a086ee7a4ed6d5abf1a
--- /dev/null
+++ b/src/main/resources/templates/job_offer/confirm_demote_app.html
@@ -0,0 +1,58 @@
+<!--
+
+    TAM
+    Copyright (C) 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/>.
+
+-->
+<!doctype html>
+<html
+    lang="en"
+    xmlns:th="http://www.thymeleaf.org"
+    xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
+    <body>
+        <dialog
+            th:fragment="overlay"
+            th:id="|confirm-demote-app-${app.personId}-overlay|"
+            data-closable
+            class="dialog">
+            <form
+                th:action="@{|/application/demote/${app.personId}/${app.jobOfferId}|}"
+                th:method="post"
+                class="flex vertical p-7">
+                <h1 class="underlined font-500" th:text="#{jobOffer.demote.confirm}"></h1>
+
+                <p th:text="#{jobOffer.demote.confirm.desc}"></p>
+                <ul class="list">
+                    <li th:text="|${app.person.displayName} - (${app.person.username})|"></li>
+                </ul>
+
+                <div class="flex space-between mt-5">
+                    <button
+                        type="button"
+                        class="button p-less"
+                        data-style="outlined"
+                        th:text="#{general.cancel}"
+                        data-cancel></button>
+                    <button
+                        type="submit"
+                        class="button p-less"
+                        data-type="error"
+                        th:text="#{general.yes}"></button>
+                </div>
+            </form>
+        </dialog>
+    </body>
+</html>
diff --git a/src/main/resources/templates/job_offer/confirm_offer_all_app.html b/src/main/resources/templates/job_offer/confirm_offer_all_app.html
index 942c4102cccad12977631e6b257d42a5056427bd..59f879bdc9241140c4eefa9f434294167f813c8a 100644
--- a/src/main/resources/templates/job_offer/confirm_offer_all_app.html
+++ b/src/main/resources/templates/job_offer/confirm_offer_all_app.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/job_offer/confirm_offer_app.html b/src/main/resources/templates/job_offer/confirm_offer_app.html
index 308269235d8e96c0b2d23ff454047267320688c0..d5ee031144a230bbe600a7042388aed1e30c5e2e 100644
--- a/src/main/resources/templates/job_offer/confirm_offer_app.html
+++ b/src/main/resources/templates/job_offer/confirm_offer_app.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/job_offer/confirm_promote_app.html b/src/main/resources/templates/job_offer/confirm_promote_app.html
new file mode 100644
index 0000000000000000000000000000000000000000..e7e546edbad831805580da2846c306cc980a2489
--- /dev/null
+++ b/src/main/resources/templates/job_offer/confirm_promote_app.html
@@ -0,0 +1,54 @@
+<!--
+
+    TAM
+    Copyright (C) 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/>.
+
+-->
+<!doctype html>
+<html
+    lang="en"
+    xmlns:th="http://www.thymeleaf.org"
+    xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
+    <body>
+        <dialog
+            th:fragment="overlay"
+            th:id="|confirm-promote-app-${app.personId}-overlay|"
+            data-closable
+            class="dialog">
+            <form
+                th:action="@{|/application/promote/${app.personId}/${app.jobOfferId}|}"
+                method="post"
+                class="flex vertical p-7">
+                <h1 class="underlined font-500" th:text="#{jobOffer.promote.confirm}"></h1>
+
+                <p class="form-group-desc" th:text="#{jobOffer.promote.confirm.desc}"></p>
+                <ul class="list">
+                    <li th:text="|${app.person.displayName} - (${app.person.username})|"></li>
+                </ul>
+
+                <div class="flex space-between">
+                    <button
+                        type="button"
+                        class="button p-less"
+                        data-style="outlined"
+                        th:text="#{general.cancel}"
+                        data-cancel></button>
+                    <button type="submit" class="button p-less" th:text="#{general.yes}"></button>
+                </div>
+            </form>
+        </dialog>
+    </body>
+</html>
diff --git a/src/main/resources/templates/job_offer/confirm_reject.html b/src/main/resources/templates/job_offer/confirm_reject.html
index 99b73d5c9151b00c306405e07173775d2eb88580..92479797567d4129017a7e88ac23799ab9a582f0 100644
--- a/src/main/resources/templates/job_offer/confirm_reject.html
+++ b/src/main/resources/templates/job_offer/confirm_reject.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/job_offer/confirm_reject_app.html b/src/main/resources/templates/job_offer/confirm_reject_app.html
index 4af5abaa00f9d04afdcb552f732787cadf05e1f6..6d59bb05d801038ee53555b772bda1735b77e7bd 100644
--- a/src/main/resources/templates/job_offer/confirm_reject_app.html
+++ b/src/main/resources/templates/job_offer/confirm_reject_app.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/job_offer/confirm_reject_selected.html b/src/main/resources/templates/job_offer/confirm_reject_selected.html
new file mode 100644
index 0000000000000000000000000000000000000000..028996a98a1b510fa142e538717202ab7e7a258d
--- /dev/null
+++ b/src/main/resources/templates/job_offer/confirm_reject_selected.html
@@ -0,0 +1,64 @@
+<!--
+
+    TAM
+    Copyright (C) 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/>.
+
+-->
+<!doctype html>
+<html
+    lang="en"
+    xmlns:th="http://www.thymeleaf.org"
+    xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
+    <body>
+        <dialog
+            th:fragment="overlay"
+            id="confirm-reject-selected-overlay"
+            class="dialog"
+            th:open="${specificRejectCandidates == null ? null : ''}">
+            <form
+                th:action="@{|/application/reject/${offer.id}/|}"
+                th:method="post"
+                class="flex vertical p-7">
+                <h1 class="underlined font-500" th:text="#{jobOffer.rejectSelected}"></h1>
+
+                <label for="identifiersAll"></label>
+                <textarea
+                    id="identifiersAll"
+                    th:name="identifiersAll"
+                    th:text="${specificRejectQuery != null ? specificRejectQuery : ''}"
+                    type="text"
+                    class="textfield hidden"></textarea>
+
+                <p th:text="#{jobOffer.rejectSelected.desc}"></p>
+                <ul class="list">
+                    <li
+                        th:each="student : ${specificRejectCandidates}"
+                        th:text="|${student.getDisplayName()} - (${student.getUsername()})|"></li>
+                </ul>
+
+                <div class="flex space-between">
+                    <button
+                        type="button"
+                        class="button p-less"
+                        data-style="outlined"
+                        th:text="#{general.cancel}"
+                        data-cancel></button>
+                    <button type="submit" class="button p-less" th:text="#{general.yes}"></button>
+                </div>
+            </form>
+        </dialog>
+    </body>
+</html>
diff --git a/src/main/resources/templates/job_offer/confirm_retract_app.html b/src/main/resources/templates/job_offer/confirm_retract_app.html
index 1e32a4302689d0c3454e00a37ffe3dfb956d9b1e..c52f0f3aca731a20eeb78fbde71454623be99250 100644
--- a/src/main/resources/templates/job_offer/confirm_retract_app.html
+++ b/src/main/resources/templates/job_offer/confirm_retract_app.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/job_offer/confirm_retract_my_app_page.html b/src/main/resources/templates/job_offer/confirm_retract_my_app_page.html
index 39a4d3814609e50dd2e7c9da0272c19d80fa10d5..452e93bacc985c6db1a15accce44ba87acb704ca 100644
--- a/src/main/resources/templates/job_offer/confirm_retract_my_app_page.html
+++ b/src/main/resources/templates/job_offer/confirm_retract_my_app_page.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/job_offer/create.html b/src/main/resources/templates/job_offer/create.html
index 152609d3711520fc17aa1d7b75219950f43388fc..59eb32ce0dd11da7d42013e3ea66eea03eac360c 100644
--- a/src/main/resources/templates/job_offer/create.html
+++ b/src/main/resources/templates/job_offer/create.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/job_offer/default_container.html b/src/main/resources/templates/job_offer/default_container.html
index 1977c55884c832d005163cb5e64c4fec96503201..37c42ff5fbd9c4eee88c13625ebc477d813c2b0b 100644
--- a/src/main/resources/templates/job_offer/default_container.html
+++ b/src/main/resources/templates/job_offer/default_container.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/job_offer/edit.html b/src/main/resources/templates/job_offer/edit.html
index 926d053bd1add32876176dcc3e1f6a5da8853148..9de196c08e63ed61fc61bea2cb3b4a287d9e19f8 100644
--- a/src/main/resources/templates/job_offer/edit.html
+++ b/src/main/resources/templates/job_offer/edit.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/job_offer/import.html b/src/main/resources/templates/job_offer/import.html
index 5ef20a6da855f34282b19f29e57e8c95239caa20..db981711e61544e0e7f31c92180789f3364181de 100644
--- a/src/main/resources/templates/job_offer/import.html
+++ b/src/main/resources/templates/job_offer/import.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/job_offer/manage.html b/src/main/resources/templates/job_offer/manage.html
index ccc6de533eac032b7ada88465f5b029892eaf540..f08740c6715f19a26a8cc3a3b2f6fe0d66d4352c 100644
--- a/src/main/resources/templates/job_offer/manage.html
+++ b/src/main/resources/templates/job_offer/manage.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/job_offer/missing_editions.html b/src/main/resources/templates/job_offer/missing_editions.html
index 4ab13195f5011ea8a3908947a923857b31706dc1..ff5b61d952379cf216b83b4cb374ec2adabfcee9 100644
--- a/src/main/resources/templates/job_offer/missing_editions.html
+++ b/src/main/resources/templates/job_offer/missing_editions.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/job_offer/view_hiring_message.html b/src/main/resources/templates/job_offer/view_hiring_message.html
index 45fbeeaa5e6d1f6219b00e88d87424bc4be717d0..215eea01f113ff61059e8ddd8c8d376a52de0fbb 100644
--- a/src/main/resources/templates/job_offer/view_hiring_message.html
+++ b/src/main/resources/templates/job_offer/view_hiring_message.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/job_offer/view_many.html b/src/main/resources/templates/job_offer/view_many.html
index deb890ff3fd6eee722fd1fd5ac3bcbcb87238732..a7e58b44f40fb95da6655075270979e37c0dee92 100644
--- a/src/main/resources/templates/job_offer/view_many.html
+++ b/src/main/resources/templates/job_offer/view_many.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/job_offer/view_one.html b/src/main/resources/templates/job_offer/view_one.html
index d6e0fd484fdc988651ba195121c438c3b2d752b7..4ff2d4d9d93797429b8a56a55dfbb14f122d7205 100644
--- a/src/main/resources/templates/job_offer/view_one.html
+++ b/src/main/resources/templates/job_offer/view_one.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
@@ -199,7 +199,7 @@
                         document.addEventListener("DOMContentLoaded", function () {
                             const editButton = document.getElementById("edit-information-button");
                             const cancelButton = document.getElementById(
-                                "cancel-edit-information-button"
+                                "cancel-edit-information-button",
                             );
                             const viewDesc = document.getElementById("view-information");
                             const editDesc = document.getElementById("edit-information");
@@ -215,6 +215,67 @@
                                 editDesc.classList.add("hidden");
                                 descField.value = originalDesc;
                             });
+
+                            const selectAllButton = document.getElementById("select-all-button");
+                            const offerButton = document.getElementById("offer-to-selected-button");
+                            const rejectButton = document.getElementById("reject-selected-button");
+                            const checkboxes = document.querySelectorAll(
+                                '.check-box input[name="identifiers"]',
+                            );
+
+                            selectAllButton.addEventListener("click", function () {
+                                let allChecked = !Array.from(checkboxes).some(
+                                    checkbox => !checkbox.checked,
+                                );
+
+                                checkboxes.forEach(checkbox => {
+                                    checkbox.checked = !allChecked;
+                                });
+
+                                selectAllButton.innerText = allChecked
+                                    ? "Select All"
+                                    : "Deselect All";
+                                updateOfferAndRejectButtonState();
+                            });
+
+                            function getIdentifiers() {
+                                return Array.from(checkboxes)
+                                    .filter(checkbox => checkbox.checked)
+                                    .map(checkbox => checkbox.value)
+                                    .join("\n\r");
+                            }
+
+                            function fillIdentifiersForForm(formId) {
+                                document
+                                    .getElementById(formId)
+                                    .querySelector(`textarea[name="identifiers"]`).value =
+                                    getIdentifiers();
+                            }
+
+                            offerButton.addEventListener("click", function () {
+                                fillIdentifiersForForm("submit-offers-form");
+                            });
+
+                            rejectButton.addEventListener("click", function () {
+                                fillIdentifiersForForm("reject-selected-form");
+                            });
+
+                            function updateOfferAndRejectButtonState() {
+                                let noCheckboxesChecked = !Array.from(checkboxes).some(
+                                    checkbox => checkbox.checked,
+                                );
+                                document.getElementById("offer-to-selected-button").disabled =
+                                    noCheckboxesChecked;
+                                document.getElementById("reject-selected-button").disabled =
+                                    noCheckboxesChecked;
+                            }
+
+                            checkboxes.forEach(function (checkbox) {
+                                checkbox.addEventListener(
+                                    "change",
+                                    updateOfferAndRejectButtonState,
+                                );
+                            });
                         });
                     </script>
                 </div>
@@ -294,6 +355,11 @@
                 </div>
 
                 <div class="flex gap-3">
+                    <button
+                        class="button"
+                        data-style="outlined"
+                        th:text="#{jobOffer.selectAll}"
+                        id="select-all-button"></button>
                     <a
                         class="button"
                         data-style="outlined"
@@ -314,8 +380,51 @@
                 </div>
             </div>
 
+            <form
+                th:id="submit-offers-form"
+                th:action="@{/person/find-candidate}"
+                th:method="post"
+                hidden>
+                <label>
+                    <input
+                        th:form="submit-offers-form"
+                        class="hidden"
+                        th:name="offerId"
+                        th:value="${offer.id}" />
+                </label>
+                <label>
+                    <textarea
+                        type="text"
+                        th:form="submit-offers-form"
+                        class="hidden"
+                        th:name="identifiers"></textarea>
+                </label>
+            </form>
+
+            <form
+                th:id="reject-selected-form"
+                th:action="@{/person/find-candidate-to-reject}"
+                th:method="post"
+                hidden>
+                <label>
+                    <input
+                        th:form="reject-selected-form"
+                        class="hidden"
+                        th:name="offerId"
+                        th:value="${offer.id}" />
+                </label>
+                <label>
+                    <textarea
+                        type="text"
+                        th:form="reject-selected-form"
+                        class="hidden"
+                        th:name="identifiers"></textarea>
+                </label>
+            </form>
+
             <table class="table">
                 <tr class="table__header">
+                    <th th:text="#{person.select}"></th>
                     <th th:text="#{person.name}"></th>
                     <th th:text="#{person.number}"></th>
                     <th th:text="#{jobOffer.before}"></th>
@@ -324,17 +433,52 @@
                         th:text="#{application.content}"></th>
                     <th th:text="#{jobOffer.status}"></th>
                     <th th:text="#{jobOffer.queueFeedback}"></th>
-                    <th></th>
+                    <td>
+                        <div class="flex justify-end">
+                            <button
+                                id="offer-to-selected-button"
+                                th:form="submit-offers-form"
+                                type="submit"
+                                class="button p-min"
+                                data-style="outlined"
+                                th:text="#{jobOffer.offerToSelected}"
+                                disabled></button>
+                            <button
+                                id="reject-selected-button"
+                                th:form="reject-selected-form"
+                                type="submit"
+                                class="button p-min"
+                                data-type="error"
+                                data-style="outlined"
+                                th:text="#{jobOffer.rejectSelectedButton}"
+                                disabled></button>
+                            <th:block
+                                th:replace="~{job_offer/confirm_reject_selected :: overlay}"></th:block>
+                        </div>
+                    </td>
                 </tr>
                 <tr
+                    class="table-row"
                     th:each="app : ${applications}"
-                    th:data-status="${#strings.toLowerCase(app.status.name())}">
+                    th:data-status="${#strings.toLowerCase(app.status.name())}"
+                    th:with="headTA = ${@roleService.hasHeadTARole(app.personId, offer.editionId)}">
                     <td>
-                        <a
-                            class="link"
-                            target="_blank"
-                            th:href="@{|/profile/${app.personId}|}"
-                            th:text="${app.person.displayName}"></a>
+                        <label class="check-box">
+                            <input
+                                type="checkbox"
+                                name="identifiers"
+                                th:value="${app.person.number}" />
+                        </label>
+                    </td>
+                    <td>
+                        <div>
+                            <a
+                                class="link"
+                                target="_blank"
+                                th:href="@{|/profile/${app.personId}|}"
+                                th:text="${app.person.displayName}"></a>
+                            <span th:if="${headTA}" class="chip" th:text="#{role.head_ta}"></span>
+                        </div>
                     </td>
                     <td th:text="${app.person.number}"></td>
                     <td th:text="${app.taBefore} ? #{general.yes} : #{general.no}"></td>
@@ -362,7 +506,7 @@
                     <td>
                         <div class="flex justify-end gap-3" th:with="status=${app.status.name()}">
                             <div
-                                th:unless="${status == 'OFFERED' || status == 'ACCEPTED'}"
+                                th:unless="${status == 'OFFERED' || status == 'ACCEPTED' || headTA}"
                                 data-scroll-to="true">
                                 <button
                                     type="submit"
@@ -373,7 +517,26 @@
                                 <div th:replace="~{job_offer/confirm_offer_app :: overlay}"></div>
                             </div>
                             <div
-                                th:unless="${app.status.handled() && status != 'ACCEPTED'}"
+                                th:unless="${status != 'ACCEPTED' || headTA}"
+                                data-scroll-to="true">
+                                <button
+                                    type="submit"
+                                    class="button p-min"
+                                    data-type="accept"
+                                    th:text="#{jobOffer.promote}"
+                                    th:data-dialog="|confirm-promote-app-${app.personId}-overlay|"></button>
+                                <div th:replace="~{job_offer/confirm_promote_app :: overlay}"></div>
+                            </div>
+                            <div th:unless="${!headTA}" data-scroll-to="true">
+                                <button
+                                    class="button p-min"
+                                    data-type="error"
+                                    th:text="#{jobOffer.demote}"
+                                    th:data-dialog="|confirm-demote-app-${app.personId}-overlay|"></button>
+                                <div th:replace="~{job_offer/confirm_demote_app :: overlay}"></div>
+                            </div>
+                            <div
+                                th:unless="${app.status.handled() && status != 'ACCEPTED' && !headTA}"
                                 data-scroll-to="true">
                                 <button
                                     class="button p-min"
diff --git a/src/main/resources/templates/job_offer/view_reject_message.html b/src/main/resources/templates/job_offer/view_reject_message.html
index bd82e49ff7b62d34cce4c2ef533f65c46521339b..d66d000aeb896a67639b3c8fd337ad6ed736457e 100644
--- a/src/main/resources/templates/job_offer/view_reject_message.html
+++ b/src/main/resources/templates/job_offer/view_reject_message.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/layout.html b/src/main/resources/templates/layout.html
index 31a0629eac0fbbe48878132c32e55988a6c8ac21..cd50645be445183368451e009d065748d7240831 100644
--- a/src/main/resources/templates/layout.html
+++ b/src/main/resources/templates/layout.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html
index 4b3877026942806e1d73a45bef4c2ff8d3ea58f3..75140a2c97f3bee73a23fdd21ade0348f7273b77 100644
--- a/src/main/resources/templates/login.html
+++ b/src/main/resources/templates/login.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/pagination.html b/src/main/resources/templates/pagination.html
index 84a5d335d734f270c83417c31f0a0a3a7e385229..2796d937bee470c999317db220b73c1377946b36 100644
--- a/src/main/resources/templates/pagination.html
+++ b/src/main/resources/templates/pagination.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/privacy.html b/src/main/resources/templates/privacy.html
index d12e641e206141c7a0ce5298fe9258bc5014652c..7c53bdbd1672b1645c58ac4c38dbfc08ba23a94d 100644
--- a/src/main/resources/templates/privacy.html
+++ b/src/main/resources/templates/privacy.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/profile/create_raise_request.html b/src/main/resources/templates/profile/create_raise_request.html
index cbbf33c4b8cda2586267472620bf2c2d4a5d13e2..41fc492027af9251b81fbf2788f77db452af0374 100644
--- a/src/main/resources/templates/profile/create_raise_request.html
+++ b/src/main/resources/templates/profile/create_raise_request.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/profile/edit_notifications.html b/src/main/resources/templates/profile/edit_notifications.html
index 8284c1efc5819e81856b6738c3e3d28335cfeae6..4fb0b4eb25cc5aa71bb43965d2f277f329dab0e4 100644
--- a/src/main/resources/templates/profile/edit_notifications.html
+++ b/src/main/resources/templates/profile/edit_notifications.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/profile/edit_polo.html b/src/main/resources/templates/profile/edit_polo.html
index 94c15911e1e82ee43f92d0a5fddf3bf5130e081b..883ae667df66df1d39c95ed6a3c12c052cb1691e 100644
--- a/src/main/resources/templates/profile/edit_polo.html
+++ b/src/main/resources/templates/profile/edit_polo.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/profile/view.html b/src/main/resources/templates/profile/view.html
index b820434f6fcb59d64e5f0e8a62e0633d573be04e..f5580a1d9f7823685dff6fd9a857573d577fcd8b 100644
--- a/src/main/resources/templates/profile/view.html
+++ b/src/main/resources/templates/profile/view.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/profile/view_training_details.html b/src/main/resources/templates/profile/view_training_details.html
index 0318c6131a24073a620406df3cef047df1df6fb9..72d90c8201de9027fb4aff90fda14aedf8cc4b1e 100644
--- a/src/main/resources/templates/profile/view_training_details.html
+++ b/src/main/resources/templates/profile/view_training_details.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/raise_request/import.html b/src/main/resources/templates/raise_request/import.html
index 66bea115d5714afdb44b1ed9172ba3bbf3087b6a..299efbbad5379c74a7b655040e54a74b80e1029c 100644
--- a/src/main/resources/templates/raise_request/import.html
+++ b/src/main/resources/templates/raise_request/import.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/raise_request/import_results.html b/src/main/resources/templates/raise_request/import_results.html
index 53b9fe1cd4e6b1e27519e527a5ecf0ed8167e1d1..2a149df6a55da00109f7a50062e9ca7c3d872e81 100644
--- a/src/main/resources/templates/raise_request/import_results.html
+++ b/src/main/resources/templates/raise_request/import_results.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/raise_request/submitted.html b/src/main/resources/templates/raise_request/submitted.html
index 95ddc7c8ebbfb4a678dfae8bd7c0f0b9169d2957..fa3b86571492d7d1bc6784356620c5f2236c0070 100644
--- a/src/main/resources/templates/raise_request/submitted.html
+++ b/src/main/resources/templates/raise_request/submitted.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/raise_request/view.html b/src/main/resources/templates/raise_request/view.html
index 30e115109a402de33bf8777b87505db0a5433deb..472735c2951ff7eae85a78c1a38bb77ef59a2479 100644
--- a/src/main/resources/templates/raise_request/view.html
+++ b/src/main/resources/templates/raise_request/view.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
@@ -33,7 +33,8 @@
 
                 <div class="mb-2 flex vertical gap-0">
                     <span th:text="|Student: ${request.person.displayName}|"></span>
-                    <span th:text="|Pay scale: ${request.payScale.detailedString()}|"></span>
+                    <span
+                        th:text="|Requested pay scale: ${request.payScale.detailedString()}|"></span>
                 </div>
 
                 <object
diff --git a/src/main/resources/templates/training_approval/import.html b/src/main/resources/templates/training_approval/import.html
index fe6a8a4c5b84f80672709d32f98c0dbabfbeca45..b4827c2c49a1a59a72972779198d84cc7a261a91 100644
--- a/src/main/resources/templates/training_approval/import.html
+++ b/src/main/resources/templates/training_approval/import.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/training_approval/import_csv.html b/src/main/resources/templates/training_approval/import_csv.html
index a22b1e99b0db8143d36e47ad5df9ef0d47b3731f..ccd7cbf2dfcd4eaa80509ee8eb5b3922d1207643 100644
--- a/src/main/resources/templates/training_approval/import_csv.html
+++ b/src/main/resources/templates/training_approval/import_csv.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/main/resources/templates/training_approval/import_results.html b/src/main/resources/templates/training_approval/import_results.html
index b761df1e01e59fd4285ae608d3b737d90df80b7a..89b40de585898d8a96ca5717f928b24962daa6e2 100644
--- a/src/main/resources/templates/training_approval/import_results.html
+++ b/src/main/resources/templates/training_approval/import_results.html
@@ -17,7 +17,7 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 -->
-<!DOCTYPE html>
+<!doctype html>
 <html
     lang="en"
     xmlns:th="http://www.thymeleaf.org"
diff --git a/src/test/java/nl/tudelft/tam/cache/EditionRolesCacheManagerTest.java b/src/test/java/nl/tudelft/tam/cache/EditionRolesCacheManagerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..66e4c2c2d774db62a82af21451608c8142153ba9
--- /dev/null
+++ b/src/test/java/nl/tudelft/tam/cache/EditionRolesCacheManagerTest.java
@@ -0,0 +1,104 @@
+/*
+ * TAM
+ * Copyright (C) 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.tam.cache;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.*;
+
+import java.util.List;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.SpyBean;
+import org.springframework.transaction.annotation.Transactional;
+
+import application.test.TestTAMApplication;
+import nl.tudelft.labracore.api.EditionControllerApi;
+import nl.tudelft.labracore.api.dto.PersonSummaryDTO;
+import nl.tudelft.labracore.api.dto.RolePersonDetailsDTO;
+import reactor.core.publisher.Flux;
+
+@SpringBootTest(classes = TestTAMApplication.class)
+@Transactional
+public class EditionRolesCacheManagerTest {
+
+	@SpyBean
+	EditionRolesCacheManager cacheManager;
+
+	@Autowired
+	private EditionControllerApi eApi;
+	final long PERSON_ID = 5001;
+	final long PERSON_ID2 = 5002;
+	final long EDITION_ID = 7000;
+
+	RolePersonDetailsDTO roleCorrectPersonAndEdition;
+	RolePersonDetailsDTO roleInCorrectPersonAndEdition;
+
+	@BeforeEach
+	void setup() {
+		cacheManager = new EditionRolesCacheManager(eApi);
+		roleCorrectPersonAndEdition = new RolePersonDetailsDTO()
+				.type(RolePersonDetailsDTO.TypeEnum.STUDENT)
+				.person(new PersonSummaryDTO().id(PERSON_ID));
+		roleInCorrectPersonAndEdition = new RolePersonDetailsDTO()
+				.type(RolePersonDetailsDTO.TypeEnum.TA)
+				.person(new PersonSummaryDTO().id(PERSON_ID2));
+	}
+
+	@Test
+    void testGetRolesInEdition() {
+        when(eApi.getEditionParticipants(EDITION_ID))
+                .thenReturn(Flux.just(roleCorrectPersonAndEdition, roleInCorrectPersonAndEdition));
+
+        EditionRolesCacheManager.RoleHolder result = cacheManager.getRolesInEdition(PERSON_ID, EDITION_ID);
+
+        verify(eApi, times(1)).getEditionParticipants(EDITION_ID);
+
+        assertEquals(List.of(roleCorrectPersonAndEdition), result.getRoles());
+        assertEquals(EDITION_ID, result.getId());
+    }
+
+	@Test
+    void testGetRolesInEditionEmptyInput() {
+        when(eApi.getEditionParticipants(EDITION_ID))
+                .thenReturn(Flux.just());
+
+        EditionRolesCacheManager.RoleHolder result = cacheManager.getRolesInEdition(PERSON_ID, EDITION_ID);
+
+        verify(eApi, times(1)).getEditionParticipants(EDITION_ID);
+
+        assertEquals(List.of(), result.getRoles());
+        assertEquals(EDITION_ID, result.getId());
+    }
+
+	@Test
+    void testGetRolesInEditionEmptyResult() {
+        when(eApi.getEditionParticipants(EDITION_ID))
+                .thenReturn(Flux.just(roleInCorrectPersonAndEdition));
+
+        EditionRolesCacheManager.RoleHolder result = cacheManager.getRolesInEdition(PERSON_ID, EDITION_ID);
+
+        verify(eApi, times(1)).getEditionParticipants(EDITION_ID);
+
+        assertEquals(List.of(), result.getRoles());
+        assertEquals(EDITION_ID, result.getId());
+    }
+
+}
diff --git a/src/test/java/nl/tudelft/tam/controller/ApplicationControllerTest.java b/src/test/java/nl/tudelft/tam/controller/ApplicationControllerTest.java
index 8f2cb2f1c659daf91686604eaf9ea2497ee75616..d6d6f5d78a32908383659f0143f5eb0a6abfdef2 100644
--- a/src/test/java/nl/tudelft/tam/controller/ApplicationControllerTest.java
+++ b/src/test/java/nl/tudelft/tam/controller/ApplicationControllerTest.java
@@ -275,6 +275,34 @@ class ApplicationControllerTest {
 		verify(applicationService).reject(PERSON_ID_1, JOFFER_ID_1, ADMIN_ID);
 	}
 
+	@Test
+	@WithUserDetails("admin")
+	void promoteTest() throws Exception {
+		when(authService.isManagerOfAny()).thenReturn(true);
+		doNothing().when(applicationService).promote(anyLong(), anyLong());
+
+		mvc.perform(post("/application/promote/{personId}/{offerId}", PERSON_ID_1, JOFFER_ID_1).with(csrf()))
+				.andExpect(status().is3xxRedirection())
+				.andExpect(redirectedUrl("/job-offer/" + JOFFER_ID_1));
+
+		verify(authService).isManagerOfAny();
+		verify(applicationService).promote(PERSON_ID_1, JOFFER_ID_1);
+	}
+
+	@Test
+	@WithUserDetails("admin")
+	void demoteTest() throws Exception {
+		when(authService.isManagerOfAny()).thenReturn(true);
+		doNothing().when(applicationService).promote(anyLong(), anyLong());
+
+		mvc.perform(post("/application/demote/{personId}/{offerId}", PERSON_ID_1, JOFFER_ID_1).with(csrf()))
+				.andExpect(status().is3xxRedirection())
+				.andExpect(redirectedUrl("/job-offer/" + JOFFER_ID_1));
+
+		verify(authService).isManagerOfAny();
+		verify(applicationService).demote(PERSON_ID_1, JOFFER_ID_1);
+	}
+
 	@Test
 	@WithUserDetails("admin")
 	void rejectAllOpenJobOfferTest() throws Exception {
@@ -324,6 +352,26 @@ class ApplicationControllerTest {
 		verify(applicationService).bulkOffer(JOFFER_ID_1, id, ADMIN_ID);
 	}
 
+	@Test
+	@WithUserDetails("admin")
+	void bulkRejectApplicationTest() throws Exception {
+		PersonSummaryDTO p1 = new PersonSummaryDTO().id(PERSON_ID_1);
+		PersonSummaryDTO p2 = new PersonSummaryDTO().id(PERSON_ID_2);
+		when(authService.isManagerOfJob(JOFFER_ID_1)).thenReturn(true);
+		when(personService.searchForPeople(anyString())).thenReturn(List.of(p1, p2));
+		doNothing().when(applicationService).bulkReject(anyLong(), anyString(), anyLong());
+
+		String body = "test";
+		String id = "test";
+		mvc.perform(post("/application/reject/{offerId}", JOFFER_ID_1)
+				.param("identifiersAll", body).with(csrf()))
+				.andExpect(status().is3xxRedirection())
+				.andExpect(redirectedUrl("/job-offer/" + JOFFER_ID_1));
+
+		verify(authService).isManagerOfJob(JOFFER_ID_1);
+		verify(applicationService).bulkReject(JOFFER_ID_1, id, ADMIN_ID);
+	}
+
 	@Test
 	@WithUserDetails("username")
 	void acceptApplicationTest() throws Exception {
diff --git a/src/test/java/nl/tudelft/tam/controller/DeclarationControllerTest.java b/src/test/java/nl/tudelft/tam/controller/DeclarationControllerTest.java
index 7f56185898974868f0820f369b101d1a06a10d96..dc92ab0916dfa32641df2bab7b7f67cca7ed3d97 100644
--- a/src/test/java/nl/tudelft/tam/controller/DeclarationControllerTest.java
+++ b/src/test/java/nl/tudelft/tam/controller/DeclarationControllerTest.java
@@ -180,6 +180,7 @@ class DeclarationControllerTest {
 	@WithUserDetails("admin")
 	void submitDeclarationTest() throws Exception {
 		doNothing().when(declarationService).submit(any());
+		when(authService.canDeclareHours(anyLong())).thenReturn(true);
 
 		DeclarationCreateDTO createDto = DeclarationCreateDTO.builder()
 				.personId(PERSON_ID_1)
@@ -188,7 +189,7 @@ class DeclarationControllerTest {
 				.status(Status.SUBMITTED)
 				.build();
 
-		mvc.perform(post("/declaration/submit").with(csrf())
+		mvc.perform(post("/declaration/submit?workId=1").with(csrf())
 				.contentType(MediaType.APPLICATION_FORM_URLENCODED)
 				.flashAttr("createDto", createDto))
 				.andExpect(status().is3xxRedirection())
@@ -201,6 +202,7 @@ class DeclarationControllerTest {
 	@WithUserDetails("admin")
 	void submitDeclarationTestWithPage() throws Exception {
 		doNothing().when(declarationService).submit(any());
+		when(authService.canDeclareHours(anyLong())).thenReturn(true);
 
 		DeclarationCreateDTO createDto = DeclarationCreateDTO.builder()
 				.personId(PERSON_ID_1)
@@ -209,7 +211,7 @@ class DeclarationControllerTest {
 				.status(Status.SUBMITTED)
 				.build();
 
-		mvc.perform(post("/declaration/submit?page=abc").with(csrf())
+		mvc.perform(post("/declaration/submit?page=abc&workId=1").with(csrf())
 				.contentType(MediaType.APPLICATION_FORM_URLENCODED)
 				.flashAttr("createDto", createDto))
 				.andExpect(status().is3xxRedirection())
diff --git a/src/test/java/nl/tudelft/tam/controller/JobOfferControllerTest.java b/src/test/java/nl/tudelft/tam/controller/JobOfferControllerTest.java
index d8779339db4ba38963050af2236b530dde7b4bba..52f578dd2535a94720b42b077b2bf3461d5e08d3 100644
--- a/src/test/java/nl/tudelft/tam/controller/JobOfferControllerTest.java
+++ b/src/test/java/nl/tudelft/tam/controller/JobOfferControllerTest.java
@@ -87,6 +87,9 @@ class JobOfferControllerTest {
 	@MockBean
 	EditionService editionService;
 
+	@MockBean
+	RoleService roleService;
+
 	@MockBean
 	CourseService courseService;
 
@@ -463,6 +466,7 @@ class JobOfferControllerTest {
 		when(applicationService.getFilteredApplicationsForJobOffer(anyLong(), any()))
 				.thenReturn(List.of(ap1, ap2));
 
+
 		int[] stats = { 2, 1, 0 };
 		when(applicationService.getStatsForApps(anyList())).thenReturn(stats);
 
diff --git a/src/test/java/nl/tudelft/tam/controller/PersonControllerTest.java b/src/test/java/nl/tudelft/tam/controller/PersonControllerTest.java
index 021c3088f9a942e25425cb8cd78191767dc3f8e6..ae08791be0f82ccf7ba1e85d03303e57c623fe7f 100644
--- a/src/test/java/nl/tudelft/tam/controller/PersonControllerTest.java
+++ b/src/test/java/nl/tudelft/tam/controller/PersonControllerTest.java
@@ -89,6 +89,27 @@ public class PersonControllerTest {
 		verify(personService).searchForPeople(user);
 	}
 
+	@Test
+	@WithUserDetails("admin")
+	void findCandidatesToRejectTest() throws Exception {
+		when(authService.isManagerOfAny()).thenReturn(true);
+		when(personService.searchForPeople(anyString())).thenReturn(List.of(person1, person2));
+		when(applicationService.appExistsFor(anyLong(), anyLong())).thenReturn(false);
+
+		String user = "test";
+
+		mvc.perform(post("/person/find-candidate-to-reject")
+						.param("offerId", String.valueOf(JOFFER_ID_1))
+						.param("identifiers", user).with(csrf()))
+				.andExpect(status().is3xxRedirection())
+				.andExpect(flash().attribute("specificRejectCandidates", List.of(person1, person2)))
+				.andExpect(flash().attribute("specificRejectQuery", user))
+				.andExpect(redirectedUrl("/job-offer/" + JOFFER_ID_1));
+
+		verify(authService).isManagerOfAny();
+		verify(personService).searchForPeople(user);
+	}
+
 	@Test
 	@WithUserDetails("admin")
 	void findCandidatesWithStudentNumberTest() throws Exception {
diff --git a/src/test/java/nl/tudelft/tam/service/ApplicationServiceMockTest.java b/src/test/java/nl/tudelft/tam/service/ApplicationServiceMockTest.java
index 4512c049448f90eae19689535a5c89c9b0ef18b9..ea9d2c2b75036bdf04a3cbe2b186d10b25a395f9 100644
--- a/src/test/java/nl/tudelft/tam/service/ApplicationServiceMockTest.java
+++ b/src/test/java/nl/tudelft/tam/service/ApplicationServiceMockTest.java
@@ -19,6 +19,7 @@ package nl.tudelft.tam.service;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.mockito.Mockito.*;
 
 import java.io.IOException;
@@ -429,6 +430,52 @@ class ApplicationServiceMockTest {
 		verify(roleService).changeToTa(PERSON_ID_2, JOFFER_ID_1);
 	}
 
+	@Test
+	void testPromoteFromAcceptedStatus() {
+		app1b.setStatus(Status.ACCEPTED);
+		when(repository.findByIdOrThrow(app1b.getId())).thenReturn(app1b);
+		assertThat(app1b.getStatus()).isEqualTo(Status.ACCEPTED);
+
+		doNothing().when(roleService).changeToHeadTa(anyLong(), anyLong());
+
+		service.promote(PERSON_ID_2, JOFFER_ID_1);
+
+		assertThat(app1b.getStatus()).isEqualTo(Status.ACCEPTED);
+
+		verify(roleService).changeToHeadTa(PERSON_ID_2, JOFFER_ID_1);
+	}
+
+	@Test
+	void testPromoteFromNotAcceptedStatus() {
+		when(repository.findByIdOrThrow(app1b.getId())).thenReturn(app1b);
+		assertThrows(IllegalStateException.class, () -> service.promote(PERSON_ID_2, JOFFER_ID_1));
+
+		verify(roleService, never()).changeToHeadTa(anyLong(), anyLong());
+	}
+
+	@Test
+	void testDemoteFromAcceptedStatus() {
+		app1b.setStatus(Status.ACCEPTED);
+		when(repository.findByIdOrThrow(app1b.getId())).thenReturn(app1b);
+		assertThat(app1b.getStatus()).isEqualTo(Status.ACCEPTED);
+
+		doNothing().when(roleService).changeToTa(anyLong(), anyLong());
+
+		service.demote(PERSON_ID_2, JOFFER_ID_1);
+
+		assertThat(app1b.getStatus()).isEqualTo(Status.ACCEPTED);
+
+		verify(roleService).changeToTa(PERSON_ID_2, JOFFER_ID_1);
+	}
+
+	@Test
+	void testDemoteFromNotAcceptedStatus() {
+		when(repository.findByIdOrThrow(app1b.getId())).thenReturn(app1b);
+		assertThrows(IllegalStateException.class, () -> service.demote(PERSON_ID_2, JOFFER_ID_1));
+
+		verify(roleService, never()).changeToTa(anyLong(), anyLong());
+	}
+
 	@Test
 	void reject() {
 		when(repository.findByIdOrThrow(app1a.getId())).thenReturn(app1a);
@@ -460,6 +507,20 @@ class ApplicationServiceMockTest {
 		verify(roleService).removeRole(PERSON_ID_1, JOFFER_ID_1);
 	}
 
+	@Test
+	void bulkRejectTest() {
+		PersonSummaryDTO s1 = new PersonSummaryDTO().id(PERSON_ID_1);
+		PersonSummaryDTO s2 = new PersonSummaryDTO().id(PERSON_ID_2);
+		List<PersonSummaryDTO> persons = List.of(s1, s2);
+
+		doNothing().when(service).reject(anyLong(), anyLong(), anyLong());
+		when(personService.searchForPeople(anyString())).thenReturn(persons);
+
+		service.bulkReject(anyLong(), anyString(), anyLong());
+
+		verify(service, times(persons.size())).reject(anyLong(), anyLong(), anyLong());
+	}
+
 	@Test
 	void rejectAllOpenInJobOffer() {
 		when(jobOfferService.findByIdOrThrow(JOFFER_ID_1)).thenReturn(offer1);
diff --git a/src/test/java/nl/tudelft/tam/service/RoleServiceTest.java b/src/test/java/nl/tudelft/tam/service/RoleServiceTest.java
index aa073fbe129d0de70b506662f116f6053ef9b5ce..795a462058e2a215adde10213ce6db72aab21688 100644
--- a/src/test/java/nl/tudelft/tam/service/RoleServiceTest.java
+++ b/src/test/java/nl/tudelft/tam/service/RoleServiceTest.java
@@ -18,6 +18,7 @@
 package nl.tudelft.tam.service;
 
 import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+import static org.junit.jupiter.api.Assertions.*;
 import static org.mockito.Mockito.*;
 
 import java.util.List;
@@ -59,7 +60,7 @@ public class RoleServiceTest {
 	final long PERSON_ID = 5001;
 	final long EDITION_ID = 7000;
 
-	RoleEditionDetailsDTO roleCorrectEdition;
+	RoleEditionDetailsDTO roleCorrectEdition, roleCorrectEditionHeadTA;
 	RoleEditionDetailsDTO roleIncorrectEdition;
 
 	@BeforeEach
@@ -68,6 +69,10 @@ public class RoleServiceTest {
 				.type(RoleEditionDetailsDTO.TypeEnum.STUDENT)
 				.edition(new EditionSummaryDTO().id(EDITION_ID));
 
+		roleCorrectEditionHeadTA = new RoleEditionDetailsDTO()
+				.type(RoleEditionDetailsDTO.TypeEnum.HEAD_TA)
+				.edition(new EditionSummaryDTO().id(EDITION_ID));
+
 		roleIncorrectEdition = new RoleEditionDetailsDTO()
 				.type(RoleEditionDetailsDTO.TypeEnum.STUDENT)
 				.edition(new EditionSummaryDTO().id(0L));
@@ -140,6 +145,38 @@ public class RoleServiceTest {
 				.type(RoleCreateDTO.TypeEnum.TA));
 	}
 
+	@Test
+	void changeToHeadTaPatchRole() {
+		when(jobOfferService.getEditionFromJobOffer(JOFFER_ID)).thenReturn(EDITION_ID);
+
+		when(personService.getRolesForPerson(anyLong())).thenReturn(List.of(roleCorrectEdition));
+
+		when(roleControllerApi.patchRole(any(), anyLong(), anyLong()))
+				.thenReturn(Mono.just(new Id().personId(PERSON_ID).editionId(EDITION_ID)));
+
+		service.changeToHeadTa(PERSON_ID, JOFFER_ID);
+
+		verify(roleControllerApi).patchRole(new RolePatchDTO().type(RolePatchDTO.TypeEnum.HEAD_TA), PERSON_ID,
+				EDITION_ID);
+	}
+
+	@Test
+	void changeToHeadTaAddRole() {
+		when(jobOfferService.getEditionFromJobOffer(JOFFER_ID)).thenReturn(EDITION_ID);
+
+		when(personService.getRolesForPerson(anyLong())).thenReturn(List.of(roleIncorrectEdition));
+
+		when(roleControllerApi.addRole(any()))
+				.thenReturn(Mono.just(new Id().personId(PERSON_ID).editionId(EDITION_ID)));
+
+		service.changeToHeadTa(PERSON_ID, JOFFER_ID);
+
+		verify(roleControllerApi).addRole(new RoleCreateDTO()
+				.edition(new EditionIdDTO().id(EDITION_ID))
+				.person(new PersonIdDTO().id(PERSON_ID))
+				.type(RoleCreateDTO.TypeEnum.HEAD_TA));
+	}
+
 	@Test
 	void changeToTeacherPatchRole() {
 		when(personService.getRolesForPerson(anyLong())).thenReturn(List.of(roleCorrectEdition));