Skip to content
Snippets Groups Projects
Commit 0bbda2a4 authored by Matthias Adrowski's avatar Matthias Adrowski
Browse files

[TASK] Make coverletter work using ajax+dropzone

parent 5bf94c79
No related branches found
No related tags found
1 merge request!37Feature new uploadhandling
<?php
namespace SGalinski\SgJobs\Controller\Ajax;
use SGalinski\SgAjax\Controller\Ajax\AbstractAjaxController;
use SGalinski\SgComments\Domain\Service\FileAndFolderService;
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/***************************************************************
* 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!
***************************************************************/
/**
* Uploads for the applicationForm
*/
class UploadController extends AbstractAjaxController {
public const TYPO3_TMP_FOLDER = 'typo3temp/';
/**
* Returns the calculated max file size.
*
* @return int
*/
protected function getMaxFileSize(): int {
$maxFileSize = (int) $this->settings['fileUpload']['maxfileSize'];
if ($maxFileSize <= 0 && isset($GLOBALS['TYPO3_CONF_VARS']['BE']['maxFileSize'])) {
$maxFileSize = (int) $GLOBALS['TYPO3_CONF_VARS']['BE']['maxFileSize']; // Example value: 800000
return ($maxFileSize * 1000);
}
return $maxFileSize * 1000 * 1000;
}
/**
* Uploads a new image.
*
* @return void
* @throws \TYPO3\CMS\Core\Resource\Exception\ExistingTargetFileNameException
* @throws \InvalidArgumentException
*/
public function uploadCoverletterAction() {
$success = FALSE;
$filePath = '';
if (\count($_FILES) > 0) {
$firstFile = current($_FILES);
$pathInfo = pathinfo($firstFile['name']);
$fileAndFolderService = $this->objectManager->get(FileAndFolderService::class);
$storage = $fileAndFolderService->getStorage();
$fileName = $storage->sanitizeFileName(
strtolower(
str_replace(' ', '_', trim($pathInfo['filename'] . '.' . strtolower($pathInfo['extension'])))
)
);
$filePath = Environment::getPublicPath() . '/' . self::TYPO3_TMP_FOLDER . $fileName;
$tempFilePath = $firstFile['tmp_name'];
$success = GeneralUtility::upload_copy_move($tempFilePath, $filePath);
}
$this->returnData(
[
'success' => $success,
'path' => $filePath,
]
);
}
}
......@@ -392,6 +392,9 @@ class JoblistController extends ActionController {
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
*/
protected function initializeApplyAction() {
$arguments = ($this->request->getArguments());
$parsedBody = $this->request->getParsedBody();
$uniqueFolderName = $arguments['folderName'] ?? '';
try {
$uniqueFolderName = $this->request->getArgument('folderName');
} catch (NoSuchArgumentException $exception) {
......@@ -547,6 +550,20 @@ class JoblistController extends ActionController {
}
}
/**
* Debug Action for us to check for all Data from form
*
*
*/
public function emptyAction(): ?\Psr\Http\Message\ResponseInterface {
$request = $this->request;
$arguments = $this->request->getArguments();
$parsedBody = $this->request->getParsedBody();
$uploadedFiles = $this->request->getUploadedFiles();
$basicBody = $this->request->getBody()->getContents();
return $this->htmlResponse('');
}
/**
* Assign filter values
*
......
......@@ -374,7 +374,7 @@ class UploadedFileReferenceConverter implements TypeConverterInterface {
$filePathInfo = PathUtility::pathinfo($uploadInfo['name']);
$fileName = $this->getTargetUploadFileName();
$finalFileName = $fileName . '.' . strtolower($filePathInfo['extension']);
if (!$uploadInfo['wasUploaded']) {
if (!array_key_exists('wasUploaded', $uploadInfo) || !$uploadInfo['wasUploaded']) {
if (!\TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Resource\Security\FileNameValidator::class)->isValid($uploadInfo['name'])) {
throw new TypeConverterException(
'Uploading files with PHP file extensions is not allowed!',
......
......@@ -339,6 +339,21 @@
<trans-unit id="frontend.teaserHeadline">
<source><![CDATA[Vacancies worldwide]]></source>
</trans-unit>
<trans-unit id="frontend.DropFiles">
<source><![CDATA[Drop file here]]></source>
</trans-unit>
<trans-unit id="frontend.CancelUpload">
<source><![CDATA[Stop upload]]></source>
</trans-unit>
<trans-unit id="frontend.FileType">
<source><![CDATA[Filetype]]></source>
</trans-unit>
<trans-unit id="frontend.coverLetterUplodadButton">
<source><![CDATA[Upload coverletter]]></source>
</trans-unit>
<trans-unit id="frontend.cvUplodadButton">
<source><![CDATA[Upload cv]]></source>
</trans-unit>
</body>
</file>
</xliff>
{namespace h=SGalinski\SgJobs\ViewHelpers}
{namespace base=SGalinski\ProjectBase\ViewHelpers}
{namespace sgajax=SGalinski\SgAjax\ViewHelpers}
<f:layout name="Default"/>
<f:section name="main">
<f:asset.script identifier="dropzone-js" crossorigin="anonymous" src="EXT:sg_jobs/node_modules/dropzone/dist/dropzone-min.js" priority="1" />
<f:if condition="{job}">
<f:render partial="ApplyFormSchema" arguments="{_all}"/>
</f:if>
......@@ -479,9 +481,18 @@
<f:translate key="frontend.apply.allowed_file_extensions"/>
{allowedFileExtensions})
</label>
<h:form.upload property="coverLetter" resourceName="coverLetter" id="apply-cover-letter"
class="form-control" additionalAttributes="{accept: '{allowedMimeTypes}'}"/>
<button type="button" class="coverLetter-upload-button">
<span class="sr-only"><f:translate key="frontend.coverLetterUplodadButton"/></span>
</button>
<div class="coverLetter-upload hidden" data-max-file-amount="1"
data-valid-file-extensions="{settings.fileUpload.fileTypes}" data-max-file-size="{maxFileSize}"
data-pid="{storagePid}"
data-inner-text="{f:translate(key: 'frontend.DropFiles', extensionName: 'sg_jobs')}"
data-cancel-upload="{f:translate(key: 'frontend.CancelUpload', extensionName: 'sg_jobs')}"
data-remove-file="{f:translate(key: 'frontend.RemoveFile', extensionName: 'sg_jobs')}"
data-file-type-error="{f:translate(key: 'frontend.FileType', extensionName: 'sg_jobs')}"
data-upload-ajax="{sgajax:uri.ajax(extensionName: 'SgJobs', controller: 'Ajax\\Upload', action: 'uploadCoverletter', format: 'json', parameters: '{pageId: storagePid}')}"
></div>
<f:if condition="{coverLetter.name}">
<p class="help-block">
Aktuell: {coverLetter.name}
......@@ -525,8 +536,6 @@
{allowedFileExtensions})
</label>
<h:form.upload property="cv" resourceName="cv" id="apply-cv" class="form-control"
additionalAttributes="{accept: '{allowedMimeTypes}'}"/>
<f:if condition="{cv.name}">
<p class="help-block">
Aktuell: {cv.name}
......
const { Dropzone } = window;
/**
* This module handles the integration of Dropzone
*/
export default () => {
if (!Dropzone) {
return;
}
document.querySelectorAll('#apply').forEach((item) => {
console.log('init sg_jobs');
addFileUpload(item, 'coverLetter');
});
function addFileUpload(item, uploadName) {
const uploadBox = item.querySelector('.' + uploadName + '-upload');
const uploadButton = item.querySelector('.' + uploadName + '-upload-button');
console.log(item);
console.log(uploadBox);
console.log(uploadButton);
if (!uploadBox || !uploadButton || !item) {
return;
}
console.log('all elements found');
const {
maxFileAmount,
validFileExtensions,
maxFileSize,
innerText,
cancelUpload,
removeFile,
fileTypeError,
uploadAjax
} = uploadBox.dataset;
console.log(uploadBox.dataset);
console.log(uploadAjax);
uploadBox.classList.add('dropzone');
uploadButton.addEventListener('click', () => {
uploadBox.classList.toggle('hidden');
});
const dropzone = new Dropzone('#apply .' + uploadName + '-upload', {
url: uploadAjax,
addRemoveLinks: true,
paramName: 'files',
dictDefaultMessage: innerText,
maxFilesize: maxFileSize,
maxFiles: maxFileAmount,
acceptedFiles: validFileExtensions,
dictInvalidFileType: fileTypeError,
dictCancelUpload: cancelUpload,
dictRemoveFile: removeFile,
});
dropzone.on('success', (file) => handleUpload(file, item, uploadName));
dropzone.on('removedfile', (file) => handleRemoval(file, item, uploadName));
}
function handleUpload(file, item, uploadName) {
const { uuid } = file.upload;
const response = JSON.parse(file.xhr.response);
item.insertAdjacentHTML(
'beforeend',
`<input data-uuid='${uuid}' type='hidden' name='${uploadName}[${uuid}][uuid]' value='${uuid}'>
<input data-uuid='${uuid}' type='hidden' name='${uploadName}[${uuid}][path]' value='${response.path}'>`,
);
}
function handleRemoval(file, item, uploadName) {
const { uuid } = file.upload;
item.querySelectorAll(`[data-uuid='${uuid}']`).forEach((item) => {
item.remove();
});
}
};
import Upload from './Form/upload';
/**
* This module handles the Isotope integration for sg_teaser
*/
export default class SgJobs {
/**
* Kicks things off
*/
constructor(_root = {}) {
console.log('sg_jobs js constructed');
Upload();
}
}
......@@ -48,11 +48,11 @@ call_user_func(
'JobApplication',
[
// Available actions
\SGalinski\SgJobs\Controller\JoblistController::class => 'applyForm, apply'
\SGalinski\SgJobs\Controller\JoblistController::class => 'applyForm, apply, empty'
],
[
// Uncacheable actions
\SGalinski\SgJobs\Controller\JoblistController::class => 'applyForm, apply'
\SGalinski\SgJobs\Controller\JoblistController::class => 'applyForm, apply, empty'
]
);
\TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin(
......@@ -67,6 +67,14 @@ call_user_func(
\SGalinski\SgJobs\Controller\JobTeaserController::class => ''
]
);
\SGalinski\SgAjax\Service\AjaxRegistration::configureAjaxFrontendPlugin(
'sg_jobs',
[
\SGalinski\SgJobs\Controller\Ajax\UploadController::class => 'uploadCoverletter',
]
);
// Backend preview for plugins
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/class.tx_cms_layout.php']['tt_content_drawItem']['sg_jobs']
= \SGalinski\SgJobs\Hooks\PageLayoutView\PluginRenderer::class;
......
{
"name": "sg_comments",
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"@swc/helpers": {
"version": "0.2.14",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.2.14.tgz",
"integrity": "sha512-wpCQMhf5p5GhNg2MmGKXzUNwxe7zRiCsmqYsamez2beP7mKPCSiu+BjZcdN95yYSzO857kr0VfQewmGpS77nqA=="
},
"dropzone": {
"version": "6.0.0-beta.2",
"resolved": "https://registry.npmjs.org/dropzone/-/dropzone-6.0.0-beta.2.tgz",
"integrity": "sha512-k44yLuFFhRk53M8zP71FaaNzJYIzr99SKmpbO/oZKNslDjNXQsBTdfLs+iONd0U0L94zzlFzRnFdqbLcs7h9fQ==",
"requires": {
"@swc/helpers": "^0.2.13",
"just-extend": "^5.0.0"
}
},
"just-extend": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/just-extend/-/just-extend-5.1.1.tgz",
"integrity": "sha512-b+z6yF1d4EOyDgylzQo5IminlUmzSeqR1hs/bzjBNjuGras4FXq/6TrzjxfN0j+TmI0ltJzTNlqXUMCniciwKQ=="
}
}
}
{
"name": "sg_comments",
"description": "",
"dependencies": {
"dropzone": "^6.0.0-beta.2"
}
}
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