Skip to content

Commit 9f4b5e6

Browse files
committed
fix: Delete files on submission/question/form deletion
Signed-off-by: Kostiantyn Miakshyn <molodchick@gmail.com>
1 parent 91f55e3 commit 9f4b5e6

4 files changed

Lines changed: 154 additions & 16 deletions

File tree

lib/Controller/ApiController.php

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,10 @@
4646
use OCP\AppFramework\OCS\OCSNotFoundException;
4747
use OCP\AppFramework\OCSController;
4848
use OCP\BackgroundJob\IJobList;
49+
use OCP\Files\Folder;
4950
use OCP\Files\IMimeTypeDetector;
5051
use OCP\Files\IRootFolder;
52+
use OCP\Files\NotFoundException;
5153
use OCP\IL10N;
5254
use OCP\IRequest;
5355
use OCP\IUser;
@@ -742,6 +744,10 @@ public function deleteQuestion(int $formId, int $questionId): DataResponse {
742744
$question->setOrder(0);
743745
$this->questionMapper->update($question);
744746

747+
if ($question->getType() === Constants::ANSWER_TYPE_FILE) {
748+
$this->deleteQuestionFolder($form, $question);
749+
}
750+
745751
// Update all question-order > deleted order.
746752
$formQuestions = $this->questionMapper->findByForm($formId);
747753
foreach ($formQuestions as $question) {
@@ -1589,7 +1595,7 @@ public function deleteSubmission(int $formId, int $submissionId): DataResponse {
15891595
}
15901596

15911597
// Delete submission (incl. Answers)
1592-
$this->submissionMapper->deleteById($submissionId);
1598+
$this->submissionMapper->deleteById($form, $submissionId);
15931599
$this->formMapper->update($form);
15941600

15951601
return new DataResponse($submissionId);
@@ -1743,8 +1749,7 @@ public function uploadFiles(int $formId, int $questionId, string $shareHash = ''
17431749
} else {
17441750
$folder = $userFolder->newFolder($path);
17451751
}
1746-
/** @var \OCP\Files\Folder $folder */
1747-
1752+
/** @var Folder $folder */
17481753
$fileName = $folder->getNonExistingName($uploadedFile['name']);
17491754
$file = $folder->newFile($fileName, file_get_contents($uploadedFile['tmp_name']));
17501755

@@ -1819,8 +1824,7 @@ private function storeAnswersForQuestion(Form $form, $submissionId, array $quest
18191824
} else {
18201825
$folder = $userFolder->newFolder($path);
18211826
}
1822-
/** @var \OCP\Files\Folder $folder */
1823-
1827+
/** @var Folder $folder */
18241828
$file = $userFolder->getById($uploadedFile->getFileId())[0];
18251829
$name = $folder->getNonExistingName($file->getName());
18261830
$file->move($folder->getPath() . '/' . $name);
@@ -1992,4 +1996,43 @@ private function handleOwnerTransfer(Form $form, int $formId, string $currentUse
19921996
$this->formMapper->update($form);
19931997
return new DataResponse($form->getOwnerId());
19941998
}
1999+
2000+
2001+
2002+
/**
2003+
* Delete question folders from all submissions
2004+
* @param Form $form The form
2005+
* @param Question $question The question
2006+
*/
2007+
private function deleteQuestionFolder(Form $form, Question $question): void {
2008+
try {
2009+
$userFolder = $this->rootFolder->getUserFolder($form->getOwnerId());
2010+
$formFolderPath = $this->formsService->getFormUploadedFilesFolderPath($form);
2011+
2012+
$formFolder = $userFolder->get($formFolderPath);
2013+
if (!$formFolder instanceof Folder) {
2014+
return;
2015+
}
2016+
$questionFolderPrefix = $question->getId() . ' - ';
2017+
2018+
// Iterate through submission folders and delete matching question folders
2019+
foreach ($formFolder->getDirectoryListing() as $submissionFolder) {
2020+
if (!$submissionFolder instanceof Folder) {
2021+
continue;
2022+
}
2023+
foreach ($submissionFolder->getDirectoryListing() as $node) {
2024+
if (str_starts_with($node->getName(), $questionFolderPrefix)) {
2025+
$node->delete();
2026+
}
2027+
}
2028+
}
2029+
} catch (NotFoundException) {
2030+
// Do nothing
2031+
} catch (\Throwable $e) {
2032+
$this->logger->warning('Failed to delete question folders: {error}', [
2033+
'error' => $e->getMessage(),
2034+
'questionId' => $question->getId(),
2035+
]);
2036+
}
2037+
}
19952038
}

lib/Db/AnswerMapper.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,26 @@ public function deleteBySubmission(int $submissionId): void {
5454

5555
$qb->executeStatement();
5656
}
57+
58+
/**
59+
* Collect all fileIds for answers of a specific submission
60+
* @param int $submissionId
61+
* @return int[] Array of fileIds
62+
*/
63+
public function findFileIdsBySubmission(int $submissionId): array {
64+
$qb = $this->db->getQueryBuilder();
65+
66+
$qb->select('file_id')
67+
->from($this->getTableName())
68+
->where(
69+
$qb->expr()->eq('submission_id', $qb->createNamedParameter($submissionId, IQueryBuilder::PARAM_INT))
70+
)
71+
->andWhere($qb->expr()->isNotNull('file_id'));
72+
73+
$result = $qb->executeQuery();
74+
$rows = $result->fetchFirstColumn();
75+
$result->closeCursor();
76+
77+
return array_map('intval', $rows);
78+
}
5779
}

lib/Db/FormMapper.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,12 @@
1313
use OCP\AppFramework\Db\QBMapper;
1414
use OCP\Comments\ICommentsManager;
1515
use OCP\DB\QueryBuilder\IQueryBuilder;
16+
use OCP\Files\Folder;
17+
use OCP\Files\IRootFolder;
18+
use OCP\Files\NotFoundException;
1619
use OCP\IDBConnection;
1720
use OCP\Share\IShare;
21+
use Psr\Log\LoggerInterface;
1822

1923
/**
2024
* @extends QBMapper<Form>
@@ -32,6 +36,8 @@ public function __construct(
3236
private SubmissionMapper $submissionMapper,
3337
private ConfigService $configService,
3438
private ICommentsManager $commentsManager,
39+
private IRootFolder $rootFolder,
40+
private LoggerInterface $logger,
3541
) {
3642
parent::__construct($db, 'forms_v2_forms', Form::class);
3743
}
@@ -225,6 +231,37 @@ public function deleteForm(Form $form): void {
225231
$this->shareMapper->deleteByForm($formId);
226232
$this->questionMapper->deleteByForm($formId);
227233
$this->commentsManager->deleteCommentsAtObject('forms', (string)$formId);
234+
$this->deleteFormFolder($form);
228235
$this->delete($form);
229236
}
237+
238+
/**
239+
* Delete the form folder from the file system
240+
* @param Form $form The form instance
241+
*/
242+
private function deleteFormFolder(Form $form): void {
243+
try {
244+
$userFolder = $this->rootFolder->getUserFolder($form->getOwnerId());
245+
$formsFolder = $userFolder->get(Constants::FILES_FOLDER);
246+
247+
if (!$formsFolder instanceof Folder) {
248+
return;
249+
}
250+
$formFolderPrefix = $form->getId() . ' - ';
251+
252+
// Iterate through form folders and delete matching folders
253+
foreach ($formsFolder->getDirectoryListing() as $node) {
254+
if (str_starts_with($node->getName(), $formFolderPrefix)) {
255+
$node->delete();
256+
}
257+
}
258+
} catch (NotFoundException) {
259+
// do nothing
260+
} catch (\Throwable $e) {
261+
$this->logger->warning('Failed to delete form folder: {error}', [
262+
'error' => $e->getMessage(),
263+
'formId' => $form->getId(),
264+
]);
265+
}
266+
}
230267
}

lib/Db/SubmissionMapper.php

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,15 @@
77

88
namespace OCA\Forms\Db;
99

10+
use OCA\Forms\Service\FormsService;
1011
use OCP\AppFramework\Db\DoesNotExistException;
1112
use OCP\AppFramework\Db\QBMapper;
1213
use OCP\DB\QueryBuilder\IQueryBuilder;
14+
use OCP\Files\Folder;
15+
use OCP\Files\IRootFolder;
16+
use OCP\Files\NotFoundException;
1317
use OCP\IDBConnection;
18+
use Psr\Log\LoggerInterface;
1419

1520
/**
1621
* @extends QBMapper<Submission>
@@ -20,10 +25,16 @@ class SubmissionMapper extends QBMapper {
2025
* SubmissionMapper constructor.
2126
* @param IDBConnection $db
2227
* @param AnswerMapper $answerMapper
28+
* @param IRootFolder $rootFolder
29+
* @param LoggerInterface $logger
30+
* @param FormsService $formsService
2331
*/
2432
public function __construct(
2533
IDBConnection $db,
2634
private AnswerMapper $answerMapper,
35+
private IRootFolder $rootFolder,
36+
private LoggerInterface $logger,
37+
private FormsService $formsService,
2738
) {
2839
parent::__construct($db, 'forms_v2_submissions', Submission::class);
2940
}
@@ -179,22 +190,17 @@ protected function countSubmissionsWithFilters(int $formId, ?string $userId = nu
179190

180191
/**
181192
* Delete the Submission, including answers.
193+
* @param Form $form Form the submission belongs to.
182194
* @param int $id of the submission to delete
183195
*/
184-
public function deleteById(int $id): void {
185-
$qb = $this->db->getQueryBuilder();
186-
187-
// First delete corresponding answers.
196+
public function deleteById(Form $form, int $id): void {
188197
$submissionEntity = $this->findById($id);
189-
$this->answerMapper->deleteBySubmission($submissionEntity->getId());
190198

191-
//Delete Submission
192-
$qb->delete($this->getTableName())
193-
->where(
194-
$qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT))
195-
);
199+
$this->deleteSubmissionFolder($form, $submissionEntity->getId());
196200

197-
$qb->executeStatement();
201+
$this->answerMapper->deleteBySubmission($submissionEntity->getId());
202+
203+
$this->delete($submissionEntity);
198204
}
199205

200206
/**
@@ -218,4 +224,34 @@ public function deleteByForm(int $formId): void {
218224

219225
$qb->executeStatement();
220226
}
227+
228+
/**
229+
* Delete the submission folder from the file system
230+
* @param Form $form The form instance
231+
* @param int $submissionId The submission ID
232+
*/
233+
private function deleteSubmissionFolder(Form $form, int $submissionId): void {
234+
try {
235+
$userFolder = $this->rootFolder->getUserFolder($form->getOwnerId());
236+
$formFolderPath = $this->formsService->getFormUploadedFilesFolderPath($form);
237+
238+
$formFolder = $userFolder->get($formFolderPath);
239+
if (!$formFolder instanceof Folder) {
240+
return;
241+
}
242+
243+
$submissionFolder = $formFolder->get((string)$submissionId);
244+
if ($submissionFolder instanceof Folder) {
245+
$submissionFolder->delete();
246+
}
247+
} catch (NotFoundException) {
248+
// Folder doesn't exist, do nothing
249+
} catch (\Throwable $e) {
250+
$this->logger->warning('Failed to delete submission folder: {error}', [
251+
'error' => $e->getMessage(),
252+
'submissionId' => $submissionId,
253+
'formId' => $form->getId(),
254+
]);
255+
}
256+
}
221257
}

0 commit comments

Comments
 (0)