diff --git a/Classes/Controller/BackendController.php b/Classes/Controller/BackendController.php index 8a4a68ce82bd08ce4d8a07b4b917e60f0e91d6b6..85c165b1946d40f52f60ae2ac0873aa6e9c4258e 100644 --- a/Classes/Controller/BackendController.php +++ b/Classes/Controller/BackendController.php @@ -87,76 +87,70 @@ class BackendController extends ActionController { * @return void */ public function indexAction(array $filters = []): void { - try { - $pageUid = (int) GeneralUtility::_GP('id'); - /** @var BackendUserAuthentication $backendUser */ - $backendUser = $GLOBALS['BE_USER']; - if ($filters === []) { - $filters = $backendUser->getModuleData('tools_beuser/index.php/web_SgJobsBackend_filters', 'ses') ?: []; - } else { - $backendUser->pushModuleData('tools_beuser/index.php/web_SgJobsBackend_filters', $filters); - } - // create docheader + buttons - $pageInfo = BackendUtility::readPageAccess($pageUid, $GLOBALS['BE_USER']->getPagePermsClause(1)); - if ($pageInfo === FALSE) { - $pageInfo = ['uid' => $pageUid]; - } - - $this->docHeaderComponent = GeneralUtility::makeInstance(DocHeaderComponent::class); - $this->docHeaderComponent->setMetaInformation($pageInfo); - BackendService::makeButtons($this->docHeaderComponent, $this->request); - $this->view->assign('docHeader', $this->docHeaderComponent->docHeaderContent()); - // get all jobs - /** @var ObjectStorage $jobs */ - $jobs = $this->jobRepository->findBackendJobs($pageUid, $filters); - - $totalJobCount = \count($jobs); - - $this->view->assign('pages', BackendService::getPagesWithJobRecords()); - $this->view->assign('pageUid', $pageUid); - - $sortingData = []; - if ($GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['sg_jobs']['allowManualSorting'] && count($filters) <= 0) { - - /** - * - * Somehow the pId is changed so data gets lost. - * - * - * $previousUid = 0; - * $sortingData = []; - * foreach ($jobs as $job) { - * if ($previousUid) { - * $sortingData['prev'][$job->getUid()] = $previousUid; - * $sortingData['next'][$previousUid] = $job->getUid(); - * } - * $previousUid = $job->getUid(); - * } - */ - $this->view->assign('manualSortingDestroysEverything', TRUE); - } - - $this->view->assign('sortingData', $sortingData); - - // get all Locations - /** @noinspection PhpUndefinedMethodInspection */ - /** @var QueryResultInterface $companies */ - $companies = $this->companyRepository->findByPid($pageUid); - $this->view->assign('locationOptions', $companies); - if ($totalJobCount || $companies->count()) { - $this->view->assign('jobs', $jobs); - $this->view->assign('filters', $filters); - } else { - $this->view->assign('noRecords', 1); - $this->view->assign('isAdmin', $GLOBALS['BE_USER']->isAdmin()); - $this->addFlashMessage( - LocalizationUtility::translate('backend.notice.noRecords', 'SgJobs'), '', FlashMessage::INFO - ); - } - - } catch (\Exception $exception) { - // check for NULL value in view and render an error message - $this->view->assign('docHeader', NULL); + $pageUid = (int) GeneralUtility::_GP('id'); + + /** @var BackendUserAuthentication $backendUser */ + $backendUser = $GLOBALS['BE_USER']; + if ($filters === []) { + $filters = $backendUser->getModuleData('tools_beuser/index.php/web_SgJobsBackend_filters', 'ses') ?: []; + } else { + $backendUser->pushModuleData('tools_beuser/index.php/web_SgJobsBackend_filters', $filters); + } + + // create docheader + buttons + $pageInfo = BackendUtility::readPageAccess($pageUid, $GLOBALS['BE_USER']->getPagePermsClause(1)); + if ($pageInfo === FALSE) { + $pageInfo = ['uid' => $pageUid]; + } + + $this->docHeaderComponent = GeneralUtility::makeInstance(DocHeaderComponent::class); + $this->docHeaderComponent->setMetaInformation($pageInfo); + BackendService::makeButtons($this->docHeaderComponent, $this->request); + $this->view->assign('docHeader', $this->docHeaderComponent->docHeaderContent()); + + $this->view->assign('pageUid', $pageUid); + $this->view->assign('pages', BackendService::getPagesWithJobRecords()); + + $sortingData = []; + if ($GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['sg_jobs']['allowManualSorting'] && count($filters) <= 0) { + /** + * Somehow the pId is changed so data gets lost. + * + * + * $previousUid = 0; + * $sortingData = []; + * foreach ($jobs as $job) { + * if ($previousUid) { + * $sortingData['prev'][$job->getUid()] = $previousUid; + * $sortingData['next'][$previousUid] = $job->getUid(); + * } + * $previousUid = $job->getUid(); + * } + */ + $this->view->assign('manualSortingDestroysEverything', TRUE); + } + $this->view->assign('sortingData', $sortingData); + + /** @var ObjectStorage $jobs */ + $jobs = $this->jobRepository->findBackendJobs($pageUid, $filters); + $totalJobCount = \count($jobs); + + // get all Locations + /** @noinspection PhpUndefinedMethodInspection */ + /** @var QueryResultInterface $companies */ + $companies = $this->companyRepository->findByPid($pageUid); + $this->view->assign('locationOptions', $companies); + + $this->view->assign('isAdmin', $GLOBALS['BE_USER']->isAdmin()); + $this->view->assign('filters', $filters); + $this->view->assign('jobs', $jobs); + if (!$totalJobCount && $pageUid) { + // clear previously set filters again, + // without this, the user would be stuck without filters and just the flash message + $backendUser->pushModuleData('tools_beuser/index.php/web_SgJobsBackend_filters', []); + $this->addFlashMessage( + LocalizationUtility::translate('backend.notice.noRecords', 'SgJobs'), '', FlashMessage::INFO + ); } } } diff --git a/Classes/Controller/JobTeaserController.php b/Classes/Controller/JobTeaserController.php index 32c486187663712c9e5e183382491f2bbc9d3b71..b6400c306fe6980e51c7daf3473feeb70bdfa9db 100644 --- a/Classes/Controller/JobTeaserController.php +++ b/Classes/Controller/JobTeaserController.php @@ -37,7 +37,14 @@ class JobTeaserController extends ActionController { /** * @var JobRepository */ - private $jobRepository; + protected $jobRepository; + + /** + * @param JobRepository $jobRepository + */ + public function injectJobRepository(JobRepository $jobRepository) { + $this->jobRepository = $jobRepository; + } /** * Get marked offers and display them @@ -47,7 +54,7 @@ class JobTeaserController extends ActionController { */ public function indexAction(): void { $allowedLocations = []; - if (!empty($this->settings['locations'])) { + if ($this->settings['locations'] !== '') { $allowedLocations = GeneralUtility::trimExplode(',', $this->settings['locations']); } $totalAmountOfOffers = $this->jobRepository->countAll($allowedLocations); @@ -58,14 +65,7 @@ class JobTeaserController extends ActionController { $featuredOffers = $this->jobRepository->findByFeaturedOffer($allowedLocations); $this->view->assign('totalAmountOfOffers', $totalAmountOfOffers); - $this->view->assign('filteredLocations', !empty($allowedLocations)); + $this->view->assign('filteredLocations', (bool) count($allowedLocations)); $this->view->assign('featuredOffers', $featuredOffers); } - - /** - * @param JobRepository $jobRepository - */ - public function injectJobRepository(JobRepository $jobRepository) { - $this->jobRepository = $jobRepository; - } } diff --git a/Classes/Controller/JoblistController.php b/Classes/Controller/JoblistController.php index 19418c4315762ecb1bc39057ede505474f3cdf66..88b9eec3d1fb32b69158564ecc4ea5f81319e698 100644 --- a/Classes/Controller/JoblistController.php +++ b/Classes/Controller/JoblistController.php @@ -36,15 +36,20 @@ use SGalinski\SgJobs\Domain\Repository\JobApplicationRepository; use SGalinski\SgJobs\Domain\Repository\JobRepository; use SGalinski\SgJobs\Property\TypeConverter\UploadedFileReferenceConverter; use SGalinski\SgMail\Service\MailTemplateService; +use SGalinski\SgSeo\Service\HeadTagService; use TYPO3\CMS\Core\Context\Context; use TYPO3\CMS\Core\Core\Environment; +use TYPO3\CMS\Core\Http\ImmediateResponseException; use TYPO3\CMS\Core\Resource\ResourceFactory; use TYPO3\CMS\Core\Site\SiteFinder; +use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface; use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; use TYPO3\CMS\Extbase\Mvc\Exception\NoSuchArgumentException; use TYPO3\CMS\Extbase\Utility\LocalizationUtility; +use TYPO3\CMS\Frontend\Controller\ErrorController; +use TYPO3\CMS\Frontend\Page\PageAccessFailureReasons; /** * The joblist plugin controller @@ -123,15 +128,31 @@ class JoblistController extends ActionController { $this->jobRepository = $jobRepository; } + /** + * Initialize the indexAction to set the currentPageBrowserPage parameter + * + * @throws \TYPO3\CMS\Extbase\Mvc\Exception\InvalidArgumentNameException + */ + public function initializeIndexAction() { + $currentPageBrowserPage = (int) GeneralUtility::_GP('tx_sgjobs_pagebrowser')['currentPage']; + if ($currentPageBrowserPage > 0) { + $this->request->setArgument('currentPageBrowserPage', $currentPageBrowserPage); + } + } + /** * Show all job offers and options to manage them * * @param array $filters * @param int $jobId + * @param int $currentPageBrowserPage * @return void - * @throws \InvalidArgumentException + * @throws \TYPO3\CMS\Core\Context\Exception\AspectNotFoundException + * @throws \TYPO3\CMS\Core\Error\Http\PageNotFoundException + * @throws ImmediateResponseException + * @throws \TYPO3\CMS\Core\Package\Exception */ - public function indexAction(array $filters = [], $jobId = NULL): void { + public function indexAction(array $filters = [], int $jobId = NULL, int $currentPageBrowserPage = 0): void { if ($filters) { $this->view->assign('selectedCountry', $filters['filterCountry']); $this->view->assign('selectedCompany', $filters['filterCompany']); @@ -157,17 +178,27 @@ class JoblistController extends ActionController { throw new \InvalidArgumentException('Given Job Id is invalid!'); } - $GLOBALS['TSFE']->page['titlebyextension'] = $job->getTitle(); - $GLOBALS['TSFE']->page['description'] = \strip_tags(\substr($job->getDescription(), 0, 200)); - $GLOBALS['TSFE']->page['extensionArgumentsForCanonicalAndHrefLang'] = - '&tx_sgjobs_jobapplication[jobId]=' . $jobId; + if (version_compare(ExtensionManagementUtility::getExtensionVersion('sg_seo'), '5.0.0', '>=')) { + $headTagService = GeneralUtility::makeInstance( + HeadTagService::class, + TRUE, + $job->getTitle(), + $job->getDescription(), + '&tx_sgjobs_jobapplication[jobId]=' . $jobId + ); + $headTagService->execute(); + } else { + $GLOBALS['TSFE']->page['titlebyextension'] = $job->getTitle(); + $GLOBALS['TSFE']->page['description'] = \strip_tags(\substr($job->getDescription(), 0, 200)); + $GLOBALS['TSFE']->page['extensionArgumentsForCanonicalAndHrefLang'] = + '&tx_sgjobs_jobapplication[jobId]=' . $jobId; + } $jobs = [$job]; $numberOfPages = 1; } else { // pagination logic $offset = 0; - $currentPageBrowserPage = (int) GeneralUtility::_GP('tx_sgjobs_pagebrowser')['currentPage']; if ($currentPageBrowserPage && $jobLimit) { $offset = $currentPageBrowserPage * $jobLimit; } @@ -192,7 +223,17 @@ class JoblistController extends ActionController { // get all jobs for the current page $allJobs = $this->jobRepository->findJobsByFilter($filters)->toArray(); - $numberOfPages = ($jobLimit <= 0 ? 0 : \ceil(\count($allJobs) / $jobLimit)); + $numberOfPages = (int) ($jobLimit <= 0 ? 0 : \ceil(\count($allJobs) / $jobLimit)); + if ($numberOfPages !== 0 && $currentPageBrowserPage >= $numberOfPages) { + /** @var ErrorController $errorController */ + $errorController = GeneralUtility::makeInstance(ErrorController::class); + $response = $errorController->pageNotFoundAction( + $GLOBALS['TYPO3_REQUEST'], + 'The requested page does not exist', + ['code' => PageAccessFailureReasons::PAGE_NOT_FOUND] + ); + throw new ImmediateResponseException($response); + } } $this->view->assign('jobs', $jobs); @@ -203,16 +244,15 @@ class JoblistController extends ActionController { /** * Renders the application form with an optional job * - * @param JobApplication $applyData + * @param JobApplication|null $applyData * @param string $error * @param int $jobId + * @throws \TYPO3\CMS\Core\Context\Exception\AspectNotFoundException * @throws \TYPO3\CMS\Extbase\Mvc\Exception\InvalidArgumentNameException * @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException - * @throws \TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException - * @throws \TYPO3\CMS\Core\Context\Exception\AspectNotFoundException */ - public function applyFormAction(JobApplication $applyData = NULL, $error = NULL, $jobId = NULL): void { - if ($error !== NULL && $error !== '') { + public function applyFormAction(JobApplication $applyData = NULL, string $error = '', int $jobId = NULL): void { + if ($error !== '') { $this->view->assign('internalError', $error); $this->request->setArgument('error', NULL); } @@ -244,10 +284,21 @@ class JoblistController extends ActionController { /** @var Job $job */ $job = $this->jobRepository->findByUid($jobId); if ($job) { - $GLOBALS['TSFE']->page['titlebyextension'] = $job->getTitle(); - $GLOBALS['TSFE']->page['description'] = \strip_tags(\substr($job->getDescription(), 0, 200)); - $GLOBALS['TSFE']->page['extensionArgumentsForCanonicalAndHrefLang'] = - '&tx_sgjobs_jobapplication[jobId]=' . $jobId; + if (version_compare(ExtensionManagementUtility::getExtensionVersion('sg_seo'), '5.0.0', '>=')) { + $headTagService = GeneralUtility::makeInstance( + HeadTagService::class, + FALSE, + $job->getTitle(), + $job->getDescription(), + '&tx_sgjobs_jobapplication[jobId]=' . $jobId + ); + $headTagService->execute(); + } else { + $GLOBALS['TSFE']->page['titlebyextension'] = $job->getTitle(); + $GLOBALS['TSFE']->page['description'] = \strip_tags(\substr($job->getDescription(), 0, 200)); + $GLOBALS['TSFE']->page['extensionArgumentsForCanonicalAndHrefLang'] = + '&tx_sgjobs_jobapplication[jobId]=' . $jobId; + } } $this->view->assign('job', $job); } else { @@ -263,8 +314,11 @@ class JoblistController extends ActionController { $site = GeneralUtility::makeInstance(SiteFinder::class) ->getSiteByPageId($GLOBALS['TSFE']->id) ->getLanguageById($sysLanguageUid); - $countryRepository = $this->objectManager->get(CountryRepository::class); - $countries = $countryRepository->findAllOrderedByLanguage($site->getTwoLetterIsoCode()); + $countries = []; + if (ExtensionManagementUtility::isLoaded('project_base')) { + $countryRepository = $this->objectManager->get(CountryRepository::class); + $countries = $countryRepository->findAllOrderedByLanguage($site->getTwoLetterIsoCode()); + } $this->view->assign('countries', $countries); $this->view->assign('sysLanguageUid', $sysLanguageUid); @@ -302,7 +356,6 @@ class JoblistController extends ActionController { * @return void * @throws NoSuchArgumentException * @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException - * @throws \TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException */ protected function initializeApplyAction(): void { try { @@ -333,17 +386,14 @@ class JoblistController extends ActionController { * @param string $folderName * @return void * @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException - * @throws \RuntimeException - * @throws \InvalidArgumentException - * @throws \TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException - * @throws \TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException */ protected function submitApplicationFiles(JobApplication $applicationData, $folderName): void { $resourceFactory = $this->objectManager->get(ResourceFactory::class); $newName = \date('Ymd-His') . '_' . $applicationData->getJobId() . '-' . $applicationData->getFirstName() . '-' . $applicationData->getLastName(); $storage = $resourceFactory->getStorageObject(1); - $applicationFilePath = Environment::getPublicPath() . '/' . $storage->getConfiguration()['basePath'] . 'JobApplication/' . $folderName . + $applicationFilePath = Environment::getPublicPath() . '/' . $storage->getConfiguration( + )['basePath'] . 'JobApplication/' . $folderName . '/' . $newName . '.csv'; $this->writeApplicationFile($applicationData, $applicationFilePath); } @@ -363,9 +413,7 @@ class JoblistController extends ActionController { $applyData->setPid($GLOBALS['TSFE']->id); $job = $applyData->getJob(); // look for a configured default job, in case of unsolicited application - if (($job === '' || $job === NULL || $applyData->getJobId() === NULL) - && $applyData->getCompany() !== NULL - ) { + if ((!$job || $applyData->getJobId() === NULL) && $applyData->getCompany() !== NULL) { $applyData->setJobId($applyData->getCompany()->getJobId()); } @@ -378,7 +426,6 @@ class JoblistController extends ActionController { $this->moveTmpFolder($folderName); $this->submitApplicationFiles($applyData, $folderName); - /** @noinspection PhpMethodParametersCountMismatchInspection */ $mailService = $this->objectManager->get( MailTemplateService::class, 'application_mail', 'sg_jobs', $this->getApplicationMailMarkers($applyData) @@ -426,8 +473,11 @@ class JoblistController extends ActionController { $redirectPageUid = (int) $this->settings['redirectPage']; if ($redirectPageUid) { - $url = $this->configurationManager->getContentObject()->getTypoLink_URL($redirectPageUid); - $this->redirectToUri($url); + $contentObject = $this->configurationManager->getContentObject(); + if ($contentObject) { + $url = $contentObject->getTypoLink_URL($redirectPageUid); + $this->redirectToUri($url); + } } $this->redirect('applyForm'); @@ -448,7 +498,7 @@ class JoblistController extends ActionController { * Assign filter values * * @param int $rootPageId - * @throws \InvalidArgumentException + * @throws \TYPO3\CMS\Core\Context\Exception\AspectNotFoundException */ protected function assignFilterValues($rootPageId): void { $countries = $this->companyRepository->getAllCountries($rootPageId); @@ -473,7 +523,7 @@ class JoblistController extends ActionController { * @param JobApplication $applyData * @return array */ - protected function getApplicationMailMarkers($applyData): array { + protected function getApplicationMailMarkers(JobApplication $applyData): array { $location = ''; if ($applyData->getCompany() !== NULL) { $location = $applyData->getCompany()->getCity(); @@ -499,13 +549,36 @@ class JoblistController extends ActionController { * * @param JobApplication $data * @param string $filePath - * @throws \TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException * @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException - * @throws \InvalidArgumentException - * @throws \RuntimeException - * @throws \TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException */ protected function writeApplicationFile(JobApplication $data, $filePath): void { + $coverLetter = ''; + $coverLetterObject = $data->getCoverLetter(); + if ($coverLetterObject) { + $coverLetterObject = $coverLetterObject->getOriginalResource(); + if ($coverLetterObject) { + $coverLetter = $coverLetterObject->getPublicUrl(); + } + } + + $cv = ''; + $cvObject = $data->getCv(); + if ($cvObject) { + $cvObject = $cvObject->getOriginalResource(); + if ($cvObject) { + $cv = $cvObject->getPublicUrl(); + } + } + + $certificate = ''; + $certificateObject = $data->getCertificate(); + if ($certificateObject) { + $certificateObject = $certificateObject->getOriginalResource(); + if ($certificateObject) { + $certificate = $certificateObject->getPublicUrl(); + } + } + $dataToInsertArr = [ $data->getJobId(), $data->getFirstName(), @@ -520,9 +593,9 @@ class JoblistController extends ActionController { $data->getNationality(), $data->getPhone(), $data->getEmail(), - $data->getCoverLetter() === NULL ? '' : $data->getCoverLetter()->getOriginalResource()->getPublicUrl(), - $data->getCv() === NULL ? '' : $data->getCv()->getOriginalResource()->getPublicUrl(), - $data->getCertificate() === NULL ? '' : $data->getCertificate()->getOriginalResource()->getPublicUrl(), + $coverLetter, + $cv, + $certificate, $data->getMessage() ]; @@ -583,7 +656,7 @@ class JoblistController extends ActionController { /** * If for any reason something goes wrong, delete the tmp upload folder * - * @return string|void + * @return void * @throws NoSuchArgumentException */ public function errorAction() { diff --git a/Classes/Controller/PageBrowserController.php b/Classes/Controller/PageBrowserController.php deleted file mode 100644 index 42f0a5f1848d705285b595b2f660e55f3c2ef74e..0000000000000000000000000000000000000000 --- a/Classes/Controller/PageBrowserController.php +++ /dev/null @@ -1,146 +0,0 @@ -<?php - -namespace SGalinski\SgJobs\Controller; - -/*************************************************************** - * 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\Extbase\Mvc\Controller\ActionController; - -/** - * Controller that handles the page browser. - */ -class PageBrowserController extends ActionController { - /** - * @var string - */ - protected $pageParameterName = ''; - - /** - * @var int - */ - protected $numberOfPages = 0; - - /** - * @var int - */ - protected $currentPage = 0; - - /** - * @var int - */ - protected $pagesBefore = 0; - - /** - * @var int - */ - protected $pagesAfter = 0; - - /** - * @var bool - */ - protected $enableMorePages = FALSE; - - /** - * @var bool - */ - protected $enableLessPages = FALSE; - - /** - * Renders the index view. - * - * @param int $currentPage - * @return void - */ - public function indexAction($currentPage = 0): void { - $this->currentPage = (int) $currentPage; - - $this->initializeConfiguration(); - $this->addDataToView(); - } - - /** - * Initializes the configuration. - * - * @return void - */ - protected function initializeConfiguration(): void { - $this->pageParameterName = 'tx_sgjobs_pagebrowser[currentPage]'; - $this->numberOfPages = (int) $this->settings['numberOfPages']; - $this->pagesBefore = (int) $this->settings['pagesBefore']; - $this->pagesAfter = (int) $this->settings['pagesAfter']; - $this->enableMorePages = (bool) $this->settings['enableMorePages']; - $this->enableLessPages = (bool) $this->settings['enableLessPages']; - } - - /** - * Adds the necessary data to the view. - * - * @return void - */ - protected function addDataToView(): void { - if ($this->numberOfPages <= 1) { - $this->view = NULL; - return; - } - - $pageLinks = []; - $start = max($this->currentPage - $this->pagesBefore, 0); - $end = min($this->numberOfPages, $this->currentPage + $this->pagesAfter + 1); - for ($i = $start; $i < $end; $i++) { - $pageLinks[] = [ - 'number' => $i + 1, - 'link' => $this->getPageLink($i), - 'isCurrentPage' => $i === $this->currentPage, - ]; - } - - $this->view->assignMultiple( - [ - 'enableLessPages' => $this->enableLessPages, - 'enableMorePages' => $this->enableMorePages, - 'previousLink' => $this->getPageLink($this->currentPage - 1), - 'nextLink' => $this->getPageLink($this->currentPage + 1), - 'enableLessPagesLink' => $this->getPageLink($this->currentPage - $this->pagesBefore - 1), - 'enableMorePagesLink' => $this->getPageLink($this->currentPage + $this->pagesAfter + 1), - 'pageLinks' => $pageLinks, - 'prevPageExist' => $this->currentPage > 0, - 'showLessPages' => ($this->currentPage - $this->pagesBefore) > 0, - 'showNextPages' => ($this->currentPage + $this->pagesAfter + 1) < $this->numberOfPages, - 'nextPageExist' => $this->currentPage < ($this->numberOfPages - 1), - ] - ); - } - - /** - * Generates page link. Keeps all current URL parameters except for cHash and tx_pagebrowse_pi1[page]. - * - * @param int $page Page number starting from 1 - * @return string - */ - protected function getPageLink($page): string { - return $this->uriBuilder->reset()->setAddQueryString(TRUE) - ->uriFor('index', ['currentPage' => $page,]); - } -} diff --git a/Classes/Domain/Model/Job.php b/Classes/Domain/Model/Job.php index 181b61a963f17df780f245a6481097888ae3efe8..92c649b28d9d0235a06abd4bec6dbf33a70c1a52 100644 --- a/Classes/Domain/Model/Job.php +++ b/Classes/Domain/Model/Job.php @@ -34,13 +34,6 @@ use TYPO3\CMS\Extbase\Persistence\ObjectStorage; * The Job model */ class Job extends AbstractEntity { - /** - * Job constructor. - */ - public function __construct() { - $this->attachment = new ObjectStorage(); - } - /** * @var string $title */ @@ -102,6 +95,11 @@ class Job extends AbstractEntity { */ protected $startDate = 0; + /** + * @var string $location + */ + protected $location = ''; + /** * @var int $sorting */ @@ -167,6 +165,16 @@ class Job extends AbstractEntity { */ protected $maxSalary = ''; + /** @var string */ + protected $applyExternalLink = ''; + + /** + * Job constructor. + */ + public function __construct() { + $this->attachment = new ObjectStorage(); + } + /** * @return string */ @@ -339,6 +347,20 @@ class Job extends AbstractEntity { $this->alternativeStartDate = $alternativeStartDate; } + /** + * @return string + */ + public function getLocation() { + return $this->location; + } + + /** + * @param string $location + */ + public function setLocation(string $location) { + $this->location = $location; + } + /** * @return bool */ @@ -520,4 +542,18 @@ class Job extends AbstractEntity { public function setMaxSalary(string $maxSalary): void { $this->maxSalary = $maxSalary; } + + /** + * @return string + */ + public function getApplyExternalLink() { + return $this->applyExternalLink; + } + + /** + * @param string $applyExternalLink + */ + public function setApplyExternalLink(string $applyExternalLink) { + $this->applyExternalLink = $applyExternalLink; + } } diff --git a/Classes/Domain/Repository/JobRepository.php b/Classes/Domain/Repository/JobRepository.php index cda7e43036167c8b84d6387c3b9236b86f57cd32..60d828192a671246964700fac160b439199beeef 100644 --- a/Classes/Domain/Repository/JobRepository.php +++ b/Classes/Domain/Repository/JobRepository.php @@ -26,7 +26,6 @@ namespace SGalinski\SgJobs\Domain\Repository; * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ -use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Object\ObjectManagerInterface; use TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings; use TYPO3\CMS\Extbase\Persistence\QueryInterface; @@ -49,7 +48,6 @@ class JobRepository extends Repository { public function __construct(ObjectManagerInterface $objectManager) { parent::__construct($objectManager); - // Appearently some of the extbase classes still need the objectManager $querySettings = $objectManager->get(Typo3QuerySettings::class); $querySettings->setRespectStoragePage(TRUE); $this->setDefaultQuerySettings($querySettings); @@ -77,7 +75,7 @@ class JobRepository extends Repository { * @param int $limit * @param int $offset * @return mixed - * @throws \InvalidArgumentException + * @throws \TYPO3\CMS\Extbase\Persistence\Exception\InvalidQueryException */ public function findBackendJobs($recordPageId, array $filters = [], $limit = 0, $offset = 0) { $query = $this->createQuery(); @@ -111,7 +109,7 @@ class JobRepository extends Repository { return $query->matching($query->logicalAnd($constraints))->execute(); } - if(\count($constraints) > 0) { + if (\count($constraints) > 0) { return $query->matching($constraints[0])->execute(); } @@ -146,7 +144,7 @@ class JobRepository extends Repository { $constraints = []; - if ($jobIds !== NULL && \is_array($jobIds) && \count($jobIds)) { + if (\is_array($jobIds) && \count($jobIds)) { $companyConstraints = []; foreach ($jobIds as $jobId) { if ($jobId) { @@ -261,7 +259,6 @@ class JobRepository extends Repository { * @throws \TYPO3\CMS\Extbase\Persistence\Exception\InvalidQueryException */ public function countAll(array $companies = []): int { - /** @var QueryInterface $query */ $query = $this->createQuery(); $constraints = []; @@ -284,32 +281,30 @@ class JobRepository extends Repository { * Gets the featured jobs filtered by companies * * @param array $companies - * @return mixed + * @param int $limit + * @return array|ExtbaseQueryResultInterface * @throws \TYPO3\CMS\Extbase\Persistence\Exception\InvalidQueryException */ - public function findByFeaturedOffer(array $companies = []) { - /** @var QueryInterface $query */ + public function findByFeaturedOffer(array $companies = [], int $limit = 3) { $query = $this->createQuery(); - $constraints = []; $storagePageIds = $query->getQuerySettings()->getStoragePageIds(); - if (empty($storagePageIds)) { + if (count($storagePageIds) <= 0) { // if no record storage page has been selected in the plugin, ignore it $query->getQuerySettings()->setRespectStoragePage(FALSE); } - if (\count($companies) !== 0) { - $constraints[] = $query->in('company', $companies); - $query->setOrderings( - [ - 'featured_offer' => QueryInterface::ORDER_DESCENDING - ] - ); - $query->setLimit(3); - } else { - $constraints[] = $query->equals('featured_offer', TRUE); + if (\count($companies)) { + $query->matching($query->in('company', $companies)); } - return $query->matching($query->logicalAnd($constraints))->execute(); + $query->setOrderings( + [ + 'featured_offer' => QueryInterface::ORDER_DESCENDING + ] + ); + + $query->setLimit($limit); + return $query->execute(); } } diff --git a/Classes/Event/Listener/AccessPageListEventListener.php b/Classes/Event/Listener/AccessPageListEventListener.php index 9fcd678e9157be05ae6489ab89fe2cdaf8d2544a..ad206758d59242d68e60a6bf3fb93429cd206670 100644 --- a/Classes/Event/Listener/AccessPageListEventListener.php +++ b/Classes/Event/Listener/AccessPageListEventListener.php @@ -31,7 +31,10 @@ class AccessPageListEventListener { */ public function __invoke(AccessPageListEvent $event) { $pageList = $event->getPageList(); - $additionalPageList = $this->sitemapService->generatePagesList($event->getSysLanguageUid()); + $additionalPageList = $this->sitemapService->generatePagesList( + $event->getSysLanguageUid(), $event->getSite()->getRootPageId() + ); + ArrayUtility::mergeRecursiveWithOverrule($pageList, $additionalPageList); $event->setPageList($pageList); } diff --git a/Classes/Service/SitemapService.php b/Classes/Service/SitemapService.php index 17f4743920c8daf1d1e3d22f1ed734f40d8e1214..fb156ac49879f09a71ce0877c084938bae78c3ae 100644 --- a/Classes/Service/SitemapService.php +++ b/Classes/Service/SitemapService.php @@ -3,6 +3,7 @@ namespace SGalinski\SgJobs\Service; use TYPO3\CMS\Core\Context\Context; +use TYPO3\CMS\Core\Context\LanguageAspect; use TYPO3\CMS\Core\Database\Connection; use TYPO3\CMS\Core\Database\ConnectionPool; use SgJobsPageRepository; @@ -28,15 +29,19 @@ class SitemapService { * Generate a pageList array for the sitemap generation * * @param int $sysLanguageUid + * @param int $rootPageId * @return array + * @throws \TYPO3\CMS\Core\Context\Exception\AspectNotFoundException */ - public function generatePagesList(int $sysLanguageUid): array { + public function generatePagesList(int $sysLanguageUid, int $rootPageId = 0): array { $pageList = []; // find sites where job detail plugin is added $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) ->getQueryBuilderForTable('tt_content'); - $databaseResource = $queryBuilder->select('pid', 'pages') + $databaseResource = $queryBuilder->select('tt_content.pid', 'pages', 'tt_content.tstamp') ->from('tt_content') + ->innerJoin('tt_content', 'pages', 'pages', 'pages.uid = tt_content.pid + AND pages.no_search = 0') ->where( $queryBuilder->expr()->andX( $queryBuilder->expr()->eq('CType', $queryBuilder->createNamedParameter('list')), @@ -45,9 +50,9 @@ class SitemapService { $queryBuilder->createNamedParameter(self::PLUGIN_NAME) ), $queryBuilder->expr()->orX( - $queryBuilder->expr()->eq('sys_language_uid', -1), + $queryBuilder->expr()->eq('tt_content.sys_language_uid', -1), $queryBuilder->expr()->eq( - 'sys_language_uid', + 'tt_content.sys_language_uid', $queryBuilder->createNamedParameter($sysLanguageUid, Connection::PARAM_INT) ) ) @@ -55,11 +60,13 @@ class SitemapService { ) ->execute(); - $rows = $databaseResource->fetchAll(); $context = GeneralUtility::makeInstance(Context::class); - foreach ($rows as $row) { + while ($row = $databaseResource->fetch()) { try { $site = GeneralUtility::makeInstance(SiteFinder::class)->getSiteByPageId($row['pid']); + if ($rootPageId > 0 && $site->getRootPageId() !== $rootPageId) { + continue; + } $jobs = $this->getJobsByPid($row['pages'], $sysLanguageUid); foreach ($jobs as $job) { $url = $site->getRouter($context)->generateUri( @@ -73,7 +80,8 @@ class SitemapService { ); $pageList[] = [ 'url' => htmlspecialchars($url), - 'title' => '' + 'title' => '', + 'SYS_LASTCHANGED' => $row['tstamp'], ]; } } catch (SiteNotFoundException $exception) { @@ -107,6 +115,8 @@ class SitemapService { $rows = $databaseResource->fetchAll(); $pageRepository = GeneralUtility::makeInstance(SgJobsPageRepository::class); $isLanguageVisibilityLoaded = ExtensionManagementUtility::isLoaded('languagevisibility'); + $languageAspect = GeneralUtility::makeInstance(Context::class)->getAspect('language'); + $overlayType = $languageAspect->getLegacyOverlayType(); foreach ($rows as $row) { $table = 'tx_sgjobs_domain_model_job'; @@ -127,7 +137,11 @@ class SitemapService { } if ($languageId > 0) { - $row = $pageRepository->getRecordOverlay($table, $row, $languageId); + /** @var LanguageAspect $languageAspect */ + $row = $pageRepository->getRecordOverlay($table, $row, $languageId, $overlayType); + if (!is_array($row) || !count($row)) { + continue; + } } $visibleRows[] = $row; diff --git a/Classes/ViewHelpers/Backend/ControlViewHelper.php b/Classes/ViewHelpers/Backend/ControlViewHelper.php index eac8f91167ca96f4ec5f2d4b8bcac9dc80230d16..75b0026a93a93494cf674c238de6ad93c68eb9c8 100644 --- a/Classes/ViewHelpers/Backend/ControlViewHelper.php +++ b/Classes/ViewHelpers/Backend/ControlViewHelper.php @@ -26,6 +26,7 @@ namespace SGalinski\SgJobs\ViewHelpers\Backend; * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ +use SGalinski\SgJobs\Domain\Model\Job; use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Core\Page\PageRenderer; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -40,41 +41,31 @@ class ControlViewHelper extends AbstractViewHelper { /** * Initialize the ViewHelper arguments */ - public function initializeArguments() { + public function initializeArguments(): void { parent::initializeArguments(); $this->registerArgument('table', 'string', 'The table to control', TRUE); - $this->registerArgument('row', 'mixed', 'The row of the record', TRUE); - $this->registerArgument('sortingData', 'array', 'The sorting data', FALSE, []); + $this->registerArgument('row', 'object', 'The row of the record', TRUE); } /** * Renders the control buttons for the specified record * * @return string - * @throws \InvalidArgumentException - * @throws \UnexpectedValueException */ public function render(): string { - $row = $this->arguments['row']; $table = $this->arguments['table']; - $sortingData = $this->arguments['sortingData']; + /** @var Job $row */ + $row = $this->arguments['row']; + $row = BackendUtility::getRecord('tx_sgjobs_domain_model_job', $row->getUid()); - /** @var DatabaseRecordList $databaseRecordList */ $databaseRecordList = GeneralUtility::makeInstance(DatabaseRecordList::class); - - if (!\is_array($row)) { - $row = BackendUtility::getRecord($table, $row->getUid()); - } - $pageInfo = BackendUtility::readPageAccess($row['pid'], $GLOBALS['BE_USER']->getPagePermsClause(1)); + $databaseRecordList->calcPerms = $GLOBALS['BE_USER']->calcPerms($pageInfo); $pageRenderer = GeneralUtility::makeInstance(PageRenderer::class); $pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/AjaxDataHandler'); $pageRenderer->addInlineLanguageLabelFile('EXT:backend/Resources/Private/Language/locallang_alt_doc.xlf'); $languageService = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Localization\LanguageService::class); $languageService->includeLLFile('EXT:backend/Resources/Private/Language/locallang_alt_doc.xlf'); - $databaseRecordList->calcPerms = $GLOBALS['BE_USER']->calcPerms($pageInfo); - $databaseRecordList->currentTable = $sortingData; return $databaseRecordList->makeControl($table, $row); } - } diff --git a/Classes/ViewHelpers/Backend/IconViewHelper.php b/Classes/ViewHelpers/Backend/IconViewHelper.php new file mode 100644 index 0000000000000000000000000000000000000000..beef6cc6f8245740bfb71c6eb3d21523dbf8caa2 --- /dev/null +++ b/Classes/ViewHelpers/Backend/IconViewHelper.php @@ -0,0 +1,71 @@ +<?php + +namespace SGalinski\SgJobs\ViewHelpers\Backend; + +/*************************************************************** + * 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\Backend\Utility\BackendUtility; +use TYPO3\CMS\Core\Imaging\Icon; +use TYPO3\CMS\Core\Imaging\IconFactory; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper; + +/** + * Class EditLink + **/ +class IconViewHelper extends AbstractViewHelper { + /** + * Register the ViewHelper arguments + */ + public function initializeArguments(): void { + parent::initializeArguments(); + $this->registerArgument('table', 'string', 'The table for the icon', TRUE); + $this->registerArgument('row', 'object', 'The row of the record', TRUE); + $this->registerArgument('clickMenu', 'bool', 'Render a clickMenu around the icon', FALSE, TRUE); + } + + /** + * Renders the icon for the specified record + * + * @return string + */ + public function render(): string { + $row = $this->arguments['row']; + $row = BackendUtility::getRecord('tx_sgjobs_domain_model_job', $row->getUid()); + + $table = $this->arguments['table']; + $clickMenu = $this->arguments['clickMenu']; + $iconFactory = GeneralUtility::makeInstance(IconFactory::class); + $toolTip = BackendUtility::getRecordToolTip($row, $table); + $iconImg = '<span ' . $toolTip . '>' + . $iconFactory->getIconForRecord($table, $row, Icon::SIZE_SMALL)->render() + . '</span>'; + if ($clickMenu) { + return BackendUtility::wrapClickMenuOnIcon($iconImg, $table, $row['uid']); + } + return $iconImg; + } + +} diff --git a/Classes/ViewHelpers/Backend/Widget/Controller/PaginateController.php b/Classes/ViewHelpers/Backend/Widget/Controller/PaginateController.php new file mode 100644 index 0000000000000000000000000000000000000000..47265580a5adfcdcb6aeb7e7e530f2825c95898a --- /dev/null +++ b/Classes/ViewHelpers/Backend/Widget/Controller/PaginateController.php @@ -0,0 +1,98 @@ +<?php + +namespace SGalinski\SgJobs\ViewHelpers\Backend\Widget\Controller; + +/*************************************************************** + * 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! + ***************************************************************/ + +/** + * PaginateController + */ +class PaginateController extends \TYPO3\CMS\Fluid\ViewHelpers\Be\Widget\Controller\PaginateController { + + /** + * @var mixed + */ + protected $objects; + + /** + * Renders the paginator + * + * @param int $currentPage + * @return void + */ + public function indexAction($currentPage = 1) { + // set current page + $this->currentPage = (int) $currentPage; + if ($this->currentPage < 1) { + $this->currentPage = 1; + } + + if ($this->currentPage > $this->numberOfPages) { + // set $modifiedObjects to NULL if the page does not exist + $modifiedObjects = NULL; + } else { + // modify query + $this->itemsPerPage = (int) $this->configuration['itemsPerPage']; + $this->offset = $this->itemsPerPage * ($this->currentPage - 1); + if (\is_array($this->objects)) { + $modifiedObjects = []; + for ($index = $this->offset; $index < $this->offset + $this->itemsPerPage; $index++) { + if (isset($this->objects[$index])) { + $modifiedObjects[] = $this->objects[$index]; + } else { + break; + } + } + } else { + $query = $this->objects->getQuery(); + $query->setLimit($this->itemsPerPage); + if ($this->currentPage > 1) { + $query->setOffset($this->offset); + } + + $modifiedObjects = $query->execute(); + } + } + $this->view->assign( + 'contentArguments', [ + $this->widgetConfiguration['as'] => $modifiedObjects + ] + ); + $this->view->assign('configuration', $this->configuration); + $this->view->assign('pagination', $this->buildPagination()); + } + + /** + * Returns an array with the keys "pages", "current", "numberOfPages", + * "nextPage" & "previousPage" + * + * @return array + */ + protected function buildPagination() { + $pagination = parent::buildPagination(); + $pagination['totalObjects'] = \count($this->objects); + return $pagination; + } +} diff --git a/Classes/ViewHelpers/Backend/Widget/PaginateViewHelper.php b/Classes/ViewHelpers/Backend/Widget/PaginateViewHelper.php new file mode 100644 index 0000000000000000000000000000000000000000..db62e1b0da75dfedd58b013bff8e0c850898b89c --- /dev/null +++ b/Classes/ViewHelpers/Backend/Widget/PaginateViewHelper.php @@ -0,0 +1,81 @@ +<?php + +namespace SGalinski\SgJobs\ViewHelpers\Backend\Widget; + +/*************************************************************** + * 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 SGalinski\SgJobs\ViewHelpers\Backend\Widget\Controller\PaginateController; +use TYPO3\CMS\Fluid\Core\Widget\AbstractWidgetViewHelper; + +/** + * Class PaginateViewHelper + * + * @package SGalinski\SgJobs\ViewHelpers\Backend\Widget + */ +class PaginateViewHelper extends AbstractWidgetViewHelper { + /** + * @var PaginateController + */ + protected $controller; + + /** + * Initializes the controller + * + * @param PaginateController $controller + */ + public function injectPaginateController(PaginateController $controller): void { + $this->controller = $controller; + } + + /** + * Register the ViewHelper arguments + */ + public function initializeArguments(): void { + parent::initializeArguments(); + $this->registerArgument('objects', 'array', 'The objects to paginate', TRUE); + $this->registerArgument('as', 'string', 'The name of the variable inside the pagination', TRUE); + $this->registerArgument( + 'configuration', + 'array', + 'The configuration of the pagination', + FALSE, + [ + 'itemsPerPage' => 10, + 'insertAbove' => FALSE, + 'insertBelow' => TRUE, + 'recordsLabel' => '' + ] + ); + } + + /** + * Renders the paginator + * + * @return string + */ + public function render(): string { + return $this->initiateSubRequest(); + } +} diff --git a/Classes/ViewHelpers/PageBrowserViewHelper.php b/Classes/ViewHelpers/PageBrowserViewHelper.php index 495f9038bc64253432691aa3a9bb9a1a6fab463a..4679093d30963c032d04d03400ae310c0499bfb7 100644 --- a/Classes/ViewHelpers/PageBrowserViewHelper.php +++ b/Classes/ViewHelpers/PageBrowserViewHelper.php @@ -26,26 +26,34 @@ namespace SGalinski\SgJobs\ViewHelpers; * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ +use TYPO3\CMS\Core\TypoScript\TypoScriptService; use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Extbase\Object\ObjectManager; -use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; +use TYPO3\CMS\Fluid\View\StandaloneView; use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper; /** * View helper that renders a page browser based upon the pagebrowse extension. * * Example: - * {namespace sg=SGalinski\SgJobs\ViewHelpers} + * {namespace sg=SGalinski\SgNews\ViewHelpers} * <sg:pageBrowser numberOfPages="" /> */ class PageBrowserViewHelper extends AbstractViewHelper { + /** + * Specifies whether the escaping interceptors should be disabled or enabled for the render-result of this ViewHelper + * + * @see isOutputEscapingEnabled() + * + * @var boolean + */ + protected $escapeOutput = FALSE; /** - * Register the ViewHelper arguments + * Initialize the ViewHelpers arguments */ public function initializeArguments() { parent::initializeArguments(); - $this->registerArgument('numberOfPages', 'int', 'The number of pages', TRUE); + $this->registerArgument('numberOfPages', 'int', 'The number of pages to browse', TRUE); } /** @@ -55,13 +63,48 @@ class PageBrowserViewHelper extends AbstractViewHelper { * @throws \UnexpectedValueException */ public function render(): string { - $configuration = $GLOBALS['TSFE']->tmpl->setup['plugin.']['tx_sgjobs.']['pagebrowser.']; - $configuration['settings.']['numberOfPages'] = (int) $this->arguments['numberOfPages']; + /** @var TypoScriptService $typoScriptService */ + $typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class); + $configuration = $typoScriptService->convertTypoScriptArrayToPlainArray( + $GLOBALS['TSFE']->tmpl->setup['plugin.']['tx_sgjobs.'] + ); + $view = GeneralUtility::makeInstance(StandaloneView::class); + $view->setRenderingContext($this->renderingContext); + $view->setTemplateRootPaths($configuration['view']['templateRootPaths']); + $view->setPartialRootPaths($configuration['view']['partialRootPaths']); + $view->setLayoutRootPaths($configuration['view']['layoutRootPaths']); + $view->setTemplate('PageBrowser/Index'); + + $currentPage = 0; + $pageBrowserVars = GeneralUtility::_GP('tx_sgjobs_pagebrowser'); + if (isset($pageBrowserVars['currentPage']) && (int) $pageBrowserVars['currentPage'] > 0) { + $currentPage = (int) $pageBrowserVars['currentPage']; + } + + $pageLinks = []; + $start = \max($currentPage - 2, 0); + $end = \min($this->arguments['numberOfPages'], $currentPage + 2); + for ($i = $start; $i < $end; $i++) { + $pageLinks[] = [ + 'number' => $i + 1, + 'page' => $i, + 'isCurrentPage' => $i === $currentPage, + ]; + } - /** @var ContentObjectRenderer $contentObject */ - $objectManager = GeneralUtility::makeInstance(ObjectManager::class); - $contentObject = $objectManager->get(ContentObjectRenderer::class); - $contentObject->start([]); - return $contentObject->cObjGetSingle('USER', $configuration); + $view->assignMultiple( + [ + 'enableLessPages' => 1, + 'enableMorePages' => 1, + 'pageLinks' => $pageLinks, + 'currentPage' => $currentPage, + 'prevPageExist' => $currentPage >= 1, + 'showLessPages' => ($currentPage - 1) > 1, + 'showNextPages' => ($currentPage + 2) < $this->arguments['numberOfPages'], + 'nextPageExist' => $currentPage < $this->arguments['numberOfPages'] - 1, + 'numberOfPages' => $this->arguments['numberOfPages'] + ] + ); + return $view->render(); } } diff --git a/Classes/ViewHelpers/Widget/UriViewHelper.php b/Classes/ViewHelpers/Widget/UriViewHelper.php new file mode 100644 index 0000000000000000000000000000000000000000..e33ba7e35f1bbc69613fefa7a321f4bc15d2760d --- /dev/null +++ b/Classes/ViewHelpers/Widget/UriViewHelper.php @@ -0,0 +1,157 @@ +<?php + +namespace SGalinski\SgJobs\ViewHelpers\Widget; + +/*************************************************************** + * 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\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext; +use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; +use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper; +use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic; + +/** + * Class UriViewHelper + */ +class UriViewHelper extends AbstractViewHelper { + + use CompileWithRenderStatic; + + /** + * @var boolean + */ + protected $escapeOutput = FALSE; + + /** + * @var boolean + */ + protected $escapeChildren = FALSE; + + /** + * Initialize arguments + */ + public function initializeArguments(): void { + $this->registerArgument('addQueryStringMethod', 'string', 'Method to be used for query string'); + $this->registerArgument('action', 'string', 'Target action'); + $this->registerArgument('arguments', 'array', 'Arguments', FALSE, []); + $this->registerArgument('section', 'string', 'The anchor to be added to the URI', FALSE, ''); + $this->registerArgument('format', 'string', 'The requested format, e.g. ".html', FALSE, ''); + $this->registerArgument( + 'ajax', 'bool', 'TRUE if the URI should be to an AJAX widget, FALSE otherwise.', FALSE, FALSE + ); + } + + /** + * @param array $arguments + * @param \Closure $renderChildrenClosure + * @param RenderingContextInterface $renderingContext + * @return string + */ + public static function renderStatic( + array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext + ): string { + $ajax = $arguments['ajax']; + if ($ajax === TRUE) { + return static::getAjaxUri($renderingContext, $arguments); + } + return static::getWidgetUri($renderingContext, $arguments); + } + + /** + * Render the Uri. + * + * @return string The rendered link + * @api + */ + public function render(): string { + $ajax = $this->arguments['ajax']; + + if ($ajax === TRUE) { + return static::getAjaxUri($this->renderingContext, $this->arguments); + } + return static::getWidgetUri($this->renderingContext, $this->arguments); + } + + /** + * Get the URI for an AJAX Request. + * + * @param RenderingContextInterface $renderingContext + * @param array $arguments + * @return string the AJAX URI + */ + protected static function getAjaxUri(RenderingContextInterface $renderingContext, array $arguments): string { + /** @var ControllerContext $controllerContext */ + $controllerContext = $renderingContext->getControllerContext(); + $action = $arguments['action']; + $arguments = $arguments['arguments']; + if ($action === NULL) { + $action = $controllerContext->getRequest()->getControllerActionName(); + } + + $arguments['id'] = $GLOBALS['TSFE']->id; + // @todo page type should be configurable + $arguments['type'] = 7076; + $arguments['fluid-widget-id'] = $controllerContext->getRequest()->getWidgetContext()->getAjaxWidgetIdentifier(); + $arguments['action'] = $action; + return '?' . http_build_query($arguments, NULL, '&'); + } + + /** + * Get the URI for a non-AJAX Request. + * + * @param RenderingContextInterface $renderingContext + * @param array $arguments + * @return string the Widget URI + */ + protected static function getWidgetUri(RenderingContextInterface $renderingContext, array $arguments): string { + /** @var ControllerContext $controllerContext */ + $controllerContext = $renderingContext->getControllerContext(); + $uriBuilder = $controllerContext->getUriBuilder(); + $argumentPrefix = $controllerContext->getRequest()->getArgumentPrefix(); + $parentNamespace = $controllerContext->getRequest()->getWidgetContext()->getParentPluginNamespace(); + $parentArguments = GeneralUtility::_GP($parentNamespace); + $allArguments = [$argumentPrefix => $arguments['arguments']] ?? []; + if ($parentArguments && isset($parentArguments['filters'])) { + $allArguments[$parentNamespace . '[filters]'] = $parentArguments['filters']; + } + + if ($arguments['action'] ?? FALSE) { + $allArguments[$argumentPrefix]['action'] = $arguments['action']; + } + + if (($arguments['format'] ?? '') !== '') { + $allArguments[$argumentPrefix]['format'] = $arguments['format']; + } + + return $uriBuilder->reset() + ->setArguments($allArguments) + ->setSection($arguments['section']) + ->setAddQueryString(TRUE) + ->setAddQueryStringMethod($arguments['addQueryStringMethod'] ?? '') + ->setArgumentsToBeExcludedFromQueryString([$argumentPrefix, 'cHash']) + ->setFormat($arguments['format']) + ->build(); + } +} diff --git a/Configuration/FlexForms/JobTeaser.xml b/Configuration/FlexForms/JobTeaser.xml index c22ed8b701db2e3879322ee8b24aac6c06256301..cd97b2a033c90d555ed07148a00598ef66eebdf7 100644 --- a/Configuration/FlexForms/JobTeaser.xml +++ b/Configuration/FlexForms/JobTeaser.xml @@ -54,6 +54,7 @@ <type>select</type> <renderType>selectMultipleSideBySide</renderType> <foreign_table>tx_sgjobs_domain_model_company</foreign_table> + <foreign_table_where>AND tx_sgjobs_domain_model_company.sys_language_uid IN (0,-1) ORDER BY tx_sgjobs_domain_model_company.name ASC</foreign_table_where> <wizards> <suggest> <type>suggest</type> diff --git a/Configuration/Routes/config.yaml b/Configuration/Routes/config.yaml new file mode 100644 index 0000000000000000000000000000000000000000..8d4875c51d5259733d0ec996426a6873f7895cc4 --- /dev/null +++ b/Configuration/Routes/config.yaml @@ -0,0 +1,55 @@ +routeEnhancers: + PageBrowserJobs: + type: Plugin + namespace: tx_sgjobs_pagebrowser + routePath: '/{localizedSegment}/{currentPage}' + aspects: + localizedSegment: + type: LocaleModifier + default: page-jobs + localeMap: + - + locale: 'de_DE.*' + value: seite-jobs + SgJobApplication: + type: Extbase + extension: SgJobs + plugin: JobApplication + routes: + - + routePath: '/{localizedSegment}/{jobTitle}' + _controller: 'Joblist::applyForm' + _arguments: + jobTitle: jobId + - + routePath: '/{localizedSegment}' + _controller: 'Joblist::apply' + defaultController: 'Joblist::applyForm' + aspects: + jobTitle: + type: PersistedAliasMapper + tableName: tx_sgjobs_domain_model_job + routeFieldName: path_segment + localizedSegment: + type: LocaleModifier + default: apply + localeMap: + - + locale: 'de_DE.*' + value: bewerbung + SgJobList: + type: Extbase + extension: SgJobs + plugin: Joblist + routes: + - + routePath: '/job/{jobTitle}' + _controller: 'Joblist::index' + _arguments: + jobTitle: jobId + defaultController: 'Joblist::index' + aspects: + jobTitle: + type: PersistedAliasMapper + tableName: tx_sgjobs_domain_model_job + routeFieldName: path_segment diff --git a/Configuration/TCA/tx_sgjobs_domain_model_contact.php b/Configuration/TCA/tx_sgjobs_domain_model_contact.php index 22921923ce0e7515bf2e5c10f240ddcbe12737e1..4566858174e6ff5df25f2c4e425c6233f1b637fa 100644 --- a/Configuration/TCA/tx_sgjobs_domain_model_contact.php +++ b/Configuration/TCA/tx_sgjobs_domain_model_contact.php @@ -71,8 +71,6 @@ $columns = [ 'config' => [ 'type' => 'select', 'renderType' => 'selectSingle', - 'foreign_table' => 'sys_language', - 'foreign_table_where' => 'ORDER BY sys_language.title', 'special' => 'languages', 'default' => 0, 'items' => [ diff --git a/Configuration/TCA/tx_sgjobs_domain_model_department.php b/Configuration/TCA/tx_sgjobs_domain_model_department.php index bd9f203d35d0c0f9e4a87d5aedf61429f671d0aa..e021bbe1b4a38e4774a4c456794d7950c7f36b99 100644 --- a/Configuration/TCA/tx_sgjobs_domain_model_department.php +++ b/Configuration/TCA/tx_sgjobs_domain_model_department.php @@ -69,8 +69,6 @@ $columns = [ 'config' => [ 'type' => 'select', 'renderType' => 'selectSingle', - 'foreign_table' => 'sys_language', - 'foreign_table_where' => 'ORDER BY sys_language.title', 'special' => 'languages', 'default' => 0, 'items' => [ @@ -79,7 +77,7 @@ $columns = [ -1, 'flags-multiple' ] - ], + ] ], ], 'l10n_parent' => [ diff --git a/Configuration/TCA/tx_sgjobs_domain_model_experience_level.php b/Configuration/TCA/tx_sgjobs_domain_model_experience_level.php index b5297d0ce000efadbc8ff9ed37e27e8802872307..9f363e7f230c28e8ea921c2ed0f8799508712b31 100644 --- a/Configuration/TCA/tx_sgjobs_domain_model_experience_level.php +++ b/Configuration/TCA/tx_sgjobs_domain_model_experience_level.php @@ -69,8 +69,6 @@ $columns = [ 'config' => [ 'type' => 'select', 'renderType' => 'selectSingle', - 'foreign_table' => 'sys_language', - 'foreign_table_where' => 'ORDER BY sys_language.title', 'special' => 'languages', 'default' => 0, 'items' => [ @@ -79,7 +77,7 @@ $columns = [ -1, 'flags-multiple' ] - ], + ] ], ], 'l10n_parent' => [ diff --git a/Configuration/TCA/tx_sgjobs_domain_model_job.php b/Configuration/TCA/tx_sgjobs_domain_model_job.php index 0c1b7f24c4df1b478f1a4035ef31fead3f74af0c..e9186cf43e8ab2330ca342bd7780eff2afab531f 100644 --- a/Configuration/TCA/tx_sgjobs_domain_model_job.php +++ b/Configuration/TCA/tx_sgjobs_domain_model_job.php @@ -36,7 +36,7 @@ $columns = [ 'crdate' => 'crdate', 'cruser_id' => 'cruser_id', 'dividers2tabs' => TRUE, - 'searchFields' => 'title, job_id, path_segment, start_date, alternative_start_date, company, contact, department, experience_level', + 'searchFields' => 'title, job_id, path_segment, start_date, alternative_start_date, location, apply_external_link, description, task, qualification', 'versioningWS' => 2, 'versioning_followPages' => TRUE, 'origUid' => 't3_origuid', @@ -56,15 +56,14 @@ $columns = [ 'types' => [ '1' => [ 'showitem' => '--palette--;;sysLanguageAndHidden, --palette--;;palette_title, - --palette--;;palette_title_start, + --palette--;;palette_title_start_location, --palette--;;palette_apply_function, + apply_external_link, department, experience_level, company, contact, - description, - attachment, - --div--; LLL:EXT:sg_jobs/Resources/Private/Language/locallang_db.xlf:tca.qualification_tab, task, qualification, + --div--; LLL:EXT:sg_jobs/Resources/Private/Language/locallang_db.xlf:tca.qualification_tab, description, task, qualification, attachment, --div--; LLL:EXT:sg_jobs/Resources/Private/Language/locallang_db.xlf:tca.seo_tab, --palette--;;palette_location_specifications, employment_types, @@ -80,7 +79,7 @@ $columns = [ 'canNotCollapse' => 1, ], 'palette_title' => ['showitem' => 'title, path_segment, job_id', 'canNotCollapse' => 1], - 'palette_title_start' => ['showitem' => 'start_date, alternative_start_date', 'canNotCollapse' => 1], + 'palette_title_start_location' => ['showitem' => 'start_date, alternative_start_date, location', 'canNotCollapse' => 1], 'palette_apply_function' => ['showitem' => 'hide_apply_by_email, hide_apply_by_postal, featured_offer', 'canNotCollapse' => 1], 'palette_location_specifications' => ['showitem' => 'telecommute_possible, office_work_possible'], 'palette_seo_dates' => ['showitem' => 'date_posted, valid_through'], @@ -324,6 +323,15 @@ $columns = [ 'eval' => 'trim' ], ], + 'location' => [ + 'exclude' => TRUE, + 'label' => 'LLL:EXT:sg_jobs/Resources/Private/Language/locallang_db.xlf:tx_sgjobs_domain_model_job.location', + 'config' => [ + 'type' => 'input', + 'size' => 30, + 'eval' => 'trim' + ], + ], 'task' => [ 'exclude' => TRUE, 'label' => 'LLL:EXT:sg_jobs/Resources/Private/Language/locallang_db.xlf:tx_sgjobs_domain_model_job.task', @@ -502,10 +510,19 @@ $columns = [ 'max' => 15, ], ], + 'apply_external_link' => [ + 'exclude' => TRUE, + 'label' => 'LLL:EXT:sg_jobs/Resources/Private/Language/locallang_db.xlf:tx_sgjobs_domain_model_job.apply_external_link', + 'config' => [ + 'type' => 'input', + 'size' => 512, + 'eval' => 'trim' + ], + ], ], ]; if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '10.3.0', '<')) { - $columns['interface']['showRecordFieldList'] = 'sys_language_uid, l10n_parent, l10n_diffsource, hidden, pid, title, path_segment, job_id, department, experience_level, hide_apply_by_email, hide_apply_by_postal, featured_offer, start_date, alternative_start_date,company, task, qualification, description, contact, telecommute, employment_types, date_posted, valid_through, salary_currency, base_salary, max_salary, salary_unit'; + $columns['interface']['showRecordFieldList'] = 'sys_language_uid, l10n_parent, l10n_diffsource, hidden, pid, title, path_segment, job_id, department, experience_level, hide_apply_by_email, hide_apply_by_postal, featured_offer, start_date, alternative_start_date, location, company, task, qualification, description, contact, telecommute, employment_types, date_posted, valid_through, salary_currency, base_salary, max_salary, salary_unit'; } return $columns; diff --git a/Configuration/TCA/tx_sgjobs_domain_model_job_application.php b/Configuration/TCA/tx_sgjobs_domain_model_job_application.php index 6d0b29450d43cbff885c4ae345c29447a591d60f..40f8d7b40e4530405286d6c363eeb8a4eb1e73d9 100644 --- a/Configuration/TCA/tx_sgjobs_domain_model_job_application.php +++ b/Configuration/TCA/tx_sgjobs_domain_model_job_application.php @@ -378,7 +378,7 @@ $columns = [ ], ]; if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '10.3.0', '<')) { - $columns['interface']['showRecordFieldList'] = 'sys_language_uid, l10n_parent, l10n_diffsource, hidden, pid, job, job_id, job_title, company, department, experience_level, start_date, alternative_start_date,company, task, qualification, description, contact, privacy_policy'; + $columns['interface']['showRecordFieldList'] = 'sys_language_uid, l10n_parent, l10n_diffsource, hidden, pid, job, job_id, job_title, company, department, experience_level, start_date, alternative_start_date, location, company, task, qualification, description, contact, privacy_policy'; } return $columns; diff --git a/Configuration/TypoScript/Frontend/constants.typoscript b/Configuration/TypoScript/Frontend/constants.typoscript index e0f95ba9896846d07d55c8088a24e83b6428e2ef..e45e158259a5bb8c2240e75b253d9c68d6fe814d 100644 --- a/Configuration/TypoScript/Frontend/constants.typoscript +++ b/Configuration/TypoScript/Frontend/constants.typoscript @@ -1,13 +1,13 @@ plugin.tx_sgjobs { view { # cat=plugin.tx_tx_extensionskeleton/file; type=string; label=Path to template root (FE) - templateRootPath = EXT:tx_sgjobs/Resources/Private/Templates/ + templateRootPath = EXT:sg_jobs/Resources/Private/Templates/ # cat=plugin.tx_tx_extensionskeleton/file; type=string; label=Path to template partials (FE) - partialRootPath = EXT:tx_sgjobs/Resources/Private/Partials/ + partialRootPath = EXT:sg_jobs/Resources/Private/Partials/ # cat=plugin.tx_tx_extensionskeleton/file; type=string; label=Path to template layouts (FE) - layoutRootPath = EXT:tx_sgjobs/Resources/Private/Layouts/ + layoutRootPath = EXT:sg_jobs/Resources/Private/Layouts/ } settings { @@ -26,18 +26,4 @@ plugin.tx_sgjobs { # cat=plugin.tx_sgjobs/other; type=int+; If set to 1, the application-form page will redirect to the overview if no jobId is passed disallowUnsolicitedApplication = 0 } - - pagebrowser.settings { - # Number of page links to show before the current page - pagesBefore = 1 - - # Number of page links to show before the current page - pagesAfter = 1 - - # Enables section for "more" pages. This section is shown after links to next pages, normally like three dots (1 2 3 ...). Notice that you can also hide it by emptying corresponding template section. - enableMorePages = 1 - - # Enables section for "less" pages. This section is shown after links to next pages, normally like three dots (... 1 2 3) Notice that you can also hide it by emptying corresponding template section. - enableLessPages = 1 - } } diff --git a/Configuration/TypoScript/Frontend/setup.typoscript b/Configuration/TypoScript/Frontend/setup.typoscript index 9f0c1a20f1135014584f8b4ba15e2420f9221945..7bc2e71f1978b889e5d85d5fac1029135bf34cd6 100644 --- a/Configuration/TypoScript/Frontend/setup.typoscript +++ b/Configuration/TypoScript/Frontend/setup.typoscript @@ -2,17 +2,14 @@ plugin.tx_sgjobs { view { templateRootPaths { 0 = {$plugin.tx_sgjobs.view.templateRootPath} - 1 = } partialRootPaths { 0 = {$plugin.tx_sgjobs.view.partialRootPath} - 1 = } layoutRootPaths { 0 = {$plugin.tx_sgjobs.view.layoutRootPath} - 1 = } } @@ -37,41 +34,6 @@ plugin.tx_sgjobs { legacy { enableLegacyFlashMessageHandling = 0 } - - pagebrowser = USER - pagebrowser { - userFunc = TYPO3\CMS\Extbase\Core\Bootstrap->run - extensionName = SgJobs - pluginName = PageBrowser - vendorName = SGalinski - controller = PageBrowser - action = index - view < plugin.tx_sgjobs.view - persistence < plugin.tx_sgjobs.persistence - features < plugin.tx_sgjobs.features - # cHash can't be generated for pageBrowser call; will cause a 404 error if not set to 0 - features.requireCHashArgumentForActionArguments = 0 - legacy < plugin.tx_sgjobs.legacy - settings { - # Number of page links to show before the current page - pagesBefore = {$plugin.tx_sgjobs.pagebrowser.settings.pagesBefore} - - # Number of page links to show before the current page - pagesAfter = {$plugin.tx_sgjobs.pagebrowser.settings.pagesAfter} - - # Enables section for "more" pages. This section is shown after links to next pages, normally like three dots (1 2 3 ...). Notice that you can also hide it by emptying corresponding template section. - enableMorePages = {$plugin.tx_sgjobs.pagebrowser.settings.enableMorePages} - - # Enables section for "less" pages. This section is shown after links to next pages, normally like three dots (... 1 2 3) Notice that you can also hide it by emptying corresponding template section. - enableLessPages = {$plugin.tx_sgjobs.pagebrowser.settings.enableLessPages} - - # The number of the pages - numberOfPages = 0 - - # The current page - currentPage = 0 - } - } } config.recordLinks { diff --git a/Resources/Private/Backend/Layouts/Default.html b/Resources/Private/Backend/Layouts/Default.html index b9a09ad482f184bc13c629ce4e537693833b5d33..fa390fae138239dca41e2ef3741dc94728f5e2aa 100644 --- a/Resources/Private/Backend/Layouts/Default.html +++ b/Resources/Private/Backend/Layouts/Default.html @@ -35,7 +35,7 @@ <h1> <f:render section="headline" /> </h1> - <f:render section="content" /> + <f:render section="main" /> </div> </div> </f:be.container> diff --git a/Resources/Private/Backend/Partials/JobList.html b/Resources/Private/Backend/Partials/JobList.html deleted file mode 100644 index ccaf4f273e7eaa15cff745053578b0ff6b0b6bca..0000000000000000000000000000000000000000 --- a/Resources/Private/Backend/Partials/JobList.html +++ /dev/null @@ -1,36 +0,0 @@ -{namespace sg=SGalinski\SgJobs\ViewHelpers} -{namespace be=TYPO3\CMS\Backend\ViewHelpers} - -<p> - <f:translate key="backend.message.sorting" /> -</p> - -<f:if condition="{manualSortingDestroysEverything}"> - <h2><f:translate key="backend.manualSortingBug" /></h2> -</f:if> - -<div class="panel panel-default recordlist"> - <div class="table-fit"> - <f:be.widget.paginate objects="{jobs}" as="paginatedJobs" configuration="{insertAbove: 1, itemsPerPage: 20}"> - <table data-table="tx_sgjobs_domain_model_job" class="table table-striped table-hover"> - <tbody> - <f:for each="{paginatedJobs}" as="job"> - <tr data-uid="{job.uid}"> - <td nowrap="nowrap" class="col-icon"> - <core:icon identifier="tcarecords-tx_sgjobs_domain_model_job-default"></core:icon> - </td> - <td style="white-space: normal;"> - <be:link.editRecord uid="{job.uid}" table="tx_sgjobs_domain_model_job"> - <span>{job.title} - {job.company.name}, {job.company.city}</span> - </be:link.editRecord> - </td> - <td nowrap="nowrap" class="col-control"> - <f:format.raw><sg:backend.control table="tx_sgjobs_domain_model_job" row="{job}" sortingData="{sortingData}"/></f:format.raw> - </td> - </tr> - </f:for> - </tbody> - </table> - </f:be.widget.paginate> - </div> -</div> diff --git a/Resources/Private/Backend/Templates/Index.html b/Resources/Private/Backend/Templates/Index.html index fbfb7918a15413ca552e8c4e0d98423ac4ce4cf1..55343fa2927644a8108ec91b55cfa5e41671307b 100644 --- a/Resources/Private/Backend/Templates/Index.html +++ b/Resources/Private/Backend/Templates/Index.html @@ -1,62 +1,63 @@ {namespace sg=SGalinski\SgJobs\ViewHelpers} +{namespace be=TYPO3\CMS\Backend\ViewHelpers} -<f:layout name="Default" /> +<f:layout name="Default"/> <f:section name="iconButtons"> </f:section> <f:section name="headline"> - <f:translate key="backend.jobs.header" /> + <f:translate key="backend.jobs.header"/> </f:section> -<f:section name="content"> + +<f:section name="main"> <f:flashMessages /> - <f:if condition="{pageUid}"> + <f:if condition="{jobs}"> <f:then> - <f:if condition="{docHeader}"> - <f:then> - <f:if condition="{noRecords}"> - <f:then> - <f:if condition="{locationOptions}"> - <f:then> - <f:render partial="Filter" arguments="{filters: filters, locationOptions: locationOptions}" /> - <f:render partial="CreateJob" arguments="{pageUid:pageUid}" /> - <p> - <f:translate key="backend.noJobsMessage" /> - </p> - </f:then> - <f:else> - <f:render partial="SelectRoot" arguments="{pages: pages}" /> - <f:render partial="CreateJob" arguments="{pageUid:pageUid}" /> - </f:else> - </f:if> - </f:then> - <f:else> - <f:render partial="Filter" arguments="{filters: filters, locationOptions: locationOptions}" /> - <f:render partial="CreateJob" arguments="{pageUid:pageUid}" /> - <f:if condition="{jobs}"> - <f:then> - <f:render partial="JobList" arguments="{jobs: jobs, manualSortingDestroysEverything: manualSortingDestroysEverything}" /> - </f:then> - <f:else> - <p> - <f:translate key="backend.noJobsMessage" /> - </p> - </f:else> - </f:if> - </f:else> - </f:if> - </f:then> - <f:else> - <f:render partial="Error" /> - </f:else> + <f:render partial="Filter" arguments="{_all}"/> + <f:render partial="CreateJob" arguments="{_all}"/> + + <p> + <f:translate key="backend.message.sorting"/> + </p> + + <f:if condition="{manualSortingDestroysEverything}"> + <h2> + <f:translate key="backend.manualSortingBug"/> + </h2> </f:if> + + <div class="panel panel-default recordlist"> + <div class="table-fit"> + <table data-table="tx_sgjobs_domain_model_job" class="table table-striped table-hover"> + <sg:backend.widget.paginate objects="{jobs}" as="paginatedJobs" configuration="{insertAbove: 1, itemsPerPage: 20}"> + <tbody> + <f:for each="{paginatedJobs}" as="job"> + <tr data-uid="{job.uid}"> + <td nowrap="nowrap" class="col-icon"> + <f:format.raw><sg:backend.icon table="tx_sgjobs_domain_model_job" row="{job}" /></f:format.raw> + </td> + <td style="white-space: normal;"> + <be:link.editRecord uid="{job.uid}" table="tx_sgjobs_domain_model_job"> + <span>{job.title} - {job.company.name}, {job.company.city}</span> + </be:link.editRecord> + </td> + <td nowrap="nowrap" class="col-control"> + <f:format.raw><sg:backend.control table="tx_sgjobs_domain_model_job" row="{job}" /></f:format.raw> + </td> + </tr> + </f:for> + </tbody> + </sg:backend.widget.paginate> + </table> + </div> + </div> </f:then> <f:else> - <f:render partial="SelectRoot" arguments="{pages: pages}" /> + <f:render partial="SelectRoot" arguments="{pages: pages}"/> <f:if condition="{isAdmin}"> - <f:render partial="CreateJob" arguments="{pageUid:pageUid}" /> + <f:render partial="CreateJob" arguments="{pageUid:pageUid}"/> </f:if> </f:else> </f:if> - </f:section> diff --git a/Resources/Private/Backend/Templates/ViewHelpers/Backend/Widget/Paginate/Index.html b/Resources/Private/Backend/Templates/ViewHelpers/Backend/Widget/Paginate/Index.html new file mode 100644 index 0000000000000000000000000000000000000000..1dd3b4ac4a673bc9fbd2f99fb6e825c2f62e8039 --- /dev/null +++ b/Resources/Private/Backend/Templates/ViewHelpers/Backend/Widget/Paginate/Index.html @@ -0,0 +1,125 @@ +{namespace core=TYPO3\CMS\Core\ViewHelpers} +{namespace sg=SGalinski\SgJobs\ViewHelpers} + +<f:if condition="{configuration.insertAbove}"> + <thead> + <tr> + <td colspan="3"> + <f:render section="paginator" arguments="{pagination: pagination, position:'top', recordsLabel: configuration.recordsLabel}" /> + </td> + </tr> + </thead> +</f:if> + +<f:renderChildren arguments="{contentArguments}" /> + +<f:if condition="{configuration.insertBelow}"> + <tfoot> + <tr> + <td colspan="3"> + <f:render section="paginator" arguments="{pagination: pagination, position:'bottom', recordsLabel: configuration.recordsLabel}" /> + </td> + </tr> + </tfoot> +</f:if> + +<f:section name="paginator"> + <nav class="pagination-wrap"> + <ul class="pagination pagination-block"> + <f:if condition="{pagination.hasLessPages}"> + <f:then> + <li> + <a href="{sg:widget.uri(arguments:{currentPage: 1})}" title="{f:translate(key:'widget.pagination.first')}"> + <core:icon identifier="actions-view-paging-first" /> + </a> + </li> + <li> + <a href="{sg:widget.uri(arguments:{currentPage: pagination.previousPage})}" title="{f:translate(key:'widget.pagination.previous')}"> + <core:icon identifier="actions-view-paging-previous" /> + </a> + </li> + </f:then> + <f:else> + <li class="disabled"> + <span> + <core:icon identifier="actions-view-paging-first" /> + </span> + </li> + <li class="disabled"> + <span> + <core:icon identifier="actions-view-paging-previous" /> + </span> + </li> + </f:else> + </f:if> + <li> + <span> + <f:if condition="{recordsLabel}"> + <f:then> + {recordsLabel} + </f:then> + <f:else> + <f:translate key="widget.pagination.records" /> + </f:else> + </f:if> + {pagination.startRecord} - {pagination.endRecord} / {pagination.totalObjects} + </span> + </li> + <li> + <span> + <f:translate key="widget.pagination.page" /> + + <form id="paginator-form-{position}" onsubmit="goToPage{position}(this); return false;" style="display:inline;"> + <script type="text/javascript"> + function goToPage{position}(formObject) { + var url = '{sg:widget.uri(arguments:{currentPage: 987654321}) -> f:format.raw()}'; + var page = formObject.elements['paginator-target-page'].value; + if (page > {pagination.numberOfPages}) { + page = {pagination.numberOfPages}; + } else if (page < 1) { + page = 1; + } + url = url.replace('987654321', page); + self.location.href= url; + } + </script> + <f:form.textfield id="paginator-{position}" name="paginator-target-page" additionalAttributes="{min: '1'}" class="form-control input-sm paginator-input" size="5" value="{pagination.current}" type="number" /> + </form> + + / {pagination.numberOfPages} + </span> + </li> + <f:if condition="{pagination.hasMorePages}"> + <f:then> + <li> + <a href="{sg:widget.uri(arguments:{currentPage: pagination.nextPage})}" title="{f:translate(key:'widget.pagination.next')}"> + <core:icon identifier="actions-view-paging-next" /> + </a> + </li> + <li> + <a href="{sg:widget.uri(arguments:{currentPage: pagination.numberOfPages})}" title="{f:translate(key:'widget.pagination.last')}"> + <core:icon identifier="actions-view-paging-last" /> + </a> + </li> + </f:then> + <f:else> + <li class="disabled"> + <span> + <core:icon identifier="actions-view-paging-next" /> + </span> + </li> + <li class="disabled"> + <span> + <core:icon identifier="actions-view-paging-last" /> + </span> + </li> + </f:else> + </f:if> + <li> + <a href="{sg:widget.uri(arguments:{currentPage: pagination.current})}" title="{f:translate(key:'widget.pagination.refresh')}"> + <core:icon identifier="actions-refresh" /> + </a> + </li> + </ul> + </nav> +</f:section> diff --git a/Resources/Private/Language/de.locallang.xlf b/Resources/Private/Language/de.locallang.xlf index 41029ee55546c18af43c7bd55f8d806daa6354a3..652d3b469938d9c8b646a278d47b8c934a502223 100644 --- a/Resources/Private/Language/de.locallang.xlf +++ b/Resources/Private/Language/de.locallang.xlf @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <xliff version="1.0"> - <file source-language="en" target-language="de" datatype="plaintext" original="messages" date="2019-08-06T13:15:41Z"> + <file source-language="en" target-language="de" datatype="plaintext" original="messages" date="2021-05-27T11:07:16Z"> <header> <type>module</type> <description>General language labels used in frontend and backend.</description> @@ -101,7 +101,7 @@ <source><![CDATA[Locations]]></source> <target><![CDATA[Arbeitsorte]]></target> </trans-unit> - <trans-unit id="backend.filters.locations.description" approved="yes"> + <trans-unit id="backend.filters.locations.description" approved="yes" xml:space="preserve"> <source><![CDATA[(Use the ctrl or cmd keys to deselect an option or to select multiple options) ]]></source> <target><![CDATA[Benutze die STRG oder CMD-Tasten, um eine Option abzuwählen oder mehrere anzuwählen ]]></target> @@ -287,8 +287,8 @@ <target><![CDATA[Initiativbewerbung]]></target> </trans-unit> <trans-unit id="frontend.apply.zip" approved="yes"> - <source><![CDATA[Zip code *]]></source> - <target><![CDATA[Postleitzahl *]]></target> + <source><![CDATA[Zip *]]></source> + <target><![CDATA[PLZ *]]></target> </trans-unit> <trans-unit id="frontend.applyNow" approved="yes"> <source><![CDATA[Apply online now]]></source> @@ -367,8 +367,8 @@ <target><![CDATA[Standort]]></target> </trans-unit> <trans-unit id="frontend.filter.remote" approved="yes"> - <source><![CDATA[Show only jobs that can be executed remotely.]]></source> - <target><![CDATA[Zeige nur Stellen an, die remote ausgeführt werden können.]]></target> + <source><![CDATA[Show only home office jobs]]></source> + <target><![CDATA[Zeige nur Home-Office-Stellen]]></target> </trans-unit> <trans-unit id="frontend.filter.selections.all" approved="yes"> <source><![CDATA[All]]></source> @@ -390,6 +390,10 @@ <source><![CDATA[Location]]></source> <target><![CDATA[Standort]]></target> </trans-unit> + <trans-unit id="frontend.locationLabel" approved="yes"> + <source><![CDATA[<b>Location:</b>]]></source> + <target><![CDATA[<b>Standort:</b>]]></target> + </trans-unit> <trans-unit id="frontend.organisation" approved="yes"> <source><![CDATA[Organisation]]></source> <target><![CDATA[Organisation]]></target> diff --git a/Resources/Private/Language/de.locallang_db.xlf b/Resources/Private/Language/de.locallang_db.xlf index 65592189d6d132e7b57604a6cec9e69eb8430bb0..18873f01d625036b3d415a019fa72335084d4964 100644 --- a/Resources/Private/Language/de.locallang_db.xlf +++ b/Resources/Private/Language/de.locallang_db.xlf @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <xliff version="1.0"> - <file source-language="en" target-language="de" datatype="plaintext" original="messages" date="2019-10-08T14:28:53Z"> + <file source-language="en" target-language="de" datatype="plaintext" original="messages" date="2021-10-17T20:44:41Z"> <header> <type>database</type> <description>General language labels used in frontend and backend.</description> @@ -189,6 +189,10 @@ <source><![CDATA[Display apply by postal service section]]></source> <target><![CDATA[Bewerbung per Post einblenden]]></target> </trans-unit> + <trans-unit id="tx_sgjobs_domain_model_job.apply_external_link" approved="yes"> + <source><![CDATA[External Application URL (If set, the apply form will be disabled)]]></source> + <target><![CDATA[Externe URL für Bewerbungen (Falls gesetzt, wird das Bewerbungsformular deaktiviert)]]></target> + </trans-unit> <trans-unit id="tx_sgjobs_domain_model_job.attachment" approved="yes"> <source><![CDATA[Attachment]]></source> <target><![CDATA[Anhang]]></target> @@ -511,4 +515,4 @@ </trans-unit> </body> </file> -</xliff> +</xliff> \ No newline at end of file diff --git a/Resources/Private/Language/locallang.xlf b/Resources/Private/Language/locallang.xlf index 8fb1f5629d4750f5563cf96a194bd7f2a5345146..fbb5707a95fbb8a205e5bf0ce7f3081bc3ccddd9 100644 --- a/Resources/Private/Language/locallang.xlf +++ b/Resources/Private/Language/locallang.xlf @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <xliff version="1.0"> - <file source-language="en" datatype="plaintext" original="messages" date="2019-08-06T13:15:41Z"> + <file source-language="en" datatype="plaintext" original="messages" date="2021-05-27T11:07:16Z"> <header> <type>module</type> <description>General language labels used in frontend and backend.</description> @@ -217,7 +217,7 @@ <source><![CDATA[Unsolicited Application]]></source> </trans-unit> <trans-unit id="frontend.apply.zip"> - <source><![CDATA[Zip code *]]></source> + <source><![CDATA[Zip *]]></source> </trans-unit> <trans-unit id="frontend.applyNow"> <source><![CDATA[Apply online now]]></source> @@ -277,7 +277,7 @@ <source><![CDATA[Location]]></source> </trans-unit> <trans-unit id="frontend.filter.remote"> - <source><![CDATA[Show only jobs that can be executed remotely.]]></source> + <source><![CDATA[Show only home office jobs]]></source> </trans-unit> <trans-unit id="frontend.filter.selections.all"> <source><![CDATA[All]]></source> @@ -294,6 +294,9 @@ <trans-unit id="frontend.location"> <source><![CDATA[Location]]></source> </trans-unit> + <trans-unit id="frontend.locationLabel"> + <source><![CDATA[<b>Location:</b>]]></source> + </trans-unit> <trans-unit id="frontend.organisation"> <source><![CDATA[Organisation]]></source> </trans-unit> diff --git a/Resources/Private/Language/locallang_db.xlf b/Resources/Private/Language/locallang_db.xlf index cd2d99f8f6bdaa94a8942a6a1d23c5c190dc1477..ebe6a2c238b68a799d173efd400d1c0666aafbb1 100644 --- a/Resources/Private/Language/locallang_db.xlf +++ b/Resources/Private/Language/locallang_db.xlf @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <xliff version="1.0"> - <file source-language="en" datatype="plaintext" original="messages" date="2019-10-08T14:28:53Z"> + <file source-language="en" datatype="plaintext" original="messages" date="2021-10-17T20:44:41Z"> <header> <type>database</type> <description>General language labels used in frontend and backend.</description> @@ -147,6 +147,9 @@ <trans-unit id="tx_sgjobs_domain_model_job.applyByPostal"> <source><![CDATA[Display apply by postal service section]]></source> </trans-unit> + <trans-unit id="tx_sgjobs_domain_model_job.apply_external_link"> + <source><![CDATA[External Application URL (If set, the apply form will be disabled)]]></source> + </trans-unit> <trans-unit id="tx_sgjobs_domain_model_job.attachment"> <source><![CDATA[Attachment]]></source> </trans-unit> @@ -389,4 +392,4 @@ </trans-unit> </body> </file> -</xliff> +</xliff> \ No newline at end of file diff --git a/Resources/Private/Language/zh.locallang.xlf b/Resources/Private/Language/zh.locallang.xlf index a25adf0e709532d33096e631533c26c5023d23f2..bd20bd02020387ed1b05dcee332e0ca0b7c36222 100644 --- a/Resources/Private/Language/zh.locallang.xlf +++ b/Resources/Private/Language/zh.locallang.xlf @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <xliff version="1.0"> - <file source-language="en" target-language="zh" datatype="plaintext" original="messages" date="2019-08-06T13:15:41Z"> + <file source-language="en" target-language="zh" datatype="plaintext" original="messages" date="2021-05-27T11:07:16Z"> <header> <type>module</type> <description>General language labels used in frontend and backend.</description> @@ -215,11 +215,11 @@ </trans-unit> <trans-unit id="frontend.filter.remote" approved="yes"> <source><![CDATA[Show only jobs that can be executed remotely.]]></source> - <target><![CDATA[]]></target> + <target><![CDATA[Show only jobs that can be executed remotely.]]></target> </trans-unit> <trans-unit id="frontend.filter.selections.all" approved="yes"> <source><![CDATA[All]]></source> - <target><![CDATA[]]></target> + <target><![CDATA[All]]></target> </trans-unit> <trans-unit id="frontend.location" approved="yes"> <source><![CDATA[Location]]></source> @@ -239,4 +239,4 @@ </trans-unit> </body> </file> -</xliff> +</xliff> \ No newline at end of file diff --git a/Resources/Private/Partials/ApplyFormSchema.html b/Resources/Private/Partials/ApplyFormSchema.html index 14e7812ad70620d25209e6aa79f65c84458eac61..24a3527a33bc27bb868b119be55aac170785a461 100644 --- a/Resources/Private/Partials/ApplyFormSchema.html +++ b/Resources/Private/Partials/ApplyFormSchema.html @@ -6,14 +6,12 @@ "@context": "https://schema.org/", "@type": "JobPosting", "title": "{job.title}", - "description": "<f:format.htmlentities>{job.description}{job.task}{job.qualification}</f:format.htmlentities>", - + "description": "<f:format.htmlentities><f:format.html>{job.description}{job.task}{job.qualification}</f:format.html></f:format.htmlentities>", "employmentType": [ <f:for each="{h:explodeString(string: '{job.employmentTypes}', delimiter: ',')}" as="employmentType" iteration="iterator"> "{employmentType}"{f:if(condition: '{iterator.isLast} == FALSE', then: ',')} </f:for> ], - "datePosted": "{f:format.date(date: '{f:if(condition: job.datePosted, then: job.datePosted, else: \'now\')}', format: 'Y-m-d')}", <f:if condition="{job.validThrough}"> "validThrough": "{f:format.date(date: job.validThrough, format: 'Y-m-d')}", diff --git a/Resources/Private/Partials/Filter.html b/Resources/Private/Partials/Filter.html index 9c00602db470c787d90cc70bfab8e3a0399449ec..87b42b53db51e69fb87cc01622cc7a6384c90e8a 100644 --- a/Resources/Private/Partials/Filter.html +++ b/Resources/Private/Partials/Filter.html @@ -7,8 +7,8 @@ <div class="sgjobs-filter-bar"> <div class="row default-content-element"> <div class="col-md-6 col-sm-6 col-cs-12"> - <div class="sgjobs-filter-bar-form-control"> - <label for="filter-countries"> + <div class="sgjobs-filter-bar-form-control selectdiv"> + <label for="filter-countries" class="smart-label"> <f:translate key="frontend.filter.countries"/> </label> <f:form.select @@ -25,8 +25,8 @@ </div> <div class="col-md-6 col-sm-6 col-cs-12"> - <div class="sgjobs-filter-bar-form-control"> - <label for="filter-locations"> + <div class="sgjobs-filter-bar-form-control selectdiv"> + <label for="filter-locations" class="smart-label"> <f:translate key="frontend.filter.locations"/> </label> <f:form.select @@ -45,8 +45,8 @@ <div class="row default-content-element"> <div class="col-md-6 col-sm-6 col-cs-12"> - <div class="sgjobs-filter-bar-form-control"> - <label for="filter-departments"> + <div class="sgjobs-filter-bar-form-control selectdiv"> + <label for="filter-departments" class="smart-label"> <f:translate key="frontend.filter.departments"/> </label> <f:form.select @@ -63,8 +63,8 @@ </div> </div> <div class="col-md-6 col-sm-6 col-cs-12"> - <div class="sgjobs-filter-bar-form-control"> - <label for="filter-experienceLevels"> + <div class="sgjobs-filter-bar-form-control selectdiv"> + <label for="filter-experienceLevels" class="smart-label"> <f:translate key="frontend.filter.experienceLevels"/> </label> <f:form.select diff --git a/Resources/Private/Partials/Job.html b/Resources/Private/Partials/Job.html index 9f8803b8c6d431c8c14072bf27fe0df0f2342356..5051dd07e9d7b4d0a60e61b6bfdbed34d0b2839d 100644 --- a/Resources/Private/Partials/Job.html +++ b/Resources/Private/Partials/Job.html @@ -28,7 +28,19 @@ </f:if> </li> <li> - <f:format.raw><f:translate key="frontend.jobLocation" extensionName="project_theme" /></f:format.raw> + <f:if condition="{job.location}"> + <f:then> + <f:format.raw> + <f:translate key="frontend.locationLabel" extensionName="sg_jobs"/> + </f:format.raw> + {job.location} + </f:then> + <f:else> + <f:format.raw> + <f:translate key="frontend.jobLocation" extensionName="project_theme"/> + </f:format.raw> + </f:else> + </f:if> </li> </ul> diff --git a/Resources/Private/Templates/JobTeaser/Index.html b/Resources/Private/Templates/JobTeaser/Index.html index e29e81632f6af6c33a55dd7781226dcbfe61c4a1..07383307cae5501374a449f70c7c2f3fa1f94d92 100644 --- a/Resources/Private/Templates/JobTeaser/Index.html +++ b/Resources/Private/Templates/JobTeaser/Index.html @@ -8,8 +8,8 @@ <ul class="sgjobs-job-offers-teaser-list"> <f:for each="{featuredOffers}" as="offer"> <li> - <f:link.action pageUid="{settings.offersPage}" controller="Joblist" action="index" - pluginName="Joblist" arguments="{jobId: offer.uid}"> + <f:link.action pageUid="{settings.applyPage}" controller="Joblist" action="applyForm" + pluginName="JobApplication" arguments="{jobId: offer.uid}"> {offer.title} </f:link.action> </li> diff --git a/Resources/Private/Templates/Joblist/ApplyForm.html b/Resources/Private/Templates/Joblist/ApplyForm.html index 2d6c9dcf70c6db7625c5cf09a4f72525a4c709ba..2527242285bef5a376da2a5c302a3c382e16db44 100644 --- a/Resources/Private/Templates/Joblist/ApplyForm.html +++ b/Resources/Private/Templates/Joblist/ApplyForm.html @@ -1,7 +1,7 @@ {namespace h=SGalinski\SgJobs\ViewHelpers} {namespace base=SGalinski\ProjectBase\ViewHelpers} -<f:layout name="Default" /> +<f:layout name="Default"/> <f:section name="main"> <f:if condition="{job}"> <f:render partial="ApplyFormSchema" arguments="{_all}"/> @@ -10,10 +10,14 @@ <div class="default-content-element sgjobs-header"> <f:if condition="{job}"> <f:then> - <h1><f:translate key="frontend.apply.applyAs" /> <span>{job.title}</span></h1> + <h1> + <f:translate key="frontend.apply.applyAs"/> + <span>{job.title}</span></h1> </f:then> <f:else> - <h1><f:translate key="frontend.apply.unsolicitedApplication" /></h1> + <h1> + <f:translate key="frontend.apply.unsolicitedApplication"/> + </h1> </f:else> </f:if> </div> @@ -21,12 +25,14 @@ <div class="row"> <div class="container"> <div class="row default-content-element sgjobs-description"> - <input id="maxFileSize" type="hidden" data-maxFileSize="{maxFileSize}" /> - <input id="maxFileSizeMessage" type="hidden" data-maxFileSizeMessage="{maxFileSizeMessage}" /> + <input id="maxFileSize" type="hidden" data-maxFileSize="{maxFileSize}"/> + <input id="maxFileSizeMessage" type="hidden" data-maxFileSizeMessage="{maxFileSizeMessage}"/> <div class="col-md-8 col-sm-6 col-xs-12"> <div class="default-content-element"> <f:format.html parseFuncTSPath="lib.parseFunc_RTE">{job.task}</f:format.html> - <h3 class="h4"><f:translate key="frontend.qualification" /></h3> + <h3 class="h4"> + <f:translate key="frontend.qualification"/> + </h3> <f:format.html parseFuncTSPath="lib.parseFunc_RTE">{job.qualification}</f:format.html> </div> </div> @@ -34,24 +40,37 @@ <div class="highlight-box bg-card sgjobs-meta-box"> <ul class="default-list"> <li> - <f:format.raw><f:translate key="frontend.jobStart" extensionName="project_theme" /></f:format.raw> + <f:format.raw><f:translate key="frontend.jobStart" extensionName="project_theme"/></f:format.raw> <f:if condition="{job.alternativeStartDate}"> <f:then> {job.alternativeStartDate} </f:then> <f:else> - <f:format.date date="{job.startDate}" format="d.m.Y" /> + <f:format.date date="{job.startDate}" format="d.m.Y"/> + </f:else> + </f:if> + </li> + <li> + <f:if condition="{job.location}"> + <f:then> + <f:format.raw><f:translate key="frontend.locationLabel" extensionName="sg_jobs"/></f:format.raw> + {job.location} + </f:then> + <f:else> + <f:format.raw><f:translate key="frontend.jobLocation" extensionName="project_theme"/></f:format.raw> </f:else> </f:if> </li> - <li><f:format.raw><f:translate key="frontend.jobLocation" extensionName="project_theme" /></f:format.raw></li> </ul> <hr> - <h3><f:format.raw><f:translate key="frontend.jobApplyNow" extensionName="project_theme" /></f:format.raw></h3> + <h3> + <f:format.raw><f:translate key="frontend.jobApplyNow" extensionName="project_theme"/></f:format.raw> + </h3> <f:if condition="!{job.hideApplyByPostal}"> <p> - <f:format.raw><f:translate key="frontend.job.via.post" extensionName="project_theme" /></f:format.raw><br> + <f:format.raw><f:translate key="frontend.job.via.post" extensionName="project_theme"/></f:format.raw> + <br> {job.company.name}<br> {job.contact.firstName} {job.contact.lastName}<br> {job.company.street}<br> @@ -63,24 +82,38 @@ </f:if> <f:if condition="!{job.hideApplyByEmail}"> <p> - <f:format.raw><f:translate key="frontend.job.via.email" extensionName="project_theme" /></f:format.raw><br> + <f:format.raw><f:translate key="frontend.job.via.email" extensionName="project_theme"/></f:format.raw> + <br> <f:comment><!-- Spam Protection (lib.parseFunc encodes adresses --></f:comment> - <f:format.html parseFuncTSPath="lib.parseFunc"><a href="mailto:{job.contact.email}"><f:translate key="frontend.emailContact" /></a></f:format.html> + <f:format.html parseFuncTSPath="lib.parseFunc"><a href="mailto:{job.contact.email}"><f:translate key="frontend.emailContact"/></a></f:format.html> </p> </f:if> - <p><f:format.raw><f:translate key="frontend.job.suggestForm" extensionName="project_theme" /></f:format.raw></p> + <p> + <f:format.raw><f:translate key="frontend.job.suggestForm" extensionName="project_theme"/></f:format.raw> + </p> <div class="default-content-element sg-cta sg-cta-with-icon"> - <a href="#apply" class="btn btn-warning btn-lg"> - <f:translate key="frontend.applyNow" /> - </a> + <f:if condition="{job.applyExternalLink}"> + <f:then> + <a href="{job.applyExternalLink}" class="btn btn-warning btn-lg"> + <f:translate key="frontend.applyNow"/> + </a> + </f:then> + <f:else> + <a href="#apply" class="btn btn-warning btn-lg"> + <f:translate key="frontend.applyNow"/> + </a> + </f:else> + </f:if> </div> </div> <f:if condition="{job.contact}"> <div class="highlight-box bg-card sgjobs-meta-box"> <div class="default-content-element sg-jobs-contact-box"> <div class="sg-jobs-contact-box__text"> - <p class="h4"><f:translate key="frontend.apply.contact" /></p> + <p class="h4"> + <f:translate key="frontend.apply.contact"/> + </p> <f:if condition="{job.contact.firstName}"> {job.contact.firstName} {job.contact.lastName} </f:if> @@ -91,27 +124,33 @@ <br/> <f:if condition="!{job.hideApplyByEmail}"> <p> - <f:comment><!-- Spam Protection (lib.parseFunc encodes adresses --></f:comment> - <f:format.html parseFuncTSPath="lib.parseFunc"><a href="mailto:{job.contact.email}"><f:translate key="frontend.emailContact" /></a></f:format.html> + <f:comment> + <!-- Spam Protection (lib.parseFunc encodes adresses --></f:comment> + <f:format.html parseFuncTSPath="lib.parseFunc"><a href="mailto:{job.contact.email}"> <f:translate key="frontend.emailContact"/></a></f:format.html> </p> </f:if> </f:if> </div> <f:if condition="{job.contact.image}"> - <f:image image="{job.contact.image}" maxWidth="100" maxHeight="100" alt="{job.contact.firstName} {job.contact.lastName}" /> + <f:image image="{job.contact.image}" maxWidth="100" maxHeight="100" + alt="{job.contact.firstName} {job.contact.lastName}"/> </f:if> </div> <hr> <div class="default-content-element sgjobs-social-sharer"> - <p class="h4"><f:translate key="frontend.apply.recommend" /></p> - <base:sharer /> + <p class="h4"> + <f:translate key="frontend.apply.recommend"/> + </p> + <base:sharer/> </div> </div> </f:if> <f:if condition="{job.attachment}"> <div class="highlight-box bg-card sgjobs-meta-box"> <div class="default-content-element downloads"> - <f:link.typolink target="_blank" parameter="{job.attachment.0.originalResource.publicUrl}" class="download-link"> + <f:link.typolink target="_blank" + parameter="{job.attachment.0.originalResource.publicUrl}" + class="download-link"> <span class="download-icon download-pdf"> </span> <span class="download-link__file-description"> @@ -127,64 +166,92 @@ </div> </div> </f:if> - <div class="row default-content-element {data.flexform_additionalClassesRow}"> - <div class="col-md-10 col-sm-10 col-xs-12"> + <f:if condition="!{job.applyExternalLink}"> + <div class="row default-content-element {data.flexform_additionalClassesRow}"> + <div class="col-md-10 col-sm-10 col-xs-12"> <div class="default-content-element"> <f:if condition="{job}"> - <h2><f:translate key="frontend.apply.applyAs" /> <span id="apply-title">{job.title}</span></h2> + <h2> + <f:translate key="frontend.apply.applyAs"/> + <span id="apply-title">{job.title}</span></h2> </f:if> </div> - <div class="default-content-element"> - <f:form action="apply" class="sgjobs-apply-form" id="apply" controller="Joblist" method="post" name="applyData" object="{applyData}" enctype="multipart/form-data"> - <f:if condition="{job}"> - <f:then> - <f:form.hidden property="job" value="{job}" /> - <f:form.hidden property="jobId" value="{job.jobId}" /> - <f:form.hidden property="jobTitle" value="{job.title}" /> - <f:form.hidden property="company" value="{job.company.uid}" /> - </f:then> - </f:if> - <input type="hidden" name="tx_sgjobs_jobapplication[folderName]" value="{folderName}" /> - - <f:comment><!-- To output all validation errors - <f:form.validationResults> - <f:if condition="{validationResults.flattenedErrors}"> - <ul class="errors"> - <f:for each="{validationResults.flattenedErrors}" as="errors" key="propertyPath"> - <li>{propertyPath} - <ul> - <f:for each="{errors}" as="error"> - <li>{error.code}: {error}</li> - </f:for> - </ul> - </li> - </f:for> - </ul> - </f:if> - </f:form.validationResults> - --> - </f:comment> - - <f:if condition="{internalError}"> - <ul class="sg-jobs-validation-error parsley-errors-list filled"> - <li class="parsley-required"><f:translate key="frontend.apply.error.general" />: {internalError} + <div class="default-content-element"> + <f:form action="apply" class="sgjobs-apply-form" id="apply" controller="Joblist" method="post" + name="applyData" object="{applyData}" enctype="multipart/form-data"> + <f:if condition="{job}"> + <f:then> + <f:form.hidden property="job" value="{job}"/> + <f:form.hidden property="jobId" value="{job.jobId}"/> + <f:form.hidden property="jobTitle" value="{job.title}"/> + <f:form.hidden property="company" value="{job.company.uid}"/> + </f:then> + </f:if> + <input type="hidden" name="tx_sgjobs_jobapplication[folderName]" value="{folderName}"/> + + <f:comment><!-- To output all validation errors + <f:form.validationResults> + <f:if condition="{validationResults.flattenedErrors}"> + <ul class="errors"> + <f:for each="{validationResults.flattenedErrors}" as="errors" key="propertyPath"> + <li>{propertyPath} + <ul> + <f:for each="{errors}" as="error"> + <li>{error.code}: {error}</li> + </f:for> + </ul> </li> + </f:for> </ul> - </f:if> + </f:if> + </f:form.validationResults> + --> + </f:comment> - <f:if condition="!{job}"> - <f:then> - <div class="form-group"> - <label for="apply-company"><f:translate key="frontend.apply.company" /></label> - <f:form.select property="company" multiple="0" size="1" - id="apply-company" - class="form-control" - options="{companies}" - optionLabelField="city" - optionValueField="uid" - prependOptionLabel="{f:translate(key:'frontend.apply.country.empty')}" /> - - <f:form.validationResults for="applyData.company"> + <f:if condition="{internalError}"> + <ul class="sg-jobs-validation-error parsley-errors-list filled"> + <li class="parsley-required"> + <f:translate key="frontend.apply.error.general"/> + : {internalError} + </li> + </ul> + </f:if> + + <f:if condition="!{job}"> + <f:then> + <div class="col-xs-6"> + <div class="form-group selectdiv"> + <label for="apply-company" class="smart-label"> + <f:translate key="frontend.apply.company"/> + </label> + <f:form.select property="company" multiple="0" size="1" + id="apply-company" + class="form-control" + options="{companies}" + optionLabelField="city" + optionValueField="uid" + prependOptionLabel="{f:translate(key:'frontend.apply.country.empty')}"/> + + <f:form.validationResults for="applyData.company"> + <ul class="sg-jobs-validation-error parsley-errors-list filled"> + <f:for each="{validationResults.errors}" as="error"> + <li class="parsley-required">{error.message}</li> + </f:for> + </ul> + </f:form.validationResults> + </div> + </div> + </f:then> + </f:if> + + <div class="col-xs-6"> + <div class="form-group selectdiv"> + <label for="apply-gender" class="smart-label"> + <f:translate key="frontend.apply.gender"/> + </label> + <f:form.select property="gender" id="apply-gender" class="form-control" + options="{Male: '{f:translate(key: \'frontend.apply.gender.male\')}', Female: '{f:translate(key: \'frontend.apply.gender.female\')}'}"/> + <f:form.validationResults for="applyData.gender"> <ul class="sg-jobs-validation-error parsley-errors-list filled"> <f:for each="{validationResults.errors}" as="error"> <li class="parsley-required">{error.message}</li> @@ -192,285 +259,390 @@ </ul> </f:form.validationResults> </div> - </f:then> - </f:if> - - <div class="form-group"> - <label for="apply-gender"><f:translate key="frontend.apply.gender" /></label> - <f:form.select property="gender" id="apply-gender" class="form-control" options="{Male: '{f:translate(key: \'frontend.apply.gender.male\')}', Female: '{f:translate(key: \'frontend.apply.gender.female\')}'}" /> - <f:form.validationResults for="applyData.gender"> - <ul class="sg-jobs-validation-error parsley-errors-list filled"> - <f:for each="{validationResults.errors}" as="error"> - <li class="parsley-required">{error.message}</li> - </f:for> - </ul> - </f:form.validationResults> - </div> - - <div class="form-group"> - <label for="apply-firstName"><f:translate key="frontend.apply.first_name" /></label> - <f:form.textfield property="firstName" class="form-control" id="apply-firstName" /> - <f:form.validationResults for="applyData.firstName"> - <ul class="sg-jobs-validation-error parsley-errors-list filled"> - <f:for each="{validationResults.errors}" as="error"> - <li class="parsley-required">{error.message}</li> - </f:for> - </ul> - </f:form.validationResults> - </div> - - <div class="form-group"> - <label for="apply-lastName"><f:translate key="frontend.apply.last_name" /></label> - <f:form.textfield property="lastName" id="apply-lastName" class="form-control" /> - <f:form.validationResults for="applyData.lastName"> - <ul class="sg-jobs-validation-error parsley-errors-list filled"> - <f:for each="{validationResults.errors}" as="error"> - <li class="parsley-required">{error.message}</li> - </f:for> - </ul> - </f:form.validationResults> - </div> - - <div class="form-group"> - <label for="apply-street"><f:translate key="frontend.apply.street" /></label> - <f:form.textfield property="street" id="apply-street" class="form-control" /> - <f:form.validationResults for="applyData.street"> - <ul class="sg-jobs-validation-error parsley-errors-list filled"> - <f:for each="{validationResults.errors}" as="error"> - <li class="parsley-required">{error.message}</li> - </f:for> - </ul> - </f:form.validationResults> - </div> + </div> - <div class="form-group"> - <label for="apply-city"><f:translate key="frontend.apply.city" /></label> - <f:form.textfield property="city" id="apply-city" class="form-control" /> - <f:form.validationResults for="applyData.city"> - <ul class="sg-jobs-validation-error parsley-errors-list filled"> - <f:for each="{validationResults.errors}" as="error"> - <li class="parsley-required">{error.message}</li> - </f:for> - </ul> - </f:form.validationResults> - </div> + <div class="col-xs-6"> + <div class="form-group"> + <label for="apply-firstName" class="smart-label"> + <f:translate key="frontend.apply.first_name"/> + </label> + <f:form.textfield property="firstName" class="form-control" id="apply-firstName" + placeholder="{f:translate(key:'frontend.apply.first_name')}"/> + <f:form.validationResults for="applyData.firstName"> + <ul class="sg-jobs-validation-error parsley-errors-list filled"> + <f:for each="{validationResults.errors}" as="error"> + <li class="parsley-required">{error.message}</li> + </f:for> + </ul> + </f:form.validationResults> + </div> + </div> - <div class="form-group"> - <label for="apply-zip"><f:translate key="frontend.apply.zip" /></label> - <f:form.textfield property="zip" id="apply-zip" class="form-control" /> - <f:form.validationResults for="applyData.zip"> - <ul class="sg-jobs-validation-error parsley-errors-list filled"> - <f:for each="{validationResults.errors}" as="error"> - <li class="parsley-required">{error.message}</li> - </f:for> - </ul> - </f:form.validationResults> - </div> + <div class="col-xs-6"> + <div class="form-group"> + <label for="apply-lastName" class="smart-label"> + <f:translate key="frontend.apply.last_name"/> + </label> + <f:form.textfield property="lastName" id="apply-lastName" class="form-control" + placeholder="{f:translate(key:'frontend.apply.last_name')}"/> + <f:form.validationResults for="applyData.lastName"> + <ul class="sg-jobs-validation-error parsley-errors-list filled"> + <f:for each="{validationResults.errors}" as="error"> + <li class="parsley-required">{error.message}</li> + </f:for> + </ul> + </f:form.validationResults> + </div> + </div> - <div class="form-group"> - <label for="apply-country"><f:translate key="frontend.apply.country" /></label> - <f:form.select value="{f:if(condition: '{sysLanguageUid} == 0', then: 'Deutschland', else: 'Germany')}" property="country" id="apply-country" class="form-control" - options="{countries}" optionLabelField="{f:if(condition: '{sysLanguageUid} == 0', then: 'cnShortDe', else: 'cnShortEn')}" - optionValueField="{f:if(condition: '{sysLanguageUid} == 0', then: 'cnShortDe', else: 'cnShortEn')}" - /> - <f:form.validationResults for="applyData.country"> - <ul class="sg-jobs-validation-error parsley-errors-list filled"> - <f:for each="{validationResults.errors}" as="error"> - <li class="parsley-required">{error.message}</li> - </f:for> - </ul> - </f:form.validationResults> - </div> + <div class="col-xs-6"> + <div class="form-group"> + <label for="apply-street" class="smart-label"> + <f:translate key="frontend.apply.street"/> + </label> + <f:form.textfield property="street" id="apply-street" class="form-control" + placeholder="{f:translate(key:'frontend.apply.street')}"/> + <f:form.validationResults for="applyData.street"> + <ul class="sg-jobs-validation-error parsley-errors-list filled"> + <f:for each="{validationResults.errors}" as="error"> + <li class="parsley-required">{error.message}</li> + </f:for> + </ul> + </f:form.validationResults> + </div> + </div> - <div class="form-group"> - <label for="apply-nationality"><f:translate key="frontend.apply.nationality" /></label> - <f:form.select value="{f:if(condition: '{sysLanguageUid} == 0', then: 'Deutschland', else: 'Germany')}" property="nationality" id="apply-nationality" class="form-control" options="{countries}" - optionLabelField="{f:if(condition: '{sysLanguageUid} == 0', then: 'cnShortDe', else: 'cnShortEn')}" - optionValueField="{f:if(condition: '{sysLanguageUid} == 0', then: 'cnShortDe', else: 'cnShortEn')}" - /> - <f:form.validationResults for="applyData.nationality"> - <ul class="sg-jobs-validation-error parsley-errors-list filled"> - <f:for each="{validationResults.errors}" as="error"> - <li class="parsley-required">{error.message}</li> - </f:for> - </ul> - </f:form.validationResults> - </div> + <div class="col-xs-4"> + <div class="form-group"> + <label for="apply-city" class="smart-label"> + <f:translate key="frontend.apply.city"/> + </label> + <f:form.textfield property="city" id="apply-city" class="form-control" + placeholder="{f:translate(key:'frontend.apply.city')}"/> + <f:form.validationResults for="applyData.city"> + <ul class="sg-jobs-validation-error parsley-errors-list filled"> + <f:for each="{validationResults.errors}" as="error"> + <li class="parsley-required">{error.message}</li> + </f:for> + </ul> + </f:form.validationResults> + </div> + </div> - <div class="form-group"> - <label for="apply-education"><f:translate key="frontend.apply.education" /></label> - <f:form.textfield property="education" id="apply-education" class="form-control" /> - <f:form.validationResults for="applyData.education"> - <ul class="sg-jobs-validation-error parsley-errors-list filled"> - <f:for each="{validationResults.errors}" as="error"> - <li class="parsley-required">{error.message}</li> - </f:for> - </ul> - </f:form.validationResults> - </div> + <div class="col-xs-2"> + <div class="form-group"> + <label for="apply-zip" class="smart-label"> + <f:translate key="frontend.apply.zip"/> + </label> + <f:form.textfield property="zip" id="apply-zip" class="form-control" + placeholder="{f:translate(key:'frontend.apply.zip')}"/> + <f:form.validationResults for="applyData.zip"> + <ul class="sg-jobs-validation-error parsley-errors-list filled"> + <f:for each="{validationResults.errors}" as="error"> + <li class="parsley-required">{error.message}</li> + </f:for> + </ul> + </f:form.validationResults> + </div> + </div> - <div class="form-group"> - <label for="apply-birthDate"><f:translate key="frontend.apply.birthDate" /></label> - <f:form.textfield property="birthDate" id="apply-birthDate" class="form-control" /> - <f:form.validationResults for="applyData.birthDate"> - <ul class="sg-jobs-validation-error parsley-errors-list filled"> - <f:for each="{validationResults.errors}" as="error"> - <li class="parsley-required">{error.message}</li> - </f:for> - </ul> - </f:form.validationResults> - </div> + <div class="col-xs-6"> + <div class="form-group selectdiv"> + <label for="apply-country" class="smart-label"> + <f:translate key="frontend.apply.country"/> + </label> + <f:form.select + value="{f:if(condition: '{sysLanguageUid} == 0', then: 'Deutschland', else: 'Germany')}" + property="country" id="apply-country" class="form-control" + options="{countries}" + optionLabelField="{f:if(condition: '{sysLanguageUid} == 0', then: 'cnShortDe', else: 'cnShortEn')}" + optionValueField="{f:if(condition: '{sysLanguageUid} == 0', then: 'cnShortDe', else: 'cnShortEn')}" + /> + <f:form.validationResults for="applyData.country"> + <ul class="sg-jobs-validation-error parsley-errors-list filled"> + <f:for each="{validationResults.errors}" as="error"> + <li class="parsley-required">{error.message}</li> + </f:for> + </ul> + </f:form.validationResults> + </div> + </div> - <div class="form-group"> - <label for="apply-phone"><f:translate key="frontend.apply.phone" /></label> - <f:form.textfield property="phone" id="apply-phone" class="form-control" /> - <f:form.validationResults for="applyData.phone"> - <ul class="sg-jobs-validation-error parsley-errors-list filled"> - <f:for each="{validationResults.errors}" as="error"> - <li class="parsley-required">{error.message}</li> - </f:for> - </ul> - </f:form.validationResults> - </div> + <div class="col-xs-6"> + <div class="form-group selectdiv"> + <label for="apply-nationality" class="smart-label"> + <f:translate key="frontend.apply.nationality"/> + </label> + <f:form.select + value="{f:if(condition: '{sysLanguageUid} == 0', then: 'Deutschland', else: 'Germany')}" + property="nationality" id="apply-nationality" class="form-control" options="{countries}" + optionLabelField="{f:if(condition: '{sysLanguageUid} == 0', then: 'cnShortDe', else: 'cnShortEn')}" + optionValueField="{f:if(condition: '{sysLanguageUid} == 0', then: 'cnShortDe', else: 'cnShortEn')}" + /> + <f:form.validationResults for="applyData.nationality"> + <ul class="sg-jobs-validation-error parsley-errors-list filled"> + <f:for each="{validationResults.errors}" as="error"> + <li class="parsley-required">{error.message}</li> + </f:for> + </ul> + </f:form.validationResults> + </div> + </div> - <div class="form-group"> - <label for="apply-mobile"><f:translate key="frontend.apply.mobile" /></label> - <f:form.textfield property="mobile" id="apply-mobile" class="form-control" /> - <f:form.validationResults for="applyData.mobile"> - <ul class="sg-jobs-validation-error parsley-errors-list filled"> - <f:for each="{validationResults.errors}" as="error"> - <li class="parsley-required">{error.message}</li> - </f:for> - </ul> - </f:form.validationResults> - </div> + <div class="col-xs-6"> + <div class="form-group"> + <label for="apply-education" class="smart-label"> + <f:translate key="frontend.apply.education"/> + </label> + <f:form.textfield property="education" id="apply-education" class="form-control" + placeholder="{f:translate(key:'frontend.apply.education')}"/> + <f:form.validationResults for="applyData.education"> + <ul class="sg-jobs-validation-error parsley-errors-list filled"> + <f:for each="{validationResults.errors}" as="error"> + <li class="parsley-required">{error.message}</li> + </f:for> + </ul> + </f:form.validationResults> + </div> + </div> - <div class="form-group"> - <label for="apply-email"><f:translate key="frontend.apply.email" /></label> - <f:form.textfield type="email" property="email" id="apply-email" class="form-control" /> - <f:form.validationResults for="applyData.email"> - <ul class="sg-jobs-validation-error parsley-errors-list filled"> - <f:for each="{validationResults.errors}" as="error"> - <li class="parsley-required">{error.message}</li> - </f:for> - </ul> - </f:form.validationResults> - </div> + <div class="col-xs-6"> + <div class="form-group"> + <label for="apply-birthDate" class="smart-label"> + <f:translate key="frontend.apply.birthDate"/> + </label> + <f:form.textfield property="birthDate" id="apply-birthDate" class="form-control" + placeholder="{f:translate(key:'frontend.apply.birthDate')}"/> + <f:form.validationResults for="applyData.birthDate"> + <ul class="sg-jobs-validation-error parsley-errors-list filled"> + <f:for each="{validationResults.errors}" as="error"> + <li class="parsley-required">{error.message}</li> + </f:for> + </ul> + </f:form.validationResults> + </div> + </div> - <div class="form-group"> - <label for="apply-cover-letter"> - <f:translate key="frontend.apply.cover_letter" /> - (<f:translate key="frontend.apply.allowed_file_extensions" /> {allowedFileExtensions}) - </label> - - <h:form.upload property="coverLetter" resourceName="coverLetter" id="apply-cover-letter" additionalAttributes="{accept: '{allowedMimeTypes}'}" /> - <f:if condition="{coverLetter.name}"> - <p class="help-block"> - Aktuell: {coverLetter.name} - - <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> + <div class="col-xs-6"> + <div class="form-group"> + <label for="apply-phone" class="smart-label"> + <f:translate key="frontend.apply.phone"/> + </label> + <f:form.textfield property="phone" id="apply-phone" class="form-control" + placeholder="{f:translate(key:'frontend.apply.phone')}"/> + <f:form.validationResults for="applyData.phone"> + <ul class="sg-jobs-validation-error parsley-errors-list filled"> + <f:for each="{validationResults.errors}" as="error"> + <li class="parsley-required">{error.message}</li> + </f:for> + </ul> + </f:form.validationResults> + </div> + </div> - <f:form.validationResults for="applyData.coverLetter"> - <ul class="sg-jobs-validation-error parsley-errors-list filled"> - <f:for each="{validationResults.errors}" as="error"> - <li class="parsley-required">{error.message}</li> - </f:for> - </ul> - </f:form.validationResults> - </div> + <div class="col-xs-6"> + <div class="form-group"> + <label for="apply-mobile" class="smart-label"> + <f:translate key="frontend.apply.mobile"/> + </label> + <f:form.textfield property="mobile" id="apply-mobile" class="form-control" + placeholder="{f:translate(key:'frontend.apply.mobile')}"/> + <f:form.validationResults for="applyData.mobile"> + <ul class="sg-jobs-validation-error parsley-errors-list filled"> + <f:for each="{validationResults.errors}" as="error"> + <li class="parsley-required">{error.message}</li> + </f:for> + </ul> + </f:form.validationResults> + </div> + </div> - <div class="form-group"> - <label for="apply-cv"> - <f:translate key="frontend.apply.cv" /> - (<f:translate key="frontend.apply.allowed_file_extensions" /> {allowedFileExtensions}) - </label> - - <h:form.upload property="cv" resourceName="cv" id="apply-cv" additionalAttributes="{accept: '{allowedMimeTypes}'}" /> - <f:if condition="{cv.name}"> - <p class="help-block"> - Aktuell: {cv.name} - - <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> + <div class="col-xs-6"> + <div class="form-group"> + <label for="apply-email" class="smart-label"> + <f:translate key="frontend.apply.email"/> + </label> + <f:form.textfield type="email" property="email" id="apply-email" class="form-control" + placeholder="{f:translate(key:'frontend.apply.email')}"/> + <f:form.validationResults for="applyData.email"> + <ul class="sg-jobs-validation-error parsley-errors-list filled"> + <f:for each="{validationResults.errors}" as="error"> + <li class="parsley-required">{error.message}</li> + </f:for> + </ul> + </f:form.validationResults> + </div> + </div> - <f:form.validationResults for="applyData.cv"> - <ul class="sg-jobs-validation-error parsley-errors-list filled"> - <f:for each="{validationResults.errors}" as="error"> - <li class="parsley-required">{error.message}</li> - </f:for> - </ul> - </f:form.validationResults> - </div> + <div class="col-xs-12"> + <div class="form-group"> + <label for="apply-cover-letter" class="smart-label filled"> + <f:translate key="frontend.apply.cover_letter"/> + ( + <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}'}"/> + <f:if condition="{coverLetter.name}"> + <p class="help-block"> + Aktuell: {coverLetter.name} + + <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:form.validationResults for="applyData.coverLetter"> + <ul class="sg-jobs-validation-error parsley-errors-list filled"> + <f:for each="{validationResults.errors}" as="error"> + <li class="parsley-required">{error.message}</li> + </f:for> + </ul> + </f:form.validationResults> + </div> + </div> - <div class="form-group"> - <label for="apply-certificate"> - <f:translate key="frontend.apply.certificate" /> - (<f:translate key="frontend.apply.allowed_file_extensions" /> {allowedFileExtensions}) - </label> - - <h:form.upload property="certificate" resourceName="certificate" id="apply-certificate" additionalAttributes="{accept: '{allowedMimeTypes}'}" /> - <f:if condition="{certificate.name}"> - <p class="help-block"> - Aktuell: {certificate.name} - - <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> + <div class="col-xs-12"> + <div class="form-group"> + <label for="apply-cv" class="smart-label filled"> + <f:translate key="frontend.apply.cv"/> + ( + <f:translate key="frontend.apply.allowed_file_extensions"/> + {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} + + <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:form.validationResults for="applyData.cv"> + <ul class="sg-jobs-validation-error parsley-errors-list filled"> + <f:for each="{validationResults.errors}" as="error"> + <li class="parsley-required">{error.message}</li> + </f:for> + </ul> + </f:form.validationResults> + </div> + </div> - <f:form.validationResults for="applyData.certificate"> - <ul class="sg-jobs-validation-error parsley-errors-list filled"> - <f:for each="{validationResults.errors}" as="error"> - <li class="parsley-required">{error.message}</li> - </f:for> - </ul> - </f:form.validationResults> - </div> + <div class="col-xs-12"> + <div class="form-group"> + <label for="apply-certificate" class="smart-label filled"> + <f:translate key="frontend.apply.certificate"/> + ( + <f:translate key="frontend.apply.allowed_file_extensions"/> + {allowedFileExtensions}) + </label> + + <h:form.upload property="certificate" resourceName="certificate" id="apply-certificate" + class="form-control" additionalAttributes="{accept: '{allowedMimeTypes}'}"/> + <f:if condition="{certificate.name}"> + <p class="help-block"> + Aktuell: {certificate.name} + + <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:form.validationResults for="applyData.certificate"> + <ul class="sg-jobs-validation-error parsley-errors-list filled"> + <f:for each="{validationResults.errors}" as="error"> + <li class="parsley-required">{error.message}</li> + </f:for> + </ul> + </f:form.validationResults> + </div> + </div> - <div class="form-group"> - <label for="apply-message"><f:translate key="frontend.apply.message" /></label> - <f:form.textarea class="form-control" rows="10" property="message" id="apply-message" /> - </div> + <div class="col-xs-12"> + <div class="form-group"> + <label for="apply-message" class="smart-label"> + <f:translate key="frontend.apply.message"/> + </label> + <f:form.textarea class="form-control" rows="10" property="message" id="apply-message" + placeholder="{f:translate(key:'frontend.apply.message')}"/> + </div> - <div class="form-group"> - <label> - <f:form.checkbox id="privacy-policy" property="privacyPolicy" value="1" /> - <f:format.raw><f:translate key="frontend.apply.privacyPolicy" arguments="{0: '{f:render(section:\'privacyPolicyCheckboxLink\')}'}"/></f:format.raw> - </label> - <f:form.validationResults for="applyData.privacyPolicy"> - <ul class="sg-jobs-validation-error parsley-errors-list filled"> - <f:for each="{validationResults.errors}" as="error"> - <li class="parsley-required">{error.message}</li> - </f:for> - </ul> - </f:form.validationResults> - </div> - <button type="submit" class="btn btn-lg btn-warning">{f:translate(key:'frontend.applyNow')} <i class="fa fa-paper-plane" aria-hidden="true"></i></button> - </f:form> + <div class="form-group"> + <label> + <f:form.checkbox id="privacy-policy" property="privacyPolicy" value="1"/> + <f:format.raw><f:translate key="frontend.apply.privacyPolicy" arguments="{0: '{f:render(section:\'privacyPolicyCheckboxLink\')}'}"/></f:format.raw> + </label> + <f:form.validationResults for="applyData.privacyPolicy"> + <ul class="sg-jobs-validation-error parsley-errors-list filled"> + <f:for each="{validationResults.errors}" as="error"> + <li class="parsley-required">{error.message}</li> + </f:for> + </ul> + </f:form.validationResults> + </div> + </div> + <button type="submit" class="btn btn-lg btn-warning">{f:translate(key:'frontend.applyNow')} <i + class="fa fa-paper-plane" aria-hidden="true"></i></button> + </f:form> + </div> </div> </div> - </div> + </f:if> </f:section> <f:section name="privacyPolicyCheckboxLink"> <f:link.typolink target="_blank" parameter="{settings.privacyPolicyPage}"> - <f:translate key="frontend.apply.privacyPolicy.link" /> + <f:translate key="frontend.apply.privacyPolicy.link"/> </f:link.typolink> </f:section> diff --git a/Resources/Private/Templates/PageBrowser/Index.html b/Resources/Private/Templates/PageBrowser/Index.html index eac92ae96b0da3126679d4c02c9ce38a338c6e70..1b673790b98f48b7e8da1617ba4cce97037d465a 100644 --- a/Resources/Private/Templates/PageBrowser/Index.html +++ b/Resources/Private/Templates/PageBrowser/Index.html @@ -1,85 +1,98 @@ {namespace sg=SGalinski\SgJobs\ViewHelpers} -<f:layout name="PageBrowser" /> +<f:layout name="PageBrowser"/> <f:section name="main"> - <nav> - <ul class="pagination"> - <f:if condition="{prevPageExist}"> - <f:then> - <li class="tx-pagebrowse-prev"> - <a href="{previousLink}" aria-label="Previous"> - « - </a> - </li> - </f:then> - <f:else> - <li class="tx-pagebrowse-prev disabled"> - <a aria-label="Previous"> - <span aria-hidden="true"> - « - </span> - </a> - </li> - </f:else> - </f:if> - - <f:if condition="{enableLessPages} && {showLessPages}"> - <li> - <a href="{enableLessPagesLink}"> - ... - </a> - </li> - </f:if> - - <f:for each="{pageLinks}" as="pageLink"> - <f:if condition="{pageLink.isCurrentPage}"> + <f:if condition="{numberOfPages} > 1"> + <nav> + <ul class="pagination"> + <f:if condition="{prevPageExist}"> <f:then> - <li class="tx-pagebrowse-current active"> - <a href="#"> - {pageLink.number} - <span class="sr-only"> - (current) - </span> - </a> + <li class="tx-pagebrowse-prev"> + <f:variable name="prevPage" value="{currentPage - 1}"/> + <f:link.action addQueryString="1" + additionalParams="{tx_sgjobs_pagebrowser: {currentPage: prevPage}}" + additionalAttributes="{aria-label: 'Previous'}"> + « + </f:link.action> </li> </f:then> <f:else> - <li class="tx-pagebrowse-page"> - <a href="{pageLink.link}"> - {pageLink.number} + <li class="tx-pagebrowse-prev disabled"> + <a aria-label="Previous"> + <span aria-hidden="true"> + « + </span> </a> </li> </f:else> </f:if> - </f:for> - <f:if condition="{enableMorePages} && {showNextPages}"> - <li> - <a href="{enableMorePagesLink}"> - ... - </a> - </li> - </f:if> + <f:if condition="{enableLessPages} && {showLessPages}"> + <li> + <f:variable name="lessPage" value="{currentPage - 2}"/> + <f:link.action addQueryString="1" + additionalParams="{tx_sgjobs_pagebrowser: {currentPage: lessPage}}"> + ... + </f:link.action> + </li> + </f:if> + + <f:for each="{pageLinks}" as="pageLink"> + <f:if condition="{pageLink.isCurrentPage}"> + <f:then> + <li class="tx-pagebrowse-current active"> + <a href="#"> + {pageLink.number} + <span class="sr-only"> + (current) + </span> + </a> + </li> + </f:then> + <f:else> + <li class="tx-pagebrowse-page"> + <f:link.action addQueryString="1" + additionalParams="{tx_sgjobs_pagebrowser: {currentPage: pageLink.page}}"> + {pageLink.number} + </f:link.action> + </li> + </f:else> + </f:if> + </f:for> - <f:if condition="{nextPageExist}"> - <f:then> - <li class="tx-pagebrowse-next"> - <a href="{nextLink}" aria-label="Next"> - » - </a> + <f:if condition="{enableMorePages} && {showNextPages}"> + <li> + <f:variable name="morePage" value="{currentPage + 2}"/> + <f:link.action addQueryString="1" + additionalParams="{tx_sgjobs_pagebrowser: {currentPage: morePage}}"> + ... + </f:link.action> </li> - </f:then> - <f:else> - <li class="tx-pagebrowse-next disabled"> - <a aria-label="Next"> - <span aria-hidden="true"> + </f:if> + + <f:if condition="{nextPageExist}"> + <f:then> + <li class="tx-pagebrowse-next"> + <f:variable name="nextPage" value="{currentPage + 1}"/> + <f:link.action addQueryString="1" + additionalParams="{tx_sgjobs_pagebrowser: {currentPage: nextPage}}" + additionalAttributes="{aria-label: 'Next'}"> » - </span> - </a> - </li> - </f:else> - </f:if> - </ul> - </nav> + </f:link.action> + </li> + </f:then> + <f:else> + <li class="tx-pagebrowse-next disabled"> + <a aria-label="Next"> + <span aria-hidden="true"> + » + </span> + </a> + </li> + </f:else> + </f:if> + </ul> + </nav> + </f:if> </f:section> diff --git a/Resources/Public/JavaScript/sgJobs.js b/Resources/Public/JavaScript/sgJobs.js deleted file mode 100644 index cddfc685289a74ea61142f754cb8d7eac1edeb41..0000000000000000000000000000000000000000 --- a/Resources/Public/JavaScript/sgJobs.js +++ /dev/null @@ -1,76 +0,0 @@ -'use strict'; - -import $ from 'jquery'; - -/** - * frontend javascript for sg_jobs - */ -export default class SgJobs { - - /** - * Kicks things off - */ - constructor() { - $('.remove-file').on('click', this._removeFile); - $('#sgjobs-joblist').on('change', '.sgjobs-select', SgJobs._filterJoblist); - $('#apply-cover-letter').on('change', SgJobs._checkFileSize); - $('#apply-cv').on('change', SgJobs._checkFileSize); - $('#apply-certificate').on('change', SgJobs._checkFileSize); - if ($('.sg-jobs-validation-error').children().length > 0) { - this._scrollToFirstError(); - } - } - - /** - * Remove file from application logic - * - * @param event - * @private - */ - _removeFile(event) { - let currentElement = $(this); - event.preventDefault(); - - $.post( - '?tx_sgajax[extensionName]=SgJobs&tx_sgajax[controller]=Ajax%5CJoblist&tx_sgajax[action]=deleteTempFile&tx_sgajax[format]=json', - { - file: currentElement.parent().children('.filename').html(), - type: currentElement.attr('filetype') - } - ).done(function() { - currentElement.parent().remove(); - }); - } - - /** - * Scroll to the first error of the form - * - * @private - */ - _scrollToFirstError() { - let $errors = $('.sg-jobs-validation-error:has(li)'), - errorOffset = $errors.parent().offset(); - $(window).scrollTop(errorOffset.top); - } - - /** - * Filter for the joblist, simply apply the form - * - * @private - */ - static _filterJoblist() { - $('#sgjobs-filter').submit(); - } - - /** - * Check the file size of the upload fields - * - * @private - */ - static _checkFileSize(event) { - if (event.currentTarget.files[0].size > document.querySelector('#maxFileSize').getAttribute('data-maxFilesize') * 1000) { - alert(document.querySelector('#maxFileSizeMessage').getAttribute('data-maxFileSizeMessage')); - event.currentTarget.value = ''; - } - } -} diff --git a/Resources/Public/Sass/_sg-jobs.scss b/Resources/Public/Sass/_sg-jobs.scss index 033e4b027cc567a1c6b67e67645f259eaaf82150..4b41d62b07aefff2865f45034c3893834f6dcada 100644 --- a/Resources/Public/Sass/_sg-jobs.scss +++ b/Resources/Public/Sass/_sg-jobs.scss @@ -1,3 +1,14 @@ +.tx-sgjobs .default-content-element .sgjobs-apply-form div[class*="col-xs"] { + margin: 0; + padding: 0 5px; +} + +@media (max-width: 550px) { + .tx-sgjobs .default-content-element .sgjobs-apply-form div[class*="col-xs"] { + width: 100%; + } +} + .sgjobs-highlight-area { @extend %sg-card-shadow-1; background: $sg-jobs-background-color; diff --git a/composer.json b/composer.json index 0bb4a801caa491dd43d98d4e1b786a267f6dfa4c..c770301c8f96260addd419694616063cc8fb8b56 100644 --- a/composer.json +++ b/composer.json @@ -6,21 +6,25 @@ "license": [ "GPL-2.0-or-later" ], - "version": "4.0.11", + "version": "4.3.5", "support": { "issues": "https://gitlab.sgalinski.de/typo3/sg_jobs" }, "require": { "typo3/cms-core": "^9.5.2 || ^10.4.0", - "sgalinski/sg-mail": ">=6.0.0", - "sgalinski/sg-seo": ">=4.0.0" - }, - "require-dev": { - "roave/security-advisories": "dev-master" + "sgalinski/sg-mail": ">=6.0.0" }, + "suggest": { + "sgalinski/sg-seo": ">=4.0.0" + }, "replace": { "sgalinski/sg_jobs": "self.version" }, + "extra": { + "typo3/cms": { + "extension-key": "sg_jobs" + } + }, "autoload": { "psr-4": { "SGalinski\\SgJobs\\": "Classes/" diff --git a/ext_emconf.php b/ext_emconf.php index 17c9bf5c05561babb47f6fd2530c0cef834cd5d4..d657438f920269abe2171e3ec0cb52eb5f713f8b 100644 --- a/ext_emconf.php +++ b/ext_emconf.php @@ -4,7 +4,7 @@ $EM_CONF['sg_jobs'] = [ 'title' => 'Jobs', 'description' => 'Manage and display your Job offers.', 'category' => 'plugin', - 'version' => '4.0.11', + 'version' => '4.3.5', 'state' => 'stable', 'uploadfolder' => FALSE, 'createDirs' => '', @@ -18,9 +18,7 @@ $EM_CONF['sg_jobs'] = [ [ 'typo3' => '9.5.0-10.4.99', 'php' => '7.3.0-7.4.99', - 'project_base' => '7.0.0', - 'sg_mail' => '6.0.0', - 'sg_seo' => '4.0.0' + 'sg_mail' => '6.0.0' ], 'conflicts' => [ diff --git a/ext_localconf.php b/ext_localconf.php index b6be5cbca1ccc39c034878c4f325f17948d7e2a4..0b4b7a24f96eddd6a1bf00bd63151a6b3432d87d 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -33,7 +33,7 @@ call_user_func( 'SGalinski.sg_jobs', 'Joblist', [ - // Cacheable actions + // Available actions 'Joblist' => 'index' ], [ @@ -45,7 +45,7 @@ call_user_func( 'SGalinski.sg_jobs', 'JobApplication', [ - // Cacheable actions + // Available actions 'Joblist' => 'applyForm, apply' ], [ @@ -57,7 +57,7 @@ call_user_func( 'SGalinski.sg_jobs', 'JobTeaser', [ - // Cacheable actions + // Available actions 'JobTeaser' => 'index' ], [ @@ -65,12 +65,6 @@ call_user_func( 'JobTeaser' => '' ] ); - \TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin( - 'SGalinski.sg_jobs', - 'PageBrowser', - ['PageBrowser' => 'index',], - ['PageBrowser' => '',] - ); // 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/ext_tables.sql b/ext_tables.sql index 3ab4477cd6984986a9d08883ecdc089d2e7120d9..251f3834e7538e47432c0aab30c08be42f7241c9 100644 --- a/ext_tables.sql +++ b/ext_tables.sql @@ -7,6 +7,7 @@ CREATE TABLE tx_sgjobs_domain_model_job ( qualification text DEFAULT '' NOT NULL, alternative_start_date text DEFAULT '' NOT NULL, start_date int(11) unsigned DEFAULT '0' NOT NULL, + location text DEFAULT '' NOT NULL, company text DEFAULT '' NOT NULL, telecommute_possible tinyint(4) unsigned DEFAULT '0' NOT NULL, office_work_possible tinyint(4) unsigned DEFAULT '1' NOT NULL, @@ -23,7 +24,8 @@ CREATE TABLE tx_sgjobs_domain_model_job ( contact int(11) unsigned DEFAULT '0' NOT NULL, featured_offer tinyint(4) unsigned DEFAULT '0' NOT NULL, hide_apply_by_email tinyint(4) unsigned DEFAULT '0' NOT NULL, - hide_apply_by_postal tinyint(4) unsigned DEFAULT '0' NOT NULL + hide_apply_by_postal tinyint(4) unsigned DEFAULT '0' NOT NULL, + apply_external_link varchar(512) DEFAULT '' NOT NULL ); CREATE TABLE tx_sgjobs_domain_model_department (