Skip to content
Snippets Groups Projects
Commit 3b8dc9ab authored by Stefan Galinski's avatar Stefan Galinski :video_game:
Browse files

[FEATURE] File uploads foo finally works... Thanks for nothing TYPO3

parent ad297cdf
No related branches found
No related tags found
No related merge requests found
...@@ -26,6 +26,7 @@ namespace SGalinski\SgJobs\Controller; ...@@ -26,6 +26,7 @@ namespace SGalinski\SgJobs\Controller;
* This copyright notice MUST APPEAR in all copies of the script! * This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/ ***************************************************************/
use SGalinski\SgJobs\Domain\Model\Job;
use SGalinski\SgJobs\Domain\Model\JobApplication; use SGalinski\SgJobs\Domain\Model\JobApplication;
use SGalinski\SgJobs\Property\TypeConverter\UploadedFileReferenceConverter; use SGalinski\SgJobs\Property\TypeConverter\UploadedFileReferenceConverter;
use SGalinski\SgJobs\Service\FrontendFilterService; use SGalinski\SgJobs\Service\FrontendFilterService;
...@@ -98,12 +99,11 @@ class JoblistController extends ActionController { ...@@ -98,12 +99,11 @@ class JoblistController extends ActionController {
/** /**
* Renders the application form with an optional job * Renders the application form with an optional job
* *
* @param JobApplication $applyData * @param JobApplication|NULL $applyData
* @param string $error * @param string|NULL $error
* @param string $uid * @param string|NULL $folderName
* @throws \InvalidArgumentException
*/ */
public function applyFormAction(JobApplication $applyData = NULL, $error = NULL, $uid = NULL) { public function applyFormAction(JobApplication $applyData = NULL, $error = NULL, $folderName = NULL) {
// $uploadedFiles = $this->getExistingApplicationFiles($uniqueFolderName); // $uploadedFiles = $this->getExistingApplicationFiles($uniqueFolderName);
// if (\count($uploadedFiles['coverLetter']) <= 0 && \count($uploadedFiles['cv']) <= 0 // if (\count($uploadedFiles['coverLetter']) <= 0 && \count($uploadedFiles['cv']) <= 0
// && \count($uploadedFiles['certificate']) <= 0 // && \count($uploadedFiles['certificate']) <= 0
...@@ -117,8 +117,15 @@ class JoblistController extends ActionController { ...@@ -117,8 +117,15 @@ class JoblistController extends ActionController {
$this->view->assign('internalError', $error); $this->view->assign('internalError', $error);
} }
if ($folderName === NULL) {
$folderName = md5(uniqid('sgjobs-', TRUE));
}
$this->view->assign('folderName', $folderName);
$jobId = $this->request->getArguments()['uid']; $jobId = $this->request->getArguments()['uid'];
$jobData = NULL;
if (!empty($jobId)) { if (!empty($jobId)) {
/** @var Job $jobData */
$jobData = $this->jobRepository->findByUid($jobId); $jobData = $this->jobRepository->findByUid($jobId);
$this->view->assign('job', $jobData); $this->view->assign('job', $jobData);
} }
...@@ -133,6 +140,15 @@ class JoblistController extends ActionController { ...@@ -133,6 +140,15 @@ class JoblistController extends ActionController {
$this->view->assign('allowedMimeTypes', $allowedMimeTypes); $this->view->assign('allowedMimeTypes', $allowedMimeTypes);
$allowedFileExtensions = $this->settings['allowedFileExtensions']; $allowedFileExtensions = $this->settings['allowedFileExtensions'];
$this->view->assign('allowedFileExtensions', $allowedFileExtensions); $this->view->assign('allowedFileExtensions', $allowedFileExtensions);
if ($applyData === NULL) {
/** @noinspection CallableParameterUseCaseInTypeContextInspection */
$applyData = $this->objectManager->get(JobApplication::class);
if ($jobData) {
$applyData->setJobId($jobData->getJobId());
}
}
$this->view->assign('applyData', $applyData); $this->view->assign('applyData', $applyData);
} }
...@@ -140,25 +156,10 @@ class JoblistController extends ActionController { ...@@ -140,25 +156,10 @@ class JoblistController extends ActionController {
* Pre-apply action setup, configures model-property mapping and handles file upload * Pre-apply action setup, configures model-property mapping and handles file upload
* *
* @return void * @return void
* @throws \TYPO3\CMS\Core\Resource\Exception\InsufficientFolderWritePermissionsException
* @throws \TYPO3\CMS\Core\Resource\Exception\InsufficientFolderAccessPermissionsException
* @throws \TYPO3\CMS\Core\Resource\Exception\ExistingTargetFolderException
* @throws \InvalidArgumentException
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\InvalidArgumentNameException
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\NoSuchArgumentException * @throws \TYPO3\CMS\Extbase\Mvc\Exception\NoSuchArgumentException
* @throws \Exception
*/ */
protected function initializeApplyAction() { protected function initializeApplyAction() {
if ($this->request->hasArgument('folderName')) { $uniqueFolderName = $this->request->getArgument('folderName');
$uniqueFolderName = $this->request->getArgument('folderName');
} else {
$uniqueFolderName = uniqid('sgjobs-', TRUE);
$resourceFactory = $this->objectManager->get(ResourceFactory::class);
$storage = $resourceFactory->getStorageObject(1);
$storage->createFolder('/JobApplication/' . $uniqueFolderName);
}
$this->request->setArgument('folderName', $uniqueFolderName);
$propertyMappingConfiguration = $this->arguments->getArgument('applyData')->getPropertyMappingConfiguration(); $propertyMappingConfiguration = $this->arguments->getArgument('applyData')->getPropertyMappingConfiguration();
$typeConverter1 = $this->objectManager->get(UploadedFileReferenceConverter::class); $typeConverter1 = $this->objectManager->get(UploadedFileReferenceConverter::class);
...@@ -210,6 +211,8 @@ class JoblistController extends ActionController { ...@@ -210,6 +211,8 @@ class JoblistController extends ActionController {
public function applyAction(JobApplication $applyData) { public function applyAction(JobApplication $applyData) {
try { try {
$applyData->setPid($GLOBALS['TSFE']->id); $applyData->setPid($GLOBALS['TSFE']->id);
// @TODO repository is missing / OR UPDATE!!!
// $this->jobApplicationRepository->add($applyData);
$folderName = $this->request->getArgument('folderName'); $folderName = $this->request->getArgument('folderName');
$this->submitApplicationFiles($applyData, $folderName); $this->submitApplicationFiles($applyData, $folderName);
...@@ -232,7 +235,7 @@ class JoblistController extends ActionController { ...@@ -232,7 +235,7 @@ class JoblistController extends ActionController {
$this->redirectToUri($uri); $this->redirectToUri($uri);
} catch (\Exception $exception) { } catch (\Exception $exception) {
$this->forward('applyForm', NULL, NULL, ['error' => $exception->getMessage()]); $this->forward('applyForm', NULL, NULL, ['applyData' => $applyData, 'error' => $exception->getMessage()]);
} }
} }
......
...@@ -29,6 +29,7 @@ namespace SGalinski\SgJobs\Property\TypeConverter; ...@@ -29,6 +29,7 @@ namespace SGalinski\SgJobs\Property\TypeConverter;
use TYPO3\CMS\Core\Resource\DuplicationBehavior; use TYPO3\CMS\Core\Resource\DuplicationBehavior;
use TYPO3\CMS\Core\Resource\File as FalFile; use TYPO3\CMS\Core\Resource\File as FalFile;
use TYPO3\CMS\Core\Resource\FileReference as FalFileReference; use TYPO3\CMS\Core\Resource\FileReference as FalFileReference;
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\PathUtility; use TYPO3\CMS\Core\Utility\PathUtility;
use TYPO3\CMS\Extbase\Domain\Model\FileReference; use TYPO3\CMS\Extbase\Domain\Model\FileReference;
...@@ -276,40 +277,22 @@ class UploadedFileReferenceConverter implements TypeConverterInterface { ...@@ -276,40 +277,22 @@ class UploadedFileReferenceConverter implements TypeConverterInterface {
* @param array $convertedChildProperties * @param array $convertedChildProperties
* @param PropertyMappingConfigurationInterface $configuration * @param PropertyMappingConfigurationInterface $configuration
* @return null|\TYPO3\CMS\Core\Resource\FileInterface|FileReference|Error * @return null|\TYPO3\CMS\Core\Resource\FileInterface|FileReference|Error
* @throws \TYPO3\CMS\Extbase\Security\Exception\InvalidHashException
* @throws \TYPO3\CMS\Extbase\Security\Exception\InvalidArgumentForHashGenerationException
* @throws \TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException
* @api * @api
* @throws \TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException
*/ */
public function convertFrom( public function convertFrom(
$source, $targetType, array $convertedChildProperties = [], $source, $targetType, array $convertedChildProperties = [],
PropertyMappingConfigurationInterface $configuration = NULL PropertyMappingConfigurationInterface $configuration = NULL
) { ) {
if (!isset($source['error']) || $source['error'] === \UPLOAD_ERR_NO_FILE) { if ($source['name'] === '' && \is_array($source['submittedFile'])) {
if (isset($source['submittedFile']['resourcePointer'])) { $source = $source['submittedFile'];
try { $source['wasUploaded'] = TRUE;
$resourcePointer = $this->hashService->validateAndStripHmac( }
$source['submittedFile']['resourcePointer']
); if ($source['error'] === \UPLOAD_ERR_NO_FILE) {
if (strpos($resourcePointer, 'file:') === 0) {
$fileUid = substr($resourcePointer, 5);
return $this->createFileReferenceFromFalFileObject(
$this->resourceFactory->getFileObject($fileUid)
);
}
return $this->createFileReferenceFromFalFileReferenceObject(
$this->resourceFactory->getFileReferenceObject($resourcePointer), $resourcePointer
);
} catch (\InvalidArgumentException $e) {
// Nothing to do. No file is uploaded and resource pointer is invalid. Discard!
}
}
return NULL; return NULL;
} }
if ($source['error'] !== \UPLOAD_ERR_OK) { if ($source['error'] !== '' && (int) $source['error'] !== \UPLOAD_ERR_OK) {
switch ($source['error']) { switch ($source['error']) {
case \UPLOAD_ERR_INI_SIZE: case \UPLOAD_ERR_INI_SIZE:
case \UPLOAD_ERR_FORM_SIZE: case \UPLOAD_ERR_FORM_SIZE:
...@@ -346,49 +329,70 @@ class UploadedFileReferenceConverter implements TypeConverterInterface { ...@@ -346,49 +329,70 @@ class UploadedFileReferenceConverter implements TypeConverterInterface {
* *
* @param array $uploadInfo * @param array $uploadInfo
* @return \SGalinski\SgJobs\Domain\Model\FileReference * @return \SGalinski\SgJobs\Domain\Model\FileReference
* @throws \InvalidArgumentException
* @throws TypeConverterException * @throws TypeConverterException
* @throws \TYPO3\CMS\Extbase\Security\Exception\InvalidArgumentForHashGenerationException * @throws \Exception
* @throws \TYPO3\CMS\Extbase\Security\Exception\InvalidHashException * @throws \TYPO3\CMS\Core\Resource\Exception\ExistingTargetFolderException
* @throws \TYPO3\CMS\Core\Resource\Exception\InsufficientFolderAccessPermissionsException
* @throws \TYPO3\CMS\Core\Resource\Exception\InsufficientFolderWritePermissionsException
*/ */
protected function importUploadedResource(array $uploadInfo): FileReference { protected function importUploadedResource(array $uploadInfo): FileReference {
if (!GeneralUtility::verifyFilenameAgainstDenyPattern($uploadInfo['name'])) {
throw new TypeConverterException('Uploading files with PHP file extensions is not allowed!', 1399312430);
}
$allowedFileExtensions = $this->getAllowedFileExtensions();
$filePathInfo = PathUtility::pathinfo($uploadInfo['name']); $filePathInfo = PathUtility::pathinfo($uploadInfo['name']);
if ($allowedFileExtensions !== NULL) { $fileName = $this->getTargetUploadFileName();
if (!GeneralUtility::inList($allowedFileExtensions, strtolower($filePathInfo['extension']))) { $finalFileName = $fileName . '.' . strtolower($filePathInfo['extension']);
throw new TypeConverterException('File extension is not allowed!', 1399312430); if (!$uploadInfo['wasUploaded']) {
if (!GeneralUtility::verifyFilenameAgainstDenyPattern($uploadInfo['name'])) {
throw new TypeConverterException(
'Uploading files with PHP file extensions is not allowed!', 1399312430
);
} }
}
$uploadFolderId = $this->getUploadFolder(); $allowedFileExtensions = $this->getAllowedFileExtensions();
$conflictMode = $this->getUploadConflictMode(); if ($allowedFileExtensions !== NULL) {
$uploadFolder = $this->resourceFactory->retrieveFileOrFolderObject($uploadFolderId); if (!GeneralUtility::inList($allowedFileExtensions, strtolower($filePathInfo['extension']))) {
$uploadedFile = $uploadFolder->addUploadedFile($uploadInfo, $conflictMode); throw new TypeConverterException('File extension is not allowed!', 1399312430);
}
}
$fileName = $this->getTargetUploadFileName(); $uploadFolderId = $this->getUploadFolder();
if ($fileName !== '') { $resourceFactory = $this->objectManager->get(ResourceFactory::class);
$uploadedFile->rename($fileName . '.' . $filePathInfo['extension']); $storage = $resourceFactory->getStorageObject(1);
} $folderWithoutStorage = preg_replace('/^1:/', '', $uploadFolderId);
if (!$storage->hasFolder($folderWithoutStorage)) {
$storage->createFolder($folderWithoutStorage);
}
$uploadFolder = $this->resourceFactory->retrieveFileOrFolderObject($uploadFolderId);
$uploadedFile = $uploadFolder->addUploadedFile($uploadInfo, $this->getUploadConflictMode());
if ($fileName !== '') {
$uploadedFile->rename($finalFileName, $this->getUploadConflictMode());
}
$resourcePointer = isset($uploadInfo['submittedFile']['resourcePointer']) && } else {
strpos($uploadInfo['submittedFile']['resourcePointer'], 'file:') === FALSE $uploadFolderId = $this->getUploadFolder();
? $this->hashService->validateAndStripHmac($uploadInfo['submittedFile']['resourcePointer']) // Security protection to not allow manipulations outside of this specific folder
: NULL; if (strpos($uploadFolderId, '/JobApplication/') !== FALSE) {
try {
$uploadedFile = $this->resourceFactory->retrieveFileOrFolderObject(
$uploadFolderId . '/' . $finalFileName
);
} catch (\Exception $exception) {
// nope
}
$fileReferenceModel = $this->createFileReferenceFromFalFileObject($uploadedFile, $resourcePointer); } else {
throw new \Exception('Not allowed!');
}
}
return $fileReferenceModel; return $this->createFileReferenceFromFalFileObject($uploadedFile);
} }
/** /**
* @param FalFile $file * @param FalFile $file
* @param int $resourcePointer
* @return \SGalinski\SgJobs\Domain\Model\FileReference * @return \SGalinski\SgJobs\Domain\Model\FileReference
*/ */
protected function createFileReferenceFromFalFileObject(FalFile $file, $resourcePointer = NULL protected function createFileReferenceFromFalFileObject(FalFile $file
): \SGalinski\SgJobs\Domain\Model\FileReference { ): \SGalinski\SgJobs\Domain\Model\FileReference {
$fileReference = $this->resourceFactory->createFileReferenceObject( $fileReference = $this->resourceFactory->createFileReferenceObject(
[ [
...@@ -398,28 +402,18 @@ class UploadedFileReferenceConverter implements TypeConverterInterface { ...@@ -398,28 +402,18 @@ class UploadedFileReferenceConverter implements TypeConverterInterface {
'crop' => NULL, 'crop' => NULL,
] ]
); );
return $this->createFileReferenceFromFalFileReferenceObject($fileReference, $resourcePointer); return $this->createFileReferenceFromFalFileReferenceObject($fileReference);
} }
/** /**
* @param FalFileReference $falFileReference * @param FalFileReference $falFileReference
* @param int $resourcePointer
* @return \SGalinski\SgJobs\Domain\Model\FileReference * @return \SGalinski\SgJobs\Domain\Model\FileReference
*/ */
protected function createFileReferenceFromFalFileReferenceObject( protected function createFileReferenceFromFalFileReferenceObject(FalFileReference $falFileReference
FalFileReference $falFileReference, $resourcePointer = NULL
): \SGalinski\SgJobs\Domain\Model\FileReference { ): \SGalinski\SgJobs\Domain\Model\FileReference {
if ($resourcePointer === NULL) { /** @var \SGalinski\SgJobs\Domain\Model\FileReference $fileReference */
/** @var \SGalinski\SgJobs\Domain\Model\FileReference $fileReference */ $fileReference = $this->objectManager->get(FileReference::class);
$fileReference = $this->objectManager->get(FileReference::class);
} else {
$fileReference = $this->persistenceManager->getObjectByIdentifier(
$resourcePointer, FileReference::class, FALSE
);
}
$fileReference->setOriginalResource($falFileReference); $fileReference->setOriginalResource($falFileReference);
return $fileReference; return $fileReference;
} }
} }
...@@ -28,7 +28,7 @@ namespace SGalinski\SgJobs\ViewHelpers\Form; ...@@ -28,7 +28,7 @@ namespace SGalinski\SgJobs\ViewHelpers\Form;
* This copyright notice MUST APPEAR in all copies of the script! * This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/ ***************************************************************/
use TYPO3\CMS\Extbase\Domain\Model\FileReference; /** @noinspection LongInheritanceChainInspection */
/** /**
* Class UploadViewHelper * Class UploadViewHelper
...@@ -46,6 +46,7 @@ class UploadViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\Form\UploadViewHelpe ...@@ -46,6 +46,7 @@ class UploadViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\Form\UploadViewHelpe
*/ */
protected $propertyMapper; protected $propertyMapper;
/** @noinspection PhpDocMissingThrowsInspection */
/** /**
* Render the upload field including possible resource pointer * Render the upload field including possible resource pointer
* *
...@@ -53,32 +54,19 @@ class UploadViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\Form\UploadViewHelpe ...@@ -53,32 +54,19 @@ class UploadViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\Form\UploadViewHelpe
* @api * @api
* @param string $resourceName * @param string $resourceName
* @throws \InvalidArgumentException * @throws \InvalidArgumentException
* @throws \TYPO3\CMS\Extbase\Property\Exception
*/ */
public function render($resourceName = 'resource'): string { public function render($resourceName = 'resource'): string {
$output = ''; $output = '';
$resource = $this->getUploadedResource(); $resource = $this->getUploadedResource();
if ($resource !== NULL) { if (\is_array($resource) && $resource['name'] === '' && isset($resource['submittedFile'])) {
$resourcePointerIdAttribute = ''; $resource = $resource['submittedFile'];
if ($this->hasArgument('id')) { }
$resourcePointerIdAttribute = ' id="' . htmlspecialchars($this->arguments['id']) . '-file-reference"';
}
$resourcePointerValue = $resource->getUid();
if ($resourcePointerValue === NULL) {
// Newly created file reference which is not persisted yet.
// Use the file UID instead, but prefix it with "file:" to communicate this to the type converter
$resourcePointerValue = 'file:' . $resource->getOriginalResource()->getOriginalFile()->getUid();
}
$output .= '<input type="hidden" name="' . $this->getName() .
'[submittedFile][resourcePointer]" value="' .
htmlspecialchars(
$this->hashService->appendHmac((string) $resourcePointerValue)
) . '"' . $resourcePointerIdAttribute . ' />';
if ($resource !== NULL) {
/** @noinspection PhpUnhandledExceptionInspection */
$this->templateVariableContainer->add($resourceName, $resource); $this->templateVariableContainer->add($resourceName, $resource);
$output .= $this->renderChildren(); $output .= $this->renderChildren();
$this->templateVariableContainer->remove('resource');
} }
$output .= parent::render(); $output .= parent::render();
...@@ -89,19 +77,14 @@ class UploadViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\Form\UploadViewHelpe ...@@ -89,19 +77,14 @@ class UploadViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\Form\UploadViewHelpe
* Return a previously uploaded resource. * Return a previously uploaded resource.
* Return NULL if errors occurred during property mapping for this property. * Return NULL if errors occurred during property mapping for this property.
* *
* @return FileReference|NULL * @return array
* @throws \TYPO3\CMS\Extbase\Property\Exception
*/ */
protected function getUploadedResource() { protected function getUploadedResource() {
if ($this->getMappingResultsForProperty()->hasErrors()) { if ($this->getMappingResultsForProperty()->hasErrors()) {
return NULL; return NULL;
} }
$resource = $this->getValueAttribute(); $this->respectSubmittedDataValue = TRUE;
if ($resource instanceof FileReference) { return $this->getValueAttribute();
return $resource;
}
return $this->propertyMapper->convert($resource, FileReference::class);
} }
} }
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
<f:if condition="{job}"> <f:if condition="{job}">
<p> <p>
<f:form.hidden value="{job.jobId}" property="jobId" /> <f:form.hidden value="{applyData.jobId}" property="jobId" />
<label for="apply-title"><f:translate key="frontend.apply.title" /></label> <label for="apply-title"><f:translate key="frontend.apply.title" /></label>
<span id="apply-title">{job.title}</span> <span id="apply-title">{job.title}</span>
</p> </p>
...@@ -106,7 +106,7 @@ ...@@ -106,7 +106,7 @@
<p> <p>
<label for="apply-nationality"><f:translate key="frontend.apply.nationality" /></label> <label for="apply-nationality"><f:translate key="frontend.apply.nationality" /></label>
<f:form.select property="nationality" id="apply-nationality" options="{countries}" optionLabelField="{f:if(condition: '{sysLanguageUid} == 0', then: 'shortNameDe', else: 'shortNameEn')}" optionValueField="{f:if(condition: '{sysLanguageUid} == 0', then: 'shortNameDe', else: 'shortNameEn')}"/> <f:form.select property="nationality" id="apply-nationality" options="{countries}" optionLabelField="{f:if(condition: '{sysLanguageUid} == 0', then: 'shortNameDe', else: 'shortNameEn')}" optionValueField="{f:if(condition: '{sysLanguageUid} == 0', then: 'shortNameDe', else: 'shortNameEn')}" />
<f:form.validationResults for="applyData.nationality"> <f:form.validationResults for="applyData.nationality">
<f:for each="{validationResults.errors}" as="error"> <f:for each="{validationResults.errors}" as="error">
<div class="sg-jobs-validation-error"> <div class="sg-jobs-validation-error">
...@@ -176,51 +176,65 @@ ...@@ -176,51 +176,65 @@
</f:form.validationResults> </f:form.validationResults>
</p> </p>
<p> <div>
<label for="apply-cover-letter"> <label for="apply-cover-letter">
<f:translate key="frontend.apply.cover_letter" /> <f:translate key="frontend.apply.cover_letter" />
(<f:translate key="frontend.apply.allowed_file_extensions" /> {allowedFileExtensions}) (<f:translate key="frontend.apply.allowed_file_extensions" /> {allowedFileExtensions})
</label> </label>
<h:form.upload property="coverLetter" resourceName="coverLetter" id="apply-cover-letter" additionalAttributes="{accept: '{allowedMimeTypes}'}" /> <h:form.upload property="coverLetter" resourceName="coverLetter" id="apply-cover-letter" additionalAttributes="{accept: '{allowedMimeTypes}'}" />
<f:if condition="{coverLetter}"> <f:if condition="{coverLetter.name}">
<div class="sg-jobs-uploaded-file"> <p>
<f:image image="{coverLetter}" alt="" width="50"/> Aktuell: {coverLetter.name}
</div>
<f:comment><!-- Important, due to a fluid cache issue with the fluid syntax--></f:comment>
<input type="hidden" name="tx_sgjobs_jobapplication[applyData][coverLetter][submittedFile][name]" value="{coverLetter.name}" />
<input type="hidden" name="tx_sgjobs_jobapplication[applyData][coverLetter][submittedFile][type]" value="{coverLetter.type}" />
<input type="hidden" name="tx_sgjobs_jobapplication[applyData][coverLetter][submittedFile][tmp_name]" value="{coverLetter.tmp_name}" />
<input type="hidden" name="tx_sgjobs_jobapplication[applyData][coverLetter][submittedFile][error]" value="0" />
<input type="hidden" name="tx_sgjobs_jobapplication[applyData][coverLetter][submittedFile][size]" value="{coverLetter.size}" />
</p>
</f:if> </f:if>
<f:form.validationResults for="applyData.coverLetter"> <f:form.validationResults for="applyData.coverLetter">
<f:for each="{validationResults.errors}" as="error"> <f:for each="{validationResults.errors}" as="error">
<div class="sg-jobs-validation-error"> <p class="sg-jobs-validation-error">
{error.message} {error.message}
</div> </p>
</f:for> </f:for>
</f:form.validationResults> </f:form.validationResults>
</p> </div>
<p> <div>
<label for="apply-cv"> <label for="apply-cv">
<f:translate key="frontend.apply.cv" /> <f:translate key="frontend.apply.cv" />
(<f:translate key="frontend.apply.allowed_file_extensions" /> {allowedFileExtensions}) (<f:translate key="frontend.apply.allowed_file_extensions" /> {allowedFileExtensions})
</label> </label>
<h:form.upload property="cv" resourceName="cv" id="apply-cv" additionalAttributes="{accept: '{allowedMimeTypes}'}" /> <h:form.upload property="cv" resourceName="cv" id="apply-cv" additionalAttributes="{accept: '{allowedMimeTypes}'}" />
<f:if condition="{cv}"> <f:if condition="{cv.name}">
<div class="sg-jobs-uploaded-file"> <p>
<f:image image="{cv}" alt="" width="50"/> Aktuell: {cv.name}
</div>
<f:comment><!-- Important, due to a fluid cache issue with the fluid syntax--></f:comment>
<input type="hidden" name="tx_sgjobs_jobapplication[applyData][cv][submittedFile][name]" value="{cv.name}" />
<input type="hidden" name="tx_sgjobs_jobapplication[applyData][cv][submittedFile][type]" value="{cv.type}" />
<input type="hidden" name="tx_sgjobs_jobapplication[applyData][cv][submittedFile][tmp_name]" value="{cv.tmp_name}" />
<input type="hidden" name="tx_sgjobs_jobapplication[applyData][cv][submittedFile][error]" value="0" />
<input type="hidden" name="tx_sgjobs_jobapplication[applyData][cv][submittedFile][size]" value="{cv.size}" />
</p>
</f:if> </f:if>
<f:form.validationResults for="applyData.cv"> <f:form.validationResults for="applyData.cv">
<f:for each="{validationResults.errors}" as="error"> <f:for each="{validationResults.errors}" as="error">
<div class="sg-jobs-validation-error"> <p class="sg-jobs-validation-error">
{error.message} {error.message}
</div> </p>
</f:for> </f:for>
</f:form.validationResults> </f:form.validationResults>
</p> </div>
<p> <div>
<label for="apply-certificate"> <label for="apply-certificate">
<f:translate key="frontend.apply.certificate" /> <f:translate key="frontend.apply.certificate" />
(<f:translate key="frontend.apply.allowed_file_extensions" /> {allowedFileExtensions}) (<f:translate key="frontend.apply.allowed_file_extensions" /> {allowedFileExtensions})
...@@ -228,19 +242,26 @@ ...@@ -228,19 +242,26 @@
<h:form.upload property="certificate" resourceName="certificate" id="apply-certificate" additionalAttributes="{accept: '{allowedMimeTypes}'}" /> <h:form.upload property="certificate" resourceName="certificate" id="apply-certificate" additionalAttributes="{accept: '{allowedMimeTypes}'}" />
<f:if condition="{certificate.name}"> <f:if condition="{certificate.name}">
<div class="sg-jobs-uploaded-file"> <p>
<f:image image="{certificate}" alt="" width="50"/> Aktuell: {certificate.name}
</div>
<f:comment><!-- Important, due to a fluid cache issue with the fluid syntax--></f:comment>
<input type="hidden" name="tx_sgjobs_jobapplication[applyData][certificate][submittedFile][name]" value="{certificate.name}" />
<input type="hidden" name="tx_sgjobs_jobapplication[applyData][certificate][submittedFile][type]" value="{certificate.type}" />
<input type="hidden" name="tx_sgjobs_jobapplication[applyData][certificate][submittedFile][tmp_name]" value="{certificate.tmp_name}" />
<input type="hidden" name="tx_sgjobs_jobapplication[applyData][certificate][submittedFile][error]" value="0" />
<input type="hidden" name="tx_sgjobs_jobapplication[applyData][certificate][submittedFile][size]" value="{certificate.size}" />
</p>
</f:if> </f:if>
<f:form.validationResults for="applyData.certificate"> <f:form.validationResults for="applyData.certificate">
<f:for each="{validationResults.errors}" as="error"> <f:for each="{validationResults.errors}" as="error">
<div class="sg-jobs-validation-error"> <p class="sg-jobs-validation-error">
{error.message} {error.message}
</div> </p>
</f:for> </f:for>
</f:form.validationResults> </f:form.validationResults>
</p> </div>
<p> <p>
<label for="apply-message"><f:translate key="frontend.apply.message" /></label> <label for="apply-message"><f:translate key="frontend.apply.message" /></label>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment