|
62 | 62 | * @psalm-import-type FormsPartialForm from ResponseDefinitions |
63 | 63 | * @psalm-import-type FormsQuestion from ResponseDefinitions |
64 | 64 | * @psalm-import-type FormsQuestionType from ResponseDefinitions |
| 65 | + * @psalm-import-type FormsSubmission from ResponseDefinitions |
65 | 66 | * @psalm-import-type FormsSubmissions from ResponseDefinitions |
66 | 67 | * @psalm-import-type FormsUploadedFile from ResponseDefinitions |
67 | 68 | */ |
@@ -172,6 +173,7 @@ public function newForm(?int $fromId = null): DataResponse { |
172 | 173 | 'showToAllUsers' => false, |
173 | 174 | ]); |
174 | 175 | $form->setSubmitMultiple(false); |
| 176 | + $form->setAllowEditSubmissions(false); |
175 | 177 | $form->setShowExpiration(false); |
176 | 178 | $form->setExpires(0); |
177 | 179 | $form->setIsAnonymous(false); |
@@ -1158,7 +1160,11 @@ public function getSubmissions(int $formId, ?string $fileFormat = null): DataRes |
1158 | 1160 | } |
1159 | 1161 |
|
1160 | 1162 | // Load submissions and currently active questions |
1161 | | - $submissions = $this->submissionService->getSubmissions($formId); |
| 1163 | + if (in_array(Constants::PERMISSION_RESULTS, $this->formsService->getPermissions($form))) { |
| 1164 | + $submissions = $this->submissionService->getSubmissions($formId); |
| 1165 | + } else { |
| 1166 | + $submissions = $this->submissionService->getSubmissions($formId, $this->currentUser->getUID()); |
| 1167 | + } |
1162 | 1168 | $questions = $this->formsService->getQuestions($formId); |
1163 | 1169 |
|
1164 | 1170 | // Append Display Names |
@@ -1195,6 +1201,54 @@ public function getSubmissions(int $formId, ?string $fileFormat = null): DataRes |
1195 | 1201 | return new DataResponse($response); |
1196 | 1202 | } |
1197 | 1203 |
|
| 1204 | + /** |
| 1205 | + * Get a specific submission |
| 1206 | + * |
| 1207 | + * @param int $formId of the form |
| 1208 | + * @param int $submissionId of the submission |
| 1209 | + * @return DataResponse<Http::STATUS_OK, FormsSubmission, array{}> |
| 1210 | + * @throws OCSBadRequestException Submission doesn't belong to given form |
| 1211 | + * @throws OCSNotFoundException Could not find form |
| 1212 | + * @throws OCSNotFoundException Submission doesn't exist |
| 1213 | + * @throws OCSForbiddenException The current user has no permission to get this submission |
| 1214 | + * |
| 1215 | + * 200: the submissions of the form |
| 1216 | + */ |
| 1217 | + #[CORS()] |
| 1218 | + #[NoAdminRequired()] |
| 1219 | + #[BruteForceProtection(action: 'form')] |
| 1220 | + #[ApiRoute(verb: 'GET', url: '/api/v3/forms/{formId}/submissions/{submissionId}')] |
| 1221 | + public function getSubmission(int $formId, int $submissionId): DataResponse|DataDownloadResponse { |
| 1222 | + $form = $this->getFormIfAllowed($formId, Constants::PERMISSION_RESULTS); |
| 1223 | + |
| 1224 | + $submission = $this->submissionService->getSubmission($submissionId); |
| 1225 | + if ($submission === null) { |
| 1226 | + throw new OCSNotFoundException('Submission doesn\'t exist'); |
| 1227 | + } |
| 1228 | + |
| 1229 | + if ($submission['formId'] !== $formId) { |
| 1230 | + throw new OCSBadRequestException('Submission doesn\'t belong to given form'); |
| 1231 | + } |
| 1232 | + |
| 1233 | + // Append Display Names |
| 1234 | + if (substr($submission['userId'], 0, 10) === 'anon-user-') { |
| 1235 | + // Anonymous User |
| 1236 | + // TRANSLATORS On Results when listing the single Responses to the form, this text is shown as heading of the Response. |
| 1237 | + $submission['userDisplayName'] = $this->l10n->t('Anonymous response'); |
| 1238 | + } else { |
| 1239 | + $userEntity = $this->userManager->get($submission['userId']); |
| 1240 | + |
| 1241 | + if ($userEntity instanceof IUser) { |
| 1242 | + $submission['userDisplayName'] = $userEntity->getDisplayName(); |
| 1243 | + } else { |
| 1244 | + // Fallback, should not occur regularly. |
| 1245 | + $submission['userDisplayName'] = $submission['userId']; |
| 1246 | + } |
| 1247 | + } |
| 1248 | + |
| 1249 | + return new DataResponse($submission); |
| 1250 | + } |
| 1251 | + |
1198 | 1252 | /** |
1199 | 1253 | * Delete all submissions of a specified form |
1200 | 1254 | * |
@@ -1309,6 +1363,84 @@ public function newSubmission(int $formId, array $answers, string $shareHash = ' |
1309 | 1363 | return new DataResponse(null, Http::STATUS_CREATED); |
1310 | 1364 | } |
1311 | 1365 |
|
| 1366 | + /** |
| 1367 | + * Update an existing submission |
| 1368 | + * |
| 1369 | + * @param int $formId the form id |
| 1370 | + * @param int $submissionId the submission id |
| 1371 | + * @param array<string, list<string>> $answers [question_id => arrayOfString] |
| 1372 | + * @return DataResponse<Http::STATUS_OK, int, array{}> |
| 1373 | + * @throws OCSBadRequestException Can only update submission if allowEditSubmissions is set and the answers are valid |
| 1374 | + * @throws OCSForbiddenException Can only update your own submission |
| 1375 | + * |
| 1376 | + * 200: the id of the updated submission |
| 1377 | + */ |
| 1378 | + #[CORS()] |
| 1379 | + #[NoAdminRequired()] |
| 1380 | + #[NoCSRFRequired()] |
| 1381 | + #[PublicPage()] |
| 1382 | + #[ApiRoute(verb: 'PUT', url: '/api/v3/forms/{formId}/submissions/{submissionId}')] |
| 1383 | + public function updateSubmission(int $formId, int $submissionId, array $answers): DataResponse { |
| 1384 | + $this->logger->debug('Updating submission: formId: {formId}, answers: {answers}', [ |
| 1385 | + 'formId' => $formId, |
| 1386 | + 'answers' => $answers, |
| 1387 | + ]); |
| 1388 | + |
| 1389 | + // submissions can't be updated on public shares, so passing empty shareHash |
| 1390 | + $form = $this->loadFormForSubmission($formId, ''); |
| 1391 | + |
| 1392 | + if (!$form->getAllowEditSubmissions()) { |
| 1393 | + throw new OCSBadRequestException('Can only update if allowEditSubmissions is set'); |
| 1394 | + } |
| 1395 | + |
| 1396 | + $questions = $this->formsService->getQuestions($formId); |
| 1397 | + try { |
| 1398 | + // Is the submission valid |
| 1399 | + $this->submissionService->validateSubmission($questions, $answers, $form->getOwnerId()); |
| 1400 | + } catch (\InvalidArgumentException $e) { |
| 1401 | + throw new OCSBadRequestException($e->getMessage()); |
| 1402 | + } |
| 1403 | + |
| 1404 | + // get existing submission of this user |
| 1405 | + try { |
| 1406 | + $submission = $this->submissionMapper->findById($submissionId); |
| 1407 | + } catch (DoesNotExistException $e) { |
| 1408 | + throw new OCSBadRequestException('Submission doesn\'t exist'); |
| 1409 | + } |
| 1410 | + |
| 1411 | + if ($formId !== $submission->getFormId()) { |
| 1412 | + throw new OCSBadRequestException('Submission doesn\'t belong to given form'); |
| 1413 | + } |
| 1414 | + |
| 1415 | + if ($this->currentUser->getUID() !== $submission->getUserId()) { |
| 1416 | + throw new OCSForbiddenException('Can only update your own submissions'); |
| 1417 | + } |
| 1418 | + |
| 1419 | + $submission->setTimestamp(time()); |
| 1420 | + $this->submissionMapper->update($submission); |
| 1421 | + |
| 1422 | + // Delete current answers |
| 1423 | + $this->answerMapper->deleteBySubmission($submissionId); |
| 1424 | + |
| 1425 | + // Process Answers |
| 1426 | + foreach ($answers as $questionId => $answerArray) { |
| 1427 | + // Search corresponding Question, skip processing if not found |
| 1428 | + $questionIndex = array_search($questionId, array_column($questions, 'id')); |
| 1429 | + if ($questionIndex === false) { |
| 1430 | + continue; |
| 1431 | + } |
| 1432 | + |
| 1433 | + $question = $questions[$questionIndex]; |
| 1434 | + |
| 1435 | + $this->storeAnswersForQuestion($form, $submission->getId(), $question, $answerArray); |
| 1436 | + } |
| 1437 | + |
| 1438 | + //Create Activity |
| 1439 | + $this->formsService->notifyNewSubmission($form, $submission); |
| 1440 | + |
| 1441 | + return new DataResponse($submissionId); |
| 1442 | + } |
| 1443 | + |
1312 | 1444 | /** |
1313 | 1445 | * Delete a specific submission |
1314 | 1446 | * |
@@ -1343,6 +1475,13 @@ public function deleteSubmission(int $formId, int $submissionId): DataResponse { |
1343 | 1475 | throw new OCSBadRequestException('Submission doesn\'t belong to given form'); |
1344 | 1476 | } |
1345 | 1477 |
|
| 1478 | + if ( |
| 1479 | + !in_array(Constants::PERMISSION_RESULTS_DELETE, $this->formsService->getPermissions($form)) |
| 1480 | + && $this->currentUser->getUID() !== $submission->getUserId() |
| 1481 | + ) { |
| 1482 | + throw new OCSForbiddenException('Can only delete your own submissions'); |
| 1483 | + } |
| 1484 | + |
1346 | 1485 | // Delete submission (incl. Answers) |
1347 | 1486 | $this->submissionMapper->deleteById($submissionId); |
1348 | 1487 | $this->formMapper->update($form); |
|
0 commit comments