<?php namespace SGalinski\SgJobs\Controller; /*************************************************************** * Copyright notice * * (c) sgalinski Internet Services (https://www.sgalinski.de) * * All rights reserved * * This script is part of the TYPO3 project. The TYPO3 project is * free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * The GNU General Public License can be found at * http://www.gnu.org/copyleft/gpl.html. * * This script 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 General Public License for more details. * * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ use Psr\Http\Message\ResponseInterface; use SGalinski\ProjectBase\Domain\Repository\CountryRepository; use SGalinski\SgJobs\Domain\Model\Company; use SGalinski\SgJobs\Domain\Model\Job; use SGalinski\SgJobs\Domain\Model\JobApplication; use SGalinski\SgJobs\Domain\Repository\CompanyRepository; use SGalinski\SgJobs\Domain\Repository\DepartmentRepository; use SGalinski\SgJobs\Domain\Repository\ExperienceLevelRepository; use SGalinski\SgJobs\Domain\Repository\JobApplicationRepository; use SGalinski\SgJobs\Domain\Repository\JobRepository; use SGalinski\SgJobs\Service\FileAndFolderService; use SGalinski\SgMail\Service\MailTemplateService; use SGalinski\SgSeo\Service\HeadTagService; use TYPO3\CMS\Core\Context\Context; use TYPO3\CMS\Core\Context\Exception\AspectNotFoundException; use TYPO3\CMS\Core\Core\Environment; use TYPO3\CMS\Core\Error\Http\PageNotFoundException; use TYPO3\CMS\Core\Exception\SiteNotFoundException; use TYPO3\CMS\Core\Http\ImmediateResponseException; use TYPO3\CMS\Core\Resource\Exception\ExistingTargetFileNameException; use TYPO3\CMS\Core\Resource\Exception\ExistingTargetFolderException; use TYPO3\CMS\Core\Resource\Exception\InsufficientFolderAccessPermissionsException; use TYPO3\CMS\Core\Resource\Exception\InsufficientFolderReadPermissionsException; use TYPO3\CMS\Core\Resource\Exception\InsufficientFolderWritePermissionsException; use TYPO3\CMS\Core\Resource\ResourceFactory; use TYPO3\CMS\Core\Site\SiteFinder; use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\PathUtility; use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface; use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; use TYPO3\CMS\Extbase\Mvc\Exception\NoSuchArgumentException; use TYPO3\CMS\Extbase\Mvc\Exception\StopActionException; use TYPO3\CMS\Extbase\Property\Exception\TypeConverterException; use TYPO3\CMS\Extbase\Utility\LocalizationUtility; use TYPO3\CMS\Extbase\Validation\Validator\ConjunctionValidator; use TYPO3\CMS\Extbase\Validation\Validator\GenericObjectValidator; use TYPO3\CMS\Frontend\Controller\ErrorController; use TYPO3\CMS\Frontend\Page\PageAccessFailureReasons; /** * The joblist plugin controller */ class JoblistController extends ActionController { // the array key for the error message in the post array public const ERROR_KEY_IN_POST = 'error'; private const UPLOADED_FILES = [ 'coverLetter', 'cv', 'certificate', ]; /** * @var CompanyRepository */ protected $companyRepository; /** * @var JobRepository */ protected $jobRepository; /** * @var JobApplicationRepository */ protected $jobApplicationRepository; /** * @var DepartmentRepository */ protected $departmentRepository; /** * @var ExperienceLevelRepository */ protected $experienceLevelRepository; /** * @var FileAndFolderService */ protected $fileAndFolderService; /** * Inject the CompanyRepository * * @param CompanyRepository $companyRepository */ public function injectCompanyRepository(CompanyRepository $companyRepository): void { $this->companyRepository = $companyRepository; } /** * Inject the CompanyRepository * * @param FileAndFolderService $fileAndFolderService */ public function injectFileAndFolderService(FileAndFolderService $fileAndFolderService): void { $this->fileAndFolderService = $fileAndFolderService; } /** * Inject the DepartmentRepository * * @param DepartmentRepository $departmentRepository */ public function injectDepartmentRepository(DepartmentRepository $departmentRepository): void { $this->departmentRepository = $departmentRepository; } /** * Inject the ExperienceLevelRepository * * @param ExperienceLevelRepository $experienceLevelRepository */ public function injectExperienceLevelRepository(ExperienceLevelRepository $experienceLevelRepository): void { $this->experienceLevelRepository = $experienceLevelRepository; } /** * Inject the JobApplicationRepository * * @param JobApplicationRepository $jobApplicationRepository */ public function injectJobApplicationRepository(JobApplicationRepository $jobApplicationRepository): void { $this->jobApplicationRepository = $jobApplicationRepository; } /** * Inject the JobRepository * * @param JobRepository $jobRepository */ public function injectJobRepository(JobRepository $jobRepository): void { $this->jobRepository = $jobRepository; } /** * Initialize the indexAction to set the currentPageBrowserPage parameter */ public function initializeIndexAction(): void { $currentPageBrowserPage = GeneralUtility::_GP('tx_sgjobs_pagebrowser') ? (int) GeneralUtility::_GP('tx_sgjobs_pagebrowser')['currentPage'] : 0; if ($currentPageBrowserPage > 0) { $this->request->setArgument('currentPageBrowserPage', $currentPageBrowserPage); } } /** * Show all job offers and options to manage them * * @param array $filters * @param int|null $jobId * @param int $currentPageBrowserPage * @return ResponseInterface|null * @throws ImmediateResponseException * @throws AspectNotFoundException * @throws PageNotFoundException */ public function indexAction( array $filters = [], int $jobId = NULL, int $currentPageBrowserPage = 0 ): ?ResponseInterface { if ($filters) { $this->view->assign('selectedCountry', $filters['filterCountry'] ?? ''); $this->view->assign('selectedCompany', $filters['filterCompany'] ?? ''); $this->view->assign('selectedLocation', $filters['filterLocation'] ?? ''); $this->view->assign('selectedDepartment', $filters['filterDepartment'] ?? ''); $this->view->assign('selectedExperienceLevel', $filters['filterExperienceLevel'] ?? ''); $this->view->assign('selectedFunction', $filters['filterFunction'] ?? ''); $this->view->assign('selectedRemote', $filters['filterRemote'] ?? ''); } $filters['filterByDepartment'] = $this->settings['filterByDepartment'] ?? ''; $filters['filterByExperienceLevel'] = $this->settings['filterByExperienceLevel'] ?? ''; $filters['filterByLocation'] = $this->settings['filterByLocation'] ?? ''; foreach ($filters as $name => &$filter) { if ($name === 'filterByLocation' || $name === 'filterByExperienceLevel' || $name === 'filterByDepartment') { $filter = $filter ? GeneralUtility::trimExplode(',', $filter) : NULL; if (!$filter) { continue; } foreach ($filter as &$value) { $value = (int) $value; } } } $storagePid = (int) $this->configurationManager->getConfiguration( ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK )['persistence']['storagePid']; $this->assignFilterValues($storagePid, $filters); $this->view->assign('recordPageId', $storagePid); $jobLimit = (int) $this->settings['jobLimit']; if ($jobId) { /** @var Job $job */ $job = $this->jobRepository->findByUid($jobId); if (!$job) { throw new \InvalidArgumentException('Given Job Id is invalid!'); } $headTagService = GeneralUtility::makeInstance( HeadTagService::class, TRUE, $job->getTitle(), $job->getDescription(), '&tx_sgjobs_jobapplication[jobId]=' . $jobId ); $headTagService->execute(); $jobs = [$job]; $numberOfPages = 1; } else { // pagination logic $offset = 0; if ($currentPageBrowserPage && $jobLimit) { $offset = $currentPageBrowserPage * $jobLimit; } $frontendPluginSettings = $this->configurationManager->getConfiguration( ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK ); $frontendPluginSettings = $frontendPluginSettings['settings']; // get all jobs for the current page $ordering = (int) $frontendPluginSettings['orderBy']; $jobs = $this->jobRepository->findJobsByFilter($filters, $jobLimit, $offset, $ordering)->toArray(); // get all jobs for the current page $allJobs = $this->jobRepository->findJobsByFilter($filters)->toArray(); $numberOfPages = (int) ($jobLimit <= 0 ? 0 : \ceil(\count($allJobs) / $jobLimit)); if ($numberOfPages !== 0 && $currentPageBrowserPage >= $numberOfPages) { /** @var ErrorController $errorController */ $errorController = GeneralUtility::makeInstance(ErrorController::class); $response = $errorController->pageNotFoundAction( $GLOBALS['TYPO3_REQUEST'], 'The requested page does not exist', ['code' => PageAccessFailureReasons::PAGE_NOT_FOUND] ); throw new ImmediateResponseException($response); } } $this->view->assign('jobs', $jobs); $this->view->assign('jobs2', 'just a job'); $this->view->assign('limit', $jobLimit); $this->view->assign('numberOfPages', $numberOfPages); if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) { return NULL; } return $this->htmlResponse(); } /** * Renders the application form with an optional job * * @param JobApplication|null $applyData * @param string $error * @param int|null $jobId * @return ResponseInterface|null * @throws AspectNotFoundException * @throws StopActionException * @throws SiteNotFoundException */ public function applyFormAction( JobApplication $applyData = NULL, string $error = '', int $jobId = NULL ): ?ResponseInterface { if ($error !== '') { $this->view->assign('internalError', $error); $this->request->setArgument('error', NULL); } if ($jobId === NULL && $this->settings['disallowUnsolicitedApplication']) { $uriBuilder = $this->uriBuilder; $uri = $uriBuilder ->setTargetPageUid($this->settings['offersPage']) ->build(); $this->redirectToUri($uri, 0, 301); } $folderName = NULL; try { $folderName = $this->request->getArgument('folderName'); } catch (\Exception $exception) { // this happens for the initial call, but works for any follow-up call as the form validation // throws you back to this one if something has failed } if ($folderName === NULL) { $folderName = \md5(\uniqid('sgjobs-', TRUE)); $this->request->setArgument('folderName', $folderName); } $this->view->assign('folderName', $folderName); $job = NULL; if ($jobId !== NULL) { /** @var Job $job */ $job = $this->jobRepository->findByUid($jobId); if ($job) { $headTagService = GeneralUtility::makeInstance( HeadTagService::class, FALSE, $job->getTitle(), $job->getDescription(), '&tx_sgjobs_jobapplication[jobId]=' . $jobId ); $headTagService->execute(); } $this->view->assign('job', $job); $enableAutomaticRelatedJobs = (bool) $this->settings['enableAutomaticRelatedJobs']; $automaticRelatedJobsLimit = (int) $this->settings['automaticRelatedJobsLimit']; if ($enableAutomaticRelatedJobs) { $relatedJobs = $this->jobRepository->findRelated($job, $automaticRelatedJobsLimit); $this->view->assign('relatedJobs', $relatedJobs); } } else { $storagePid = (int) $this->configurationManager->getConfiguration( ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK )['persistence']['storagePid']; $this->view->assign('companies', $this->companyRepository->getAllCompanies($storagePid)); } // display country options $context = GeneralUtility::makeInstance(Context::class); $sysLanguageUid = $context->getPropertyFromAspect('language', 'id'); $site = GeneralUtility::makeInstance(SiteFinder::class) ->getSiteByPageId($GLOBALS['TSFE']->id) ->getLanguageById($sysLanguageUid); $countries = []; if (ExtensionManagementUtility::isLoaded('project_base')) { $countryRepository = $this->objectManager->get(CountryRepository::class); $countries = $countryRepository->findAllOrderedByLanguage($site->getTwoLetterIsoCode()); } $this->view->assign('countries', $countries); $this->view->assign('sysLanguageUid', $sysLanguageUid); $allowedMimeTypes = $this->settings['allowedMimeTypes']; $this->view->assign('allowedMimeTypes', $allowedMimeTypes); $allowedFileExtensions = $this->settings['allowedFileExtensions']; $this->view->assign('allowedFileExtensions', $allowedFileExtensions); if ($applyData === NULL) { /** @noinspection CallableParameterUseCaseInTypeContextInspection */ $applyData = $this->objectManager->get(JobApplication::class); if ($job) { $applyData->setJobId($job->getJobId()); } } $this->view->assign('applyData', $applyData); $this->view->assign('maxFileSize', $this->settings['allowedMaxFileSize']); $this->view->assign('maxFileSizeMb', ((int) $this->settings['allowedMaxFileSize'] / 1000) . ' MByte'); $this->view->assign( 'maxFileSizeMessage', LocalizationUtility::translate('error.maxFileSizeMessage', 'sg_jobs') ); // This fixes a bug in the form ViewHelper that wants to serialize a Model with closures in it $arguments = $this->request->getArguments(); if (isset($arguments['applyData']) && $arguments['applyData']) { $arguments['applyData'] = (string) $arguments['applyData']; $this->request->setArguments($arguments); } if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) { return NULL; } return $this->htmlResponse(); } /** * Moves the application files from temporary to permanent storage * * @param JobApplication $applicationData * @param string $folderName * @return void * @throws StopActionException */ protected function submitApplicationFiles(JobApplication $applicationData, string $folderName): void { $resourceFactory = $this->objectManager->get(ResourceFactory::class); $newName = \date('Ymd-His') . '_' . $applicationData->getJobId() . '-' . $applicationData->getFirstName() . '-' . $applicationData->getLastName(); $storage = $resourceFactory->getStorageObject(1); $applicationFilePath = Environment::getPublicPath() . '/' . $storage->getConfiguration( )['basePath'] . 'JobApplication/' . $folderName . '/' . $newName . '.csv'; $this->writeApplicationFile($applicationData, $applicationFilePath); } /** * Pre-apply action setup, configures model-property mapping and handles file upload * * @throws NoSuchArgumentException * @throws StopActionException */ protected function initializeApplyAction(): void { $propertyMappingConfiguration = $this->arguments->getArgument('applyData')->getPropertyMappingConfiguration(); $propertyMappingConfiguration->forProperty('job')->allowAllProperties(); // remove the job validator /** @var ConjunctionValidator $validator */ $validator = $this->arguments->getArgument('applyData')->getValidator(); foreach ($validator->getValidators() as $subValidator) { /** @var GenericObjectValidator $subValidatorSub */ foreach ($subValidator->getValidators() as $subValidatorSub) { $subValidatorSub->getPropertyValidators('job')->removeAll( $subValidatorSub->getPropertyValidators('job') ); } } } /** * Saves the application send by the applyFormAction * * @param JobApplication $applyData * @return ResponseInterface|null * @throws NoSuchArgumentException * @throws StopActionException */ public function applyAction(JobApplication $applyData): ?ResponseInterface { $folderName = $this->request->getArgument('folderName'); try { $job = $applyData->getJob(); if ($job) { foreach ($job->getCompanies() as $company) { /** @var Company $company */ $applyData->setCompany($company); } } $applyData->setPid($GLOBALS['TSFE']->id); // look for a configured default job, in case of unsolicited application if ((!$job || $applyData->getJobId() === NULL) && $applyData->getCompany() !== NULL) { $applyData->setJobId($applyData->getCompany()->getJobId()); } if ($applyData->_isNew()) { $this->jobApplicationRepository->add($applyData); } else { $this->jobApplicationRepository->update($applyData); } $this->moveTmpFolder($folderName, $applyData); $this->submitApplicationFiles($applyData, $folderName); $mailService = $this->objectManager->get( MailTemplateService::class, 'application_mail', 'sg_jobs', $this->getApplicationMailMarkers($applyData) ); // set the pageId of the rootpage, otherwise the templates could be duplicated $site = GeneralUtility::makeInstance(SiteFinder::class)->getSiteByPageId($GLOBALS['TSFE']->id); $mailService->setPid($site->getRootPageId()); // get email from the job contact, fallback is TS settings $contact = NULL; if ($job !== NULL) { $contact = $job->getContact(); } if ($contact !== NULL) { $mailService->setToAddresses($contact->getEmail()); } else { $company = $applyData->getCompany(); if (($company !== NULL) && $company->getContact() !== NULL) { $mailService->setToAddresses($company->getContact()->getEmail()); } } $mailService->setMarkers( [ 'application' => $applyData, ] ); $mailService->setIgnoreMailQueue(TRUE); // add attachments for each file $coverLetter = $applyData->getCoverLetter(); if ($coverLetter) { $mailService->addFileResourceAttachment($coverLetter); } $cv = $applyData->getCv(); if ($cv) { $mailService->addFileResourceAttachment($cv); } $certificate = $applyData->getCertificate(); if ($certificate) { $mailService->addFileResourceAttachment($certificate); } $mailService->sendEmail(); $redirectPageUid = (int) $this->settings['redirectPage']; if ($redirectPageUid) { $contentObject = $this->configurationManager->getContentObject(); if ($contentObject) { $url = $contentObject->getTypoLink_URL($redirectPageUid); $this->redirectToUri($url); } } $this->redirect('applyForm'); } catch (\Exception $exception) { if ($exception instanceof StopActionException) { return NULL; } $this->deleteTmpFolder($folderName); $job = $applyData->getJob(); $jobId = $job !== NULL ? $job->getUid() : NULL; $this->request->setArgument('folderName', $folderName); $this->forward( 'applyForm', NULL, NULL, ['applyData' => $applyData, 'error' => $exception->getMessage(), 'jobId' => $jobId] ); } if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) { return NULL; } return $this->htmlResponse(); } /** * Assign filter values * * @param int $rootPageId * @throws AspectNotFoundException */ protected function assignFilterValues(int $rootPageId, array $filters = []): void { $countries = $this->companyRepository->getAllCountries($rootPageId, $filters['filterByLocation'] ?? []); $this->view->assign('countries', $countries); $cities = $this->companyRepository->getAllCities($rootPageId, $filters['filterByLocation'] ?? []); $this->view->assign('cities', $cities); $companies = $this->companyRepository->getAllCompanyNames($rootPageId, $filters['filterByLocation'] ?? []); $this->view->assign('companies', $companies); $departments = $this->departmentRepository->findAllByFilter($filters['filterByDepartment'] ?? []); $this->view->assign('departments', $departments); $experienceLevels = $this->experienceLevelRepository->findAllByFilter($filters['filterByExperienceLevel'] ?? []); $this->view->assign('experienceLevels', $experienceLevels); } /** * Returns the application mail markers * * @param JobApplication $applyData * @return array */ protected function getApplicationMailMarkers(JobApplication $applyData): array { $location = ''; if ($applyData->getCompany() !== NULL) { $location = $applyData->getCompany()->getCity(); } return [ 'salutation' => $applyData->getGender(), 'location' => $location, 'firstname' => $applyData->getFirstName(), 'lastname' => $applyData->getLastName(), 'street' => $applyData->getStreet(), 'city' => $applyData->getCity(), 'country' => $applyData->getCountry(), 'phone' => $applyData->getPhone(), 'mobile' => $applyData->getMobile(), 'email' => $applyData->getEmail(), 'message' => $applyData->getFirstName() ]; } /** * Writes the application files * * @param JobApplication $data * @param string $filePath * @throws StopActionException */ protected function writeApplicationFile(JobApplication $data, string $filePath): void { $coverLetter = ''; $coverLetterObject = $data->getCoverLetter(); if ($coverLetterObject) { $coverLetterObject = $coverLetterObject->getOriginalResource(); if ($coverLetterObject) { $coverLetter = $coverLetterObject->getPublicUrl(); } } $cv = ''; $cvObject = $data->getCv(); if ($cvObject) { $cvObject = $cvObject->getOriginalResource(); if ($cvObject) { $cv = $cvObject->getPublicUrl(); } } $certificate = ''; $certificateObject = $data->getCertificate(); if ($certificateObject) { $certificateObject = $certificateObject->getOriginalResource(); if ($certificateObject) { $certificate = $certificateObject->getPublicUrl(); } } $dataToInsertArr = [ $data->getJobId(), $data->getFirstName(), $data->getLastName(), $data->getGender(), $data->getCountry(), $data->getBirthDate(), $data->getEducation(), $data->getStreet(), $data->getZip(), $data->getCity(), $data->getNationality(), $data->getPhone(), $data->getEmail(), $coverLetter, $cv, $certificate, $data->getMessage() ]; try { GeneralUtility::mkdir_deep(\dirname($filePath)); $file = \fopen($filePath, 'wb+'); \fputcsv($file, $dataToInsertArr); \fclose($file); } catch (\RuntimeException $exception) { $this->redirect('applyForm', NULL, NULL, ['error' => $exception->getMessage()]); } } /** * Move the temp folder to its proper location * * @param string $folderName * @param JobApplication $applicationData * @throws TypeConverterException * @throws ExistingTargetFileNameException * @throws ExistingTargetFolderException * @throws InsufficientFolderAccessPermissionsException * @throws InsufficientFolderReadPermissionsException * @throws InsufficientFolderWritePermissionsException */ protected function moveTmpFolder(string $folderName, JobApplication $applicationData): void { $allowedFileExtensions = $this->getAllowedFileExtensions(); /** @var ResourceFactory $resourceFactory */ $resourceFactory = $this->objectManager->get(ResourceFactory::class); $storage = $resourceFactory->getStorageObject(1); $folder = $storage->getFolder('/JobApplication/'); $tempFolder = $storage->getFolder('/JobApplication/temp'); if (!$storage->hasFolderInFolder($folderName, $folder)) { $newFolder = $storage->createFolder($folderName, $folder); } else { $newFolder = $storage->getFolder('/JobApplication/' . $folderName); } // Move uploaded files & csv fo real folder and delete the tmp folder foreach (self::UPLOADED_FILES as $singleFilePostKey) { if (array_key_exists($singleFilePostKey, $_POST)) { foreach ($_POST[$singleFilePostKey] as $singleUploadedArr) { $filePathInfo = PathUtility::pathinfo($singleUploadedArr['path']); if (!GeneralUtility::inList($allowedFileExtensions, strtolower($filePathInfo['extension']))) { throw new TypeConverterException( LocalizationUtility::translate('error.TypeConverterException.type', 'sg_jobs'), 1399312430 ); } if (!$newFolder->hasFile($filePathInfo['basename'])) { /** @noinspection PhpUnreachableStatementInspection */ $singleFileToMove = $storage->getFileInFolder($filePathInfo['basename'], $tempFolder); // when we reload etc. this image might already be moved. $usableFile = $storage->moveFile($singleFileToMove, $newFolder); } else { /** @noinspection PhpUnreachableStatementInspection */ $usableFile = $newFolder->getFile($filePathInfo['basename']); } $fileReference = $this->fileAndFolderService->createFileReferenceFromFalFileObject($usableFile); if ($singleFilePostKey === 'coverLetter') { $applicationData->setCoverLetter($fileReference); } if ($singleFilePostKey === 'cv') { $applicationData->setCV($fileReference); } if ($singleFilePostKey === 'certificate') { $applicationData->setCertificate($fileReference); } } } else { throw new TypeConverterException( LocalizationUtility::translate( 'error.TypeConverterException.missing.' . $singleFilePostKey, 'sg_jobs' ), 1399312430 ); } } } /** * returns currently set allowedFiles * * @return mixed */ protected function getAllowedFileExtensions() { return $this->settings['allowedFileExtensions'] ?? $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']; } /** * Delete uploaded files in tmp folder * * @param string $folderName */ protected function deleteTmpFolder(string $folderName): void { /** @var ResourceFactory $resourceFactory */ $resourceFactory = $this->objectManager->get(ResourceFactory::class); $storage = $resourceFactory->getStorageObject(1); try { $tempFolder = $storage->getFolder('/JobApplication/temp/' . $folderName); $storage->deleteFolder($tempFolder, TRUE); } catch (\Exception $exception) { // folder is already deleted for some reason } } /** * If for any reason something goes wrong, delete the tmp upload folder * * @return ResponseInterface * @throws NoSuchArgumentException */ public function errorAction() { if ($this->request->hasArgument('folderName')) { $folderName = $this->request->getArgument('folderName'); $this->deleteTmpFolder($folderName); } return parent::errorAction(); } /** * Build TYPO3 11 Response * * @param string|NULL $html * @return ResponseInterface */ protected function htmlResponse(string $html = NULL): ResponseInterface { return $this->responseFactory->createResponse() ->withHeader('Content-Type', 'text/html; charset=utf-8') ->withBody($this->streamFactory->createStream($html ?? $this->view->render())); } }