From 0bbda2a4d062b8f7d2438cb0c6b3b02bbb9694ac Mon Sep 17 00:00:00 2001 From: Matthias Adrowski <matthias.adrowski@sgalinski.de> Date: Mon, 11 Jul 2022 11:09:55 +0200 Subject: [PATCH] [TASK] Make coverletter work using ajax+dropzone --- Classes/Controller/Ajax/UploadController.php | 90 +++++++++++++++++++ Classes/Controller/JoblistController.php | 17 ++++ .../UploadedFileReferenceConverter.php | 2 +- Resources/Private/Language/locallang.xlf | 15 ++++ .../Private/Templates/Joblist/ApplyForm.html | 19 ++-- Resources/Public/JavaScript/Form/upload.js | 80 +++++++++++++++++ Resources/Public/JavaScript/sgJobs.js | 15 ++++ ext_localconf.php | 12 ++- package-lock.json | 26 ++++++ package.json | 7 ++ 10 files changed, 275 insertions(+), 8 deletions(-) create mode 100644 Classes/Controller/Ajax/UploadController.php create mode 100644 Resources/Public/JavaScript/Form/upload.js create mode 100644 Resources/Public/JavaScript/sgJobs.js create mode 100644 package-lock.json create mode 100644 package.json diff --git a/Classes/Controller/Ajax/UploadController.php b/Classes/Controller/Ajax/UploadController.php new file mode 100644 index 00000000..5f06436f --- /dev/null +++ b/Classes/Controller/Ajax/UploadController.php @@ -0,0 +1,90 @@ +<?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, + ] + ); + } +} diff --git a/Classes/Controller/JoblistController.php b/Classes/Controller/JoblistController.php index c245284e..581b1dfc 100644 --- a/Classes/Controller/JoblistController.php +++ b/Classes/Controller/JoblistController.php @@ -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 * diff --git a/Classes/Property/TypeConverter/UploadedFileReferenceConverter.php b/Classes/Property/TypeConverter/UploadedFileReferenceConverter.php index af8bf23f..6b132333 100644 --- a/Classes/Property/TypeConverter/UploadedFileReferenceConverter.php +++ b/Classes/Property/TypeConverter/UploadedFileReferenceConverter.php @@ -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!', diff --git a/Resources/Private/Language/locallang.xlf b/Resources/Private/Language/locallang.xlf index 109d061f..3ca916f1 100644 --- a/Resources/Private/Language/locallang.xlf +++ b/Resources/Private/Language/locallang.xlf @@ -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> diff --git a/Resources/Private/Templates/Joblist/ApplyForm.html b/Resources/Private/Templates/Joblist/ApplyForm.html index 349d6392..776ac0a5 100644 --- a/Resources/Private/Templates/Joblist/ApplyForm.html +++ b/Resources/Private/Templates/Joblist/ApplyForm.html @@ -1,8 +1,10 @@ {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} diff --git a/Resources/Public/JavaScript/Form/upload.js b/Resources/Public/JavaScript/Form/upload.js new file mode 100644 index 00000000..3c0cffa6 --- /dev/null +++ b/Resources/Public/JavaScript/Form/upload.js @@ -0,0 +1,80 @@ +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(); + }); + } +}; diff --git a/Resources/Public/JavaScript/sgJobs.js b/Resources/Public/JavaScript/sgJobs.js new file mode 100644 index 00000000..fa43ee8c --- /dev/null +++ b/Resources/Public/JavaScript/sgJobs.js @@ -0,0 +1,15 @@ +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(); + } +} diff --git a/ext_localconf.php b/ext_localconf.php index b9eda9ce..5d2d56ee 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -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; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..709eddf2 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,26 @@ +{ + "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==" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..87133c03 --- /dev/null +++ b/package.json @@ -0,0 +1,7 @@ +{ + "name": "sg_comments", + "description": "", + "dependencies": { + "dropzone": "^6.0.0-beta.2" + } +} -- GitLab