diff --git a/Classes/Controller/Ajax/JoblistController.php b/Classes/Controller/Ajax/JoblistController.php index 227f5f8f68c2c20b794d3f68005907880738be96..8b767520dc5aac056d799261fba00ba70ca2759e 100644 --- a/Classes/Controller/Ajax/JoblistController.php +++ b/Classes/Controller/Ajax/JoblistController.php @@ -27,7 +27,6 @@ namespace SGalinski\SgJobs\Controller\Ajax; ***************************************************************/ use SGalinski\SgAjax\Controller\Ajax\AbstractAjaxController; -use SGalinski\SgJobs\Domain\Repository\JobRepository; use SGalinski\SgJobs\Service\BackendService; use SGalinski\SgJobs\Service\FrontendFilterService; use TYPO3\CMS\Core\Resource\ResourceFactory; @@ -48,7 +47,6 @@ class JoblistController extends AbstractAjaxController { * Show all job offers and options to manage them * * @return void - * @throws \TYPO3\CMS\Extbase\Persistence\Exception\InvalidQueryException * @throws \InvalidArgumentException */ public function filterAction() { @@ -70,7 +68,7 @@ class JoblistController extends AbstractAjaxController { $this->view->assign('countries', $this->companyRepository->getAllCountries($siteRootId)); // set all filtered jobs - $this->view->assign('jobs', FrontendFilterService::getJobs($filters,$siteRootId)); + $this->view->assign('jobs', FrontendFilterService::getJobs($filters, $siteRootId)); // set default selected values $this->view->assign('selectedCountry', $filters['country']); @@ -84,13 +82,14 @@ class JoblistController extends AbstractAjaxController { * Delete an uploaded file from temp storage * * @return void + * @throws \TYPO3\CMS\Core\Resource\Exception\FileOperationErrorException + * @throws \InvalidArgumentException + * @throws \TYPO3\CMS\Core\Resource\Exception\InsufficientFileAccessPermissionsException */ public function deleteTempFileAction() { $file = $_POST['file']; $type = $_POST['type']; - $resourceFactory = GeneralUtility::makeInstance( - ResourceFactory::class - ); + $resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class); $storage = $resourceFactory->getStorageObject(1); $fileObject = $storage->getFile('/Extension/temp/' . $GLOBALS['TSFE']->fe_user->id . '/' . $type . '/' . $file); $storage->deleteFile($fileObject); diff --git a/Classes/Controller/JoblistController.php b/Classes/Controller/JoblistController.php index 53d98e88edb94f33297a0c918d605d122a591da4..050eba0b1028ae46a5747607df4526be259779da 100644 --- a/Classes/Controller/JoblistController.php +++ b/Classes/Controller/JoblistController.php @@ -41,12 +41,6 @@ use TYPO3\CMS\Extbase\Object\ObjectManager; * The joblist plugin controller */ class JoblistController extends ActionController { - /** - * @var \TYPO3\CMS\Extbase\Object\ObjectManager - * @inject - */ - protected $objectManager; - /** * @var \SGalinski\SgJobs\Domain\Repository\CompanyRepository * @inject @@ -79,23 +73,27 @@ class JoblistController extends ActionController { } /** - * + * Renders the application form with an optional job */ public function applyFormAction() { if ($this->request->getOriginalRequest()) { $uploadedFiles = $this->request->getOriginalRequest()->getArguments()['uploadedFiles']; $this->view->assign('uploadedFiles', $uploadedFiles); } + $jobId = $this->request->getArguments()['jobData']['uid']; if (!empty($jobId)) { $jobData = $this->jobRepository->findByUid($jobId); $this->view->assign('job', $jobData); } + $allowedMimeTypes = $this->settings['allowedMimeTypes']; $this->view->assign('allowedMimeTypes', $allowedMimeTypes); } /** + * Saves the application send by the applyFormAction + * * @param JobApplication $applyData * @return void * @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException @@ -112,18 +110,19 @@ class JoblistController extends ActionController { */ public function applyAction(JobApplication $applyData) { $this->submitApplicationFiles($GLOBALS['TSFE']->fe_user->id, $applyData); - // get an instance of the mail service - /** @var MailTemplateService $mailService */ - $mailService = $this->objectManager->get( - MailTemplateService::class, 'application_mail', 'sg_jobs', - $this->getApplicationMailMarkers((array) $applyData) - ); - $mailService->setIgnoreMailQueue(TRUE); - $mailService->setToAddresses($this->settings['applicationEmail']); + try { + /** @noinspection PhpMethodParametersCountMismatchInspection */ + $mailService = $this->objectManager->get( + MailTemplateService::class, 'application_mail', 'sg_jobs', + $this->getApplicationMailMarkers((array) $applyData) + ); + $mailService->setIgnoreMailQueue(TRUE); + $mailService->setToAddresses($this->settings['applicationEmail']); + $mailService->sendEmail(); } catch (Exception $exception) { - + // possible errors, because of wrong mails (maybe log that somewhere? Does this makes sense?) } } @@ -155,31 +154,30 @@ class JoblistController extends ActionController { } /** + * Assign filter values + * * @param int $rootPageId */ private function assignFilterValues($rootPageId) { - // get all countries $countries = $this->companyRepository->getAllCountries($rootPageId); $this->view->assign('countries', $countries); - // get all cities $cities = $this->companyRepository->getAllCities($rootPageId); $this->view->assign('cities', $cities); - // get all cities $companies = $this->companyRepository->getAllCompanyNames($rootPageId); $this->view->assign('companies', $companies); - // get all areas $areas = $this->jobRepository->getAllAreas($rootPageId); $this->view->assign('areas', $areas); - // get all areas $functions = $this->jobRepository->getAllFunctions($rootPageId); $this->view->assign('functions', $functions); } /** + * Returns the application mail markers + * * @param array $applyData * @return array */ @@ -285,12 +283,17 @@ class JoblistController extends ActionController { } /** + * Writes the application files + * * @param JobApplication $data * @param string $filePath + * @param array $fileNames * @throws \RuntimeException */ private function writeApplicationFile(JobApplication $data, $filePath, $fileNames) { + $certificateNames = ''; $certificatesArr = []; + /** @var array[][] $fileNames */ if (isset($fileNames['certificates'])) { foreach ($fileNames['certificates'] as $certificateName) { $certificatesArr[] = $certificateName; @@ -321,8 +324,8 @@ class JoblistController extends ActionController { $file = fopen($filePath, 'wb+'); fputcsv($file, $dataToInsertArr, '|'); fclose($file); - } catch (\RuntimeException $ex) { - throw new \RuntimeException($ex->getMessage()); + } catch (\RuntimeException $exception) { + throw new \RuntimeException($exception->getMessage()); } } @@ -336,6 +339,10 @@ class JoblistController extends ActionController { * @return void */ private function registerUploadField(array &$data, $namespace, $fieldName, $targetDirectory = '1:/_temp_/') { + if (!\is_array($_FILES[$namespace])) { + return; + } + if (!isset($data['upload'])) { $data['upload'] = []; } @@ -381,9 +388,7 @@ class JoblistController extends ActionController { // Initializing: /** @var \TYPO3\CMS\Core\Utility\File\ExtendedFileUtility $fileProcessor */ - $fileProcessor = GeneralUtility::makeInstance( - ExtendedFileUtility::class - ); + $fileProcessor = GeneralUtility::makeInstance(ExtendedFileUtility::class); $fileProcessor->setActionPermissions(['addFile' => TRUE]); $fileProcessor->setFileExtensionPermissions($this->settings['allowedFileExtensions'], ''); @@ -394,9 +399,8 @@ class JoblistController extends ActionController { $result = $fileProcessor->processData(); $uploadedFiles = []; - // Do whatever you want with $result (array of File objects) - foreach ($result['upload'] as $files) { - /** @var \TYPO3\CMS\Core\Resource\File $file */ + foreach ((array) $result['upload'] as $files) { + /** @var array $files */ foreach ($files as $file) { $uploadedFiles[] = $file; } diff --git a/Classes/Domain/Model/JobApplication.php b/Classes/Domain/Model/JobApplication.php index 06681294b677a763bf89de6a178734dc8d71135d..3436fbe756112b45ebb59e0f6cfd5983e01c8121 100644 --- a/Classes/Domain/Model/JobApplication.php +++ b/Classes/Domain/Model/JobApplication.php @@ -365,7 +365,7 @@ class JobApplication extends AbstractEntity { /** * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\TYPO3\CMS\Extbase\Domain\Model\FileReference> $coverLetter */ - public function setCoverLetter(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $coverLetter) { + public function setCoverLetter(ObjectStorage $coverLetter) { $this->coverLetter = $coverLetter; } @@ -379,7 +379,7 @@ class JobApplication extends AbstractEntity { /** * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\TYPO3\CMS\Extbase\Domain\Model\FileReference> $cv */ - public function setCV(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $cv) { + public function setCV(ObjectStorage $cv) { $this->cv = $cv; } @@ -393,7 +393,7 @@ class JobApplication extends AbstractEntity { /** * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\TYPO3\CMS\Extbase\Domain\Model\FileReference> $certificates */ - public function setCertificates(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $certificates) { + public function setCertificates(ObjectStorage $certificates) { $this->certificates = $certificates; } diff --git a/Classes/Domain/Repository/CompanyRepository.php b/Classes/Domain/Repository/CompanyRepository.php index de273e795806224b13f8812d6c24fde6685eeb0d..e69c9b1969a66ebe0878e4fc139b6829ccfaa8ed 100644 --- a/Classes/Domain/Repository/CompanyRepository.php +++ b/Classes/Domain/Repository/CompanyRepository.php @@ -48,20 +48,20 @@ class CompanyRepository extends Repository { } /** + * Returns all countries filtered by page id + * * @param int $pageUid - * @param array $filters * @return mixed */ - public function getAllCountries($pageUid, array $filters = []) { + public function getAllCountries($pageUid) { /** @var DatabaseConnection $db */ $db = $GLOBALS['TYPO3_DB']; $result = $db->exec_SELECTquery( - 'country', 'tx_sgjobs_domain_model_company', 'pid = ' . $pageUid, 'country' + 'country', 'tx_sgjobs_domain_model_company', 'pid = ' . (int) $pageUid, 'country' )->fetch_all(); - $countryArray = []; - $countryArray[] = ''; - foreach($result as $country) { + $countryArray = ['']; + foreach ($result as $country) { $countryArray[$country[0]] = $country[0]; } @@ -69,6 +69,8 @@ class CompanyRepository extends Repository { } /** + * Returns all filtered cities + * * @param int $pageUid * @param array $filters * @return mixed @@ -80,14 +82,10 @@ class CompanyRepository extends Repository { if ($filters['country'] && $filters['country'] !== 0) { $where .= ' AND country = ' . $db->quoteStr($filters['country'], self::TABLENAME); } + $result = $db->exec_SELECTquery('city', self::TABLENAME, $where, 'city')->fetch_all(); - $result = $db->exec_SELECTquery( - 'city', self::TABLENAME, $where, 'city' - )->fetch_all(); - - $cityArray = []; - $cityArray[] = ''; - foreach($result as $city) { + $cityArray = ['']; + foreach ($result as $city) { $cityArray[$city[0]] = $city[0]; } @@ -95,6 +93,8 @@ class CompanyRepository extends Repository { } /** + * Returns all companies filtered by page id + * * @param int $pageUid * @return mixed */ @@ -102,12 +102,11 @@ class CompanyRepository extends Repository { /** @var DatabaseConnection $db */ $db = $GLOBALS['TYPO3_DB']; $result = $db->exec_SELECTquery( - 'name', 'tx_sgjobs_domain_model_company', 'pid = ' . $pageUid, 'name' + 'name', 'tx_sgjobs_domain_model_company', 'pid = ' . (int) $pageUid, 'name' )->fetch_all(); - $namesArray = []; - $namesArray[] = ''; - foreach($result as $name) { + $namesArray = ['']; + foreach ($result as $name) { $namesArray[$name[0]] = $name[0]; } diff --git a/Classes/Domain/Repository/JobRepository.php b/Classes/Domain/Repository/JobRepository.php index 2b90775e6de02b733c78f9d937f4050a7cec1133..7cf489980a2a2120ca10d832ef03fe63ae52dfb2 100644 --- a/Classes/Domain/Repository/JobRepository.php +++ b/Classes/Domain/Repository/JobRepository.php @@ -36,7 +36,6 @@ use TYPO3\CMS\Extbase\Persistence\Repository; * Job Repository */ class JobRepository extends Repository { - const TABLENAME = 'tx_sgjobs_domain_model_job'; /** @@ -73,14 +72,14 @@ class JobRepository extends Repository { $constraints = []; - if (isset($filters['locations']) && is_array($filters['locations']) && count($filters['locations'])) { + if (isset($filters['locations']) && \is_array($filters['locations']) && \count($filters['locations'])) { $locationConstraints = []; - foreach ($filters['locations'] as $location) { + foreach ((array) $filters['locations'] as $location) { if ((int) $location) { $locationConstraints[] = $query->contains('location', $location); } } - if (count($locationConstraints)) { + if (\count($locationConstraints)) { $constraints[] = $query->logicalOr($locationConstraints); } } @@ -94,9 +93,9 @@ class JobRepository extends Repository { $constraints[] = $query->logicalOr($searchConstraints); } - if (count($constraints) > 1) { + if (\count($constraints) > 1) { $query->matching($query->logicalAnd($constraints)); - } elseif (count($constraints)) { + } elseif (\count($constraints)) { $query->matching($constraints[0]); } @@ -104,6 +103,8 @@ class JobRepository extends Repository { } /** + * Returns all areas filtered by page id + * * @param int $pageUid * @return mixed */ @@ -114,8 +115,7 @@ class JobRepository extends Repository { 'area', 'tx_sgjobs_domain_model_job', 'pid = ' . $pageUid, 'area' )->fetch_all(); - $areaArray = []; - $areaArray[] = ''; + $areaArray = ['']; foreach ($result as $area) { $areaArray[$area[0]] = $area[0]; } @@ -124,6 +124,8 @@ class JobRepository extends Repository { } /** + * Returns all function filtered by page id + * * @param int $pageUid * @return mixed */ @@ -134,8 +136,7 @@ class JobRepository extends Repository { 'job_function', 'tx_sgjobs_domain_model_job', 'pid = ' . $pageUid, 'job_function' )->fetch_all(); - $functionArray = []; - $functionArray[] = ''; + $functionArray = ['']; foreach ($result as $function) { $functionArray[$function[0]] = $function[0]; } @@ -144,6 +145,8 @@ class JobRepository extends Repository { } /** + * Returns a job filtered by company and page id + * * @param int $pageUid * @param array $companyIds * @return QueryResultInterface @@ -163,19 +166,19 @@ class JobRepository extends Repository { $constraints = []; - if (isset($companyIds) && is_array($companyIds) && count($companyIds)) { + if (isset($companyIds) && \is_array($companyIds) && \count($companyIds)) { $companyConstraints = []; foreach ($companyIds as $companyId) { if ((int) $companyId) { $companyConstraints[] = $query->equals('company', $companyId); } } - if (count($companyConstraints)) { + if (\count($companyConstraints)) { $constraints[] = $query->logicalOr($companyConstraints); } } - if (count($constraints)) { + if (\count($constraints)) { $query->matching($query->logicalAnd($constraints)); } diff --git a/Classes/ViewHelpers/InlineLanguageLabelsViewHelper.php b/Classes/ViewHelpers/InlineLanguageLabelsViewHelper.php index 5eada181ff3f4d5d87f3b6d0bdbbafea6977d113..aad577ef035de2042efc10667a33db871066d3b8 100644 --- a/Classes/ViewHelpers/InlineLanguageLabelsViewHelper.php +++ b/Classes/ViewHelpers/InlineLanguageLabelsViewHelper.php @@ -1,5 +1,7 @@ <?php + namespace SGalinski\SgJobs\ViewHelpers; + /*************************************************************** * Copyright notice * @@ -25,6 +27,7 @@ namespace SGalinski\SgJobs\ViewHelpers; ***************************************************************/ use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Utility\LocalizationUtility; + /** * View helper to render language labels to * json array to be used in js applications. @@ -46,7 +49,7 @@ class InlineLanguageLabelsViewHelper extends AbstractViewHelper { public function render($labels = '', $htmlEscape = FALSE) { $extensionName = $this->controllerContext->getRequest()->getControllerExtensionName(); $labels = GeneralUtility::trimExplode(',', $labels, TRUE); - $languageArray = array(); + $languageArray = []; foreach ($labels as $key) { $value = LocalizationUtility::translate($key, $extensionName); $languageArray[$key] = ($htmlEscape ? htmlentities($value) : $value); diff --git a/Classes/ViewHelpers/SetViewHelper.php b/Classes/ViewHelpers/SetViewHelper.php index d1b5dfda92bdeacf36c38b7adb8a54ae65700c73..ce0db48296a588a0073ba646ac62b6f188661be5 100644 --- a/Classes/ViewHelpers/SetViewHelper.php +++ b/Classes/ViewHelpers/SetViewHelper.php @@ -1,4 +1,5 @@ <?php + namespace SGalinski\SgJobs\ViewHelpers; /*************************************************************** diff --git a/Resources/Private/Templates/Joblist/ApplyForm.html b/Resources/Private/Templates/Joblist/ApplyForm.html index c405fe931c08ad28997592232f24ecec0187b405..08cffe0650917a5f07db6c9a0489b5c78b16b0ea 100644 --- a/Resources/Private/Templates/Joblist/ApplyForm.html +++ b/Resources/Private/Templates/Joblist/ApplyForm.html @@ -2,19 +2,10 @@ <f:section name="main"> <f:form action="apply" controller="Joblist" method="post" objectName="applyData" object="{applyData}" enctype="multipart/form-data"> - <f:form.hidden value="{job.jobId}" property="jobId" /> - <label for="apply-jobTitle"><f:translate key="frontend.apply.title" /></label> - <f:form.textfield property="jobTitle" id="apply-jobTitle" data="{}" class="" value="{job.title}" /> - <f:form.validationResults for="applyData.jobTitle"> - <f:for each="{validationResults.errors}" as="error"> - <div class="sg-jobs-validation-error"> - {error.message} - </div> - </f:for> - </f:form.validationResults> - <br /> + <f:form.hidden value="{job.job_id}" property="jobId" /> + <label for="apply-gender"><f:translate key="frontend.apply.gender" /></label> - <f:form.select property="gender" id="apply-gender" data="{}" class="" options="{None: '', Male: 'Male', Female: 'Female'}" /> + <f:form.select property="gender" id="apply-gender" class="" options="{None: '', Male: 'Male', Female: 'Female'}" /> <f:form.validationResults for="applyData.gender"> <f:for each="{validationResults.errors}" as="error"> <div class="sg-jobs-validation-error"> @@ -24,7 +15,7 @@ </f:form.validationResults> <br /> <label for="apply-firstName"><f:translate key="frontend.apply.first_name" /></label> - <f:form.textfield property="firstName" id="apply-firstName" data="{}" class="" /> + <f:form.textfield property="firstName" id="apply-firstName" class="" /> <f:form.validationResults for="applyData.firstName"> <f:for each="{validationResults.errors}" as="error"> <div class="sg-jobs-validation-error"> @@ -34,7 +25,7 @@ </f:form.validationResults> <br /> <label for="apply-lastName"><f:translate key="frontend.apply.last_name" /></label> - <f:form.textfield property="lastName" id="apply-lastName" data="{}" class="" /> + <f:form.textfield property="lastName" id="apply-lastName" class="" /> <f:form.validationResults for="applyData.lastName"> <f:for each="{validationResults.errors}" as="error"> <div class="sg-jobs-validation-error"> @@ -44,7 +35,7 @@ </f:form.validationResults> <br /> <label for="apply-street"><f:translate key="frontend.apply.street" /></label> - <f:form.textfield property="street" id="apply-street" data="{}" class="" /> + <f:form.textfield property="street" id="apply-street" class="" /> <f:form.validationResults for="applyData.street"> <f:for each="{validationResults.errors}" as="error"> <div class="sg-jobs-validation-error"> @@ -54,7 +45,7 @@ </f:form.validationResults> <br /> <label for="apply-city"><f:translate key="frontend.apply.city" /></label> - <f:form.textfield property="city" id="apply-city" data="{}" class="" /> + <f:form.textfield property="city" id="apply-city" class="" /> <f:form.validationResults for="applyData.city"> <f:for each="{validationResults.errors}" as="error"> <div class="sg-jobs-validation-error"> @@ -64,7 +55,7 @@ </f:form.validationResults> <br /> <label for="apply-zip"><f:translate key="frontend.apply.zip" /></label> - <f:form.textfield property="zip" id="apply-zip" data="{}" class="" /> + <f:form.textfield property="zip" id="apply-zip" class="" /> <f:form.validationResults for="applyData.zip"> <f:for each="{validationResults.errors}" as="error"> <div class="sg-jobs-validation-error"> @@ -72,9 +63,9 @@ </div> </f:for> </f:form.validationResults> - <br/> + <br /> <label for="apply-country"><f:translate key="frontend.apply.country" /></label> - <f:form.textfield property="country" id="apply-country" data="{}" class="" /> + <f:form.textfield property="country" id="apply-country" class="" /> <f:form.validationResults for="applyData.country"> <f:for each="{validationResults.errors}" as="error"> <div class="sg-jobs-validation-error"> @@ -84,7 +75,7 @@ </f:form.validationResults> <br /> <label for="apply-nationality"><f:translate key="frontend.apply.nationality" /></label> - <f:form.textfield property="nationality" id="apply-nationality" data="{}" class="" /> + <f:form.textfield property="nationality" id="apply-nationality" class="" /> <f:form.validationResults for="applyData.nationality"> <f:for each="{validationResults.errors}" as="error"> <div class="sg-jobs-validation-error"> @@ -92,9 +83,9 @@ </div> </f:for> </f:form.validationResults> - <br/> + <br /> <label for="apply-education"><f:translate key="frontend.apply.education" /></label> - <f:form.textfield property="education" id="apply-education" data="{}" class="" /> + <f:form.textfield property="education" id="apply-education" class="" /> <f:form.validationResults for="applyData.education"> <f:for each="{validationResults.errors}" as="error"> <div class="sg-jobs-validation-error"> @@ -102,9 +93,9 @@ </div> </f:for> </f:form.validationResults> - <br/> + <br /> <label for="apply-birthDate"><f:translate key="frontend.apply.birthDate" /></label> - <f:form.textfield property="birthDate" id="apply-birthDate" data="{}" class="" /> + <f:form.textfield property="birthDate" id="apply-birthDate" class="" /> <f:form.validationResults for="applyData.birthDate"> <f:for each="{validationResults.errors}" as="error"> <div class="sg-jobs-validation-error"> @@ -114,7 +105,7 @@ </f:form.validationResults> <br /> <label for="apply-phone"><f:translate key="frontend.apply.phone" /></label> - <f:form.textfield property="phone" id="apply-phone" data="{}" class="" /> + <f:form.textfield property="phone" id="apply-phone" class="" /> <f:form.validationResults for="applyData.phone"> <f:for each="{validationResults.errors}" as="error"> <div class="sg-jobs-validation-error"> @@ -124,7 +115,7 @@ </f:form.validationResults> <br /> <label for="apply-mobile"><f:translate key="frontend.apply.mobile" /></label> - <f:form.textfield property="mobile" id="apply-mobile" data="{}" class="" /> + <f:form.textfield property="mobile" id="apply-mobile" class="" /> <f:form.validationResults for="applyData.mobile"> <f:for each="{validationResults.errors}" as="error"> <div class="sg-jobs-validation-error"> @@ -134,7 +125,7 @@ </f:form.validationResults> <br /> <label for="apply-email"><f:translate key="frontend.apply.email" /></label> - <f:form.textfield property="email" id="apply-email" data="{}" class="" /> + <f:form.textfield property="email" id="apply-email" class="" /> <f:form.validationResults for="applyData.email"> <f:for each="{validationResults.errors}" as="error"> <div class="sg-jobs-validation-error"> @@ -185,7 +176,7 @@ </f:for> <br /> <label for="apply-message"><f:translate key="frontend.apply.message" /></label> - <f:form.textarea property="message" id="apply-message" data="{}" class="" /> + <f:form.textarea property="message" id="apply-message" class="" /> <br /> <f:form.submit value="{f:translate(key:'frontend.applyNow')}" /> </f:form>