diff --git a/lib/BackgroundJob/DeleteQuestionFoldersJob.php b/lib/BackgroundJob/DeleteQuestionFoldersJob.php new file mode 100644 index 000000000..28c5123aa --- /dev/null +++ b/lib/BackgroundJob/DeleteQuestionFoldersJob.php @@ -0,0 +1,86 @@ +logger->debug('Deleting question folders for question {questionId} in form {formId}', [ + 'questionId' => $questionId, + 'formId' => $formId, + ]); + + $formFolders = $this->filePathHelper->getAllFormFoldersById($formId, $ownerId); + if (empty($formFolders)) { + $this->logger->notice('Form folder not found, nothing to delete', [ + 'formId' => $formId, + ]); + return; + } + + $questionFolderPrefix = $questionId . ' - '; + $deletedCount = 0; + + // Iterate through all form folders (handles form renames) + foreach ($formFolders as $formFolder) { + // Iterate through submission folders and delete matching question folders + foreach ($formFolder->getDirectoryListing() as $submissionFolder) { + if (!$submissionFolder instanceof Folder) { + continue; + } + foreach ($submissionFolder->getDirectoryListing() as $questionFolder) { + if (str_starts_with($questionFolder->getName(), $questionFolderPrefix)) { + $questionFolder->delete(); + $deletedCount++; + } + } + } + } + + $this->logger->info('Deleted {count} question folders for question {questionId}', [ + 'count' => $deletedCount, + 'questionId' => $questionId, + 'formId' => $formId, + ]); + } catch (NotFoundException) { + // Folder doesn't exist, do nothing + $this->logger->notice('Question folder not found, nothing to delete', [ + 'questionId' => $questionId, + 'formId' => $formId, + ]); + } catch (\Throwable $e) { + $this->logger->warning('Failed to delete question folders: {error}', [ + 'error' => $e->getMessage(), + 'questionId' => $questionId, + 'formId' => $formId, + ]); + } + } +} diff --git a/lib/Controller/ApiController.php b/lib/Controller/ApiController.php index cde1c4f8f..3ad8ae46b 100644 --- a/lib/Controller/ApiController.php +++ b/lib/Controller/ApiController.php @@ -7,6 +7,7 @@ namespace OCA\Forms\Controller; +use OCA\Forms\BackgroundJob\DeleteQuestionFoldersJob; use OCA\Forms\BackgroundJob\SyncSubmissionsWithLinkedFileJob; use OCA\Forms\Constants; use OCA\Forms\Db\Answer; @@ -45,6 +46,7 @@ use OCP\AppFramework\OCS\OCSNotFoundException; use OCP\AppFramework\OCSController; use OCP\BackgroundJob\IJobList; +use OCP\Files\Folder; use OCP\Files\IMimeTypeDetector; use OCP\Files\IRootFolder; use OCP\IL10N; @@ -743,6 +745,14 @@ public function deleteQuestion(int $formId, int $questionId): DataResponse { $question->setOrder(0); $this->questionMapper->update($question); + if ($question->getType() === Constants::ANSWER_TYPE_FILE) { + $this->jobList->add(DeleteQuestionFoldersJob::class, [ + 'formId' => $form->getId(), + 'questionId' => $question->getId(), + 'ownerId' => $form->getOwnerId(), + ]); + } + // Update all question-order > deleted order. $formQuestions = $this->questionMapper->findByForm($formId); foreach ($formQuestions as $question) { @@ -1589,7 +1599,7 @@ public function deleteSubmission(int $formId, int $submissionId): DataResponse { } // Delete submission (incl. Answers) - $this->submissionMapper->deleteById($submissionId); + $this->submissionMapper->deleteById($form, $submissionId); $this->formMapper->update($form); return new DataResponse($submissionId); @@ -1743,8 +1753,7 @@ public function uploadFiles(int $formId, int $questionId, string $shareHash = '' } else { $folder = $userFolder->newFolder($path); } - /** @var \OCP\Files\Folder $folder */ - + /** @var Folder $folder */ $fileName = $folder->getNonExistingName($uploadedFile['name']); $file = $folder->newFile($fileName, file_get_contents($uploadedFile['tmp_name'])); @@ -1819,8 +1828,7 @@ private function storeAnswersForQuestion(Form $form, $submissionId, array $quest } else { $folder = $userFolder->newFolder($path); } - /** @var \OCP\Files\Folder $folder */ - + /** @var Folder $folder */ $file = $userFolder->getById($uploadedFile->getFileId())[0]; $name = $folder->getNonExistingName($file->getName()); $file->move($folder->getPath() . '/' . $name); diff --git a/lib/Controller/ShareApiController.php b/lib/Controller/ShareApiController.php index 7323ab951..84954dfb8 100644 --- a/lib/Controller/ShareApiController.php +++ b/lib/Controller/ShareApiController.php @@ -14,6 +14,7 @@ use OCA\Forms\Db\FormMapper; use OCA\Forms\Db\Share; use OCA\Forms\Db\ShareMapper; +use OCA\Forms\Helper\FilePathHelper; use OCA\Forms\ResponseDefinitions; use OCA\Forms\Service\CirclesService; use OCA\Forms\Service\ConfigService; @@ -62,6 +63,7 @@ public function __construct( private ISecureRandom $secureRandom, private CirclesService $circlesService, private IRootFolder $rootFolder, + private FilePathHelper $filePathHelper, private IManager $shareManager, ) { parent::__construct($appName, $request); @@ -273,7 +275,7 @@ public function updateShare(int $formId, int $shareId, array $keyValuePairs): Da if (in_array($formShare->getShareType(), [IShare::TYPE_USER, IShare::TYPE_GROUP, IShare::TYPE_USERGROUP, IShare::TYPE_CIRCLE], true)) { if (in_array(Constants::PERMISSION_RESULTS, $keyValuePairs['permissions'], true)) { $userFolder = $this->rootFolder->getUserFolder($form->getOwnerId()); - $uploadedFilesFolderPath = $this->formsService->getFormUploadedFilesFolderPath($form); + $uploadedFilesFolderPath = $this->filePathHelper->getFormUploadedFilesFolderPath($form); try { /** @var \OCP\Files\Folder $folder */ $folder = $userFolder->get($uploadedFilesFolderPath); @@ -358,7 +360,7 @@ private function removeUploadedFilesShare(Form $form, Share $formShare): void { } $userFolder = $this->rootFolder->getUserFolder($form->getOwnerId()); - $uploadedFilesFolderPath = $this->formsService->getFormUploadedFilesFolderPath($form); + $uploadedFilesFolderPath = $this->filePathHelper->getFormUploadedFilesFolderPath($form); try { $folder = $userFolder->get($uploadedFilesFolderPath); } catch (NotFoundException $e) { diff --git a/lib/Db/FormMapper.php b/lib/Db/FormMapper.php index f7df9ddad..251dc429d 100644 --- a/lib/Db/FormMapper.php +++ b/lib/Db/FormMapper.php @@ -8,13 +8,16 @@ namespace OCA\Forms\Db; use OCA\Forms\Constants; +use OCA\Forms\Helper\FilePathHelper; use OCA\Forms\Service\ConfigService; use OCP\AppFramework\Db\Entity; use OCP\AppFramework\Db\QBMapper; use OCP\Comments\ICommentsManager; use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\Files\Folder; use OCP\IDBConnection; use OCP\Share\IShare; +use Psr\Log\LoggerInterface; /** * @extends QBMapper