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

Merge branch 'feature_new_uploadhandling' into 'master'

Feature new uploadhandling

See merge request !37
parents 033179ec bc7ed9a2
No related branches found
No related tags found
1 merge request!37Feature new uploadhandling
Showing
with 1093 additions and 33 deletions
<?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 = '/JobApplication/temp/';
/**
* 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 coverletter.
*
* @return void
* @throws \TYPO3\CMS\Core\Resource\Exception\ExistingTargetFileNameException
* @throws \InvalidArgumentException
*/
public function uploadCoverletterAction() {
$this->handleAnyDropZoneUpload();
}
/**
* Uploads a new cd.
*
* @return void
* @throws \TYPO3\CMS\Core\Resource\Exception\ExistingTargetFileNameException
* @throws \InvalidArgumentException
*/
public function uploadCvAction() {
$this->handleAnyDropZoneUpload();
}
/**
* Uploads a new cd.
*
* @return void
* @throws \TYPO3\CMS\Core\Resource\Exception\ExistingTargetFileNameException
* @throws \InvalidArgumentException
*/
public function uploadCertificateAction() {
$this->handleAnyDropZoneUpload();
}
/**
* since all files are handled the same way, we can use a single function!
*
* @return void
*/
private function handleAnyDropZoneUpload(){
$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() . '/fileadmin/' . self::TYPO3_TMP_FOLDER . $fileName;
$tempFilePath = $firstFile['tmp_name'];
$success = GeneralUtility::upload_copy_move($tempFilePath, $filePath);
}
$this->returnData(
[
'success' => $success,
'path' => $filePath,
]
);
}
}
...@@ -28,6 +28,7 @@ namespace SGalinski\SgJobs\Controller; ...@@ -28,6 +28,7 @@ namespace SGalinski\SgJobs\Controller;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
use SGalinski\ProjectBase\Domain\Repository\CountryRepository; use SGalinski\ProjectBase\Domain\Repository\CountryRepository;
use SGalinski\SgComments\Domain\Model\Comment;
use SGalinski\SgJobs\Domain\Model\Company; use SGalinski\SgJobs\Domain\Model\Company;
use SGalinski\SgJobs\Domain\Model\Job; use SGalinski\SgJobs\Domain\Model\Job;
use SGalinski\SgJobs\Domain\Model\JobApplication; use SGalinski\SgJobs\Domain\Model\JobApplication;
...@@ -37,6 +38,7 @@ use SGalinski\SgJobs\Domain\Repository\ExperienceLevelRepository; ...@@ -37,6 +38,7 @@ use SGalinski\SgJobs\Domain\Repository\ExperienceLevelRepository;
use SGalinski\SgJobs\Domain\Repository\JobApplicationRepository; use SGalinski\SgJobs\Domain\Repository\JobApplicationRepository;
use SGalinski\SgJobs\Domain\Repository\JobRepository; use SGalinski\SgJobs\Domain\Repository\JobRepository;
use SGalinski\SgJobs\Property\TypeConverter\UploadedFileReferenceConverter; use SGalinski\SgJobs\Property\TypeConverter\UploadedFileReferenceConverter;
use SGalinski\SgJobs\Service\FileAndFolderService;
use SGalinski\SgMail\Service\MailTemplateService; use SGalinski\SgMail\Service\MailTemplateService;
use SGalinski\SgSeo\Service\HeadTagService; use SGalinski\SgSeo\Service\HeadTagService;
use TYPO3\CMS\Core\Context\Context; use TYPO3\CMS\Core\Context\Context;
...@@ -46,11 +48,15 @@ use TYPO3\CMS\Core\Resource\ResourceFactory; ...@@ -46,11 +48,15 @@ use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Site\SiteFinder; use TYPO3\CMS\Core\Site\SiteFinder;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
use TYPO3\CMS\Core\Utility\VersionNumberUtility; use TYPO3\CMS\Core\Utility\VersionNumberUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface; use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Extbase\Domain\Model\FileReference;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use TYPO3\CMS\Extbase\Mvc\Exception\NoSuchArgumentException; use TYPO3\CMS\Extbase\Mvc\Exception\NoSuchArgumentException;
use TYPO3\CMS\Extbase\Mvc\Exception\StopActionException;
use TYPO3\CMS\Extbase\Object\ObjectManager; use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Extbase\Property\Exception\TypeConverterException;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility; use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
use TYPO3\CMS\Extbase\Validation\Validator\ConjunctionValidator; use TYPO3\CMS\Extbase\Validation\Validator\ConjunctionValidator;
use TYPO3\CMS\Extbase\Validation\Validator\GenericObjectValidator; use TYPO3\CMS\Extbase\Validation\Validator\GenericObjectValidator;
...@@ -64,6 +70,12 @@ class JoblistController extends ActionController { ...@@ -64,6 +70,12 @@ class JoblistController extends ActionController {
// the array key for the error message in the post array // the array key for the error message in the post array
public const ERROR_KEY_IN_POST = 'error'; public const ERROR_KEY_IN_POST = 'error';
private const UPLOADED_FILES = [
'coverLetter',
'cv',
'certificate',
];
/** /**
* @var CompanyRepository * @var CompanyRepository
*/ */
...@@ -89,6 +101,11 @@ class JoblistController extends ActionController { ...@@ -89,6 +101,11 @@ class JoblistController extends ActionController {
*/ */
protected $experienceLevelRepository; protected $experienceLevelRepository;
/**
* @var FileAndFolderService
*/
protected $fileAndFolderService;
/** /**
* Inject the CompanyRepository * Inject the CompanyRepository
* *
...@@ -98,6 +115,15 @@ class JoblistController extends ActionController { ...@@ -98,6 +115,15 @@ class JoblistController extends ActionController {
$this->companyRepository = $companyRepository; $this->companyRepository = $companyRepository;
} }
/**
* Inject the CompanyRepository
*
* @param CompanyRepository $companyRepository
*/
public function injectFileAndFolderService(FileAndFolderService $fileAndFolderService): void {
$this->fileAndFolderService = $fileAndFolderService;
}
/** /**
* Inject the DepartmentRepository * Inject the DepartmentRepository
* *
...@@ -198,7 +224,6 @@ class JoblistController extends ActionController { ...@@ -198,7 +224,6 @@ class JoblistController extends ActionController {
); );
$headTagService->execute(); $headTagService->execute();
$jobs = [$job]; $jobs = [$job];
$numberOfPages = 1; $numberOfPages = 1;
} else { } else {
...@@ -392,6 +417,9 @@ class JoblistController extends ActionController { ...@@ -392,6 +417,9 @@ class JoblistController extends ActionController {
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException * @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
*/ */
protected function initializeApplyAction() { protected function initializeApplyAction() {
$arguments = ($this->request->getArguments());
$parsedBody = $this->request->getParsedBody();
$uniqueFolderName = $arguments['folderName'] ?? '';
try { try {
$uniqueFolderName = $this->request->getArgument('folderName'); $uniqueFolderName = $this->request->getArgument('folderName');
} catch (NoSuchArgumentException $exception) { } catch (NoSuchArgumentException $exception) {
...@@ -411,15 +439,15 @@ class JoblistController extends ActionController { ...@@ -411,15 +439,15 @@ class JoblistController extends ActionController {
$propertyMappingConfiguration = $this->arguments->getArgument('applyData')->getPropertyMappingConfiguration(); $propertyMappingConfiguration = $this->arguments->getArgument('applyData')->getPropertyMappingConfiguration();
$propertyMappingConfiguration->forProperty('job')->allowAllProperties(); $propertyMappingConfiguration->forProperty('job')->allowAllProperties();
foreach (['coverLetter', 'cv', 'certificate'] as $property) { // foreach (['coverLetter', 'cv', 'certificate'] as $property) {
$typeConverter = $this->objectManager->get(UploadedFileReferenceConverter::class); // $typeConverter = $this->objectManager->get(UploadedFileReferenceConverter::class);
$typeConverter->setAllowedFileExtensions( // $typeConverter->setAllowedFileExtensions(
$this->settings['allowedFileExtensions'] ?? $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'] // $this->settings['allowedFileExtensions'] ?? $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']
); // );
$typeConverter->setUploadFolder('1:/JobApplication/temp/' . $uniqueFolderName); // $typeConverter->setUploadFolder('1:/JobApplication/temp/' . $uniqueFolderName);
$typeConverter->setTargetUploadFileName($property); // $typeConverter->setTargetUploadFileName($property);
$propertyMappingConfiguration->forProperty($property)->setTypeConverter($typeConverter); // $propertyMappingConfiguration->forProperty($property)->setTypeConverter($typeConverter);
} // }
// remove the job validator // remove the job validator
/** @var ConjunctionValidator $validator */ /** @var ConjunctionValidator $validator */
...@@ -466,7 +494,7 @@ class JoblistController extends ActionController { ...@@ -466,7 +494,7 @@ class JoblistController extends ActionController {
$this->jobApplicationRepository->update($applyData); $this->jobApplicationRepository->update($applyData);
} }
$this->moveTmpFolder($folderName); $this->moveTmpFolder($folderName, $applyData);
$this->submitApplicationFiles($applyData, $folderName); $this->submitApplicationFiles($applyData, $folderName);
$mailService = $this->objectManager->get( $mailService = $this->objectManager->get(
...@@ -528,6 +556,9 @@ class JoblistController extends ActionController { ...@@ -528,6 +556,9 @@ class JoblistController extends ActionController {
$this->redirect('applyForm'); $this->redirect('applyForm');
} catch (\Exception $exception) { } catch (\Exception $exception) {
if ($exception instanceof StopActionException){
return NULL;
}
$this->deleteTmpFolder($folderName); $this->deleteTmpFolder($folderName);
$job = $applyData->getJob(); $job = $applyData->getJob();
$jobId = $job !== NULL ? $job->getUid() : NULL; $jobId = $job !== NULL ? $job->getUid() : NULL;
...@@ -671,8 +702,11 @@ class JoblistController extends ActionController { ...@@ -671,8 +702,11 @@ class JoblistController extends ActionController {
* @throws \TYPO3\CMS\Core\Resource\Exception\InsufficientFolderAccessPermissionsException * @throws \TYPO3\CMS\Core\Resource\Exception\InsufficientFolderAccessPermissionsException
* @throws \TYPO3\CMS\Core\Resource\Exception\InsufficientFolderWritePermissionsException * @throws \TYPO3\CMS\Core\Resource\Exception\InsufficientFolderWritePermissionsException
*/ */
protected function moveTmpFolder(string $folderName): void { protected function moveTmpFolder(string $folderName, JobApplication $applicationData): void {
// Move uploaded files & csv fo real folder and delete the tmp folder $filePathInfo = [];
$namesToMove = [];
$allowedFileExtensions = $this->getAllowedFileExtensions();
/** @var ResourceFactory $resourceFactory */ /** @var ResourceFactory $resourceFactory */
$resourceFactory = $this->objectManager->get(ResourceFactory::class); $resourceFactory = $this->objectManager->get(ResourceFactory::class);
$storage = $resourceFactory->getStorageObject(1); $storage = $resourceFactory->getStorageObject(1);
...@@ -682,13 +716,57 @@ class JoblistController extends ActionController { ...@@ -682,13 +716,57 @@ class JoblistController extends ActionController {
} else { } else {
$newFolder = $storage->getFolder('/JobApplication/' . $folderName); $newFolder = $storage->getFolder('/JobApplication/' . $folderName);
} }
$tempFolder = $storage->getFolder('/JobApplication/temp/' . $folderName); $tempFolder = $storage->getFolder('/JobApplication/temp/');
$filesToMove = $storage->getFilesInFolder($tempFolder);
foreach ($filesToMove as $fileToMove) { // Move uploaded files & csv fo real folder and delete the tmp folder
$storage->moveFile($fileToMove, $newFolder); foreach (self::UPLOADED_FILES as $singleFilePostKey) {
if (array_key_exists($singleFilePostKey, $_POST)) {
foreach ($_POST[$singleFilePostKey] as $singleUploadedArr) {
$filePathInfo = PathUtility::pathinfo($singleUploadedArr['path']);
$namesToMove[] = $filePathInfo['basename'];
if (!GeneralUtility::inList($allowedFileExtensions, strtolower($filePathInfo['extension']))) {
throw new TypeConverterException('File extension is not allowed!', 1399312430);
}
if(!$newFolder->hasFile($filePathInfo['basename'])) {
$singleFileToMove = $storage->getFileInFolder($filePathInfo['basename'], $tempFolder);
// when we reload etc this image might already be moved.
$usableFile = $storage->moveFile($singleFileToMove, $newFolder);
}
else {
$usableFile = $newFolder->getFile($filePathInfo['basename']);
}
$fileReference = $this->fileAndFolderService->createFileReferenceFromFalFileObject($usableFile);
if ($fileReference) {
// @todo: make this more dynamic
if ($singleFilePostKey === 'coverLetter') {
$applicationData->setCoverLetter($fileReference);
}
if ($singleFilePostKey === 'cv') {
$applicationData->setCV($fileReference);
}
if ($singleFilePostKey === 'certificate') {
$applicationData->setCertificate($fileReference);
}
}
continue;
}
} else {
throw new TypeConverterException('Missing file ' . $singleFilePostKey, 1399312430);
}
} }
} }
/**
* returns currently set allowedFiles
*
* @return mixed
*/
protected function getAllowedFileExtensions() {
return $this->settings['allowedFileExtensions'] ?? $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'];
}
/** /**
* Delete uploaded files in tmp folder * Delete uploaded files in tmp folder
* *
......
...@@ -137,19 +137,16 @@ class JobApplication extends AbstractEntity { ...@@ -137,19 +137,16 @@ class JobApplication extends AbstractEntity {
/** /**
* @var \TYPO3\CMS\Extbase\Domain\Model\FileReference $coverLetter * @var \TYPO3\CMS\Extbase\Domain\Model\FileReference $coverLetter
* @TYPO3\CMS\Extbase\Annotation\Validate("NotEmpty")
*/ */
protected $coverLetter; protected $coverLetter;
/** /**
* @var \TYPO3\CMS\Extbase\Domain\Model\FileReference $cv * @var \TYPO3\CMS\Extbase\Domain\Model\FileReference $cv
* @TYPO3\CMS\Extbase\Annotation\Validate("NotEmpty")
*/ */
protected $cv; protected $cv;
/** /**
* @var \TYPO3\CMS\Extbase\Domain\Model\FileReference $certificate * @var \TYPO3\CMS\Extbase\Domain\Model\FileReference $certificate
* @TYPO3\CMS\Extbase\Annotation\Validate("NotEmpty")
*/ */
protected $certificate; protected $certificate;
......
...@@ -374,7 +374,7 @@ class UploadedFileReferenceConverter implements TypeConverterInterface { ...@@ -374,7 +374,7 @@ class UploadedFileReferenceConverter implements TypeConverterInterface {
$filePathInfo = PathUtility::pathinfo($uploadInfo['name']); $filePathInfo = PathUtility::pathinfo($uploadInfo['name']);
$fileName = $this->getTargetUploadFileName(); $fileName = $this->getTargetUploadFileName();
$finalFileName = $fileName . '.' . strtolower($filePathInfo['extension']); $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'])) { if (!\TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Resource\Security\FileNameValidator::class)->isValid($uploadInfo['name'])) {
throw new TypeConverterException( throw new TypeConverterException(
'Uploading files with PHP file extensions is not allowed!', 'Uploading files with PHP file extensions is not allowed!',
......
<?php
namespace SGalinski\SgJobs\Service;
/***************************************************************
* 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 TYPO3\CMS\Core\Charset\CharsetConverter;
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Resource\File as FalFile;
use TYPO3\CMS\Core\Resource\FileReference as FalFileReference;
use TYPO3\CMS\Core\Resource\Folder;
use TYPO3\CMS\Core\Resource\ProcessedFile;
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Resource\ResourceStorage;
use TYPO3\CMS\Core\Resource\StorageRepository;
use TYPO3\CMS\Core\SingletonInterface;
use TYPO3\CMS\Core\Utility\File\BasicFileUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Domain\Model\FileReference;
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
use TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager;
/**
* Service for diverse file operations
*/
class FileAndFolderService implements SingletonInterface {
/**
*
* @var ObjectManagerInterface
*/
protected $objectManager;
/**
* Inject the ObjectManager
*
* @param ObjectManagerInterface $objectManager
*/
public function injectObjectManager(ObjectManagerInterface $objectManager) {
$this->objectManager = $objectManager;
}
/**
*
* @var BasicFileUtility
*/
protected $fileUtility;
/**
* Inject the FileUtility
*
* @param BasicFileUtility $fileUtility
*/
public function injectFileUtility(BasicFileUtility $fileUtility) {
$this->fileUtility = $fileUtility;
}
/**
* @var ResourceFactory
*
*/
protected $resourceFactory;
/**
* Inject the ResourceFactory
*
* @param ResourceFactory $resourceFactory
*/
public function injectResourceFactory(ResourceFactory $resourceFactory) {
$this->resourceFactory = $resourceFactory;
}
/**
*
* @var StorageRepository
*/
protected $storageRepository;
/**
* Inject the StorageRepository
*
* @param StorageRepository $storageRepository
*/
public function injectStorageRepository(StorageRepository $storageRepository) {
$this->storageRepository = $storageRepository;
}
/**
*
* @var CharsetConverter
*/
protected $characterSetConverter;
/**
* Inject the CharacterSetConverter
*
* @param CharsetConverter $characterSetConverter
*/
public function injectCharacterSetConverter(CharsetConverter $characterSetConverter) {
$this->characterSetConverter = $characterSetConverter;
}
/**
* @var PersistenceManager
*
*/
protected $persistenceManager;
/**
* Inject the PersistenceManager
*
* @param PersistenceManager $persistenceManager
*/
public function injectPersistenceManager(PersistenceManager $persistenceManager) {
$this->persistenceManager = $persistenceManager;
}
/**
* @var array
*/
protected $allowedFileExtensionCache = [];
/**
* @var array
*/
protected $settings = [];
/**
* Method for uploading a file.
*
* @param string $temporaryFileName
* @param int $size
* @return string
*/
public function uploadFile($temporaryFileName, $size): string {
if ($size <= 0) {
return '';
}
$newFile = GeneralUtility::upload_to_tempfile($temporaryFileName);
GeneralUtility::fixPermissions($newFile);
return \basename($newFile);
}
/**
* Method for clean typo3temp from unused temporary files.
*
* @param string $name
* @return boolean
*/
public function removeTemporaryFile($name): bool {
$fileRemoved = FALSE;
if (\trim($name) !== '') {
$name = GeneralUtility::getFileAbsFileName('typo3temp/' . $name);
$fileRemoved = (bool) GeneralUtility::unlink_tempfile($name);
}
return $fileRemoved;
}
/**
* This method creates the fal folder.
*
* @param string $folderIdentifier
* @param int $storageUid
* @return Folder
* @throws \InvalidArgumentException
* @throws \TYPO3\CMS\Core\Resource\Exception\InsufficientFolderWritePermissionsException
* @throws \TYPO3\CMS\Core\Resource\Exception\InsufficientFolderAccessPermissionsException
* @throws \TYPO3\CMS\Core\Resource\Exception\ExistingTargetFolderException
* @throws \Exception
*/
protected function createFolder($folderIdentifier, $storageUid): Folder {
/** @var ResourceStorage $storage */
$storage = $this->getStorage($storageUid);
return $storage->createFolder($folderIdentifier);
}
/**
* Returns a storage instance.
*
* @param int $storageUid
* @return ResourceStorage
*/
public function getStorage($storageUid = 1): ResourceStorage {
/** @var ResourceStorage $storage */
$storage = $this->storageRepository->findByUid($storageUid);
if (Environment::isCli() && $storage !== NULL) {
// if not set, the storage cannot access any folder in CLI mode (TYPO3 bug?)
$storage->setEvaluatePermissions(FALSE);
}
return $storage;
}
/**
* Returns an array with allowed file extensions.
*
* The types array can contain e.g. image or document and will be configured at
* plugin.tx_rsevents.settings.uploader in the typoscript.
*
* @param array $types
* @return array
*/
public function getAllowedFileExtensions(array $types = []): array {
$fileTypes = '';
$key = \md5(\implode('_', $types));
if (!isset($this->allowedFileExtensionCache[$key])) {
foreach ($types as $type) {
$type = \lcfirst($type . 'FileExtensions');
if (!\array_key_exists($type, $this->settings['rsUploader'])) {
continue;
}
if ($fileTypes !== '' && \substr($fileTypes, -1) !== ',') {
$fileTypes .= ',';
}
$fileTypes .= $this->settings['uploader'][$type];
}
$this->allowedFileExtensionCache[$key] = GeneralUtility::trimExplode(',', $fileTypes, TRUE);
}
return $this->allowedFileExtensionCache[$key];
}
/**
* Method create FAL records from the temporary files of the new impression. The new fileReferences will
* be attached to the new impression.
*
* @param array $fileInfo
* @param Folder $folder
* @return FalFile
*/
public function createFileRecords(array $fileInfo, Folder $folder): FalFile {
return $folder->addUploadedFile($fileInfo);
}
/**
* Returns the folder for the given folder identifier.
*
* @param string $folderIdentifier
* @throws \TYPO3\CMS\Core\Resource\Exception\InsufficientFolderAccessPermissionsException
* @throws \TYPO3\CMS\Core\Resource\Exception\InsufficientFolderWritePermissionsException
* @throws \InvalidArgumentException
* @throws \Exception
* @return Folder
* @throws \TYPO3\CMS\Core\Resource\Exception\ExistingTargetFolderException
*/
public function getFolder($folderIdentifier): Folder {
/** @var ResourceStorage $storage */
$storage = $this->getStorage();
if ($storage->hasFolder($folderIdentifier)) {
return $storage->getFolder($folderIdentifier);
}
/** @var Folder $folder */
$folder = $storage->getFolder('sg_comments');
$this->createHtaccessFile($folder);
return $storage->createFolder($folderIdentifier);
}
/**
* Write a htaccess file to the folder with the appropriate settings
*
* @param Folder $folder
*/
private function createHtaccessFile($folder) {
$path = $folder->getPublicUrl();
$file = $path . '.htaccess';
$absolutePublicPath = Environment::getPublicPath();
$file = $absolutePublicPath.$file;
if (!\file_exists($file)) {
$current = '';
$allowedImageFileExtensions = 'png|gif|bmp|ico|jpeg|jpg|ps|psd|svg|tif|tiff|dds|pspimage|tga|';
$allowedVideoFileExtensions = '3g2|3gp|avi|flv|h264|m4v|mkv|mov|mp4|mpg|mpeg|rm|swf|vob|wmv|';
$allowedAudioFileExtensions = 'aif|cda|mid|midi|mp3|mpa|ogg|wav|wma|wpl';
$current .= <<<EOT
order allow,deny
<Files ~ "\.($allowedImageFileExtensions$allowedVideoFileExtensions$allowedAudioFileExtensions)$">
allow from all
</Files>
EOT;
\file_put_contents($file, $current);
}
}
/**
* Method the returns the given subfolder. The FAL core method throws an exception if the folder does not exists.
* And this method just create the folder.
*
* @param Folder $folder
* @param string $subfolderName
* @return Folder
* @throws \InvalidArgumentException
*/
public function getSubfolder(Folder $folder, $subfolderName): Folder {
if ($folder->hasFolder($subfolderName)) {
return $folder->getSubfolder($subfolderName);
}
return $folder->createFolder($subfolderName);
}
/**
* This creates a fal file reference object.
*
* @param FalFile $file
* @param int $resourcePointer
* @return FileReference
*/
public function createFileReferenceFromFalFileObject(FalFile $file, $resourcePointer = NULL): FileReference {
$fileReference = $this->resourceFactory->createFileReferenceObject(
[
'uid_local' => $file->getUid(),
'uid_foreign' => \uniqid('NEW_', TRUE),
'uid' => \uniqid('NEW_', TRUE),
]
);
return $this->createFileReferenceFromFalFileReferenceObject($fileReference, $resourcePointer);
}
/**
* This creates a file reference extbase object from a given fal file reference object.
*
* @param FalFileReference $falFileReference
* @param int $resourcePointer
* @return FileReference
*/
protected function createFileReferenceFromFalFileReferenceObject(
FalFileReference $falFileReference,
$resourcePointer = NULL
): FileReference {
if ($resourcePointer === NULL) {
/** @var FileReference $fileReference */
$fileReference = $this->objectManager->get(FileReference::class);
} else {
$fileReference = $this->persistenceManager->getObjectByIdentifier(
$resourcePointer,
FileReference::class,
FALSE
);
}
$fileReference->setOriginalResource($falFileReference);
return $fileReference;
}
/**
* Generate thumbnail image for the given file. This function creates a sub folder for the thumbnails if this is
* needed and attaches the processed new thumbnail file to this folder.
*
* @param FalFile $file
* @param Folder $folder
* @param array $uploadSettings
* @return FalFile
* @throws \InvalidArgumentException
*/
public function generateFileUploadThumbnail(FalFile $file, Folder $folder, array $uploadSettings): FalFile {
$thumbnailSettings = $uploadSettings['thumbnail'];
$thumbnailFolder = $this->getThumbnailFolder($folder, $thumbnailSettings);
$thumbnailFileName = $this->getThumbnailFileName($file, $thumbnailSettings);
$thumbnailConfig = [
'width' => $thumbnailSettings['resizeSettings']['width'],
'height' => $thumbnailSettings['resizeSettings']['height'],
'additionalParameters' => '-quality ' . $thumbnailSettings['resizeSettings']['quality']
];
return $thumbnailFolder->addFile(
$file->process(ProcessedFile::CONTEXT_IMAGECROPSCALEMASK, $thumbnailConfig)->getPublicUrl(),
$thumbnailFileName,
'replace'
);
}
/**
* Returns name of a generated thumbnail.
*
* @param FalFile $file
* @param array $thumbnailSettings
* @return string
*/
public function getThumbnailFileName(FalFile $file, array $thumbnailSettings): string {
$thumbnailFileName = $file->getNameWithoutExtension() . '_' . $thumbnailSettings['prefix'];
$thumbnailFileName .= '.' . $file->getExtension();
return $thumbnailFileName;
}
/**
* Returns the thumbnail sub folder and creates the folder if no folder exists.
*
* @param Folder $folder
* @param array $thumbnailSettings
* @return Folder
* @throws \InvalidArgumentException
*/
public function getThumbnailFolder(Folder $folder, array $thumbnailSettings): Folder {
return $this->getSubfolder($folder, $thumbnailSettings['prefix']);
}
}
...@@ -339,6 +339,27 @@ ...@@ -339,6 +339,27 @@
<trans-unit id="frontend.teaserHeadline"> <trans-unit id="frontend.teaserHeadline">
<source><![CDATA[Vacancies worldwide]]></source> <source><![CDATA[Vacancies worldwide]]></source>
</trans-unit> </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.RemoveFile">
<source><![CDATA[Remove file]]></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>
<trans-unit id="frontend.certificateUplodadButton">
<source><![CDATA[Upload certificate]]></source>
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>
{namespace h=SGalinski\SgJobs\ViewHelpers} {namespace h=SGalinski\SgJobs\ViewHelpers}
{namespace base=SGalinski\ProjectBase\ViewHelpers} {namespace base=SGalinski\ProjectBase\ViewHelpers}
{namespace sgajax=SGalinski\SgAjax\ViewHelpers}
<f:layout name="Default"/> <f:layout name="Default"/>
<f:section name="main"> <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:asset.css identifier="dropzone-css" crossorigin="anonymous" href="EXT:sg_jobs/node_modules/dropzone/dist/dropzone.css" priority="1" />
<f:if condition="{job}"> <f:if condition="{job}">
<f:render partial="ApplyFormSchema" arguments="{_all}"/> <f:render partial="ApplyFormSchema" arguments="{_all}"/>
</f:if> </f:if>
...@@ -472,16 +475,22 @@ ...@@ -472,16 +475,22 @@
</div> </div>
<div class="col-xs-12"> <div class="col-xs-12">
<div class="form-group"> <div class="form-group jobs-upload-group">
<label for="apply-cover-letter" class="smart-label filled"> <label for="apply-cover-letter" class="smart-label filled">
<f:translate key="frontend.apply.cover_letter"/> <f:translate key="frontend.apply.cover_letter"/>
( (
<f:translate key="frontend.apply.allowed_file_extensions"/> <f:translate key="frontend.apply.allowed_file_extensions"/>
{allowedFileExtensions}) {allowedFileExtensions})
</label> </label>
<div class="coverLetter-upload jobs-upload" data-max-file-amount="1"
<h:form.upload property="coverLetter" resourceName="coverLetter" id="apply-cover-letter" data-valid-file-extensions="{settings.fileUpload.fileTypes}" data-max-file-size="{maxFileSize}"
class="form-control" additionalAttributes="{accept: '{allowedMimeTypes}'}"/> 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}"> <f:if condition="{coverLetter.name}">
<p class="help-block"> <p class="help-block">
Aktuell: {coverLetter.name} Aktuell: {coverLetter.name}
...@@ -517,7 +526,7 @@ ...@@ -517,7 +526,7 @@
</div> </div>
<div class="col-xs-12"> <div class="col-xs-12">
<div class="form-group"> <div class="form-group jobs-upload-group">
<label for="apply-cv" class="smart-label filled"> <label for="apply-cv" class="smart-label filled">
<f:translate key="frontend.apply.cv"/> <f:translate key="frontend.apply.cv"/>
( (
...@@ -525,8 +534,15 @@ ...@@ -525,8 +534,15 @@
{allowedFileExtensions}) {allowedFileExtensions})
</label> </label>
<h:form.upload property="cv" resourceName="cv" id="apply-cv" class="form-control" <div class="cv-upload jobs-upload" data-max-file-amount="1"
additionalAttributes="{accept: '{allowedMimeTypes}'}"/> 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: 'uploadCv', format: 'json', parameters: '{pageId: storagePid}')}"
></div>
<f:if condition="{cv.name}"> <f:if condition="{cv.name}">
<p class="help-block"> <p class="help-block">
Aktuell: {cv.name} Aktuell: {cv.name}
...@@ -562,7 +578,7 @@ ...@@ -562,7 +578,7 @@
</div> </div>
<div class="col-xs-12"> <div class="col-xs-12">
<div class="form-group"> <div class="form-group jobs-upload-group">
<label for="apply-certificate" class="smart-label filled"> <label for="apply-certificate" class="smart-label filled">
<f:translate key="frontend.apply.certificate"/> <f:translate key="frontend.apply.certificate"/>
( (
...@@ -570,8 +586,15 @@ ...@@ -570,8 +586,15 @@
{allowedFileExtensions}) {allowedFileExtensions})
</label> </label>
<h:form.upload property="certificate" resourceName="certificate" id="apply-certificate" <div class="certificate-upload jobs-upload" data-max-file-amount="1"
class="form-control" additionalAttributes="{accept: '{allowedMimeTypes}'}"/> 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: 'uploadCv', format: 'json', parameters: '{pageId: storagePid}')}"
></div>
<f:if condition="{certificate.name}"> <f:if condition="{certificate.name}">
<p class="help-block"> <p class="help-block">
Aktuell: {certificate.name} Aktuell: {certificate.name}
......
const { Dropzone } = window;
/**
* This module handles the integration of Dropzone
*/
export default () => {
if (!Dropzone) {
return;
}
document.querySelectorAll('#apply').forEach((item) => {
addFileUpload(item, 'coverLetter');
addFileUpload(item, 'cv');
addFileUpload(item, 'certificate');
});
function addFileUpload(item, uploadName) {
const uploadBox = item.querySelector('.' + uploadName + '-upload');
if (!uploadBox || !item) {
return;
}
const {
maxFileAmount,
validFileExtensions,
maxFileSize,
innerText,
cancelUpload,
removeFile,
fileTypeError,
uploadAjax
} = uploadBox.dataset;
uploadBox.classList.add('dropzone');
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 = {}) {
Upload();
}
}
...@@ -111,6 +111,40 @@ ...@@ -111,6 +111,40 @@
} }
} }
.sgjobs-apply-form {
.jobs-upload {
width: 100%;
min-height: 55px;
// default bootstrap border+shadow
box-shadow: inset 0 1px 1px rgb(0 0 0 / 8%);
border: 1px solid #a6a6a6;
position: relative;
border-radius: 0;
button.dz-button {
position: absolute;
bottom: 0;
right: 0;
border: 0 none;
outline: 0 none;
background: #a6a6a6;
color: $color-standard-white;
padding: 3px 12px;
&::after {
@include inline-svg($cloud-upload, $color-standard-white);
transform: translateY(4px);
content: '';
display: inline-block;
width: 22px;
height: 20px;
margin-left: 8px;
}
}
}
}
.sgjobs-description { .sgjobs-description {
.list-default li::before { .list-default li::before {
color: $sg-jobs-foreground-color; color: $sg-jobs-foreground-color;
......
...@@ -48,11 +48,11 @@ call_user_func( ...@@ -48,11 +48,11 @@ call_user_func(
'JobApplication', 'JobApplication',
[ [
// Available actions // Available actions
\SGalinski\SgJobs\Controller\JoblistController::class => 'applyForm, apply' \SGalinski\SgJobs\Controller\JoblistController::class => 'applyForm, apply, empty'
], ],
[ [
// Uncacheable actions // Uncacheable actions
\SGalinski\SgJobs\Controller\JoblistController::class => 'applyForm, apply' \SGalinski\SgJobs\Controller\JoblistController::class => 'applyForm, apply, empty'
] ]
); );
\TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin( \TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin(
...@@ -67,6 +67,14 @@ call_user_func( ...@@ -67,6 +67,14 @@ call_user_func(
\SGalinski\SgJobs\Controller\JobTeaserController::class => '' \SGalinski\SgJobs\Controller\JobTeaserController::class => ''
] ]
); );
\SGalinski\SgAjax\Service\AjaxRegistration::configureAjaxFrontendPlugin(
'sg_jobs',
[
\SGalinski\SgJobs\Controller\Ajax\UploadController::class => 'uploadCoverletter, uploadCv, uploadCertificate',
]
);
// Backend preview for plugins // Backend preview for plugins
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/class.tx_cms_layout.php']['tt_content_drawItem']['sg_jobs'] $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/class.tx_cms_layout.php']['tt_content_drawItem']['sg_jobs']
= \SGalinski\SgJobs\Hooks\PageLayoutView\PluginRenderer::class; = \SGalinski\SgJobs\Hooks\PageLayoutView\PluginRenderer::class;
......
{
"editor.formatOnSave": true
}
\ No newline at end of file
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = _applyDecoratedDescriptor;
function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
var desc = {
};
Object['ke' + 'ys'](descriptor).forEach(function(key) {
desc[key] = descriptor[key];
});
desc.enumerable = !!desc.enumerable;
desc.configurable = !!desc.configurable;
if ('value' in desc || desc.initializer) {
desc.writable = true;
}
desc = decorators.slice().reverse().reduce(function(desc1, decorator) {
return decorator ? decorator(target, property, desc1) || desc1 : desc1;
}, desc);
if (context && desc.initializer !== void 0) {
desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
desc.initializer = undefined;
}
if (desc.initializer === void 0) {
Object['define' + 'Property'](target, property, desc);
desc = null;
}
return desc;
}
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = _arrayWithHoles;
function _arrayWithHoles(arr) {
if (Array.isArray(arr)) return arr;
}
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = _arrayWithoutHoles;
function _arrayWithoutHoles(arr) {
if (Array.isArray(arr)) {
for(var i = 0, arr2 = new Array(arr.length); i < arr.length; i++){
arr2[i] = arr[i];
}
return arr2;
}
}
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = _assertThisInitialized;
function _assertThisInitialized(self) {
if (self === void 0) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return self;
}
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = AsyncGenerator;
var _awaitValue = _interopRequireDefault(require("./_await_value"));
function _instanceof(left, right) {
if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
return right[Symbol.hasInstance](left);
} else {
return left instanceof right;
}
}
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
function AsyncGenerator(gen) {
var send = function send(key, arg) {
return new Promise(function(resolve, reject) {
var request = {
key: key,
arg: arg,
resolve: resolve,
reject: reject,
next: null
};
if (back) {
back = back.next = request;
} else {
front = back = request;
resume(key, arg);
}
});
};
var settle = function settle(type, value) {
switch(type){
case "return":
front.resolve({
value: value,
done: true
});
break;
case "throw":
front.reject(value);
break;
default:
front.resolve({
value: value,
done: false
});
break;
}
front = front.next;
if (front) {
resume(front.key, front.arg);
} else {
back = null;
}
};
var front, back;
function resume(key, arg) {
try {
var result = gen[key](arg);
var value = result.value;
var wrappedAwait = _instanceof(value, _awaitValue.default);
Promise.resolve(wrappedAwait ? value.wrapped : value).then(function(arg1) {
if (wrappedAwait) {
resume("next", arg1);
return;
}
settle(result.done ? "return" : "normal", arg1);
}, function(err) {
resume("throw", err);
});
} catch (err) {
settle("throw", err);
}
}
this._invoke = send;
if (typeof gen.return !== "function") {
this.return = undefined;
}
}
if (typeof Symbol === "function" && Symbol.asyncIterator) {
AsyncGenerator.prototype[Symbol.asyncIterator] = function() {
return this;
};
}
AsyncGenerator.prototype.next = function(arg) {
return this._invoke("next", arg);
};
AsyncGenerator.prototype.throw = function(arg) {
return this._invoke("throw", arg);
};
AsyncGenerator.prototype.return = function(arg) {
return this._invoke("return", arg);
};
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = _asyncGeneratorDelegate;
function _asyncGeneratorDelegate(inner, awaitWrap) {
var pump = function pump(key, value) {
waiting = true;
value = new Promise(function(resolve) {
resolve(inner[key](value));
});
return {
done: false,
value: awaitWrap(value)
};
};
var iter = {
}, waiting = false;
;
if (typeof Symbol === "function" && Symbol.iterator) {
iter[Symbol.iterator] = function() {
return this;
};
}
iter.next = function(value) {
if (waiting) {
waiting = false;
return value;
}
return pump("next", value);
};
if (typeof inner.throw === "function") {
iter.throw = function(value) {
if (waiting) {
waiting = false;
throw value;
}
return pump("throw", value);
};
}
if (typeof inner.return === "function") {
iter.return = function(value) {
return pump("return", value);
};
}
return iter;
}
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = _asyncIterator;
function _asyncIterator(iterable) {
var method;
if (typeof Symbol === "function") {
if (Symbol.asyncIterator) {
method = iterable[Symbol.asyncIterator];
if (method != null) return method.call(iterable);
}
if (Symbol.iterator) {
method = iterable[Symbol.iterator];
if (method != null) return method.call(iterable);
}
}
throw new TypeError("Object is not async iterable");
}
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = _asyncToGenerator;
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
try {
var info = gen[key](arg);
var value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
resolve(value);
} else {
Promise.resolve(value).then(_next, _throw);
}
}
function _asyncToGenerator(fn) {
return function() {
var self = this, args = arguments;
return new Promise(function(resolve, reject) {
var gen = fn.apply(self, args);
function _next(value) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
}
function _throw(err) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
}
_next(undefined);
});
};
}
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