diff --git a/src/main/java/nl/tudelft/queue/controller/AssignmentController.java b/src/main/java/nl/tudelft/queue/controller/AssignmentController.java new file mode 100644 index 0000000000000000000000000000000000000000..867281f1232414154cd1b33b2484676123aab16f --- /dev/null +++ b/src/main/java/nl/tudelft/queue/controller/AssignmentController.java @@ -0,0 +1,158 @@ +/* + * Queue - A Queueing system that can be used to handle labs in higher education + * Copyright (C) 2016-2021 Delft University of Technology + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +package nl.tudelft.queue.controller; + +import nl.tudelft.labracore.api.AssignmentControllerApi; +import nl.tudelft.labracore.api.dto.AssignmentCreateDTO; +import nl.tudelft.labracore.api.dto.EditionDetailsDTO; +import nl.tudelft.labracore.api.dto.ModuleDetailsDTO; +import nl.tudelft.queue.cache.AssignmentCacheManager; +import nl.tudelft.queue.cache.EditionCacheManager; +import nl.tudelft.queue.cache.ModuleCacheManager; +import nl.tudelft.queue.dto.create.QueueAssignmentCreateDTO; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; + +@Controller +public class AssignmentController { + + @Autowired + private AssignmentCacheManager aCache; + + @Autowired + private ModuleCacheManager mCache; + + @Autowired + private EditionCacheManager eCache; + + @Autowired + private AssignmentControllerApi aApi; + + /** + * Gets the page for creating an assignment. This is a basic page with only the most basic of properties + * for an assignment. Further adjustments or creation options should be made available in Portal or later + * in Queue. + * + * @param moduleId The id of the module to add the assignment to. + * @param model The model to fill out for Thymeleaf template resolution. + * @return The Thymeleaf template to resolve. + */ + @GetMapping("/module/{moduleId}/assignment/create") + @PreAuthorize("@permissionService.canManageModule(#moduleId)") + public String getAssignmentCreatePage(@PathVariable Long moduleId, + Model model) { + return addCreateAssignmentAttributes(moduleId, new QueueAssignmentCreateDTO(moduleId), model); + } + + /** + * Handles a Post request to add a new assignment. The assignment is passed through a + * {@link QueueAssignmentCreateDTO}. After adding the assignment successfully, the user is redirected back + * to the edition/modules page. If something goes wrong during conversion, the user is instead directed + * back to the assignment create page. + * + * @param mId The id of the module to add the assignment to. + * @param dto The dto containing information for creating the assignment. + * @param model The model to fill out for Thymeleaf template resolution. + * @return A redirect back to the edition/modules page. + */ + @PostMapping("/module/{mId}/assignment/create") + @PreAuthorize("@permissionService.canManageModule(#mId)") + public String createAssignment(@PathVariable Long mId, + QueueAssignmentCreateDTO dto, + Model model) { + dto.setModuleId(mId); + + AssignmentCreateDTO create = dto.apply(); + if (dto.hasErrors()) { + return addCreateAssignmentAttributes(mId, dto, model); + } + + aApi.addAssignment(create).block(); + + ModuleDetailsDTO module = mCache.getOrThrow(mId); + return "redirect:/edition/" + module.getEdition().getId() + "/modules"; + } + + /** + * Gets the assignment removal page. This page is simply to confirm whether the user really wants to + * delete the assignment with the given id. + * + * @param assignmentId The id of the assignment that the user might want to delete. + * @param model The model to fill out for Thymeleaf template resolution. + * @return The Thymeleaf template to resolve. + */ + @GetMapping("/assignment/{assignmentId}/remove") + @PreAuthorize("@permissionService.canManageAssignment(#assignmentId)") + public String getAssignmentRemovePage(@PathVariable Long assignmentId, + Model model) { + var assignment = aCache.getOrThrow(assignmentId); + var module = mCache.getOrThrow(assignment.getModule().getId()); + var edition = eCache.getOrThrow(module.getEdition().getId()); + + model.addAttribute("edition", edition); + model.addAttribute("assignment", assignment); + + return "assignment/remove"; + } + + /** + * Deletes the assignment with the given id and redirects back to the modules page for the relevant + * edition. + * + * @param assignmentId The id of the assignment to delete. + * @return A redirect back to the edition/modules page. + */ + @PostMapping("/assignment/{assignmentId}/remove") + @PreAuthorize("@permissionService.canManageAssignment(#assignmentId)") + public String removeAssignment(@PathVariable Long assignmentId) { + var assignment = aCache.getOrThrow(assignmentId); + var module = mCache.getOrThrow(assignment.getModule().getId()); + + // TODO: Remove the assignment + + return "redirect:/edition/" + module.getEdition().getId() + "/modules"; + } + + /** + * Adds attributes for the create assignment page to the given model. + * + * @param moduleId The id of the module the assignment will be a part of. + * @param dto The DTO to add to the model for the page form to fill out. + * @param model The model to fill with attributes for the create-assignment page. + * @return The template to load for the create assignments page. + */ + private String addCreateAssignmentAttributes(Long moduleId, QueueAssignmentCreateDTO dto, + Model model) { + ModuleDetailsDTO module = mCache.getOrThrow(moduleId); + EditionDetailsDTO edition = eCache.getOrThrow(module.getEdition().getId()); + + model.addAttribute("edition", edition); + model.addAttribute("_module", module); + + model.addAttribute("dto", dto); + + return "assignment/create"; + } + +} diff --git a/src/main/java/nl/tudelft/queue/controller/EditionController.java b/src/main/java/nl/tudelft/queue/controller/EditionController.java index e86388f5738691d42a868e7e078e95c332b95403..44e6f7bd4c3291cc1f1add36a897929f0b7fdbdf 100644 --- a/src/main/java/nl/tudelft/queue/controller/EditionController.java +++ b/src/main/java/nl/tudelft/queue/controller/EditionController.java @@ -25,9 +25,7 @@ import java.util.List; import java.util.function.Predicate; import java.util.stream.Collectors; -import nl.tudelft.labracore.api.EditionControllerApi; -import nl.tudelft.labracore.api.PersonControllerApi; -import nl.tudelft.labracore.api.RoleControllerApi; +import nl.tudelft.labracore.api.*; import nl.tudelft.labracore.api.dto.*; import nl.tudelft.labracore.lib.security.user.AuthenticatedPerson; import nl.tudelft.labracore.lib.security.user.Person; @@ -299,6 +297,9 @@ public class EditionController { @PreAuthorize("@permissionService.canManageParticipants(#editionId)") public String createParticipant(@PathVariable Long editionId, Model model, QueueRoleCreateDTO dto) { + // TODO: Figure out a way to get rid of these additional sets + dto.setEditionId(editionId); + RoleCreateDTO create = dto.apply(); if (dto.hasErrors()) { model.addAttribute("edition", eCache.getOrThrow(editionId)); diff --git a/src/main/java/nl/tudelft/queue/controller/ModuleController.java b/src/main/java/nl/tudelft/queue/controller/ModuleController.java new file mode 100644 index 0000000000000000000000000000000000000000..49773dbc2a268c1ca0ab403048f7877eb0f35989 --- /dev/null +++ b/src/main/java/nl/tudelft/queue/controller/ModuleController.java @@ -0,0 +1,140 @@ +/* + * Queue - A Queueing system that can be used to handle labs in higher education + * Copyright (C) 2016-2021 Delft University of Technology + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +package nl.tudelft.queue.controller; + +import nl.tudelft.labracore.api.ModuleControllerApi; +import nl.tudelft.labracore.api.dto.EditionDetailsDTO; +import nl.tudelft.labracore.api.dto.ModuleCreateDTO; +import nl.tudelft.queue.cache.EditionCacheManager; +import nl.tudelft.queue.cache.ModuleCacheManager; +import nl.tudelft.queue.dto.create.QueueModuleCreateDTO; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; + +@Controller +public class ModuleController { + + @Autowired + private ModuleCacheManager mCache; + + @Autowired + private EditionCacheManager eCache; + + @Autowired + private ModuleControllerApi mApi; + + /** + * Gets the creation page for a module in the given edition. This sets up page attributes and returns the + * thymeleaf template to resolve. + * + * @param editionId The id of the edition the module should be in. + * @param model The model to fill out for Thymeleaf template resolution. + * @return The Thymeleaf template to resolve. + */ + @GetMapping("/edition/{editionId}/modules/create") + @PreAuthorize("@permissionService.canManageEdition(#editionId)") + public String getModuleCreatePage(@PathVariable Long editionId, Model model) { + return addCreateModuleAttributes(editionId, new QueueModuleCreateDTO(editionId), model); + } + + /** + * Creates a module from the given DTO in the edition with the given id. + * + * @param editionId The id of the edition to create the module in. + * @param dto The creation DTO containing all information needed to create a new module. + * @param model The model to fill out for Thymeleaf template resolution. + * @return A redirect back to the modules page or the module create page if something goes + * wrong. + */ + @PostMapping("/edition/{editionId}/modules/create") + @PreAuthorize("@permissionService.canManageEdition(#editionId)") + public String createModule(@PathVariable Long editionId, QueueModuleCreateDTO dto, Model model) { + // Ensure the DTO contains the right edition ID, as malicious users can inject a different ID in the form + dto.setEditionId(editionId); + + ModuleCreateDTO create = dto.apply(); + if (dto.hasErrors()) { + return addCreateModuleAttributes(editionId, dto, model); + } + + mApi.addModule(create).block(); + + return "redirect:/edition/" + editionId + "/modules"; + } + + /** + * Gets the module removal page. This page is only to confirm that the user does indeed want to remove the + * module and all attached assignments/student groups. + * + * @param moduleId The id of the module to remove. + * @param model The model to fill out for Thymeleaf template resolution. + * @return The Thymeleaf template to resolve. + */ + @GetMapping("/module/{moduleId}/remove") + public String getModuleRemovePage(@PathVariable Long moduleId, + Model model) { + var module = mCache.getOrThrow(moduleId); + var edition = eCache.getOrThrow(module.getEdition().getId()); + + model.addAttribute("edition", edition); + model.addAttribute("_module", module); + + return "module/remove"; + } + + /** + * Removes the module with the given id. + * + * @param moduleId The id of the module to delete. + * @return A redirect back to the edition modules page. + */ + @PostMapping("/module/{moduleId}/remove") + public String removeModule(@PathVariable Long moduleId) { + var module = mCache.getOrThrow(moduleId); + var edition = eCache.getOrThrow(module.getEdition().getId()); + + // TODO: Remove the module + + return "redirect:/edition/" + edition.getId() + "/modules"; + } + + /** + * Fills in page attributes for the module creation page with the id of the edition and the initial module + * creation DTO object. + * + * @param editionId The id of the edition to add the module to. + * @param dto The initial creation DTO for the module. + * @param model The model to fill out for Thymeleaf template resolution. + * @return The Thymeleaf template to resolve. + */ + private String addCreateModuleAttributes(Long editionId, QueueModuleCreateDTO dto, Model model) { + EditionDetailsDTO edition = eCache.getOrThrow(editionId); + + model.addAttribute("edition", edition); + model.addAttribute("dto", dto); + + return "module/create"; + } + +} diff --git a/src/main/java/nl/tudelft/queue/dto/create/QueueAssignmentCreateDTO.java b/src/main/java/nl/tudelft/queue/dto/create/QueueAssignmentCreateDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..7f329e5ad765c9e5017853c497e3e6541d5adaba --- /dev/null +++ b/src/main/java/nl/tudelft/queue/dto/create/QueueAssignmentCreateDTO.java @@ -0,0 +1,67 @@ +/* + * Queue - A Queueing system that can be used to handle labs in higher education + * Copyright (C) 2016-2021 Delft University of Technology + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +package nl.tudelft.queue.dto.create; + +import java.time.LocalDateTime; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +import lombok.*; +import nl.tudelft.labracore.api.dto.AssignmentCreateDTO; +import nl.tudelft.labracore.api.dto.Description; +import nl.tudelft.labracore.api.dto.ModuleIdDTO; +import nl.tudelft.librador.dto.create.Create; + +import org.springframework.format.annotation.DateTimeFormat; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class QueueAssignmentCreateDTO extends Create<AssignmentCreateDTO> { + private static final String TIME_FORMAT = "dd-MM-yyyy HH:mm"; + + @NotBlank + private String name; + @NotNull + @Builder.Default + private String description = ""; + + @DateTimeFormat(pattern = TIME_FORMAT) + private LocalDateTime deadline; + + @NotNull + private Long moduleId; + + public QueueAssignmentCreateDTO(Long moduleId) { + this.moduleId = moduleId; + } + + @Override + public Class<AssignmentCreateDTO> clazz() { + return AssignmentCreateDTO.class; + } + + @Override + protected void postApply(AssignmentCreateDTO data) { + data.description(new Description().text(description)); + data.module(new ModuleIdDTO().id(moduleId)); + } +} diff --git a/src/main/java/nl/tudelft/queue/dto/create/QueueModuleCreateDTO.java b/src/main/java/nl/tudelft/queue/dto/create/QueueModuleCreateDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..bc56b13233759aa4328c9e3e14a30ab24e015a25 --- /dev/null +++ b/src/main/java/nl/tudelft/queue/dto/create/QueueModuleCreateDTO.java @@ -0,0 +1,51 @@ +/* + * Queue - A Queueing system that can be used to handle labs in higher education + * Copyright (C) 2016-2021 Delft University of Technology + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +package nl.tudelft.queue.dto.create; + +import javax.validation.constraints.NotBlank; + +import lombok.*; +import nl.tudelft.labracore.api.dto.EditionIdDTO; +import nl.tudelft.labracore.api.dto.ModuleCreateDTO; +import nl.tudelft.librador.dto.create.Create; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class QueueModuleCreateDTO extends Create<ModuleCreateDTO> { + private Long editionId; + + @NotBlank + private String name; + + public QueueModuleCreateDTO(Long editionId) { + this.editionId = editionId; + } + + @Override + public Class<ModuleCreateDTO> clazz() { + return ModuleCreateDTO.class; + } + + @Override + protected void postApply(ModuleCreateDTO data) { + data.edition(new EditionIdDTO().id(editionId)); + } +} diff --git a/src/main/java/nl/tudelft/queue/service/PermissionService.java b/src/main/java/nl/tudelft/queue/service/PermissionService.java index c68f465b0241954504bb8c802062c99f0684982e..88154eec0e655db2135ae30ade637d4d110a78f1 100644 --- a/src/main/java/nl/tudelft/queue/service/PermissionService.java +++ b/src/main/java/nl/tudelft/queue/service/PermissionService.java @@ -30,6 +30,7 @@ import nl.tudelft.labracore.api.dto.*; import nl.tudelft.labracore.lib.security.LabradorUserDetails; import nl.tudelft.labracore.lib.security.user.DefaultRole; import nl.tudelft.labracore.lib.security.user.Person; +import nl.tudelft.queue.cache.*; import nl.tudelft.queue.cache.EditionCollectionCacheManager; import nl.tudelft.queue.cache.RoleCacheManager; import nl.tudelft.queue.cache.SessionCacheManager; @@ -63,11 +64,17 @@ public class PermissionService { private RequestRepository rr; @Autowired - private RoleCacheManager rCache; + private AssignmentCacheManager aCache; + + @Autowired + private ModuleCacheManager mCache; @Autowired private EditionCollectionCacheManager ecCache; + @Autowired + private RoleCacheManager rCache; + @Autowired private SessionCacheManager sCache; @@ -134,6 +141,31 @@ public class PermissionService { return lr.findById(labId).map(f).orElse(false); } + /** + * Checks whether a predicate holds given the module with the given id. If no such module could be found, + * this function just outputs {@code false}. + * + * @param moduleId The id of the module to find. + * @param f The predicate to check. + * @return The outcome of the predicate, or false if no module with the given id could be found. + */ + private boolean withModule(Long moduleId, Function<ModuleDetailsDTO, Boolean> f) { + return mCache.get(moduleId).map(f).orElse(false); + } + + /** + * Checks whether a predicate holds given the assignment with the given id. If no such assignment could be + * found, this function just outputs {@code false}. + * + * @param assignmentId The id of the assignment to find. + * @param f The predicate to check. + * @return The outcome of the predicate, or false if no assignment with the given id could be + * found. + */ + private boolean withAssignment(Long assignmentId, Function<AssignmentDetailsDTO, Boolean> f) { + return aCache.get(assignmentId).map(f).orElse(false); + } + /** * Checks whether a predicate holds given the session with the given id. If no such session could be * found, this function just outputs {@code false}. @@ -391,13 +423,27 @@ public class PermissionService { /** * @param lab The lab that the user wants to manage. - * @return Whether the authenticated user can change values in the given lab. + * @return Whether the authenticated user can change properties of the given lab. */ public boolean canManageLab(Lab lab) { return isAdmin() || withEditions(lab, editionIds -> withAnyRole(editionIds, (p, t) -> MANAGER_ROLES.contains(t))); } + /** + * @param moduleId The id of the module that the user wants to manage. + * @return Whether the authenticated user can change properties of the given module and add + * assignments/groups. + */ + public boolean canManageModule(Long moduleId) { + return isAdmin() || withModule(moduleId, module -> canManageEdition(module.getEdition().getId())); + } + + public boolean canManageAssignment(Long assignmentId) { + return isAdmin() || withAssignment(assignmentId, + assignment -> canManageModule(assignment.getModule().getId())); + } + /** * @param editionId The id of the edition the user wants to manage teachers for. * @return Whether the authenticated user can add and remove teachers to and from the edition diff --git a/src/main/resources/templates/assignment/create.html b/src/main/resources/templates/assignment/create.html new file mode 100644 index 0000000000000000000000000000000000000000..d81ae658b28337e69571c2258c919d64f9f6c35d --- /dev/null +++ b/src/main/resources/templates/assignment/create.html @@ -0,0 +1,99 @@ +<!-- + + Queue - A Queueing system that can be used to handle labs in higher education + Copyright (C) 2016-2020 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" + layout:decorate="~{edition/view}"> +<head> + <title th:text="|Create Assignment for ${'#' + edition.id}|"></title> + + <script src="/webjars/momentjs/min/moment.min.js"></script> + + <script type="application/javascript" src="/webjars/tempusdominus-bootstrap-4/js/tempusdominus-bootstrap-4.min.js"></script> + <link rel="stylesheet" href="/webjars/tempusdominus-bootstrap-4/css/tempusdominus-bootstrap-4.min.css"/> + + <script type="application/javascript" src="/webjars/bootstrap-select/js/bootstrap-select.min.js"></script> + <link rel="stylesheet" href="/webjars/bootstrap-select/css/bootstrap-select.min.css"/> +</head> + +<!--@thymesVar id="edition" type="nl.tudelft.labracore.api.dto.EditionDetailsDTO"--> + +<!--@thymesVar id="_module" type="nl.tudelft.labracore.api.dto.ModuleSummaryDTO"--> + +<!--@thymesVar id="dto" type="nl.tudelft.queue.dto.create.QueueAssignmentCreateDTO"--> + +<body> +<section layout:fragment="subcontent"> + <div class="page-header"> + <h3>Create module</h3> + </div> + + <form th:with="action = @{/module/{mId}/assignment/create(id=${edition.id}, mId=${_module.id})}" + th:action="${action}" th:object="${dto}" + class="form-horizontal" method="post"> + <input type="hidden" name="moduleId" th:value="${_module.id}"> + + <div class="form-group form-row"> + <label for="name-input" class="col-sm-2 col-form-label">Name:</label> + <div class="col-sm-4"> + <input id="name-input" + type="text" class="form-control" + required th:field="*{name}"/> + </div> + </div> + + <div class="form-group form-row"> + <label for="description-input" class="col-sm-2 col-form-label">Description:</label> + <div class="col-sm-10"> + <input id="description-input" + type="text" class="form-control" + th:field="*{description}"/> + </div> + </div> + + <div class="form-group form-row"> + <label for="deadline-input" class="col-sm-2 col-form-label">Deadline:</label> + <div class="col-sm-10 input-group" id="deadline-input" data-target-input="nearest"> + <input type="text" th:field="*{deadline}" + class="form-control datetimepicker-input" + data-target="#deadline-input"/> + <div class="input-group-append"> + <span class="input-group-text" + data-target="#deadline-input" + data-toggle="datetimepicker"> + <i class="fa fa-calendar"></i> + </span> + </div> + </div> + </div> + + <div class="form-group"> + <button type="submit" class="btn btn-primary ctrl-enter-submit float-right"> + Create new Assignment + </button> + <script type="text/javascript" src="/js/ctrl_enter_submit.js"></script> + </div> + </form> + + <script type="application/javascript"> + $("#deadline-input").datetimepicker({format: "DD-MM-YYYY HH:mm"}) + </script> +</section> +</body> +</html> diff --git a/src/main/resources/templates/assignment/remove.html b/src/main/resources/templates/assignment/remove.html new file mode 100644 index 0000000000000000000000000000000000000000..d13122753b3c36d52c0f299878094e30dff16c41 --- /dev/null +++ b/src/main/resources/templates/assignment/remove.html @@ -0,0 +1,47 @@ +<!-- + + Queue - A Queueing system that can be used to handle labs in higher education + Copyright (C) 2016-2020 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" + layout:decorate="~{edition/view}"> + +<!--@thymesVar id="edition" type="nl.tudelft.labracore.api.dto.EditionDetailsDTO"--> + +<!--@thymesVar id="assignment" type="nl.tudelft.labracore.api.dto.AssignmentDetailsDTO"--> + +<body> +<section layout:fragment="subcontent"> + <div class="page-sub-header"> + <h3>Remove Assignment</h3> + </div> + + <form th:action="@{/assignment/{id}/remove(id=${assignment.id})}" + class="form-horizontal" + method="post"> + Are you sure you want to remove <strong + th:text="${'assignment #' + assignment.id + ' (' + assignment.name + ')'}"></strong>? + <div class="text-center"> + <button class="btn btn-danger">Delete this lab</button> + <small>or <a + th:href="@{/edition/{eId}/modules(eId=${edition.id})}">go back</a></small> + </div> + </form> +</section> +</body> +</html> diff --git a/src/main/resources/templates/edition/view/labs.html b/src/main/resources/templates/edition/view/labs.html index a8e138de429cb2825a332e13e6921b5f492dfeed..a43f2688abacf14e3da89302f44abfb25a92793c 100644 --- a/src/main/resources/templates/edition/view/labs.html +++ b/src/main/resources/templates/edition/view/labs.html @@ -44,7 +44,7 @@ <a th:each="lType : ${T(nl.tudelft.queue.model.enums.LabType).values()}" href="#" th:href="@{/edition/{id}/lab/create(id=${edition.id}, type=${lType.name()})}" th:text="|Create ${lType.displayName} Lab|" - class="btn btn-sm btn-primary mr-1"> + class="btn btn-primary mr-1"> </a> </div> <h3>Labs</h3> diff --git a/src/main/resources/templates/edition/view/modules.html b/src/main/resources/templates/edition/view/modules.html index a51ef4f212a9060eb0ec99886c67951e8db3233f..9a38291a8d20bfc86da789fcef1baaf532c6d437 100644 --- a/src/main/resources/templates/edition/view/modules.html +++ b/src/main/resources/templates/edition/view/modules.html @@ -23,7 +23,7 @@ <!--@thymesVar id="edition" type="nl.tudelft.labracore.api.dto.EditionDetailsDTO"--> -<!--@thymesVar id="modules" type="nl.tudelft.labracore.api.dto.ModuleDetailsDTO"--> +<!--@thymesVar id="modules" type="java.util.List<nl.tudelft.labracore.api.dto.ModuleDetailsDTO>"--> <!--@thymesVar id="groups", type="java.util.Map<java.lang.Long, java.util.List<nl.tudelft.labracore.api.dto.StudentGroupDetailsDTO>>"--> <head> @@ -33,6 +33,10 @@ <body> <section layout:fragment="subcontent"> <div class="page-sub-header"> + <a class="btn btn-primary float-right" + th:href="@{/edition/{id}/modules/create(id=${edition.id})}"> + Create Module + </a> <h3>Modules</h3> </div> @@ -43,11 +47,11 @@ <div th:unless="${#lists.isEmpty(modules)}" class="row"> <div class="col-md-3 mb-2"> <div class="list-group" role="tablist"> - <th:block th:each="module, idx : ${modules}"> + <th:block th:each="_module, idx : ${modules}"> <a class="list-group-item list-group-item-action" th:classappend="${idx.first ? 'active' : ''}" role="tab" data-toggle="list" - th:href="'#module-' + ${module.id}" th:text="${module.name}"></a> + th:href="'#module-' + ${_module.id}" th:text="${_module.name}"></a> </th:block> </div> </div> @@ -59,25 +63,49 @@ role="tabpanel" th:id="'module-' + ${m.id}"> - <h3 class="ml-2">Assignments</h3> + <div> + <a class="btn btn-danger btn-sm float-right disabled" + th:href="@{/module/{mId}/remove(mId=${m.id})}" + th:text="|Remove module - ${m.name}|" + style="pointer-events: all !important;" + data-toggle="tooltip" data-placement="top" title="Not available yet"> + </a> + </div> + + <div> + <a class="btn btn-primary btn-sm float-right mr-4" + th:href="@{/module/{mId}/assignment/create(mId=${m.id})}"> + Create Assignment + </a> + <h3 class="ml-2">Assignments</h3> + </div> <h5 th:if="${#lists.isEmpty(m.assignments)}"> There are no assignments in this module, configure them in Portal </h5> - <div class="row col-12" th:unless="${#lists.isEmpty(m.assignments)}"> + <div class="row col-12 mt-2" th:unless="${#lists.isEmpty(m.assignments)}"> <table class="table"> <thead class="thead-light"> <tr> <th scope="col">#</th> <th scope="col">Name</th> <th scope="col">Description</th> + <th scope="col"></th> </tr> </thead> <tr th:each="assignment, aIdx : ${m.getAssignments()}"> <th scope="row" th:text="${assignment.id}"></th> <td th:text="${assignment.name}"></td> <td th:text="${assignment.getDescription().text}"></td> + <td> + <a th:href="@{/assignment/{aId}/remove(aId=${assignment.id})}" + style="pointer-events: all !important;" + class="btn btn-danger float-right to-be-implemented disabled" + data-toggle="tooltip" data-placement="top" title="Not available yet"> + <i class="fa fa-trash"></i> + </a> + </td> </tr> </table> </div> @@ -115,6 +143,12 @@ </div> </div> </div> + + <script> + $(function() { + $("[data-toggle='tooltip']").tooltip() + }) + </script> </section> </body> </html> diff --git a/src/main/resources/templates/edition/view/participants.html b/src/main/resources/templates/edition/view/participants.html index 8582e0c0a115d26474e58d8085a9a08f4efeaef8..a786b2581d13a776dba4f38f94612ca5eb7e5803 100644 --- a/src/main/resources/templates/edition/view/participants.html +++ b/src/main/resources/templates/edition/view/participants.html @@ -34,7 +34,7 @@ <div class="page-sub-header"> <div class="float-right" style="margin-right: 10px"> <a href="#" th:href="@{/edition/{id}/participants/create(id=${edition.id})}" - class="btn btn-sm btn-secondary"> + class="btn btn-secondary"> Add participant </a> </div> diff --git a/src/main/resources/templates/lab/create.html b/src/main/resources/templates/lab/create.html index 6da34ff0cd739ed13edfb71ee2769e61ccd53848..dc77ddbe4d462bf17e3bc4a71d1c0cb18dc04f41 100644 --- a/src/main/resources/templates/lab/create.html +++ b/src/main/resources/templates/lab/create.html @@ -21,6 +21,8 @@ <html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{edition/view}"> <head> + <title th:text="|Create Lab for ${'#' + (ec == null ? edition.id : ec.id)}|"></title> + <script src="/webjars/momentjs/min/moment.min.js"></script> <script type="application/javascript" diff --git a/src/main/resources/templates/module/create.html b/src/main/resources/templates/module/create.html new file mode 100644 index 0000000000000000000000000000000000000000..de51cf26b1348195cfeec64370fcdbb35c40db86 --- /dev/null +++ b/src/main/resources/templates/module/create.html @@ -0,0 +1,64 @@ +<!-- + + Queue - A Queueing system that can be used to handle labs in higher education + Copyright (C) 2016-2020 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" + layout:decorate="~{edition/view}"> +<head> + <title th:text="|Create Module for ${'#' + edition.id}|"></title> + + <script type="application/javascript" src="/webjars/bootstrap-select/js/bootstrap-select.min.js"></script> + <link rel="stylesheet" href="/webjars/bootstrap-select/css/bootstrap-select.min.css"/> +</head> + +<!--@thymesVar id="edition" type="nl.tudelft.labracore.api.dto.EditionDetailsDTO"--> + +<!--@thymesVar id="dto" type="nl.tudelft.queue.dto.create.QueueModuleCreateDTO"--> + +<body> +<section layout:fragment="subcontent"> + <div class="page-header"> + <h3>Create module</h3> + </div> + + <form th:with="action = @{/edition/{id}/modules/create(id=${edition.id})}" + th:action="${action}" th:object="${dto}" + class="form-horizontal" method="post"> + <input type="hidden" name="id" th:value="${edition.id}"> + + <div class="form-group form-row"> + <label for="title-input" class="col-md-2 col-form-label">Name:</label> + <div class="col-md-4"> + <input id="title-input" + type="text" class="form-control" + required="required" th:field="*{name}" + placeholder="Assignments"/> + </div> + + <div class="col-md-6"> + <button type="submit" class="btn btn-primary ctrl-enter-submit float-right"> + Create new module + </button> + <script type="text/javascript" src="/js/ctrl_enter_submit.js"></script> + </div> + </div> + </form> +</section> +</body> +</html> diff --git a/src/main/resources/templates/module/remove.html b/src/main/resources/templates/module/remove.html new file mode 100644 index 0000000000000000000000000000000000000000..efc76b372a707d0d11c365303591a786fbef0fb8 --- /dev/null +++ b/src/main/resources/templates/module/remove.html @@ -0,0 +1,47 @@ +<!-- + + Queue - A Queueing system that can be used to handle labs in higher education + Copyright (C) 2016-2020 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" + layout:decorate="~{edition/view}"> + +<!--@thymesVar id="edition" type="nl.tudelft.labracore.api.dto.EditionDetailsDTO"--> + +<!--@thymesVar id="_module" type="nl.tudelft.labracore.api.dto.ModuleDetailsDTO"--> + +<body> +<section layout:fragment="subcontent"> + <div class="page-sub-header"> + <h3>Remove Module</h3> + </div> + + <form th:action="@{/module/{id}/remove(id=${_module.id})}" + class="form-horizontal" + method="post"> + Are you sure you want to remove <strong + th:text="${'module #' + _module.id + ' (' + _module.name + ')'}"></strong>? + <div class="text-center"> + <button class="btn btn-danger">Delete this lab</button> + <small>or <a + th:href="@{/edition/{eId}/modules(eId=${edition.id})}">go back</a></small> + </div> + </form> +</section> +</body> +</html> diff --git a/src/test/java/nl/tudelft/queue/controller/AnnouncementControllerTest.java b/src/test/java/nl/tudelft/queue/controller/AnnouncementControllerTest.java index 392af3249deb329f72f6530335c20998500cb936..b4dc203ac8a795374228fe5f7a33f95b80a074ba 100644 --- a/src/test/java/nl/tudelft/queue/controller/AnnouncementControllerTest.java +++ b/src/test/java/nl/tudelft/queue/controller/AnnouncementControllerTest.java @@ -44,6 +44,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.security.test.context.support.WithUserDetails; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; @@ -53,6 +54,7 @@ import test.test.TestQueueApplication; @Transactional @AutoConfigureMockMvc @SpringBootTest(classes = TestQueueApplication.class) +@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS) public class AnnouncementControllerTest { @Autowired diff --git a/src/test/java/nl/tudelft/queue/controller/AssignmentControllerTest.java b/src/test/java/nl/tudelft/queue/controller/AssignmentControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8b86ca6d1ecb9a0b38f3a0e96a05d034aa4aea0c --- /dev/null +++ b/src/test/java/nl/tudelft/queue/controller/AssignmentControllerTest.java @@ -0,0 +1,151 @@ +/* + * Queue - A Queueing system that can be used to handle labs in higher education + * Copyright (C) 2016-2021 Delft University of Technology + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +package nl.tudelft.queue.controller; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.time.LocalDateTime; +import java.util.List; + +import javax.transaction.Transactional; + +import nl.tudelft.labracore.api.AssignmentControllerApi; +import nl.tudelft.labracore.api.dto.*; +import nl.tudelft.queue.cache.AssignmentCacheManager; +import nl.tudelft.queue.cache.EditionCacheManager; +import nl.tudelft.queue.cache.ModuleCacheManager; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.security.test.context.support.WithUserDetails; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; + +import reactor.core.publisher.Mono; +import test.test.TestQueueApplication; + +@Transactional +@AutoConfigureMockMvc +@SpringBootTest(classes = TestQueueApplication.class) +public class AssignmentControllerTest { + + @Autowired + private MockMvc mvc; + + @Autowired + private AssignmentCacheManager aCache; + + @Autowired + private ModuleCacheManager mCache; + + @Autowired + private EditionCacheManager eCache; + + @Autowired + private AssignmentControllerApi aApi; + + private final EditionDetailsDTO edition1 = new EditionDetailsDTO() + .id(33L).name("Edition").course(new CourseSummaryDTO().id(8732L).code("EDED").name("Coursy")); + + private final ModuleDetailsDTO module1 = new ModuleDetailsDTO() + .id(53L).name("Module 1").edition(new EditionSummaryDTO().id(33L)); + + private final AssignmentDetailsDTO assignment1 = new AssignmentDetailsDTO() + .id(98732L).name("Assignment 1").acceptLateSubmissions(false) + .module(new ModuleSummaryDTO().id(53L)); + + @BeforeEach + void setUp() { + when(aCache.getOrThrow(eq(assignment1.getId()))).thenReturn(assignment1); + when(mCache.getOrThrow(eq(module1.getId()))).thenReturn(module1); + when(eCache.getOrThrow(eq(edition1.getId()))).thenReturn(edition1); + + when(aApi.addAssignment(any())).thenReturn(Mono.just(666L)); + } + + @Test + @WithUserDetails("admin1") + void getAssignmentCreatePageLoads() throws Exception { + mvc.perform(get("/module/{moduleId}/assignment/create", module1.getId())) + .andExpect(status().isOk()) + .andExpect(view().name("assignment/create")) + .andExpect(model().attributeExists("dto", "_module", "edition")); + } + + @Test + @WithUserDetails("admin1") + void createAssignmentCreatesANewAssignment() throws Exception { + mvc.perform(post("/module/{moduleId}/assignment/create", module1.getId()).with(csrf()) + .queryParam("name", "Assignment 2") + .queryParam("description", "This is a cool assignment duh") + .queryParam("deadline", "09-09-2022 12:00") + .queryParam("moduleId", "53")) + .andExpect(redirectedUrl("/edition/33/modules")); + + verify(aApi).addAssignment(eq(new AssignmentCreateDTO() + .name("Assignment 2") + .description(new Description().text("This is a cool assignment duh")) + .deadline(LocalDateTime.of(2022, 9, 9, 12, 0)) + .module(new ModuleIdDTO().id(53L)))); + } + + @Test + @WithUserDetails("admin1") + void getAssignmentRemovePageWorks() throws Exception { + mvc.perform(get("/assignment/{assignmentId}/remove", assignment1.getId())) + .andExpect(status().isOk()) + .andExpect(view().name("assignment/remove")) + .andExpect(model().attributeExists("edition", "assignment")); + } + + @Test + @WithUserDetails("admin1") + void removeAssignmentRedirectsBackToModulesOverview() throws Exception { + mvc.perform(post("/assignment/{assignmentId}/remove", assignment1.getId()).with(csrf())) + .andExpect(redirectedUrl("/edition/" + edition1.getId() + "/modules")); + } + + @ParameterizedTest + @MethodSource(value = "protectedEndpoints") + void testWithoutUserDetailsIsForbidden(MockHttpServletRequestBuilder request) throws Exception { + mvc.perform(request.with(csrf())) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("http://localhost/login")); + } + + private static List<MockHttpServletRequestBuilder> protectedEndpoints() { + return List.of( + get("/module/{moduleId}/assignment/create", 1), + post("/module/{mId}/assignment/create", 1), + get("/assignment/{assignmentId}/remove", 1), + post("/assignment/{assignmentId}/remove", 1)); + } + +} diff --git a/src/test/java/nl/tudelft/queue/controller/ModuleControllerTest.java b/src/test/java/nl/tudelft/queue/controller/ModuleControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..7575e1317e77895844551a2d34795c90e11ac992 --- /dev/null +++ b/src/test/java/nl/tudelft/queue/controller/ModuleControllerTest.java @@ -0,0 +1,136 @@ +/* + * Queue - A Queueing system that can be used to handle labs in higher education + * Copyright (C) 2016-2021 Delft University of Technology + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +package nl.tudelft.queue.controller; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.util.List; + +import javax.transaction.Transactional; + +import nl.tudelft.labracore.api.ModuleControllerApi; +import nl.tudelft.labracore.api.dto.*; +import nl.tudelft.queue.cache.EditionCacheManager; +import nl.tudelft.queue.cache.ModuleCacheManager; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.security.test.context.support.WithUserDetails; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; + +import reactor.core.publisher.Mono; +import test.test.TestQueueApplication; + +@Transactional +@AutoConfigureMockMvc +@SpringBootTest(classes = TestQueueApplication.class) +public class ModuleControllerTest { + + @Autowired + private MockMvc mvc; + + @Autowired + private ModuleCacheManager mCache; + + @Autowired + private EditionCacheManager eCache; + + @Autowired + private ModuleControllerApi mApi; + + private final EditionDetailsDTO edition1 = new EditionDetailsDTO() + .id(33L).name("Edition").course(new CourseSummaryDTO().id(8732L).code("EDED").name("Coursy")); + + private final ModuleDetailsDTO module1 = new ModuleDetailsDTO() + .id(53L).name("Module 1").edition(new EditionSummaryDTO().id(33L)); + + @BeforeEach + void setUp() { + when(mCache.getOrThrow(eq(module1.getId()))).thenReturn(module1); + when(eCache.getOrThrow(eq(edition1.getId()))).thenReturn(edition1); + + when(mApi.addModule(any())).thenReturn(Mono.just(666L)); + } + + @Test + @WithUserDetails("admin1") + void getModuleCreatePageLoads() throws Exception { + mvc.perform(get("/edition/{id}/modules/create", edition1.getId())) + .andExpect(status().isOk()) + .andExpect(view().name("module/create")) + .andExpect(model().attributeExists("dto", "edition")); + } + + @Test + @WithUserDetails("admin1") + void createModuleCreatesANewModule() throws Exception { + mvc.perform(post("/edition/{id}/modules/create", edition1.getId()).with(csrf()) + .queryParam("editionId", "" + edition1.getId()) + .queryParam("name", "Cool Module")) + .andExpect(redirectedUrl("/edition/33/modules")); + + verify(mApi).addModule(eq(new ModuleCreateDTO() + .name("Cool Module") + .edition(new EditionIdDTO().id(edition1.getId())))); + } + + @Test + @WithUserDetails("admin1") + void getModuleRemovePageWorks() throws Exception { + mvc.perform(get("/module/{moduleId}/remove", module1.getId())) + .andExpect(status().isOk()) + .andExpect(view().name("module/remove")) + .andExpect(model().attributeExists("edition", "_module")); + } + + @Test + @WithUserDetails("admin1") + void removeModuleRedirectsBackToModulesOverview() throws Exception { + mvc.perform(post("/module/{moduleId}/remove", module1.getId()).with(csrf())) + .andExpect(redirectedUrl("/edition/" + edition1.getId() + "/modules")); + } + + @ParameterizedTest + @MethodSource(value = "protectedEndpoints") + void testWithoutUserDetailsIsForbidden(MockHttpServletRequestBuilder request) throws Exception { + mvc.perform(request.with(csrf())) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("http://localhost/login")); + } + + private static List<MockHttpServletRequestBuilder> protectedEndpoints() { + return List.of( + get("/edition/{editionId}/modules/create", 1), + post("/edition/{editionId}/modules/create", 1), + get("/module/{moduleId}/remove", 1), + post("/module/{moduleId}/remove", 1)); + } +} diff --git a/src/test/java/nl/tudelft/queue/dto/create/QueueAssignmentCreateDTOTest.java b/src/test/java/nl/tudelft/queue/dto/create/QueueAssignmentCreateDTOTest.java new file mode 100644 index 0000000000000000000000000000000000000000..55fbbcc6283c52aad6e60d55b2eb8c9cbb3d2b88 --- /dev/null +++ b/src/test/java/nl/tudelft/queue/dto/create/QueueAssignmentCreateDTOTest.java @@ -0,0 +1,46 @@ +/* + * Queue - A Queueing system that can be used to handle labs in higher education + * Copyright (C) 2016-2021 Delft University of Technology + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +package nl.tudelft.queue.dto.create; + +import java.time.LocalDateTime; +import java.util.List; + +import nl.tudelft.labracore.api.dto.AssignmentCreateDTO; + +import org.junit.jupiter.params.provider.Arguments; + +public class QueueAssignmentCreateDTOTest extends CreateTest<AssignmentCreateDTO, QueueAssignmentCreateDTO> { + @Override + protected QueueAssignmentCreateDTO filledDTO() { + return QueueAssignmentCreateDTO.builder() + .name("Oei") + .moduleId(999L) + .description("This is a simple yet complicated description") + .deadline(LocalDateTime.of(2020, 9, 9, 20, 9, 19)) + .build(); + } + + @Override + public List<Arguments> getters() { + return List.of( + of(QueueAssignmentCreateDTO::getName, AssignmentCreateDTO::getName), + of(QueueAssignmentCreateDTO::getModuleId, acd -> acd.getModule().getId()), + of(QueueAssignmentCreateDTO::getDeadline, AssignmentCreateDTO::getDeadline), + of(QueueAssignmentCreateDTO::getDescription, acd -> acd.getDescription().getText())); + } +} diff --git a/src/test/java/nl/tudelft/queue/dto/create/QueueModuleCreateDTOTest.java b/src/test/java/nl/tudelft/queue/dto/create/QueueModuleCreateDTOTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2f54aebb5bc6f3647b68845e257498b8306d2a7c --- /dev/null +++ b/src/test/java/nl/tudelft/queue/dto/create/QueueModuleCreateDTOTest.java @@ -0,0 +1,41 @@ +/* + * Queue - A Queueing system that can be used to handle labs in higher education + * Copyright (C) 2016-2021 Delft University of Technology + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +package nl.tudelft.queue.dto.create; + +import java.util.List; + +import nl.tudelft.labracore.api.dto.ModuleCreateDTO; + +import org.junit.jupiter.params.provider.Arguments; + +public class QueueModuleCreateDTOTest extends CreateTest<ModuleCreateDTO, QueueModuleCreateDTO> { + @Override + protected QueueModuleCreateDTO filledDTO() { + return QueueModuleCreateDTO.builder() + .name("This is a name for a module") + .editionId(78434L) + .build(); + } + + @Override + public List<Arguments> getters() { + return List.of( + of(QueueModuleCreateDTO::getName, ModuleCreateDTO::getName), + of(QueueModuleCreateDTO::getEditionId, mcd -> mcd.getEdition().getId())); + } +} diff --git a/src/test/java/test/BaseMockConfig.java b/src/test/java/test/BaseMockConfig.java index 83b11f749ebd385168579e036708083d645095e5..fd493e19d6e21c5cb5d2f5275f8ae033b5e8766d 100644 --- a/src/test/java/test/BaseMockConfig.java +++ b/src/test/java/test/BaseMockConfig.java @@ -67,6 +67,9 @@ public class BaseMockConfig { @MockBean private EditionCollectionControllerApi editionCollectionControllerApi; + @MockBean + private ModuleControllerApi moduleControllerApi; + @MockBean private SessionControllerApi sessionControllerApi;