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">
-							&laquo;
-						</a>
-					</li>
-				</f:then>
-				<f:else>
-					<li class="tx-pagebrowse-prev disabled">
-						<a aria-label="Previous">
-							<span aria-hidden="true">
-								&laquo;
-							</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'}">
+								&laquo;
+							</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">
+									&laquo;
+								</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">
-							&raquo;
-						</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'}">
 								&raquo;
-							</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">
+									&raquo;
+								</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 (