From 12a84920b3bbd304214a8692d3c5c8127ddacde4 Mon Sep 17 00:00:00 2001 From: Christian Beeznest Date: Wed, 7 May 2025 08:19:33 -0500 Subject: [PATCH] Assigment: Fix corrections, rating, comment replies and submission deadline check - refs BT#22568 --- .../assignments/AssignmentsForm.vue | 41 +++++---------- .../assignments/CorrectAndRateModal.vue | 52 ++++++++++++------- assets/vue/services/cstudentpublication.js | 20 ++++--- .../views/assignments/AssignmentDetail.vue | 32 +++++++++--- .../views/assignments/AssignmentMissing.vue | 2 +- .../views/assignments/AssignmentSubmit.vue | 44 ++++++++++++++-- .../CreateStudentPublicationCommentAction.php | 8 +++ .../StudentPublicationController.php | 12 +++-- .../CStudentPublicationPostStateProcessor.php | 33 +++++++++--- .../Entity/CStudentPublication.php | 17 +++--- 10 files changed, 178 insertions(+), 83 deletions(-) diff --git a/assets/vue/components/assignments/AssignmentsForm.vue b/assets/vue/components/assignments/AssignmentsForm.vue index 168df7e4713..dc01e8e5533 100644 --- a/assets/vue/components/assignments/AssignmentsForm.vue +++ b/assets/vue/components/assignments/AssignmentsForm.vue @@ -261,18 +261,11 @@ const v$ = useVuelidate(rules, assignment) const onSubmit = async () => { const result = await v$.value.$validate() - - if (!result) { - return - } + if (!result) return const publicationStudent = { title: assignment.title, description: assignment.description, - assignment: { - expiresOn: null, - endsOn: null, - }, parentResourceNode: route.params.node * 1, resourceLinkList: [ { @@ -282,32 +275,26 @@ const onSubmit = async () => { visibility: RESOURCE_LINK_PUBLISHED, }, ], + qualification: assignment.qualification, + addToCalendar: assignment.addToCalendar, + allowTextAssignment: assignment.allowTextAssignment.value, } - if (showAdvancedSettings.value) { - publicationStudent.qualification = assignment.qualification - - publicationStudent.addToCalendar = assignment.addToCalendar - - if (chkAddToGradebook.value) { - publicationStudent.gradebookCategoryId = assignment.gradebookId.id - publicationStudent.weight = assignment.weight - } - - if (chkExpiresOn.value) { - publicationStudent.assignment.expiresOn = assignment.expiresOn - } + if (chkAddToGradebook.value) { + publicationStudent.gradebookCategoryId = assignment.gradebookId.id + publicationStudent.weight = assignment.weight + } - if (chkEndsOn.value) { - publicationStudent.assignment.endsOn = assignment.endsOn - } + if (chkExpiresOn.value) { + publicationStudent.expiresOn = assignment.expiresOn.toISOString() + } - publicationStudent.allowTextAssignment = assignment.allowTextAssignment.value + if (chkEndsOn.value) { + publicationStudent.endsOn = assignment.endsOn.toISOString() } - if (props.defaultAssignment) { + if (props.defaultAssignment?.["@id"]) { publicationStudent["@id"] = props.defaultAssignment["@id"] - publicationStudent.assignment["@id"] = props.defaultAssignment.assignment["@id"] } emit("submit", publicationStudent) diff --git a/assets/vue/components/assignments/CorrectAndRateModal.vue b/assets/vue/components/assignments/CorrectAndRateModal.vue index c847d81f446..7ce50dfc50e 100644 --- a/assets/vue/components/assignments/CorrectAndRateModal.vue +++ b/assets/vue/components/assignments/CorrectAndRateModal.vue @@ -22,6 +22,26 @@ rows="5" /> +
+ + + + + +
+
props.modelValue, @@ -121,6 +150,7 @@ watch( comment.value = "" sendMail.value = false selectedFile.value = null + qualification.value = props.item.qualification ?? null comments.value = await cStudentPublicationService.loadComments(props.item.iid) } }, @@ -163,13 +193,9 @@ async function submit() { formData.append("uploadFile", selectedFile.value) } formData.append("comment", comment.value) + formData.append("qualification", qualification.value ?? "") - await cStudentPublicationService.uploadComment( - props.item.iid, - getResourceNodeId(props.item.resourceNode), - formData, - sendMail.value, - ) + await cStudentPublicationService.uploadComment(props.item.iid, parentResourceNodeId, formData, sendMail.value) notification.showSuccessNotification(t("Comment added successfully")) @@ -180,18 +206,4 @@ async function submit() { notification.showErrorNotification(error) } } - -function getResourceNodeId(resourceNode) { - if (!resourceNode) return 0 - - if (typeof resourceNode === "object" && "id" in resourceNode) { - return parseInt(resourceNode.id, 10) - } - - const idString = typeof resourceNode === "string" ? resourceNode : resourceNode["@id"] - if (!idString || typeof idString !== "string") return 0 - - const match = idString.match(/\/(\d+)$/) - return match ? parseInt(match[1], 10) : 0 -} diff --git a/assets/vue/services/cstudentpublication.js b/assets/vue/services/cstudentpublication.js index 858f3427ea9..c14b5c03a59 100644 --- a/assets/vue/services/cstudentpublication.js +++ b/assets/vue/services/cstudentpublication.js @@ -11,9 +11,13 @@ async function findStudentAssignments() { return response.json() } -async function getAssignmentMetadata(assignmentId) { - const { sid, cid, gid } = useCidReq() - const params = new URLSearchParams({ cid, ...(sid && { sid }), ...(gid && { gid }) }).toString() +async function getAssignmentMetadata(assignmentId, cid, sid = 0, gid = 0) { + const params = new URLSearchParams({ + cid, + ...(sid && { sid }), + ...(gid && { gid }), + }).toString() + const response = await axios.get(`${ENTRYPOINT}c_student_publications/${assignmentId}?${params}`) return response.data } @@ -106,15 +110,15 @@ async function sendEmailToUnsubmitted(assignmentId, queryParams = {}) { return response.data } -async function deleteAllCorrections(assignmentId) { - const { cid, sid } = useCidReq() +async function deleteAllCorrections(assignmentId, cid, sid = 0) { + const params = { cid, ...(sid && { sid }) } + await axios.delete(`/assignments/${assignmentId}/corrections/delete`, { - params: { cid, ...(sid && { sid }) }, + params, }) } -async function exportAssignmentPdf(assignmentId) { - const { cid, sid, gid } = useCidReq() +async function exportAssignmentPdf(assignmentId, cid, sid = 0, gid = 0) { const params = { cid, ...(sid && { sid }), ...(gid && { gid }) } const response = await axios.get(`/assignments/${assignmentId}/export/pdf`, { diff --git a/assets/vue/views/assignments/AssignmentDetail.vue b/assets/vue/views/assignments/AssignmentDetail.vue index d8fc91e540d..46a31f1c368 100644 --- a/assets/vue/views/assignments/AssignmentDetail.vue +++ b/assets/vue/views/assignments/AssignmentDetail.vue @@ -11,6 +11,7 @@ diff --git a/src/CoreBundle/Controller/Api/CreateStudentPublicationCommentAction.php b/src/CoreBundle/Controller/Api/CreateStudentPublicationCommentAction.php index 7b2d161623d..ba1b097fb27 100644 --- a/src/CoreBundle/Controller/Api/CreateStudentPublicationCommentAction.php +++ b/src/CoreBundle/Controller/Api/CreateStudentPublicationCommentAction.php @@ -71,6 +71,14 @@ public function __invoke( $commentEntity->setFile($filename); } + $qualification = (float) $request->get('qualification', null); + + if (null !== $qualification) { + $submission->setQualification($qualification); + $submission->setQualificatorId($user->getId()); + $submission->setDateOfQualification(new \DateTime()); + } + $em->persist($commentEntity); $em->flush(); diff --git a/src/CoreBundle/Controller/StudentPublicationController.php b/src/CoreBundle/Controller/StudentPublicationController.php index 324f343332e..26c81247402 100644 --- a/src/CoreBundle/Controller/StudentPublicationController.php +++ b/src/CoreBundle/Controller/StudentPublicationController.php @@ -6,11 +6,13 @@ namespace Chamilo\CoreBundle\Controller; +use Chamilo\CoreBundle\Entity\ResourceNode; use Chamilo\CoreBundle\Entity\Session; use Chamilo\CoreBundle\Entity\User; use Chamilo\CoreBundle\Repository\ResourceNodeRepository; use Chamilo\CoreBundle\ServiceHelper\CidReqHelper; use Chamilo\CoreBundle\ServiceHelper\MessageHelper; +use Chamilo\CourseBundle\Entity\CStudentPublication; use Chamilo\CourseBundle\Entity\CStudentPublicationCorrection; use Chamilo\CourseBundle\Repository\CStudentPublicationCorrectionRepository; use Chamilo\CourseBundle\Repository\CStudentPublicationRepository; @@ -377,13 +379,17 @@ public function deleteAllCorrections( $count = 0; + /* @var CStudentPublication $submission */ foreach ($submissions as $submission) { $correctionNode = $submission->getCorrection(); if ($correctionNode !== null) { - $em->remove($correctionNode); - $submission->setExtensions(null); - $count++; + $correctionNode = $em->getRepository(ResourceNode::class)->find($correctionNode->getId()); + if ($correctionNode) { + $em->remove($correctionNode); + $submission->setExtensions(null); + $count++; + } } } diff --git a/src/CoreBundle/State/CStudentPublicationPostStateProcessor.php b/src/CoreBundle/State/CStudentPublicationPostStateProcessor.php index dc6ac415a79..d26f6089b43 100644 --- a/src/CoreBundle/State/CStudentPublicationPostStateProcessor.php +++ b/src/CoreBundle/State/CStudentPublicationPostStateProcessor.php @@ -43,10 +43,11 @@ public function process( array $uriVariables = [], array $context = [] ): CStudentPublication { - $result = $this->persistProcessor->process($data, $operation, $uriVariables, $context); - /** @var CStudentPublication $publication */ $publication = $data; + + $result = $this->persistProcessor->process($publication, $operation, $uriVariables, $context); + $assignment = $publication->getAssignment(); $courseLink = $publication->getFirstResourceLink(); $course = $courseLink->getCourse(); @@ -56,15 +57,31 @@ public function process( /** @var User $currentUser */ $currentUser = $this->security->getUser(); - if ($publication->getQualification() > 0) { + $isUpdate = $publication->getIid() !== null; + + if (!$assignment) { + $assignment = new CStudentPublicationAssignment(); + $assignment->setPublication($publication); + $publication->setAssignment($assignment); + $this->entityManager->persist($assignment); + } + + $payload = $context['request']->toArray(); + if (isset($payload['expiresOn'])) { + $assignment->setExpiresOn(new \DateTime($payload['expiresOn'])); + } + if (isset($payload['endsOn'])) { + $assignment->setEndsOn(new \DateTime($payload['endsOn'])); + } + + if (!$isUpdate || $publication->getQualification() > 0) { $assignment->setEnableQualification(true); } if ($publication->addToCalendar) { $event = $this->saveCalendarEvent($publication, $assignment, $courseLink, $course, $session, $group); - $assignment->setEventCalendarId($event->getIid()); - } else { + } elseif (!$isUpdate) { $assignment->setEventCalendarId(0); } @@ -78,9 +95,9 @@ public function process( $this->saveGradebookConfig($publication, $course, $session); - // Save extrafields - - $this->sendEmailAlertStudentsOnNewHomework($publication, $course, $session); + if (!$isUpdate) { + $this->sendEmailAlertStudentsOnNewHomework($publication, $course, $session); + } return $result; } diff --git a/src/CourseBundle/Entity/CStudentPublication.php b/src/CourseBundle/Entity/CStudentPublication.php index bd4118ff6d3..33129d869bb 100644 --- a/src/CourseBundle/Entity/CStudentPublication.php +++ b/src/CourseBundle/Entity/CStudentPublication.php @@ -38,7 +38,10 @@ #[ORM\Entity(repositoryClass: CStudentPublicationRepository::class)] #[ApiResource( operations: [ - new Put(security: "is_granted('EDIT', object.resourceNode)"), + new Put( + security: "is_granted('EDIT', object.resourceNode)", + processor: CStudentPublicationPostStateProcessor::class + ), new Get( normalizationContext: [ 'groups' => ['student_publication:read', 'student_publication:item:get'], @@ -458,16 +461,18 @@ public function getCorrection(): ?ResourceNode return null; } - $expectedTitle = $this->getExtensions(); $children = $this->getResourceNode()->getChildren(); foreach ($children as $child) { - $name = $child->getResourceType()->getTitle(); + $resourceType = $child->getResourceType(); + if (!$resourceType) { + continue; + } + + $name = $resourceType->getTitle(); if ('student_publications_corrections' === $name) { - if ($child->getTitle() === $expectedTitle) { - return $child; - } + return $child; } }