Skip to content
Snippets Groups Projects
JoblistController.php 17 KiB
Newer Older
<?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 SGalinski\SgJobs\Domain\Model\Contact;
use SGalinski\SgJobs\Domain\Model\Job;
use SGalinski\SgJobs\Domain\Model\JobApplication;
use SGalinski\SgJobs\Property\TypeConverter\UploadedFileReferenceConverter;
use SGalinski\SgJobs\Service\FrontendFilterService;
use SGalinski\SgMail\Service\MailTemplateService;
use SJBR\StaticInfoTables\Domain\Repository\CountryRepository;
use TYPO3\CMS\Core\Resource\FileReference;
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
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\Request;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;

/**
 * The joblist plugin controller
 */
class JoblistController extends ActionController {
	// the array key for the error message in the post array
	const ERROR_KEY_IN_POST = 'error';

	 * @var \SGalinski\SgJobs\Domain\Repository\CompanyRepository
	private $companyRepository;

	/**
	 * @var \SGalinski\SgJobs\Domain\Repository\JobRepository
	 * @inject
	 */
	private $jobRepository;

	/**
	 * @var \SGalinski\SgJobs\Domain\Repository\JobApplicationRepository
	 * @inject
	 */
	private $jobApplicationRepository;

	/**
	 * Show all job offers and options to manage them
	 *
	 * @param array $filters
	 * @param int $jobId
	 * @return void
	 * @throws \InvalidArgumentException
	 */
	public function indexAction(array $filters = [], $jobId = NULL) {
		if ($filters) {
			$this->view->assign('selectedCountry', $filters['filterCountry']);
			$this->view->assign('selectedCompany', $filters['filterCompany']);
			$this->view->assign('selectedLocation', $filters['filterLocation']);
			$this->view->assign('selectedArea', $filters['filterArea']);
			$this->view->assign('selectedFunction', $filters['filterFunction']);
		}

		$storagePid = (int) $this->configurationManager->getConfiguration(
			ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK
		)['persistence']['storagePid'];
		$this->assignFilterValues($storagePid);
		$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!');
			}

			$GLOBALS['TSFE']->page['titlebyextension'] = $job->getTitle();
			$GLOBALS['TSFE']->page['description'] = substr($job->getDescription(), 0, 200);

			$jobs = [$job];
			$numberOfPages = 1;
		} else {
			// pagination logic
			$offset = 0;
			$currentPageBrowserPage = (int) GeneralUtility::_GP('tx_sgjobs_pagebrowser')['currentPage'];
			if ($currentPageBrowserPage && $jobLimit) {
				$offset = $currentPageBrowserPage * $jobLimit;
			}

			// get all jobs for the current page
			$ordering = (int) $this->configurationManager->getConfiguration(
				ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK
			)['settings']['orderBy'];
			$jobs = FrontendFilterService::getJobs($filters, $storagePid, $jobLimit, $offset, $ordering);

			// get all jobs for the current page
			$jobsCount = \count(FrontendFilterService::getJobs($filters, $storagePid));
			$numberOfPages = ($jobLimit <= 0 ? 0 : ceil($jobsCount / $jobLimit));
		}
		$this->view->assign('jobs', $jobs);
		$this->view->assign('limit', $jobLimit);
		$this->view->assign('numberOfPages', $numberOfPages);
	 * Renders the application form with an optional job
	 * @param JobApplication $applyData
	 * @param string $error
	 * @param int $jobId
	 * @throws \TYPO3\CMS\Extbase\Mvc\Exception\InvalidArgumentNameException
	public function applyFormAction(JobApplication $applyData = NULL, $error = NULL, $jobId = NULL) {
		if ($error !== NULL && $error !== '') {
			$this->view->assign('internalError', $error);
			$this->request->setArgument('error', NULL);
		$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);

		if ($jobId !== NULL) {
			$job = $this->jobRepository->findByUidLocalized($jobId);
			if ($job) {
				$GLOBALS['TSFE']->page['titlebyextension'] = $job->getTitle();
				$GLOBALS['TSFE']->page['description'] = substr($job->getDescription(), 0, 200);
			}
			$this->view->assign('job', $job);
		} else {
			$storagePid = (int) $this->configurationManager->getConfiguration(
				ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK
			)['persistence']['storagePid'];
			$this->view->assign('companies', $this->companyRepository->getAllCompanies($storagePid));
		// display country options
		$countryRepository = $this->objectManager->get(CountryRepository::class);
		$countries = $countryRepository->findAllOrderedBy('shortNameEn')->toArray();
		$this->view->assign('countries', $countries);
		$this->view->assign('sysLanguageUid', $GLOBALS['TSFE']->sys_language_uid);
		$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(
			'maxFileSizeMessage', LocalizationUtility::translate('error.maxFileSizeMessage', 'sg_jobs')
		);
Stefan Galinski's avatar
Stefan Galinski committed
	/**
	 * Pre-apply action setup, configures model-property mapping and handles file upload
	 *
	 * @return void
	 * @throws NoSuchArgumentException
	 * @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
	 * @throws \TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException
Stefan Galinski's avatar
Stefan Galinski committed
	 */
	protected function initializeApplyAction() {
		try {
			$uniqueFolderName = $this->request->getArgument('folderName');
		} catch (NoSuchArgumentException $exception) {
			$exceptionMessage = 'Eine Datei konnte nicht hochgeladen werden. Ist diese eventuell zu groß?';
			$this->redirect('applyForm', NULL, NULL, ['error' => $exceptionMessage]);
Stefan Galinski's avatar
Stefan Galinski committed
		$propertyMappingConfiguration = $this->arguments->getArgument('applyData')->getPropertyMappingConfiguration();
		$propertyMappingConfiguration->forProperty('job')->allowAllProperties();
		$typeConverter1 = $this->objectManager->get(UploadedFileReferenceConverter::class);
		$typeConverter1->setAllowedFileExtensions($this->settings['allowedFileExtensions']);
		$typeConverter1->setUploadFolder('1:/JobApplication/' . $uniqueFolderName);
		$typeConverter1->setTargetUploadFileName('coverLetter');
		$propertyMappingConfiguration->forProperty('coverLetter')->setTypeConverter($typeConverter1);

		$typeConverter2 = $this->objectManager->get(UploadedFileReferenceConverter::class);
		$typeConverter2->setAllowedFileExtensions($this->settings['allowedFileExtensions']);
		$typeConverter2->setUploadFolder('1:/JobApplication/' . $uniqueFolderName);
		$typeConverter2->setTargetUploadFileName('cv');
		$propertyMappingConfiguration->forProperty('cv')->setTypeConverter($typeConverter2);

		$typeConverter3 = $this->objectManager->get(UploadedFileReferenceConverter::class);
		$typeConverter3->setAllowedFileExtensions($this->settings['allowedFileExtensions']);
		$typeConverter3->setUploadFolder('1:/JobApplication/' . $uniqueFolderName);
		$typeConverter3->setTargetUploadFileName('certificate');
		$propertyMappingConfiguration->forProperty('certificate')->setTypeConverter($typeConverter3);
	/**
	 * Moves the application files from temporary to permanent storage
	 *
	 * @param JobApplication $applicationData
	 * @param string $folderName
	 * @return void
	 * @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
	 * @throws \RuntimeException
	 * @throws \InvalidArgumentException
	 * @throws \TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException
	 */
	private function submitApplicationFiles(JobApplication $applicationData, $folderName) {
		$resourceFactory = $this->objectManager->get(ResourceFactory::class);
		$newName = date('Ymd-His') . '_' . $applicationData->getJobId() . '-' . $applicationData->getFirstName()
			. '-' . $applicationData->getLastName();
		$storage = $resourceFactory->getStorageObject(1);
		$applicationFilePath = PATH_site . $storage->getConfiguration()['basePath'] . 'JobApplication/' . $folderName .
			'/' . $newName . '.csv';
		$this->writeApplicationFile($applicationData, $applicationFilePath);
	 * Saves the application send by the applyFormAction
	 *
	 * @param JobApplication $applyData
	 * @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
	 * @throws \InvalidArgumentException
	 * @throws \TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException
	public function applyAction(JobApplication $applyData) {
		$folderName = $this->request->getArgument('folderName');

Stefan Galinski's avatar
Stefan Galinski committed
			$applyData->setPid($GLOBALS['TSFE']->id);
			/** @var Job $job */
			$job = $applyData->getJob();

			// look for a configured default job, in case of unsolicited application
			if (($job === NULL || $applyData->getJobId() === NULL) && $applyData->getCompany() !== NULL) {
				$applyData->setJobId($applyData->getCompany()->getJobId());
			if ($applyData->_isNew()) {
				$this->jobApplicationRepository->add($applyData);
			} else {
				$this->jobApplicationRepository->update($applyData);
			}

			$this->submitApplicationFiles($applyData, $folderName);
			/** @noinspection PhpMethodParametersCountMismatchInspection */
			$mailService = $this->objectManager->get(
				MailTemplateService::class, 'application_mail', 'sg_jobs',
				$this->getApplicationMailMarkers($applyData)

			// get email from the job contact, fallback is TS settings
			$contact = NULL;
			if ($job !== NULL) {
				/** @var Contact $contact */
				$contact = $job->getContact();
			}

			if ($contact !== NULL) {
				$mailService->setToAddresses($contact->getEmail());
				$company = $applyData->getCompany();
				if ($company !== NULL) {
					if ($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) {
				$originalResource = $coverLetter->getOriginalResource();
				if ($originalResource) {
					$this->addAttachmentsToMail($originalResource, $mailService);
				}

			$cv = $applyData->getCv();
			if ($cv) {
				$originalResource = $cv->getOriginalResource();
				if ($originalResource) {
					$this->addAttachmentsToMail($originalResource, $mailService);
				}

			$certificate = $applyData->getCertificate();
			if ($certificate) {
				$originalResource = $certificate->getOriginalResource();
				if ($originalResource) {
					$this->addAttachmentsToMail($originalResource, $mailService);
				}
Stefan Galinski's avatar
Stefan Galinski committed

			$mailService->sendEmail();
			$redirectPageUid = (int) $this->settings['redirectPage'];
			if ($redirectPageUid) {
				$url = $this->configurationManager->getContentObject()->getTypoLink_URL($redirectPageUid);
				$this->redirectToUri($url);
			}
		} catch (\Exception $exception) {
			$job = $applyData->getJob();
			$jobId = $job !== NULL ? $job->getUid() : NULL;
			$this->request->setArgument('folderName', $folderName);
			$this->forward(
				'applyForm', NULL, NULL,
				['applyData' => $applyData, 'error' => $exception->getMessage(), 'uid' => $jobId]
			);
	/**
	 * add FileReference as attachment to the mail service mailMessage
	 *
	 * @param FileReference $originalResource
	 * @param MailTemplateService $mailService
	 */
	private function addAttachmentsToMail(FileReference $originalResource, MailTemplateService $mailService) {
		$storage = $originalResource->getStorage();
		if (!$storage) {
			return;
		}
		$attachmentOriginalFile = PATH_site . rawurldecode($originalResource->getPublicUrl());
		$mimeType = $originalResource->getMimeType();
		$attachment = \Swift_Attachment::fromPath($attachmentOriginalFile, $mimeType);

		$mailService->getMailMessage()->attach($attachment);
	}

	 * Assign filter values
	 *
	private function assignFilterValues($rootPageId) {
		$countries = $this->companyRepository->getAllCountries($rootPageId);
		$this->view->assign('countries', $countries);

		$cities = $this->companyRepository->getAllCities($rootPageId);
		$this->view->assign('cities', $cities);

		$companies = $this->companyRepository->getAllCompanyNames($rootPageId);
		$this->view->assign('companies', $companies);

		$areas = $this->jobRepository->getAllAreas($rootPageId);
		$this->view->assign('areas', $areas);
	}

	 * Returns the application mail markers
	 *
	 * @param JobApplication $applyData
	 * @return array
	 */
	private function getApplicationMailMarkers($applyData): array {
		$location = '';
		if ($applyData->getCompany() !== NULL) {
			$location = $applyData->getCompany()->getCity();
		}

		return [
			'salutation' => $applyData->getGender(),
			'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 \TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException
	 * @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
	 * @throws \InvalidArgumentException
	 * @throws \RuntimeException
	private function writeApplicationFile(JobApplication $data, $filePath) {
			$data->getFirstName(),
			$data->getLastName(),
			$data->getGender(),
			$data->getCountry(),
			$data->getBirthDate(),
			$data->getEducation(),
			$data->getStreet(),
			$data->getZip(),
			$data->getCity(),
			$data->getNationality(),
			$data->getPhone(),
			$data->getEmail(),
			$data->getCoverLetter() === NULL ? '' : $data->getCoverLetter()->getOriginalResource()->getPublicUrl(),
			$data->getCv() === NULL ? '' : $data->getCv()->getOriginalResource()->getPublicUrl(),
			$data->getCertificate() === NULL ? '' : $data->getCertificate()->getOriginalResource()->getPublicUrl(),
			$data->getMessage()
			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()]);

	/**
	 * checks for allowed maximum file sizes
	 *
	 * @param JobApplication $applyData
	 * @throws \InvalidArgumentException
	private function checkFileSizes($applyData): bool {
		$coverLetterSize = (int) $applyData->getCoverLetter()->getOriginalResource()->getSize() / 1000;
		$cvSize = (int) $applyData->getCv()->getOriginalResource()->getSize() / 1000;
		$certificateSize = (int) $applyData->getCertificate()->getOriginalResource()->getSize() / 1000;

		$allowedMaxFileSize = (int) $this->settings['allowedMaxFileSize'];

		if ($allowedMaxFileSize === 0) {
			return TRUE;
		}

		if ($allowedMaxFileSize < $coverLetterSize
			|| $allowedMaxFileSize < $cvSize
			|| $allowedMaxFileSize < $certificateSize) {

			return FALSE;
		}

		return TRUE;
	}