Newer
Older
<?php
/***************************************************************
* 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!
***************************************************************/
namespace SGalinski\SgJobs\Controller;
Stefan Galinski
committed
use Psr\Http\Message\ResponseInterface;
Stefan Galinski
committed
use SGalinski\ProjectBase\Domain\Repository\CountryRepository;
use SGalinski\SgJobs\Controller\Ajax\UploadController;
Stefan Galinski
committed
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 TYPO3\CMS\Core\Context\Context;
Stefan Galinski
committed
use TYPO3\CMS\Core\Context\Exception\AspectNotFoundException;
use TYPO3\CMS\Core\Core\Environment;
Stefan Galinski
committed
use TYPO3\CMS\Core\Error\Http\PageNotFoundException;
use TYPO3\CMS\Core\Exception\SiteNotFoundException;
Stefan Galinski
committed
use TYPO3\CMS\Core\Http\ImmediateResponseException;
Stefan Galinski
committed
use TYPO3\CMS\Core\Resource\Exception\AbstractFileOperationException;
Stefan Galinski
committed
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;
Kevin von Spiczak
committed
use TYPO3\CMS\Core\Resource\File;
use TYPO3\CMS\Core\Resource\Folder;
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\Core\Utility\VersionNumberUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use TYPO3\CMS\Extbase\Mvc\Exception\InvalidArgumentNameException;
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;
Stefan Galinski
committed
use TYPO3\CMS\Extbase\Validation\Validator\ConjunctionValidator;
use TYPO3\CMS\Extbase\Validation\Validator\GenericObjectValidator;
Stefan Galinski
committed
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
Stefan Galinski
committed
public const ERROR_KEY_IN_POST = 'error';
private const UPLOADED_FILES = [
'coverLetter',
'cv',
'certificate',
];
* @var CompanyRepository
Stefan Galinski
committed
* @var JobRepository
*/
/**
Stefan Galinski
committed
* @var JobApplicationRepository
Stefan Galinski
committed
* @var DepartmentRepository
*/
Stefan Galinski
committed
/**
* @var ExperienceLevelRepository
*/
/**
* @var FileAndFolderService
*/
Stefan Galinski
committed
public $jobFolderPath = 'JobApplication';
Stefan Galinski
committed
/**
* Inject the CompanyRepository
*
Stefan Galinski
committed
* @param CompanyRepository $companyRepository
*/
Stefan Galinski
committed
public function injectCompanyRepository(CompanyRepository $companyRepository): void {
$this->companyRepository = $companyRepository;
}
/**
* Inject the CompanyRepository
*
Stefan Galinski
committed
* @param FileAndFolderService $fileAndFolderService
*/
public function injectFileAndFolderService(FileAndFolderService $fileAndFolderService): void {
$this->fileAndFolderService = $fileAndFolderService;
}
/**
Stefan Galinski
committed
* Inject the DepartmentRepository
*
Stefan Galinski
committed
* @param DepartmentRepository $departmentRepository
Stefan Galinski
committed
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
*/
Stefan Galinski
committed
public function injectJobApplicationRepository(JobApplicationRepository $jobApplicationRepository): void {
$this->jobApplicationRepository = $jobApplicationRepository;
}
/**
Stefan Galinski
committed
* Inject the JobRepository
*
Stefan Galinski
committed
* @param JobRepository $jobRepository
*/
Stefan Galinski
committed
public function injectJobRepository(JobRepository $jobRepository): void {
$this->jobRepository = $jobRepository;
}
Stefan Galinski
committed
/**
* Initializes the class
*/
public function __construct() {
$extensionConfiguration = $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['sg_jobs'] ?? [];
if (isset($extensionConfiguration['jobFolderPath']) && $extensionConfiguration['jobFolderPath'] !== '') {
$this->jobFolderPath = $extensionConfiguration['jobFolderPath'];
} else {
$this->jobFolderPath = 'JobApplication';
}
}
Stefan Galinski
committed
/**
* Initialize the indexAction to set the currentPageBrowserPage parameter
*/
Stefan Galinski
committed
public function initializeIndexAction(): void {
$currentPageBrowserPage = 0;
$parameters = GeneralUtility::_GP('tx_sgjobs_pagebrowser');
if (is_array($parameters)) {
$currentPageBrowserPage = (int) $parameters['currentPage'] ?: 0;
}
Stefan Galinski
committed
if ($currentPageBrowserPage > 0) {
$this->request->setArgument('currentPageBrowserPage', $currentPageBrowserPage);
}
}
/**
* Show all job offers and options to manage them
*
Stefan Galinski
committed
* @param int|null $jobId
Stefan Galinski
committed
* @param int $currentPageBrowserPage
Stefan Galinski
committed
* @return ResponseInterface|null
Stefan Galinski
committed
* @throws ImmediateResponseException
Stefan Galinski
committed
* @throws AspectNotFoundException
* @throws PageNotFoundException
public function indexAction(
array $filters = [],
int $jobId = NULL,
int $currentPageBrowserPage = 0
Stefan Galinski
committed
): ?ResponseInterface {
$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;
}
$storagePids = GeneralUtility::trimExplode(
',', $this->configurationManager->getConfiguration(
ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK
)['persistence']['storagePid']);

Torsten Oppermann
committed
$this->assignFilterValues($storagePids, $filters);
$this->view->assign('recordPageIds', $storagePids);
$jobLimit = (int) $this->settings['jobLimit'];
/** @var Job $job */
$job = $this->jobRepository->findByUid($jobId);
if (!$job) {
throw new \InvalidArgumentException('Given Job Id is invalid!');
}
if (ExtensionManagementUtility::isLoaded('sg_seo')) {
$headTagService = GeneralUtility::makeInstance(
\SGalinski\SgSeo\Service\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;
}
Stefan Galinski
committed
$frontendPluginSettings = $this->configurationManager->getConfiguration(
ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK
);
$frontendPluginSettings = $frontendPluginSettings['settings'];
Stefan Galinski
committed
$ordering = (int) $frontendPluginSettings['orderBy'];
Stefan Galinski
committed
$jobs = $this->jobRepository->findJobsByFilter($filters, $jobLimit, $offset, $ordering)->toArray();
Stefan Galinski
committed
$allJobs = $this->jobRepository->findJobsByFilter($filters)->toArray();
Stefan Galinski
committed
$numberOfPages = (int) ($jobLimit <= 0 ? 0 : \ceil(\count($allJobs) / $jobLimit));
if ($numberOfPages !== 0 && $currentPageBrowserPage >= $numberOfPages) {
Stefan Galinski
committed
/** @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('limit', $jobLimit);
$this->view->assign('numberOfPages', $numberOfPages);
if (version_compare(VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
Stefan Galinski
committed
return $this->htmlResponse();
* Renders the application form with an optional job
Stefan Galinski
committed
* @param JobApplication|null $applyData
* @param string $error
Stefan Galinski
committed
* @param int|null $jobId
* @return ResponseInterface|null
* @throws AspectNotFoundException
* @throws StopActionException
* @throws SiteNotFoundException
* @throws InvalidArgumentNameException
public function applyFormAction(
JobApplication $applyData = NULL,
string $error = '',
int $jobId = NULL
Stefan Galinski
committed
): ?ResponseInterface {
Stefan Galinski
committed
if ($error !== '') {
$this->view->assign('internalError', $error);
$this->request->setArgument('error', NULL);
Sergiu-Lucian Petrica
committed
}
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;
/** @var Job $job */
Stefan Galinski
committed
$job = $this->jobRepository->findByUid($jobId);
if ($job) {
if (ExtensionManagementUtility::isLoaded('sg_seo')) {
$headTagService = GeneralUtility::makeInstance(
\SGalinski\SgSeo\Service\HeadTagService::class,
FALSE,
$job->getTitle(),
$job->getDescription(),
'&tx_sgjobs_jobapplication[jobId]=' . $jobId
);
$headTagService->execute();
}
$enableAutomaticRelatedJobs = (bool) $this->settings['enableAutomaticRelatedJobs'];
if ($enableAutomaticRelatedJobs) {
$automaticRelatedJobsLimit = (int) $this->settings['automaticRelatedJobsLimit'];
$relatedJobs = $this->jobRepository->findRelated($job, $automaticRelatedJobsLimit);
$this->view->assign('relatedJobs', $relatedJobs);
}
}
$this->view->assign('job', $job);
$storagePids = GeneralUtility::trimExplode(
',', $this->configurationManager->getConfiguration(
ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK
)['persistence']['storagePid']);
$this->view->assign('companies', $this->companyRepository->getAllCompanies($storagePids));
// 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) {
$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(VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
Stefan Galinski
committed
return $this->htmlResponse();
Stefan Galinski
committed
/**
Stefan Galinski
committed
*
* @param JobApplication $applicationData
* @param string $folderName
* @return void
Stefan Galinski
committed
* @throws StopActionException
Stefan Galinski
committed
*/
Stefan Galinski
committed
protected function createCsvSummary(JobApplication $applicationData, string $folderName): void {
Stefan Galinski
committed
$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';
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
$coverLetter = '';
$coverLetterObject = $applicationData->getCoverLetter();
if ($coverLetterObject) {
$coverLetterObject = $coverLetterObject->getOriginalResource();
if ($coverLetterObject) {
$coverLetter = $coverLetterObject->getPublicUrl();
}
}
$cv = '';
$cvObject = $applicationData->getCv();
if ($cvObject) {
$cvObject = $cvObject->getOriginalResource();
if ($cvObject) {
$cv = $cvObject->getPublicUrl();
}
}
$certificate = '';
$certificateObject = $applicationData->getCertificate();
if ($certificateObject) {
$certificateObject = $certificateObject->getOriginalResource();
if ($certificateObject) {
$certificate = $certificateObject->getPublicUrl();
}
}
$dataToInsertArr = [
$applicationData->getJobId(),
$applicationData->getFirstName(),
$applicationData->getLastName(),
$applicationData->getGender(),
$applicationData->getCountry(),
$applicationData->getBirthDate(),
$applicationData->getEducation(),
$applicationData->getStreet(),
$applicationData->getZip(),
$applicationData->getCity(),
$applicationData->getNationality(),
$applicationData->getPhone(),
$applicationData->getEmail(),
$coverLetter,
$cv,
$certificate,
$applicationData->getMessage()
];
try {
GeneralUtility::mkdir_deep(\dirname($applicationFilePath));
$file = \fopen($applicationFilePath, 'wb+');
\fputcsv($file, $dataToInsertArr);
\fclose($file);
} catch (\RuntimeException $exception) {
$this->redirect('applyForm', NULL, NULL, ['error' => $exception->getMessage()]);
}
Stefan Galinski
committed
}
/**
* Pre-apply action setup, configures model-property mapping and handles file upload
*
* @throws NoSuchArgumentException
Stefan Galinski
committed
* @throws StopActionException
Stefan Galinski
committed
protected function initializeApplyAction(): void {
$propertyMappingConfiguration = $this->arguments->getArgument('applyData')->getPropertyMappingConfiguration();
$propertyMappingConfiguration->forProperty('job')->allowAllProperties();
Stefan Galinski
committed
// 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
Stefan Galinski
committed
* @return ResponseInterface|null
* @throws StopActionException|InvalidArgumentNameException
Stefan Galinski
committed
public function applyAction(JobApplication $applyData): ?ResponseInterface {
$folderName = $this->request->getArgument('folderName');
Stefan Galinski
committed
if ($job) {
$applyData->setJobId($job->getJobId());
Stefan Galinski
committed
foreach ($job->getCompanies() as $company) {
/** @var Company $company */
$applyData->setCompany($company);
}
} elseif (!$applyData->getCompany()) {
/** @var Company $company */
$company = $this->companyRepository->findByUid(
(int) $_POST['tx_sgjobs_jobapplication']['applyData']['company']
);
if ($company) {
$applyData->setCompany($company);
}
Stefan Galinski
committed
}
// look for a configured default job, in case of unsolicited application
$applyData->setPid($GLOBALS['TSFE']->id);
if ((!$job || !$applyData->getJobId()) && $applyData->getCompany()) {

Torsten Oppermann
committed
$applyData->setJobId($applyData->getCompany()->getJobId());
if ($applyData->_isNew()) {
$this->jobApplicationRepository->add($applyData);
} else {
$this->jobApplicationRepository->update($applyData);
}
$this->moveTmpFolder($folderName, $applyData);
Stefan Galinski
committed
$this->createCsvSummary($applyData, $folderName);
/** @noinspection PhpParamsInspection */
$mailService = $this->objectManager->get(
MailTemplateService::class,
'application_mail',
'sg_jobs',
$this->getApplicationMailMarkers($applyData)
// set the pageId of the root page, otherwise the templates could be duplicated
$site = GeneralUtility::makeInstance(SiteFinder::class)->getSiteByPageId($GLOBALS['TSFE']->id);
$mailService->setPid($site->getRootPageId());

Torsten Oppermann
committed
// get email from the job contact, fallback is TS settings

Torsten Oppermann
committed
if ($job !== NULL) {
$contact = $job->getContact();
}
if ($contact !== NULL) {
$mailService->setToAddresses($contact->getEmail());
} else {
if (($company !== NULL) && $company->getContact() !== NULL) {
$mailService->setToAddresses($company->getContact()->getEmail());

Torsten Oppermann
committed
}
$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);

Torsten Oppermann
committed
$redirectPageUid = (int) $this->settings['redirectPage'];
if ($redirectPageUid) {
Stefan Galinski
committed
$contentObject = $this->configurationManager->getContentObject();
if ($contentObject) {
$url = $contentObject->getTypoLink_URL($redirectPageUid);
$this->redirectToUri($url);
}
}

Torsten Oppermann
committed
$this->redirect('applyForm');
} catch (\Exception $exception) {
if ($exception instanceof StopActionException) {

Torsten Oppermann
committed
$this->deleteTmpFolder($folderName);
$job = $applyData->getJob();
$jobId = $job !== NULL ? $job->getUid() : NULL;
$this->request->setArgument('folderName', $folderName);
$this->forward(
Kevin Ditscheid
committed
['applyData' => $applyData, 'error' => $exception->getMessage(), 'jobId' => $jobId]
if (version_compare(VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
Stefan Galinski
committed
return $this->htmlResponse();
Sergiu-Lucian Petrica
committed
}

Torsten Oppermann
committed
/**
* @param array $rootPageIds
Stefan Galinski
committed
* @throws AspectNotFoundException

Torsten Oppermann
committed
*/
protected function assignFilterValues(array $rootPageIds, array $filters = []): void {
$countries = $this->companyRepository->getAllCountries($rootPageIds, $filters['filterByLocation'] ?? []);

Torsten Oppermann
committed
$this->view->assign('countries', $countries);
$cities = $this->companyRepository->getAllCities($rootPageIds, $filters['filterByLocation'] ?? []);

Torsten Oppermann
committed
$this->view->assign('cities', $cities);
$companies = $this->companyRepository->getAllCompanyNames($rootPageIds, $filters['filterByLocation'] ?? []);

Torsten Oppermann
committed
$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);

Torsten Oppermann
committed
}
* Returns the application mail markers
*
* @param JobApplication $applyData
Stefan Galinski
committed
protected function getApplicationMailMarkers(JobApplication $applyData): array {
$location = '';
if ($applyData->getCompany() !== NULL) {
$location = $applyData->getCompany()->getCity();
}
'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()
/**
* Move the temp folder to its proper location
*
* @param string $folderName
Stefan Galinski
committed
* @param JobApplication $applicationData
* @throws ExistingTargetFileNameException
* @throws ExistingTargetFolderException
* @throws InsufficientFolderAccessPermissionsException
* @throws InsufficientFolderReadPermissionsException
* @throws InsufficientFolderWritePermissionsException
Stefan Galinski
committed
* @throws TypeConverterException
* @throws AbstractFileOperationException
protected function moveTmpFolder(string $folderName, JobApplication $applicationData): void {
$allowedFileExtensions = $this->getAllowedFileExtensions();
/** @var ResourceFactory $resourceFactory */
$resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);
$storage = $resourceFactory->getStorageObject(1); // fileadmin
$newFolder = $tempFolder = NULL;
Stefan Galinski
committed
$folder = $storage->getFolder($this->jobFolderPath);
if ($folder) {
$tempFolder = $storage->getFolderInFolder(UploadController::JOB_APPLICATION_TEMP_FOLDER, $folder);
if (!$storage->hasFolderInFolder($folderName, $folder)) {
$newFolder = $storage->createFolder($folderName, $folder);
} else {
Stefan Galinski
committed
$newFolder = $storage->getFolder($this->jobFolderPath . $folderName);
}
}
if (!$newFolder || !$tempFolder) {
throw new \RuntimeException('Upload folder can\'t be created (write permissions?)!');
// Move uploaded files & csv fo real folder and delete the tmp folder
Stefan Galinski
committed
$uploadedFiles = [];
foreach (self::UPLOADED_FILES as $singleFilePostKey) {
if (!array_key_exists($singleFilePostKey, $_POST)) {
Stefan Galinski
committed
throw new TypeConverterException(
LocalizationUtility::translate(
'error.TypeConverterException.missing.' . $singleFilePostKey,
'sg_jobs'
),
1399312430
Stefan Galinski
committed
);
// get the first uploaded document, should be prevented in the frontend to upload more than one
$singleUploadedFile = current($_POST[$singleFilePostKey])['path'];
$filePathInfo = PathUtility::pathinfo($singleUploadedFile);
if (!GeneralUtility::inList($allowedFileExtensions, strtolower($filePathInfo['extension']))) {
throw new TypeConverterException(
LocalizationUtility::translate('error.TypeConverterException.type', 'sg_jobs'),
1399312430
);
}
$basename = $filePathInfo['basename'];
Stefan Galinski
committed
$newFilename = strtolower($singleFilePostKey) . '.' . strtolower($filePathInfo['extension']);
if (array_key_exists($singleUploadedFile, $uploadedFiles)) {
// the same file was uploaded for different sources
Kevin von Spiczak
committed
$usableFile = $this->getFileInFolder(
$uploadedFiles[$singleUploadedFile] . '.' . strtolower($filePathInfo['extension']),
$newFolder
Stefan Galinski
committed
);
if (!$usableFile) {
throw new \RuntimeException('File not found (' . $singleFilePostKey . ')!');
}
$usableFile = $storage->copyFile($usableFile, $newFolder, $newFilename);
} elseif (!$newFolder->hasFile($newFilename)) {
// when we reload, etc. this image might already be moved.
Stefan Galinski
committed
/** @noinspection PhpUnreachableStatementInspection */
$singleFileToMove = $storage->getFileInFolder($basename, $tempFolder);
$usableFile = $storage->moveFile($singleFileToMove, $newFolder, $newFilename);
} else {
/** @noinspection PhpUnreachableStatementInspection */
Kevin von Spiczak
committed
$usableFile = $this->getFileInFolder($newFilename, $newFolder);
}
$fileReference = $this->fileAndFolderService->createFileReferenceFromFalFileObject($usableFile);
if ($singleFilePostKey === 'coverLetter') {
$applicationData->setCoverLetter($fileReference);
}
if ($singleFilePostKey === 'cv') {
$applicationData->setCV($fileReference);
}
if ($singleFilePostKey === 'certificate') {
$applicationData->setCertificate($fileReference);
}
Stefan Galinski
committed
$uploadedFiles[$singleUploadedFile] = strtolower($singleFilePostKey);
Kevin von Spiczak
committed
/**
* Gets a file in a folder.
Kevin von Spiczak
committed
* (Backwards compatible with TYPO3 10, where the Folder did not have a getFile() method yet).
*
* @param string $fileName
* @param Folder $folder
Kevin von Spiczak
committed
*/
protected function getFileInFolder(string $fileName, Folder $folder): ?File {
if (version_compare(VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '>=')) {
return $folder->getFile($fileName);
}
if ($folder->getStorage()->hasFileInFolder($fileName, $folder)) {
return $folder->getStorage()->getFileInFolder($fileName, $folder);
}
return NULL;
}
*
* @return mixed
*/
protected function getAllowedFileExtensions() {
return $this->settings['allowedFileExtensions'] ?? $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'];
}

Torsten Oppermann
committed
/**
* Delete uploaded files in tmp folder
*
* @param string $folderName
*/
Stefan Galinski
committed
protected function deleteTmpFolder(string $folderName): void {

Torsten Oppermann
committed
/** @var ResourceFactory $resourceFactory */
$resourceFactory = $this->objectManager->get(ResourceFactory::class);
$storage = $resourceFactory->getStorageObject(1);
try {
Stefan Galinski
committed
$tempFolder = $storage->getFolder('/JobApplication/' . $folderName);

Torsten Oppermann
committed
$storage->deleteFolder($tempFolder, TRUE);
// the folder is already deleted for some reason

Torsten Oppermann
committed
}
}
/**
* If for any reason something goes wrong, delete the tmp upload folder
*
Stefan Galinski
committed
* @return ResponseInterface

Torsten Oppermann
committed
*/
public function errorAction() {
if ($this->request->hasArgument('folderName')) {
$folderName = $this->request->getArgument('folderName');
$this->deleteTmpFolder($folderName);
}

Torsten Oppermann
committed
}
Stefan Galinski
committed
* Build TYPO3 11 Response
Stefan Galinski
committed
* @return ResponseInterface
Stefan Galinski
committed
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()));
}