├── public
├── jobs.svg
└── dashboard.css
├── contao
├── templates
│ └── _new
│ │ ├── .twig-root
│ │ ├── jobs_basic
│ │ ├── plenta_jobs_basic_form.html.twig
│ │ ├── plenta_jobs_basic_job_offer_empty.html.twig
│ │ └── plenta_jobs_basic_offer_default.html.twig
│ │ ├── jobs_basic_reader_parts
│ │ ├── plenta_jobs_basic_reader_image.html.twig
│ │ ├── plenta_jobs_basic_reader_attribute.html.twig
│ │ ├── plenta_jobs_basic_reader_teaser.html.twig
│ │ ├── plenta_jobs_basic_reader_description.html.twig
│ │ ├── plenta_jobs_basic_reader_salary.html.twig
│ │ └── plenta_jobs_basic_reader_job_location.html.twig
│ │ ├── content_element
│ │ ├── plenta_jobs_basic_job_offer_details.html.twig
│ │ └── plenta_jobs_basic_job_offer_teaser.html.twig
│ │ └── frontend_module
│ │ ├── plenta_jobs_basic_offer_reader.html.twig
│ │ ├── plenta_jobs_basic_offer_list.html.twig
│ │ └── plenta_jobs_basic_filter.html.twig
├── languages
│ ├── de
│ │ ├── tl_user_group.xlf
│ │ ├── tl_plenta_jobs_basic_settings_employment_type.xlf
│ │ ├── tl_plenta_jobs_basic_organization.xlf
│ │ ├── tl_content.xlf
│ │ ├── modules.xlf
│ │ ├── default.xlf
│ │ └── tl_plenta_jobs_basic_job_location.xlf
│ └── en
│ │ ├── tl_module.xlf
│ │ ├── default.xlf
│ │ └── modules.xlf
├── dca
│ ├── tl_user_group.php
│ ├── tl_content.php
│ ├── tl_plenta_jobs_basic_organization.php
│ ├── tl_plenta_jobs_basic_settings_employment_type.php
│ └── tl_plenta_jobs_basic_job_location.php
└── config
│ └── config.php
├── docs
├── contao-jobs-basic-backend-offer.png
└── contao-jobs-basic-backend-listing.png
├── config
├── config.yml
├── routing.yml
├── listener.yml
└── services.yml
├── src
├── Contao
│ └── Model
│ │ ├── PlentaJobsBasicOrganizationModel.php
│ │ ├── PlentaJobsBasicSettingsEmploymentTypeModel.php
│ │ └── PlentaJobsBasicJobLocationModel.php
├── Events
│ ├── JobOfferModelKeywordFieldsEvent.php
│ ├── JobOfferListAfterFormBuildEvent.php
│ ├── JobOfferFilterAfterFormBuildEvent.php
│ ├── JobOfferDataManipulatorEvent.php
│ ├── JobOfferReaderContentPartEvent.php
│ ├── Model
│ │ └── FindAllPublishedByTypesAndLocationEvent.php
│ ├── JobOfferListBeforeParseTemplateEvent.php
│ └── JobOfferReaderBeforeParseTemplateEvent.php
├── EventListener
│ └── Contao
│ │ ├── DCA
│ │ ├── JobOfferFields.php
│ │ ├── TlContent.php
│ │ ├── TlPlentaJobsBasicJobLocation.php
│ │ ├── SetPtableForContentListener.php
│ │ ├── TlPlentaJobsBasicSettingsEmploymentType.php
│ │ ├── TlModule.php
│ │ └── TlPlentaJobsBasicOffer.php
│ │ ├── Hooks
│ │ └── ChangelanguageNavigationListener.php
│ │ ├── SitemapListener.php
│ │ ├── BackendMenuListener.php
│ │ └── InsertTagListener.php
├── Controller
│ ├── JobsOfferFilterRequestController.php
│ └── Contao
│ │ ├── BackendController.php
│ │ ├── AjaxController.php
│ │ ├── BackendModule
│ │ └── SettingsController.php
│ │ └── ContentElement
│ │ ├── PlentaJobsBasicJobOfferTeaserController.php
│ │ └── PlentaJobsBasicJobOfferDetailsController.php
├── Helper
│ ├── SortingHelper.php
│ ├── PermissionsHelper.php
│ ├── DataTypeMapper.php
│ ├── NumberHelper.php
│ ├── CountJobsHelper.php
│ ├── EmploymentType.php
│ └── MetaFieldsHelper.php
├── PlentaContaoJobsBasicBundle.php
├── Form
│ └── Type
│ │ ├── HtmlType.php
│ │ ├── ContaoRequestTokenType.php
│ │ ├── JobSortingType.php
│ │ └── JobOfferFilterType.php
├── Csrf
│ └── JobsBasicCsrfTokenManager.php
├── DependencyInjection
│ └── PlentaContaoJobsBasicExtension.php
├── Migration
│ ├── JobLocationCountryUppercaseMigration.php
│ ├── RemoveFkConstraintsMigration.php
│ ├── BoolCharToIntMigration.php
│ ├── RenameDatabaseColumnsMigration.php
│ ├── RefactorTranslationsMigration.php
│ └── MoveRemoteJobsMigration.php
├── InsertTag
│ └── JobsCountInsertTag.php
└── ContaoManager
│ └── Plugin.php
├── templates
├── be_plenta_jobs_basic_settings.html.twig
├── be_plenta_info.html.twig
└── forms
│ └── form_layout.html.twig
├── README.md
├── composer.json
└── LICENSE
/public/jobs.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/contao/templates/_new/.twig-root:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/contao/templates/_new/jobs_basic/plenta_jobs_basic_form.html.twig:
--------------------------------------------------------------------------------
1 | {{ form(form) }}
--------------------------------------------------------------------------------
/docs/contao-jobs-basic-backend-offer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plenta/contao-jobs-basic-bundle/HEAD/docs/contao-jobs-basic-backend-offer.png
--------------------------------------------------------------------------------
/docs/contao-jobs-basic-backend-listing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plenta/contao-jobs-basic-bundle/HEAD/docs/contao-jobs-basic-backend-listing.png
--------------------------------------------------------------------------------
/config/config.yml:
--------------------------------------------------------------------------------
1 | twig:
2 | form_themes: ['@PlentaContaoJobsBasic/forms/form_layout.html.twig']
3 | globals:
4 | backend_route: '%contao.backend.route_prefix%'
--------------------------------------------------------------------------------
/contao/templates/_new/jobs_basic_reader_parts/plenta_jobs_basic_reader_image.html.twig:
--------------------------------------------------------------------------------
1 | {% block content %}
2 | {{ content_element('image', data) }}
3 | {% endblock %}
--------------------------------------------------------------------------------
/contao/templates/_new/content_element/plenta_jobs_basic_job_offer_details.html.twig:
--------------------------------------------------------------------------------
1 | {% extends '@Contao/content_element/_base.html.twig' %}
2 |
3 | {% block content %}
4 | {{ content|default('')|raw }}
5 | {% endblock %}
--------------------------------------------------------------------------------
/contao/templates/_new/jobs_basic/plenta_jobs_basic_job_offer_empty.html.twig:
--------------------------------------------------------------------------------
1 | {% extends '@Contao/frontend_module/_base.html.twig' %}
2 |
3 | {% block content %}
4 |
{{ empty }}
5 | {% endblock %}
6 |
--------------------------------------------------------------------------------
/contao/templates/_new/jobs_basic_reader_parts/plenta_jobs_basic_reader_attribute.html.twig:
--------------------------------------------------------------------------------
1 | {% block content %}
2 | {{ label }}: {{ value }}
3 | {% endblock %}
--------------------------------------------------------------------------------
/contao/templates/_new/jobs_basic_reader_parts/plenta_jobs_basic_reader_teaser.html.twig:
--------------------------------------------------------------------------------
1 | {% block content %}
2 | {{ content_element('text', {
3 | text: text,
4 | id: null,
5 | cssID: ['', class],
6 | }) }}
7 | {% endblock %}
--------------------------------------------------------------------------------
/contao/templates/_new/jobs_basic_reader_parts/plenta_jobs_basic_reader_description.html.twig:
--------------------------------------------------------------------------------
1 | {% block content %}
2 | {{ content_element('text', {
3 | text: text,
4 | id: null,
5 | cssID: ['', class],
6 | }) }}
7 | {% endblock %}
--------------------------------------------------------------------------------
/contao/templates/_new/jobs_basic_reader_parts/plenta_jobs_basic_reader_salary.html.twig:
--------------------------------------------------------------------------------
1 | {% block content %}
2 |
3 | {{ 'tl_plenta_jobs_basic_offer.salaryValue.0'|trans({}, 'contao_tl_plenta_jobs_basic_offer') }}: {{ salary }}/{{ unit }}
4 |
5 | {% endblock %}
6 |
--------------------------------------------------------------------------------
/contao/templates/_new/frontend_module/plenta_jobs_basic_offer_reader.html.twig:
--------------------------------------------------------------------------------
1 | {% extends '@Contao/frontend_module/_base.html.twig' %}
2 |
3 | {% block content %}
4 | {{ content|raw }}
5 |
6 | {% if back|default %}
7 |
8 | {{ back }}
9 |
10 | {% endif %}
11 | {% endblock %}
12 |
--------------------------------------------------------------------------------
/src/Contao/Model/PlentaJobsBasicOrganizationModel.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\Contao\Model;
14 |
15 | use Contao\Model;
16 |
17 | class PlentaJobsBasicOrganizationModel extends Model
18 | {
19 | protected static $strTable = 'tl_plenta_jobs_basic_organization';
20 | }
21 |
--------------------------------------------------------------------------------
/src/Contao/Model/PlentaJobsBasicSettingsEmploymentTypeModel.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\Contao\Model;
14 |
15 | use Contao\Model;
16 |
17 | class PlentaJobsBasicSettingsEmploymentTypeModel extends Model
18 | {
19 | protected static $strTable = 'tl_plenta_jobs_basic_settings_employment_type';
20 | }
21 |
--------------------------------------------------------------------------------
/src/Events/JobOfferModelKeywordFieldsEvent.php:
--------------------------------------------------------------------------------
1 | fields;
18 | }
19 |
20 | public function setFields(array $fields): JobOfferModelKeywordFieldsEvent
21 | {
22 | $this->fields = $fields;
23 | return $this;
24 | }
25 | }
--------------------------------------------------------------------------------
/config/routing.yml:
--------------------------------------------------------------------------------
1 | plenta_jobs_basic.offer_filter:
2 | path: /_plenta-jobs-basic/offer/filter
3 | defaults:
4 | _scope: frontend
5 | _token_check: false
6 | _controller: Plenta\ContaoJobsBasic\Controller\JobsOfferFilterRequestController::filterOffersAction
7 |
8 | Plenta\ContaoJobsBasic\Controller\Contao\BackendModule\SettingsController:
9 | path: /%contao.backend.route_prefix%/plentajobsbasic/settings
10 | defaults:
11 | _scope: backend
12 | _controller: Plenta\ContaoJobsBasic\Controller\Contao\BackendModule\SettingsController::showSettings
13 |
14 | jobs_basic:
15 | resource: ../src/Controller
16 | type: attribute
17 |
--------------------------------------------------------------------------------
/public/dashboard.css:
--------------------------------------------------------------------------------
1 | .plenta-wrapper {
2 | display: flex;
3 | flex-wrap: wrap;
4 | }
5 |
6 | .plenta-box {
7 | flex: 50%;
8 | }
9 |
10 | .plenta-box a {
11 | text-decoration: underline;
12 | }
13 |
14 | .plenta-box a:hover {
15 | text-decoration: underline;
16 | color: var(--contao);
17 | }
18 |
19 | .plenta-box.last {
20 | text-align: right;
21 | }
22 |
23 | #plentaJobsInfoPanel {
24 | padding: 15px;
25 | border-bottom: 1px solid var(--blue);
26 | }
27 |
28 | html[data-color-scheme="dark"] {
29 | .plenta-box,
30 | .plenta-box a {
31 | color: #ffffff;
32 | }
33 |
34 | .plenta-box a:hover {
35 | color: var(--contao);
36 | }
37 | }
--------------------------------------------------------------------------------
/templates/be_plenta_jobs_basic_settings.html.twig:
--------------------------------------------------------------------------------
1 | {% extends "@Contao/be_main.html.twig" %}
2 | {% set type = '' %}
3 |
4 | {% block main_content %}
5 | {% include('@PlentaContaoJobsBasic/be_plenta_info.html.twig') %}
6 |
7 |
8 |
9 |
10 | {% for key, mod in mods %}
11 |
12 | | {{ mod.title.0 }} |
13 |
14 | {% endfor %}
15 |
16 |
17 |
18 | {% endblock %}
19 |
--------------------------------------------------------------------------------
/contao/languages/de/tl_user_group.xlf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Jobs (Basic) Module
7 |
8 |
9 | Hier können Sie den Zugriff auf ein oder mehrere Jobs (Basic) Module erlauben.
10 |
11 |
12 | Einstellungen
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/EventListener/Contao/DCA/JobOfferFields.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\EventListener\Contao\DCA;
14 |
15 | class JobOfferFields
16 | {
17 | public static function getFields()
18 | {
19 | return ['title', 'tstamp', 'datePosted', 'jobLocation'];
20 | }
21 |
22 | public static function getParts()
23 | {
24 | return ['image', 'teaser', 'company', 'jobLocation', 'publicationDate', 'employmentType'];
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Contao/Model/PlentaJobsBasicJobLocationModel.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\Contao\Model;
14 |
15 | use Contao\Model;
16 |
17 | class PlentaJobsBasicJobLocationModel extends Model
18 | {
19 | protected static $strTable = 'tl_plenta_jobs_basic_job_location';
20 |
21 | public static function findByMultiplePids(array $pids)
22 | {
23 | $criteria = array_fill(0, count($pids), 'tl_plenta_jobs_basic_job_location.pid = ?');
24 |
25 | $cols = ['(' . implode(' OR ', $criteria) . ')'];
26 |
27 | return self::findBy($cols, $pids);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Controller/JobsOfferFilterRequestController.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\Controller;
14 |
15 | use Contao\Module;
16 | use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
17 | use Symfony\Component\HttpFoundation\Request;
18 | use Symfony\Component\HttpFoundation\Response;
19 |
20 | class JobsOfferFilterRequestController extends AbstractController
21 | {
22 | public function filterOffersAction(Request $request): Response
23 | {
24 | $module = Module::getFrontendModule($request->get('id'));
25 |
26 | return new Response($module);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Helper/SortingHelper.php:
--------------------------------------------------------------------------------
1 | plentaJobsBasic_filterSort) {
12 | usort($options, function ($a, $b) use ($sorting) {
13 | return match ($sorting) {
14 | 'az' => strnatcasecmp($a['name'], $b['name']),
15 | 'za' => strnatcasecmp($b['name'], $a['name']),
16 | '09' => $a['count'] <=> $b['count'],
17 | '90' => $b['count'] <=> $a['count'],
18 | };
19 | });
20 | }
21 |
22 | foreach ($options as $option) {
23 | $return[$option['id']] = $option['name'];
24 | }
25 |
26 | return $return;
27 | }
28 | }
--------------------------------------------------------------------------------
/src/PlentaContaoJobsBasicBundle.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic;
14 |
15 | use Plenta\ContaoJobsBasic\DependencyInjection\PlentaContaoJobsBasicExtension;
16 | use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
17 | use Symfony\Component\HttpKernel\Bundle\AbstractBundle;
18 | use Symfony\Component\HttpKernel\Bundle\Bundle;
19 |
20 | /**
21 | * Configures the bundle.
22 | */
23 | class PlentaContaoJobsBasicBundle extends AbstractBundle
24 | {
25 | public function getContainerExtension(): ?ExtensionInterface
26 | {
27 | return new PlentaContaoJobsBasicExtension();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/contao/dca/tl_user_group.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | use Contao\CoreBundle\DataContainer\PaletteManipulator;
14 |
15 | $GLOBALS['TL_DCA']['tl_user_group']['fields']['plenta_jobs_basic_settings'] = [
16 | 'inputType' => 'checkbox',
17 | 'exclude' => true,
18 | 'options' => [
19 | 'settings',
20 | ],
21 | 'reference' => &$GLOBALS['TL_LANG']['tl_user_group']['plenta_jobs_basic_settings'],
22 | 'eval' => [
23 | 'multiple' => true,
24 | ],
25 | 'sql' => 'blob NULL',
26 | ];
27 |
28 | PaletteManipulator::create()
29 | ->addField('plenta_jobs_basic_settings', 'modules', PaletteManipulator::POSITION_AFTER)
30 | ->applyToPalette('default', 'tl_user_group')
31 | ;
32 |
--------------------------------------------------------------------------------
/src/Form/Type/HtmlType.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\Form\Type;
14 |
15 | use Symfony\Component\Form\AbstractType;
16 | use Symfony\Component\Form\FormInterface;
17 | use Symfony\Component\Form\FormView;
18 | use Symfony\Component\OptionsResolver\OptionsResolver;
19 |
20 | class HtmlType extends AbstractType
21 | {
22 | public function configureOptions(OptionsResolver $resolver): void
23 | {
24 | $resolver->setDefaults([
25 | 'html' => '',
26 | 'label' => false,
27 | ]);
28 | }
29 |
30 | public function buildView(FormView $view, FormInterface $form, array $options)
31 | {
32 | $view->vars['html'] = $options['html'];
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Csrf/JobsBasicCsrfTokenManager.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\Csrf;
14 |
15 | use Contao\CoreBundle\Csrf\ContaoCsrfTokenManager;
16 |
17 | class JobsBasicCsrfTokenManager
18 | {
19 | private ContaoCsrfTokenManager $csrfTokenManager;
20 | private string $csrfTokenName;
21 |
22 | public function __construct(
23 | ContaoCsrfTokenManager $csrfTokenManager,
24 | string $csrfTokenName
25 | ) {
26 | $this->csrfTokenName = $csrfTokenName;
27 | $this->csrfTokenManager = $csrfTokenManager;
28 | }
29 |
30 | public function generateToken(): string
31 | {
32 | return $this->csrfTokenManager->getToken($this->csrfTokenName)->getValue();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/EventListener/Contao/DCA/TlContent.php:
--------------------------------------------------------------------------------
1 | requestStack = $requestStack;
20 | }
21 |
22 | public function __invoke(DataContainer $dc = null): void
23 | {
24 | if (null === $dc || !$dc->id || 'edit' !== $this->requestStack->getCurrentRequest()->query->get('act')) {
25 | return;
26 | }
27 |
28 | $element = ContentModel::findById($dc->id);
29 |
30 | if (null === $element || 'plenta_jobs_basic_job_offer_teaser' !== $element->type) {
31 | return;
32 | }
33 |
34 | $GLOBALS['TL_DCA']['tl_content']['fields']['text']['eval']['mandatory'] = false;
35 | }
36 | }
--------------------------------------------------------------------------------
/src/EventListener/Contao/DCA/TlPlentaJobsBasicJobLocation.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\EventListener\Contao\DCA;
14 |
15 | class TlPlentaJobsBasicJobLocation
16 | {
17 | public function listLocations(array $arrRow): string
18 | {
19 | if ($arrRow['title']) {
20 | return $arrRow['title'];
21 | }
22 |
23 | if ('onPremise' === $arrRow['jobTypeLocation']) {
24 | return ''.$arrRow['addressLocality'].', '.$arrRow['postalCode'].(!empty($arrRow['streetAddress']) ? ', '.$arrRow['streetAddress'] : '').'
';
25 | }
26 |
27 | $return = ''.$GLOBALS['TL_LANG']['MSC']['PLENTA_JOBS']['remote'];
28 |
29 | $return .= ' ['.$arrRow['requirementValue'].']';
30 |
31 | $return .= '
';
32 |
33 | return $return;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Events/JobOfferListAfterFormBuildEvent.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\Events;
14 |
15 | use Plenta\ContaoJobsBasic\Form\Type\JobOfferFilterType;
16 | use Symfony\Component\Form\FormInterface;
17 | use Symfony\Contracts\EventDispatcher\Event;
18 |
19 | class JobOfferListAfterFormBuildEvent extends Event
20 | {
21 | public const NAME = 'plenta_jobs_basic.job_offer_list.after_form_build';
22 |
23 | protected FormInterface $form;
24 |
25 | /**
26 | * @return JobOfferFilterType
27 | */
28 | public function getForm(): FormInterface
29 | {
30 | return $this->form;
31 | }
32 |
33 | /**
34 | * @param FormInterface $form
35 | *
36 | * @return JobOfferListAfterFormBuildEvent
37 | */
38 | public function setForm(FormInterface $form): self
39 | {
40 | $this->form = $form;
41 |
42 | return $this;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Contao Jobs Basic Bundle
2 |
3 | The perfect solution for your job offer site.
4 | Ideal for job offers in multiple languages.
5 |
6 | Basic Job Manager for Contao (including Google Jobs)
7 | Open source and free. 🚀
8 |
9 | 👉 [Full documentation](https://plenta.io/contao-erweiterungen/jobs-basic)
10 |
11 | ## Installation
12 |
13 | ### Install using Contao Manager
14 |
15 | Search for **job** or **jobs** and you will find this extension.
16 |
17 | ### Install using Composer
18 |
19 | ```bash
20 | composer require plenta/contao-jobs-basic-bundle
21 | ```
22 |
23 |
24 | ## System requirements
25 |
26 | - PHP: `^7.4 || ^8.1`
27 | - Contao: `^4.13` || `^5`
28 | - mvo/contao-group-widget `^1.4`
29 |
30 | ## Which version is right for you?
31 |
32 | | Contao Version | PHP Version | Jobs Version |
33 | |----------------|-------------|--------------|
34 | | 4.13.* | \>= 7.4 | 2.* |
35 | | 5.* | \>= 8.1 | 3.* |
36 |
37 |
38 | ## Screenshots
39 |
40 | ### Detail view
41 |
42 | 
43 |
44 | ### Listing
45 |
46 | 
--------------------------------------------------------------------------------
/src/Helper/PermissionsHelper.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\Helper;
14 |
15 | use Contao\BackendUser;
16 | use Contao\StringUtil;
17 | use Contao\UserGroupModel;
18 |
19 | class PermissionsHelper
20 | {
21 | public static function canAccessModule($name, $module = 'plenta_jobs_basic_settings'): bool
22 | {
23 | $user = BackendUser::getInstance();
24 |
25 | if ($user->isAdmin) {
26 | return true;
27 | }
28 | if (!empty($user->groups)) {
29 | foreach ($user->groups as $groupId) {
30 | $group = UserGroupModel::findByIdOrAlias($groupId);
31 | $moduleArr = StringUtil::deserialize($group->{$module});
32 | if (\is_array($moduleArr) && \in_array($name, $moduleArr, true)) {
33 | return true;
34 | }
35 | }
36 | }
37 |
38 | return false;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Helper/DataTypeMapper.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\Helper;
14 |
15 | use Contao\CoreBundle\Framework\ContaoFramework;
16 | use Contao\StringUtil;
17 |
18 | class DataTypeMapper
19 | {
20 | private ContaoFramework $framework;
21 |
22 | public function __construct(ContaoFramework $framework)
23 | {
24 | $this->framework = $framework;
25 | }
26 |
27 | public function serializedToJson(string $serializedData): string
28 | {
29 | /** @var StringUtil $stringUtil */
30 | $stringUtil = $this->framework->getAdapter(StringUtil::class);
31 |
32 | $data = $stringUtil->deserialize($serializedData);
33 |
34 | return json_encode($data);
35 | }
36 |
37 | public function jsonToSerialized(?string $jsonData): ?string
38 | {
39 | if (null === $jsonData) {
40 | return serialize([]);
41 | }
42 |
43 | return serialize(json_decode($jsonData, true));
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/DependencyInjection/PlentaContaoJobsBasicExtension.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\DependencyInjection;
14 |
15 | use Exception;
16 | use Symfony\Component\Config\FileLocator;
17 | use Symfony\Component\DependencyInjection\ContainerBuilder;
18 | use Symfony\Component\DependencyInjection\Extension\Extension;
19 | use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
20 | use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
21 |
22 | class PlentaContaoJobsBasicExtension extends Extension
23 | {
24 | /**
25 | * @param array $configs
26 | * @param ContainerBuilder $container
27 | *
28 | * @throws Exception
29 | */
30 | public function load(array $configs, ContainerBuilder $container): void
31 | {
32 | $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../../config'));
33 | $loader->load('services.yml');
34 | $loader->load('listener.yml');
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Controller/Contao/BackendController.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\Controller\Contao;
14 |
15 | use Contao\CoreBundle\Controller\AbstractBackendController;
16 | use Contao\System;
17 | use Plenta\ContaoJobsBasic\Contao\Model\PlentaJobsBasicOfferModel;
18 | use Symfony\Component\HttpFoundation\Request;
19 | use Symfony\Component\HttpFoundation\RequestStack;
20 | use Symfony\Component\Routing\Attribute\Route;
21 |
22 | #[Route('%contao.backend.route_prefix%/_jobs', defaults: ['_scope' => 'backend'])]
23 | class BackendController extends AbstractBackendController
24 | {
25 | #[Route('/renewDatePosted', name: 'jobsBasic_renewDatePosted')]
26 | public function renewDatePosted(Request $request, RequestStack $requestStack)
27 | {
28 | $id = $request->get('id');
29 | $objJobOffer = PlentaJobsBasicOfferModel::findByPk($id);
30 | $objJobOffer->datePosted = time();
31 | $objJobOffer->save();
32 |
33 | return $this->redirect(System::getReferer());
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/config/listener.yml:
--------------------------------------------------------------------------------
1 | services:
2 | Plenta\ContaoJobsBasic\EventListener\Contao\DCA\TlPlentaJobsBasicOffer:
3 | public: true
4 | arguments:
5 | - '@Plenta\ContaoJobsBasic\Helper\EmploymentType'
6 | - '@contao.slug'
7 | - '@request_stack'
8 | - '@twig'
9 |
10 | Plenta\ContaoJobsBasic\EventListener\Contao\DCA\TlPlentaJobsBasicJobLocation:
11 | public: true
12 |
13 | Plenta\ContaoJobsBasic\EventListener\Contao\DCA\TlPlentaJobsBasicSettingsEmploymentType:
14 | public: true
15 | arguments:
16 | - '@Plenta\ContaoJobsBasic\Helper\DataTypeMapper'
17 | - '@Plenta\ContaoJobsBasic\Helper\EmploymentType'
18 |
19 | # Contao Hooks
20 | Plenta\ContaoJobsBasic\EventListener\Contao\DCA\SetPtableForContentListener:
21 | tags:
22 | - { name: contao.hook, hook: loadDataContainer, method: setPtableForContentListener, priority: 0 }
23 | arguments:
24 | - '@request_stack'
25 | - '@contao.routing.scope_matcher'
26 |
27 | Plenta\ContaoJobsBasic\EventListener\Contao\BackendMenuListener:
28 | public: true
29 | tags:
30 | - { name: kernel.event_listener, event: contao.backend_menu_build, priority: -255 }
31 | arguments:
32 | - '@router'
33 | - '@request_stack'
34 |
--------------------------------------------------------------------------------
/src/Controller/Contao/AjaxController.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\Controller\Contao;
14 |
15 | use Contao\CoreBundle\Controller\AbstractController;
16 | use Contao\CoreBundle\String\SimpleTokenParser;
17 | use Contao\ModuleModel;
18 | use Plenta\ContaoJobsBasic\Helper\CountJobsHelper;
19 | use Symfony\Component\HttpFoundation\Request;
20 | use Symfony\Component\HttpFoundation\Response;
21 | use Symfony\Component\Routing\Attribute\Route;
22 |
23 | #[Route('_plenta-jobs-basic/offer', defaults: ['_scope' => 'frontend'])]
24 | class AjaxController extends AbstractController
25 | {
26 | #[Route('/count', methods: ['GET'])]
27 | public function count(Request $request, CountJobsHelper $countJobsHelper, SimpleTokenParser $tokenParser)
28 | {
29 | $module = ModuleModel::findByPk($request->get('module'));
30 |
31 | if (!$module) {
32 | throw new \Exception('Module not found');
33 | }
34 |
35 | $request->attributes->set('moduleModel', $module);
36 |
37 | return new Response($tokenParser->parse($module->plentaJobsBasicSubmit, ['count' => $countJobsHelper->countJobs()]));
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/Events/JobOfferFilterAfterFormBuildEvent.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\Events;
14 |
15 | use Symfony\Component\Form\FormInterface;
16 | use Symfony\Component\Routing\Attribute\Route;
17 | use Symfony\Contracts\EventDispatcher\Event;
18 |
19 | class JobOfferFilterAfterFormBuildEvent extends Event
20 | {
21 | public const NAME = 'plenta_jobs_basic.job_offer_filter.after_form_build';
22 |
23 | protected FormInterface $form;
24 |
25 | protected string $route;
26 |
27 | /**
28 | * @return FormInterface
29 | */
30 | public function getForm(): FormInterface
31 | {
32 | return $this->form;
33 | }
34 |
35 | /**
36 | * @param FormInterface $form
37 | *
38 | * @return JobOfferListAfterFormBuildEvent
39 | */
40 | public function setForm(FormInterface $form): self
41 | {
42 | $this->form = $form;
43 |
44 | return $this;
45 | }
46 |
47 | public function getRoute(): string
48 | {
49 | return $this->route;
50 | }
51 |
52 | public function setRoute(string $route): JobOfferFilterAfterFormBuildEvent
53 | {
54 | $this->route = $route;
55 | return $this;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/templates/be_plenta_info.html.twig:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Plenta Jobs (Basic) {{ version }}
5 |
6 |
12 |
13 |
--------------------------------------------------------------------------------
/contao/languages/de/tl_plenta_jobs_basic_settings_employment_type.xlf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Einstellungen
7 |
8 |
9 | Name
10 |
11 |
12 | Bitte geben Sie den Namen der Stellenart ein.
13 |
14 |
15 | Google For Jobs - Zuordung
16 |
17 |
18 | Bitte wählen Sie eine Zuordnung.
19 |
20 |
21 | Übersetzung
22 |
23 |
24 | Hier können Sie Übersetzungen anlegen.
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/contao/templates/_new/content_element/plenta_jobs_basic_job_offer_teaser.html.twig:
--------------------------------------------------------------------------------
1 | {% extends '@Contao/content_element/_base.html.twig' %}
2 |
3 | {% block content %}
4 | {% if data.text %}
5 |
6 | {{ data.text|raw }}
7 |
8 | {% endif %}
9 | {% if 'image' in parts %}
10 | {{ jobOfferMeta.image|raw }}
11 | {% endif %}
12 |
13 | <{{ data.plentaJobsBasicHeadlineTag }} class="title">
14 |
{{ jobOfferMeta.title }}
15 | {{ data.plentaJobsBasicHeadlineTag }}>
16 | {% if 'company' in parts %}
17 |
18 | {{ jobOfferMeta.company }}
19 |
20 | {% endif %}
21 | {% if 'jobLocation' in parts %}
22 |
23 | {{ jobOfferMeta.addressLocalityFormatted }}
24 |
25 | {% endif %}
26 | {% if 'teaser' in parts %}
27 |
28 | {{ jobOfferMeta.teaser }}
29 |
30 | {% endif %}
31 |
32 |
33 | {% if 'publicationDate' in parts %}
34 |
35 | {{ jobOfferMeta.publicationDateFormatted }}
36 |
37 | {% endif %}
38 | {% if 'employmentType' in parts %}
39 |
40 | {{ jobOfferMeta.employmentTypeFormatted }}
41 |
42 | {% endif %}
43 |
44 | {% endblock %}
--------------------------------------------------------------------------------
/src/EventListener/Contao/DCA/SetPtableForContentListener.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\EventListener\Contao\DCA;
14 |
15 | use Contao\CoreBundle\Routing\ScopeMatcher;
16 | use Symfony\Component\HttpFoundation\RequestStack;
17 |
18 | class SetPtableForContentListener
19 | {
20 | private RequestStack $requestStack;
21 | private ScopeMatcher $scopeMatcher;
22 |
23 | public function __construct(RequestStack $requestStack, ScopeMatcher $scopeMatcher)
24 | {
25 | $this->requestStack = $requestStack;
26 | $this->scopeMatcher = $scopeMatcher;
27 | }
28 |
29 | public function setPtableForContentListener(string $table): void
30 | {
31 | // We only want to adjust the DCA of tl_content
32 | if ('tl_content' !== $table) {
33 | return;
34 | }
35 |
36 | $request = $this->requestStack->getCurrentRequest();
37 |
38 | // Check if this is a back end request
39 | if (null === $request || !$this->scopeMatcher->isBackendRequest($request)) {
40 | return;
41 | }
42 |
43 | if ('plenta_jobs_basic_offers' === $request->query->get('do')) {
44 | $GLOBALS['TL_DCA']['tl_content']['config']['ptable'] = 'tl_plenta_jobs_basic_offer';
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/contao/languages/en/tl_module.xlf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Modification date (ascending)
7 |
8 |
9 | Publication date (ascending)
10 |
11 |
12 | A-Z
13 |
14 |
15 | Modification date (descending)
16 |
17 |
18 | Publication date (descending)
19 |
20 |
21 | Z-A
22 |
23 |
24 | Job location (ascending)
25 |
26 |
27 | Job location (descending)
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/src/Events/JobOfferDataManipulatorEvent.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\Events;
14 |
15 | use Contao\Model;
16 | use Plenta\ContaoJobsBasic\Contao\Model\PlentaJobsBasicOfferModel;
17 | use Symfony\Contracts\EventDispatcher\Event;
18 |
19 | class JobOfferDataManipulatorEvent extends Event
20 | {
21 | public const NAME = 'plenta_jobs_basic.job_offer_list.offer_data_manipulator';
22 |
23 | protected array $data;
24 | protected PlentaJobsBasicOfferModel $jobOffer;
25 | protected Model $model;
26 |
27 | public function __construct()
28 | {
29 | }
30 |
31 | public function getJob(): PlentaJobsBasicOfferModel
32 | {
33 | return $this->jobOffer;
34 | }
35 |
36 | public function setJob(PlentaJobsBasicOfferModel $jobOffer): self
37 | {
38 | $this->jobOffer = $jobOffer;
39 |
40 | return $this;
41 | }
42 |
43 | public function getData(): array
44 | {
45 | return $this->data;
46 | }
47 |
48 | public function setData(array $data): self
49 | {
50 | $this->data = $data;
51 |
52 | return $this;
53 | }
54 |
55 | public function setModel(Model $model): self
56 | {
57 | $this->model = $model;
58 |
59 | return $this;
60 | }
61 |
62 | public function getModel(): Model
63 | {
64 | return $this->model;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/Form/Type/ContaoRequestTokenType.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\Form\Type;
14 |
15 | use Plenta\ContaoJobsBasic\Csrf\JobsBasicCsrfTokenManager;
16 | use Symfony\Component\Form\AbstractType;
17 | use Symfony\Component\Form\Extension\Core\Type\HiddenType;
18 | use Symfony\Component\Form\FormInterface;
19 | use Symfony\Component\Form\FormView;
20 | use Symfony\Component\OptionsResolver\OptionsResolver;
21 |
22 | class ContaoRequestTokenType extends AbstractType
23 | {
24 | private JobsBasicCsrfTokenManager $csrfTokenManager;
25 |
26 | public function __construct(
27 | JobsBasicCsrfTokenManager $csrfTokenManager
28 | ) {
29 | $this->csrfTokenManager = $csrfTokenManager;
30 | }
31 |
32 | public function configureOptions(OptionsResolver $resolver): void
33 | {
34 | $resolver->setDefaults([
35 | 'data' => $this->csrfTokenManager->generateToken(),
36 | 'empty_data' => $this->csrfTokenManager->generateToken(),
37 | 'mapped' => false,
38 | ]);
39 | }
40 |
41 | public function getParent(): string
42 | {
43 | return HiddenType::class;
44 | }
45 |
46 | public function buildView(FormView $view, FormInterface $form, array $options): void
47 | {
48 | parent::buildView($view, $form, $options);
49 |
50 | $view->vars['full_name'] = 'REQUEST_TOKEN';
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Migration/JobLocationCountryUppercaseMigration.php:
--------------------------------------------------------------------------------
1 | connection->createSchemaManager();
34 |
35 | if (!$schemaManager->tablesExist(['tl_plenta_jobs_basic_job_location'])) {
36 | return false;
37 | }
38 |
39 | if (!isset($schemaManager->listTableColumns('tl_plenta_jobs_basic_job_location')['addresscountry'])) {
40 | return false;
41 | }
42 |
43 | $test = $this->connection->fetchOne('SELECT TRUE FROM tl_plenta_jobs_basic_job_location WHERE BINARY addressCountry!=BINARY UPPER(addressCountry) LIMIT 1');
44 |
45 | return false !== $test;
46 | }
47 |
48 | public function run(): MigrationResult
49 | {
50 | $this->connection->executeStatement('UPDATE tl_plenta_jobs_basic_job_location SET addressCountry=UPPER(addressCountry) WHERE BINARY addressCountry!=BINARY UPPER(addressCountry)');
51 |
52 | return $this->createResult(true);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/InsertTag/JobsCountInsertTag.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\InsertTag;
14 |
15 | use Contao\ArticleModel;
16 | use Contao\ContentModel;
17 | use Contao\CoreBundle\DependencyInjection\Attribute\AsInsertTag;
18 | use Contao\CoreBundle\InsertTag\InsertTagResult;
19 | use Contao\CoreBundle\InsertTag\ResolvedInsertTag;
20 | use Contao\CoreBundle\InsertTag\Resolver\InsertTagResolverNestedResolvedInterface;
21 | use Contao\CoreBundle\Routing\PageFinder;
22 | use Contao\ModuleModel;
23 | use Contao\StringUtil;
24 | use Plenta\ContaoJobsBasic\Contao\Model\PlentaJobsBasicJobLocationModel;
25 | use Plenta\ContaoJobsBasic\Contao\Model\PlentaJobsBasicOfferModel;
26 | use Plenta\ContaoJobsBasic\Controller\Contao\FrontendModule\JobOfferListController;
27 | use Plenta\ContaoJobsBasic\Helper\CountJobsHelper;
28 | use Symfony\Component\HttpFoundation\RequestStack;
29 |
30 | #[AsInsertTag('jobs')]
31 | class JobsCountInsertTag implements InsertTagResolverNestedResolvedInterface
32 | {
33 | public function __construct(protected CountJobsHelper $countJobsHelper)
34 | {
35 | }
36 |
37 | public function __invoke(ResolvedInsertTag $insertTag): InsertTagResult
38 | {
39 | if ('count' === $insertTag->getParameters()->get(0)) {
40 | $filtered = 'filtered' === $insertTag->getParameters()->get(1);
41 |
42 | return new InsertTagResult((string) $this->countJobsHelper->countJobs($filtered));
43 | }
44 |
45 | return new InsertTagResult('');
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/config/services.yml:
--------------------------------------------------------------------------------
1 | services:
2 | _defaults:
3 | autowire: true
4 | autoconfigure: true
5 | public: false
6 | bind:
7 | $csrfTokenName: '%contao.csrf_token_name%'
8 |
9 | Plenta\ContaoJobsBasic\:
10 | resource: ../src/*
11 | exclude: ../src/{Entity,Migrations,Resources,Tests}
12 |
13 | Plenta\ContaoJobsBasic\Helper\EmploymentType:
14 | class: Plenta\ContaoJobsBasic\Helper\EmploymentType
15 | arguments:
16 | - '@translator'
17 | - '@request_stack'
18 |
19 | Plenta\ContaoJobsBasic\Helper\DataTypeMapper:
20 | class: Plenta\ContaoJobsBasic\Helper\DataTypeMapper
21 | arguments:
22 | - '@contao.framework'
23 |
24 | Plenta\ContaoJobsBasic\GoogleForJobs\GoogleForJobs:
25 | public: true
26 | class: Plenta\ContaoJobsBasic\GoogleForJobs\GoogleForJobs
27 | arguments:
28 | - '@contao.image.picture_factory'
29 | - '@contao.assets.files_context'
30 | - '@Plenta\ContaoJobsBasic\Helper\EmploymentType'
31 | - '%kernel.project_dir%'
32 |
33 | # Migrations
34 | Plenta\ContaoJobsBasic\Migration\RenameDatabaseColumnsMigration:
35 | public: true
36 | tags:
37 | - { name: contao.migration, priority: 0 }
38 |
39 |
40 | Plenta\ContaoJobsBasic\Migration\MoveRemoteJobsMigration:
41 | public: true
42 | tags:
43 | - { name: contao.migration, priority: 0 }
44 |
45 | Plenta\ContaoJobsBasic\Migration\JobLocationCountryUppercaseMigration:
46 | public: true
47 | tags:
48 | - { name: contao.migration, priority: 0 }
49 |
50 | Plenta\ContaoJobsBasic\EventListener\Contao\Hooks\ChangelanguageNavigationListener:
51 | public: true
--------------------------------------------------------------------------------
/src/EventListener/Contao/DCA/TlPlentaJobsBasicSettingsEmploymentType.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\EventListener\Contao\DCA;
14 |
15 | use Contao\DataContainer;
16 | use Plenta\ContaoJobsBasic\Helper\DataTypeMapper;
17 | use Plenta\ContaoJobsBasic\Helper\EmploymentType;
18 |
19 | class TlPlentaJobsBasicSettingsEmploymentType
20 | {
21 | protected DataTypeMapper $dataTypeMapper;
22 | protected EmploymentType $employmentTypeHelper;
23 |
24 | public function __construct(
25 | DataTypeMapper $dataTypeMapper,
26 | EmploymentType $employmentTypeHelper
27 | ) {
28 | $this->dataTypeMapper = $dataTypeMapper;
29 | $this->employmentTypeHelper = $employmentTypeHelper;
30 | }
31 |
32 | public function translationSaveCallback($value, DataContainer $dc): string
33 | {
34 | return $this->dataTypeMapper->serializedToJson($value);
35 | }
36 |
37 | public function translationLoadCallback($value, DataContainer $dc): string
38 | {
39 | return $this->dataTypeMapper->jsonToSerialized($value);
40 | }
41 |
42 | public function googleForJobsMappingOptionsCallback(): array
43 | {
44 | $employmentTypes = $this->employmentTypeHelper->getGoogleForJobsEmploymentTypes();
45 |
46 | $return = [];
47 | foreach ($employmentTypes as $employmentType) {
48 | $return[$employmentType] = $this->employmentTypeHelper->getEmploymentTypeName($employmentType);
49 | }
50 |
51 | return $return;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/contao/languages/de/tl_plenta_jobs_basic_organization.xlf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Einstellungen
7 |
8 |
9 | Name
10 |
11 |
12 | Bitte geben Sie den Namen des Unternehmens ein.
13 |
14 |
15 | Unternehmens-Url
16 |
17 |
18 | Bitte geben Sie die Website-Adresse des Unternehmens ein.
19 |
20 |
21 | Firmenlogo
22 |
23 |
24 | Firmenlogo
25 |
26 |
27 | Bitte wählen Sie eine Datei aus der Dateiübersicht.
28 |
29 |
30 | Neues Unternehmen
31 |
32 |
33 | Ein neues Unternehmen anlegen
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/src/Form/Type/JobSortingType.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\Form\Type;
14 |
15 | use Symfony\Component\Form\AbstractType;
16 | use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
17 | use Symfony\Component\Form\FormBuilderInterface;
18 | use Symfony\Component\HttpFoundation\RequestStack;
19 | use Symfony\Component\OptionsResolver\OptionsResolver;
20 |
21 | class JobSortingType extends AbstractType
22 | {
23 | protected RequestStack $requestStack;
24 |
25 | public function __construct(RequestStack $requestStack)
26 | {
27 | $this->requestStack = $requestStack;
28 | }
29 |
30 | public function buildForm(FormBuilderInterface $builder, array $options): void
31 | {
32 | $request = $this->requestStack->getMainRequest();
33 | $sortBy = $request->get('sortBy', 'title');
34 | $order = $request->get('order', 'ASC');
35 |
36 | $builder->add('REQUEST_TOKEN', ContaoRequestTokenType::class);
37 | $builder->add('sort', ChoiceType::class, [
38 | 'choices' => $options['sortingOptions'],
39 | 'choice_label' => fn ($item) => 'tl_module.plentaJobsBasicSortingFields.fields.'.$item,
40 | 'translation_domain' => 'contao_tl_module',
41 | 'label' => false,
42 | 'data' => $sortBy.'__'.$order,
43 | ]);
44 | }
45 |
46 | public function getBlockPrefix()
47 | {
48 | return '';
49 | }
50 |
51 | public function configureOptions(OptionsResolver $resolver): void
52 | {
53 | $resolver->setDefaults([
54 | 'sortingOptions' => null,
55 | ]);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/EventListener/Contao/Hooks/ChangelanguageNavigationListener.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\EventListener\Contao\Hooks;
14 |
15 | use Contao\Config;
16 | use Contao\Input;
17 | use Plenta\ContaoJobsBasic\Contao\Model\PlentaJobsBasicOfferModel;
18 | use Symfony\Component\HttpFoundation\RequestStack;
19 | use Terminal42\ChangeLanguage\Event\ChangelanguageNavigationEvent;
20 |
21 | class ChangelanguageNavigationListener
22 | {
23 | protected RequestStack $requestStack;
24 |
25 | public function __construct(RequestStack $requestStack)
26 | {
27 | $this->requestStack = $requestStack;
28 | }
29 |
30 | public function onChangelanguageNavigation(ChangelanguageNavigationEvent $event): void
31 | {
32 | $targetRoot = $event->getNavigationItem()->getRootPage();
33 | $language = $targetRoot->language;
34 |
35 | $alias = Input::get('auto_item', false, true);
36 |
37 | if ($alias) {
38 | $jobOffer = PlentaJobsBasicOfferModel::findPublishedByIdOrAlias($alias);
39 |
40 | if (null !== $jobOffer) {
41 | if ($targetRoot->rootIsFallback) {
42 | $newAlias = $jobOffer->alias;
43 | } else {
44 | $translation = $jobOffer->getTranslation($language);
45 | if (!$translation) {
46 | $newAlias = $jobOffer->alias;
47 | } else {
48 | $newAlias = $translation['alias'];
49 | }
50 | }
51 |
52 | $event->getUrlParameterBag()->setUrlAttribute('auto_item', $newAlias);
53 | }
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/ContaoManager/Plugin.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\ContaoManager;
14 |
15 | use Contao\CoreBundle\ContaoCoreBundle;
16 | use Contao\ManagerPlugin\Bundle\BundlePluginInterface;
17 | use Contao\ManagerPlugin\Bundle\Config\BundleConfig;
18 | use Contao\ManagerPlugin\Bundle\Parser\ParserInterface;
19 | use Contao\ManagerPlugin\Config\ConfigPluginInterface;
20 | use Contao\ManagerPlugin\Routing\RoutingPluginInterface;
21 | use Plenta\ContaoJobsBasic\PlentaContaoJobsBasicBundle;
22 | use Symfony\Component\Config\Loader\LoaderInterface;
23 | use Symfony\Component\Config\Loader\LoaderResolverInterface;
24 | use Symfony\Component\HttpKernel\KernelInterface;
25 |
26 | /**
27 | * Class ContaoManagerPlugin.
28 | */
29 | class Plugin implements BundlePluginInterface, RoutingPluginInterface, ConfigPluginInterface
30 | {
31 | /**
32 | * {@inheritdoc}
33 | */
34 | public function getBundles(ParserInterface $parser): array
35 | {
36 | return [
37 | BundleConfig::create(PlentaContaoJobsBasicBundle::class)
38 | ->setLoadAfter([
39 | ContaoCoreBundle::class,
40 | ]),
41 | ];
42 | }
43 |
44 | /**
45 | * @throws \Exception
46 | */
47 | public function registerContainerConfiguration(LoaderInterface $loader, array $managerConfig): void
48 | {
49 | $loader->load(__DIR__.'/../../config/config.yml');
50 | }
51 |
52 | public function getRouteCollection(LoaderResolverInterface $resolver, KernelInterface $kernel)
53 | {
54 | return $resolver
55 | ->resolve(__DIR__.'/../../config/routing.yml')
56 | ->load(__DIR__.'/../../config/routing.yml')
57 | ;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/contao/templates/_new/jobs_basic/plenta_jobs_basic_offer_default.html.twig:
--------------------------------------------------------------------------------
1 | {% block content %}
2 |
3 | {% block image %}
4 | {% if 'image' in parts %}
5 |
6 | {{ jobOfferMeta.image|raw }}
7 |
8 | {% endif %}
9 | {% endblock %}
10 |
11 | {% block headline %}
12 | <{{ headlineUnit }} class="title">
13 |
{{ jobOfferMeta.title|raw }}
14 | {{ headlineUnit }}>
15 | {% endblock %}
16 | {% block company %}
17 | {% if 'company' in parts %}
18 |
19 | {{ jobOfferMeta.company }}
20 |
21 | {% endif %}
22 | {% endblock %}
23 | {% block location %}
24 | {% if 'jobLocation' in parts %}
25 |
26 | {{ jobOfferMeta.addressLocalityFormatted }}
27 |
28 | {% endif %}
29 | {% endblock %}
30 | {% block teaser %}
31 | {% if 'teaser' in parts %}
32 |
33 | {{ jobOfferMeta.teaser|raw }}
34 |
35 | {% endif %}
36 | {% endblock %}
37 |
38 | {% block publicationDate %}
39 | {% if 'publicationDate' in parts or 'employmentType' in parts %}
40 |
41 | {% if 'publicationDate' in parts %}
42 |
43 | {{ jobOfferMeta.publicationDateFormatted }}
44 |
45 | {% endif %}
46 | {% if 'employmentType' in parts %}
47 |
48 | {{ jobOfferMeta.employmentTypeFormatted }}
49 |
50 | {% endif %}
51 |
52 | {% endif %}
53 | {% endblock %}
54 |
55 | {% endblock %}
--------------------------------------------------------------------------------
/contao/templates/_new/jobs_basic_reader_parts/plenta_jobs_basic_reader_job_location.html.twig:
--------------------------------------------------------------------------------
1 | {% trans_default_domain 'contao_tl_plenta_jobs_basic_offer' %}
2 |
3 | {% block content %}
4 | {% if locations %}
5 | {{ 'tl_plenta_jobs_basic_offer.jobLocation.0'|trans }}:
6 | {% for key, location in locations %}
7 |
8 | {% if key in imgs|keys %}
9 | {{ attribute(imgs, key)|raw }}
10 | {% endif %}
11 | {% if showCompanyName %}
12 | {{ attribute(attribute(organizations, key), 'name') }}
13 | {% endif %}
14 |
15 | {% set remote = [] %}
16 | {% for loc in location %}
17 | {% if loc.jobTypeLocation == 'onPremise' %}
18 |
19 | {% if loc.title|default %}
20 | {{ loc.title }}
21 | {% endif %}
22 | {% if loc.streetAddress|trim %}
23 | {{ loc.streetAddress }}
24 | {% endif %}
25 | {{ loc.postalCode }} {{ loc.addressLocality }}
26 |
27 | {% else %}
28 | {% set remote = remote|merge([loc.requirementValue]) %}
29 | {% endif %}
30 | {% endfor %}
31 | {% if remote|length > 0 %}
32 |
33 | {% if plentaJobsBasicHideRemoteRequirements %}
34 | {{ 'tl_plenta_jobs_basic_offer.remoteIsPossible'|trans }}
35 | {% else %}
36 | {{ 'tl_plenta_jobs_basic_offer.remoteIsPossibleWithRequirements'|trans([remote|join(', ')]) }}
37 | {% endif %}
38 |
39 | {% endif %}
40 | {% endfor %}
41 | {% endif %}
42 | {% endblock %}
43 |
--------------------------------------------------------------------------------
/src/EventListener/Contao/SitemapListener.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\EventListener\Contao;
14 |
15 | use Contao\ArticleModel;
16 | use Contao\ContentModel;
17 | use Contao\CoreBundle\Event\SitemapEvent;
18 | use Contao\CoreBundle\Framework\ContaoFramework;
19 | use Contao\Database;
20 | use Contao\ModuleModel;
21 | use Contao\PageModel;
22 | use Plenta\ContaoJobsBasic\Contao\Model\PlentaJobsBasicOfferModel;
23 | use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
24 |
25 | #[AsEventListener(event: SitemapEvent::class)]
26 | class SitemapListener
27 | {
28 | public function __construct(private ContaoFramework $framework)
29 | {
30 | }
31 |
32 | public function __invoke(SitemapEvent $event): void
33 | {
34 | $arrRoot = $event->getRootPageIds();
35 |
36 | if (empty($arrRoot)) {
37 | return;
38 | }
39 |
40 | $arrPages = [];
41 | $jobs = PlentaJobsBasicOfferModel::findAllPublished();
42 |
43 | foreach ($arrRoot as $pageId) {
44 | $objPage = $this->framework->getAdapter(PageModel::class)->findPublishedById($pageId)?->loadDetails();
45 |
46 | if (null === $objPage) {
47 | continue;
48 | }
49 |
50 | if ($jobs) {
51 | foreach ($jobs as $job) {
52 | if ('noindex,nofollow' === $job->robots) {
53 | continue;
54 | }
55 |
56 | if ($page = $job->getAbsoluteUrl($objPage->language)) {
57 | if (!\in_array($page, $arrPages, true)) {
58 | $arrPages[] = $page;
59 | }
60 | }
61 | }
62 | }
63 | }
64 |
65 | foreach ($arrPages as $strUrl) {
66 | $event->addUrlToDefaultUrlSet($strUrl);
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/EventListener/Contao/BackendMenuListener.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\EventListener\Contao;
14 |
15 | use Contao\CoreBundle\Event\MenuEvent;
16 | use Plenta\ContaoJobsBasic\Controller\Contao\BackendModule\SettingsController;
17 | use Plenta\ContaoJobsBasic\Helper\PermissionsHelper;
18 | use Symfony\Component\HttpFoundation\RequestStack;
19 | use Symfony\Component\Routing\RouterInterface;
20 |
21 | class BackendMenuListener
22 | {
23 | protected $router;
24 | protected $requestStack;
25 |
26 | public function __construct(RouterInterface $router, RequestStack $requestStack)
27 | {
28 | $this->router = $router;
29 | $this->requestStack = $requestStack;
30 | }
31 |
32 | public function __invoke(MenuEvent $event): void
33 | {
34 | $factory = $event->getFactory();
35 | $tree = $event->getTree();
36 |
37 | if ('mainMenu' !== $tree->getName()) {
38 | return;
39 | }
40 |
41 | $contentNode = $tree->getChild('plenta_jobs_basic');
42 |
43 | if (!$contentNode) {
44 | return;
45 | }
46 |
47 | if (PermissionsHelper::canAccessModule('settings')) {
48 | $node = $factory
49 | ->createItem('plenta-jobs-basic-settings')
50 | ->setUri($this->router->generate(SettingsController::class))
51 | ->setLabel($GLOBALS['TL_LANG']['MOD']['plenta_jobs_basic_settings'][0])
52 | ->setLinkAttribute('title', $GLOBALS['TL_LANG']['MOD']['plenta_jobs_basic_settings'][1])
53 | ->setLinkAttribute('class', 'plenta-jobs-basic-settings')
54 | ->setCurrent($this->requestStack->getCurrentRequest()->get('_controller') === SettingsController::class.'::showSettings' || SettingsController::isActive($this->requestStack))
55 | ;
56 |
57 | $contentNode->addChild($node);
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/contao/languages/de/tl_content.xlf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Eigenschaften
7 |
8 |
9 | Bitte wählen sie die auszugebenden Eigenschaften.
10 |
11 |
12 | Überschriftentyp (Anzeigen-Titel)
13 |
14 |
15 | Bitte wählen Sie einen Überschriftentyp aus.
16 |
17 |
18 | Stellenanzeige
19 |
20 |
21 | Bitte wählen Sie eine Stellenanzeige aus.
22 |
23 |
24 | Eigenschaften
25 |
26 |
27 | Bitte wählen Sie die Eigenschaften aus, die angezeigt werden sollen.
28 |
29 |
30 | Hinweis für nicht verfügbare Stellenanzeige
31 |
32 |
33 | Hier haben Sie die Möglichkeit, einen Hinweistext einzufügen, der angezeigt wird, wenn die ausgewählte Stellenanzeige nicht mehr verfügbar ist.
34 |
35 |
36 | Stellenanzeige
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/Migration/RemoveFkConstraintsMigration.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\Migration;
14 |
15 | use Contao\CoreBundle\Migration\MigrationResult;
16 | use Doctrine\DBAL\Connection;
17 |
18 | class RemoveFkConstraintsMigration extends \Contao\CoreBundle\Migration\AbstractMigration
19 | {
20 | protected $tables = ['tl_plenta_jobs_basic_offer_translation', 'tl_plenta_jobs_basic_job_location'];
21 |
22 | protected Connection $database;
23 |
24 | public function __construct(Connection $connection)
25 | {
26 | $this->database = $connection;
27 | }
28 |
29 | public function getName(): string
30 | {
31 | return 'Plenta Jobs Basic Bundle 2.0 Update - Foreign Key Constraints';
32 | }
33 |
34 | public function shouldRun(): bool
35 | {
36 | $schemaManager = $this->database->getSchemaManager();
37 |
38 | foreach ($this->tables as $table) {
39 | if ($schemaManager->tablesExist([$table])) {
40 | $foreignKeys = $schemaManager->listTableForeignKeys($table);
41 | if (!empty($foreignKeys)) {
42 | return true;
43 | }
44 | }
45 | }
46 |
47 | return false;
48 | }
49 |
50 | public function run(): MigrationResult
51 | {
52 | $schemaManager = $this->database->getSchemaManager();
53 |
54 | foreach ($this->tables as $table) {
55 | if ($schemaManager->tablesExist([$table])) {
56 | $foreignKeys = $schemaManager->listTableForeignKeys($table);
57 | if (!empty($foreignKeys)) {
58 | foreach ($foreignKeys as $foreignKey) {
59 | $schemaManager->dropForeignKey($foreignKey, $table);
60 | }
61 | }
62 | }
63 | }
64 |
65 | return new MigrationResult(true, 'Foreign key constraints have successfully been dropped.');
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/contao/config/config.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | use Composer\InstalledVersions;
14 | use Contao\ArrayUtil;
15 | use Plenta\ContaoJobsBasic\Contao\Model\PlentaJobsBasicJobLocationModel;
16 | use Plenta\ContaoJobsBasic\Contao\Model\PlentaJobsBasicOfferModel;
17 | use Plenta\ContaoJobsBasic\Contao\Model\PlentaJobsBasicOrganizationModel;
18 | use Plenta\ContaoJobsBasic\Contao\Model\PlentaJobsBasicSettingsEmploymentTypeModel;
19 | use Plenta\ContaoJobsBasic\EventListener\Contao\Hooks\ChangelanguageNavigationListener;
20 |
21 | ArrayUtil::arrayInsert($GLOBALS['BE_MOD'], 1, [
22 | 'plenta_jobs_basic' => [
23 | 'plenta_jobs_basic_offers' => [
24 | 'tables' => ['tl_plenta_jobs_basic_offer', 'tl_content'],
25 | ],
26 | 'plenta_jobs_basic_organizations' => [
27 | 'tables' => ['tl_plenta_jobs_basic_organization', 'tl_plenta_jobs_basic_job_location'],
28 | 'hideInNavigation' => true,
29 | ],
30 | 'plenta_jobs_basic_settings_employment_type' => [
31 | 'tables' => ['tl_plenta_jobs_basic_settings_employment_type'],
32 | 'hideInNavigation' => true,
33 | ],
34 | ],
35 | ]);
36 |
37 | if (defined('TL_MODE') && TL_MODE == 'BE') {
38 | $GLOBALS['TL_CSS'][] = 'bundles/plentacontaojobsbasic/backend.css|static';
39 | }
40 |
41 | $GLOBALS['TL_MODELS'][PlentaJobsBasicOfferModel::getTable()] = PlentaJobsBasicOfferModel::class;
42 | $GLOBALS['TL_MODELS'][PlentaJobsBasicJobLocationModel::getTable()] = PlentaJobsBasicJobLocationModel::class;
43 | $GLOBALS['TL_MODELS'][PlentaJobsBasicOrganizationModel::getTable()] = PlentaJobsBasicOrganizationModel::class;
44 | $GLOBALS['TL_MODELS'][PlentaJobsBasicSettingsEmploymentTypeModel::getTable()] = PlentaJobsBasicSettingsEmploymentTypeModel::class;
45 |
46 | if (InstalledVersions::isInstalled('terminal42/contao-changelanguage')) {
47 | $GLOBALS['TL_HOOKS']['changelanguageNavigation'][] = [ChangelanguageNavigationListener::class, 'onChangelanguageNavigation'];
48 | }
49 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "plenta/contao-jobs-basic-bundle",
3 | "type": "contao-bundle",
4 | "description": "Basic Job Manager for Contao (including Google Jobs).",
5 | "license": "LGPL-3.0-or-later",
6 | "authors": [
7 | {
8 | "name": "Plenta.io",
9 | "homepage": "https://plenta.io"
10 | }
11 | ],
12 | "funding": [{
13 | "type": "github",
14 | "url": "https://github.com/sponsors/plenta"
15 | }],
16 | "support":{
17 | "issues": "https://github.com/plenta/contao-jobs-basic-bundle/issues",
18 | "source": "https://github.com/plenta/contao-jobs-basic-bundle",
19 | "docs": "https://plenta.io/contao-erweiterungen/jobs-basic"
20 | },
21 | "require": {
22 | "php": "^8.1",
23 | "ext-json": "*",
24 | "contao/conflicts": "@dev",
25 | "contao/core-bundle": "^5.3",
26 | "doctrine/dbal": "^2.10.1 || ^3.2.0",
27 | "mvo/contao-group-widget": "^1.5",
28 | "symfony/intl": "^6.0 || ^7.0 ",
29 | "symfony/form": "^6.0 || ^7.0"
30 | },
31 | "require-dev": {
32 | "contao/manager-bundle": "^5.0",
33 | "phpunit/phpunit": "^9.5.10",
34 | "contao/test-case": "^5.0",
35 | "symfony/phpunit-bridge": "^v6.4.9",
36 | "friendsofphp/php-cs-fixer": "^3.2",
37 | "contao/maker-bundle": "*"
38 | },
39 | "suggest": {
40 | "plenta/contao-jobs-basic-contact-bundle": "Contact persons for Contao Jobs Basic",
41 | "plenta/contao-jobs-basic-categories-bundle": "Categories for Contao Jobs Basic",
42 | "plenta/contao-jobs-basic-geo-search-bundle": "GEO search for Contao Jobs Basic",
43 | "plenta/contao-jobs-basic-zvoove-bundle": "zvoove Connector for Contao Jobs Basic",
44 | "plenta/contao-jobs-basic-hansalog-hr-bundle": "HANSALOG HR Connector für Contao Jobs Basic"
45 | },
46 | "extra": {
47 | "contao-manager-plugin": "Plenta\\ContaoJobsBasic\\ContaoManager\\Plugin"
48 | },
49 | "autoload": {
50 | "psr-4": {
51 | "Plenta\\ContaoJobsBasic\\": "src"
52 | }
53 | },
54 | "config": {
55 | "allow-plugins": {
56 | "composer/package-versions-deprecated": true,
57 | "contao-components/installer": true,
58 | "contao/manager-plugin": true,
59 | "contao-community-alliance/composer-plugin": true,
60 | "php-http/discovery": true
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/Events/JobOfferReaderContentPartEvent.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\Events;
14 |
15 | use Contao\ModuleModel;
16 | use Plenta\ContaoJobsBasic\Contao\Model\PlentaJobsBasicOfferModel;
17 | use Symfony\Component\HttpFoundation\Request;
18 | use Symfony\Contracts\EventDispatcher\Event;
19 |
20 | class JobOfferReaderContentPartEvent extends Event
21 | {
22 | public const NAME = 'plenta_jobs_basic.job_offer_reader.content_part';
23 |
24 | private PlentaJobsBasicOfferModel $jobOffer;
25 |
26 | private ModuleModel $model;
27 |
28 | private Request $request;
29 |
30 | private string $contentResponse = '';
31 |
32 | private string $part = '';
33 |
34 | public function getJobOffer(): PlentaJobsBasicOfferModel
35 | {
36 | return $this->jobOffer;
37 | }
38 |
39 | public function setJobOffer(PlentaJobsBasicOfferModel $jobOffer): self
40 | {
41 | $this->jobOffer = $jobOffer;
42 |
43 | return $this;
44 | }
45 |
46 | public function getModel(): ModuleModel
47 | {
48 | return $this->model;
49 | }
50 |
51 | public function setModel(ModuleModel $model): self
52 | {
53 | $this->model = $model;
54 |
55 | return $this;
56 | }
57 |
58 | public function getRequest(): Request
59 | {
60 | return $this->request;
61 | }
62 |
63 | public function setRequest(Request $request): self
64 | {
65 | $this->request = $request;
66 |
67 | return $this;
68 | }
69 |
70 | public function getContentResponse(): string
71 | {
72 | return $this->contentResponse;
73 | }
74 |
75 | public function setContentResponse(string $contentResponse): self
76 | {
77 | $this->contentResponse = $contentResponse;
78 |
79 | return $this;
80 | }
81 |
82 | public function getPart(): string
83 | {
84 | return $this->part;
85 | }
86 |
87 | public function setPart(string $part): self
88 | {
89 | $this->part = $part;
90 |
91 | return $this;
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/contao/templates/_new/frontend_module/plenta_jobs_basic_offer_list.html.twig:
--------------------------------------------------------------------------------
1 | {% extends '@Contao/frontend_module/_base.html.twig' %}
2 |
3 | {% block content %}
4 | {% block sorting %}
5 | {% if showSorting|default(false) %}
6 | {{ sortingForm|raw }}
7 |
18 | {% endif %}
19 | {% endblock %}
20 |
21 | {% for item in items %}
22 | {{ item|raw }}
23 | {% else %}
24 | {{ empty }}
25 | {% endfor %}
26 | {% if pagination is defined %}
27 | {{ pagination|raw }}
28 | {% endif %}
29 | {% endblock %}
30 | {#cssID ?>style): ?> style="= $this->style ?>"= $this->attributes ? ' '.$this->attributes : ''; ?> >
31 |
32 | block('headline'); ?>
33 | headline): ?>
34 | <= $this->hl ?>>= $this->headline ?>= $this->hl ?>>
35 |
36 | endblock(); ?>
37 |
38 | block('sorting'); ?>
39 | showSorting): ?>
40 | = $this->sortingForm; ?>
41 |
42 |
43 |
44 | endblock(); ?>
45 |
46 | block('content'); ?>
47 | items)): ?>
48 |
= $this->empty ?>
49 |
50 | = implode('', $this->items) ?>
51 | pagination): ?>
52 | = $this->pagination ?>
53 |
54 |
55 | endblock(); ?>
56 |
57 | hallo#}
58 |
--------------------------------------------------------------------------------
/contao/languages/en/default.xlf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Full time
7 |
8 |
9 | Part time
10 |
11 |
12 | Contractor
13 |
14 |
15 | Temporary
16 |
17 |
18 | Intern
19 |
20 |
21 | Volunteer
22 |
23 |
24 | Per diem
25 |
26 |
27 | Other
28 |
29 |
30 | Jobs (Basic)
31 |
32 |
33 | Job offer details
34 |
35 |
36 | Job offer details
37 |
38 |
39 | There are no jobs available at the moment.
40 |
41 |
42 | Documentation
43 |
44 |
45 | Report a error
46 |
47 |
50 |
51 | Add-ons
52 |
53 |
54 | Remote
55 |
56 |
57 | Remote in
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/src/EventListener/Contao/DCA/TlModule.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\EventListener\Contao\DCA;
14 |
15 | use Contao\CoreBundle\DependencyInjection\Attribute\AsCallback;
16 | use Contao\CoreBundle\Twig\Finder\FinderFactory;
17 | use Contao\DataContainer;
18 | use Contao\StringUtil;
19 | use Plenta\ContaoJobsBasic\Contao\Model\PlentaJobsBasicJobLocationModel;
20 |
21 | class TlModule
22 | {
23 | public function __construct(protected FinderFactory $finderFactory)
24 | {
25 | }
26 |
27 | public function jobLocationOptionsCallback(DataContainer $dc): array
28 | {
29 | if ($dc->activeRecord->plentaJobsBasicCompanies) {
30 | $companies = StringUtil::deserialize($dc->activeRecord->plentaJobsBasicCompanies);
31 |
32 | if (!empty($companies)) {
33 | $jobLocations = PlentaJobsBasicJobLocationModel::findByMultiplePids($companies);
34 | }
35 | }
36 |
37 | if (empty($jobLocations)) {
38 | $jobLocations = PlentaJobsBasicJobLocationModel::findAll();
39 | }
40 |
41 | $return = [];
42 | foreach ($jobLocations as $jobLocation) {
43 | $return[$jobLocation->id] = $jobLocation->getRelated('pid')->name.': ';
44 | if ('onPremise' === $jobLocation->jobTypeLocation) {
45 | $return[$jobLocation->id] .= $jobLocation->streetAddress;
46 |
47 | if ('' !== $jobLocation->addressLocality) {
48 | $return[$jobLocation->id] .= ', '.$jobLocation->addressLocality;
49 | }
50 | } else {
51 | $return[$jobLocation->id] .= $GLOBALS['TL_LANG']['MSC']['PLENTA_JOBS']['remote'].' ['.$jobLocation->requirementValue.']';
52 | }
53 | }
54 |
55 | return $return;
56 | }
57 |
58 | #[AsCallback(table: 'tl_module', target: 'fields.plentaJobsBasicElementTpl.options')]
59 | public function onElementTplOptionsCallback()
60 | {
61 | return $this->finderFactory
62 | ->create()
63 | ->identifier('jobs_basic/plenta_jobs_basic_offer_default')
64 | ->extension('html.twig')
65 | ->withVariants()
66 | ->asTemplateOptions();
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/Controller/Contao/BackendModule/SettingsController.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\Controller\Contao\BackendModule;
14 |
15 | use Composer\InstalledVersions;
16 | use Contao\BackendUser;
17 | use Contao\CoreBundle\Controller\AbstractBackendController;
18 | use Contao\CoreBundle\Exception\AccessDeniedException;
19 | use Contao\System;
20 | use Plenta\ContaoJobsBasic\Helper\PermissionsHelper;
21 | use Symfony\Component\HttpFoundation\RequestStack;
22 | use Symfony\Component\HttpFoundation\Response;
23 | use Twig\Environment as TwigEnvironment;
24 |
25 | class SettingsController extends AbstractBackendController
26 | {
27 | public function showSettings(): Response
28 | {
29 | if (!PermissionsHelper::canAccessModule('settings')) {
30 | throw new AccessDeniedException('The settings module of the Plenta Jobs Basic Bundle is not allowed for user "'.BackendUser::getInstance()->username.'".');
31 | }
32 |
33 | System::loadLanguageFile('modules');
34 |
35 | $GLOBALS['TL_CSS'][] = 'bundles/plentacontaojobsbasic/dashboard.css';
36 |
37 | $mods = [];
38 |
39 | foreach ($GLOBALS['BE_MOD']['plenta_jobs_basic'] as $key => $mod) {
40 | if (isset($mod['hideInNavigation']) && $mod['hideInNavigation']) {
41 | if (!PermissionsHelper::canAccessModule($key, 'modules')) {
42 | continue;
43 | }
44 | $mod['title'] = $GLOBALS['TL_LANG']['MOD'][$key];
45 | $mods[$key] = $mod;
46 | }
47 | }
48 |
49 | return $this->render(
50 | '@PlentaContaoJobsBasic/be_plenta_jobs_basic_settings.html.twig',
51 | [
52 | 'title' => $GLOBALS['TL_LANG']['MOD']['plenta_jobs_basic_settings'][0],
53 | 'mods' => $mods,
54 | 'version' => InstalledVersions::getVersion('plenta/contao-jobs-basic-bundle'),
55 | ]
56 | );
57 | }
58 |
59 | public static function isActive(RequestStack $requestStack)
60 | {
61 | $do = $requestStack->getCurrentRequest()->get('do');
62 | if (isset($GLOBALS['BE_MOD']['plenta_jobs_basic'][$do], $GLOBALS['BE_MOD']['plenta_jobs_basic'][$do]['hideInNavigation']) && $GLOBALS['BE_MOD']['plenta_jobs_basic'][$do]['hideInNavigation']) {
63 | return true;
64 | }
65 |
66 | return false;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/Migration/BoolCharToIntMigration.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\Migration;
14 |
15 | use Contao\CoreBundle\Migration\AbstractMigration;
16 | use Contao\CoreBundle\Migration\MigrationResult;
17 | use Doctrine\DBAL\Connection;
18 | use Doctrine\DBAL\Types\BooleanType;
19 |
20 | class BoolCharToIntMigration extends AbstractMigration
21 | {
22 | private Connection $database;
23 |
24 | private array $columns = ['addImage', 'addSalary'];
25 |
26 | public function __construct(Connection $database)
27 | {
28 | $this->database = $database;
29 | }
30 |
31 | public function getName(): string
32 | {
33 | return 'Plenta Jobs Basic Bundle 1.4.5 Update';
34 | }
35 |
36 | public function shouldRun(): bool
37 | {
38 | $schemaManager = $this->database->getSchemaManager();
39 |
40 | if (!$schemaManager->tablesExist(['tl_plenta_jobs_basic_offer'])) {
41 | return false;
42 | }
43 |
44 | $columns = $schemaManager->listTableColumns('tl_plenta_jobs_basic_offer');
45 |
46 | $shouldRun = false;
47 |
48 | foreach ($columns as $currentColumn) {
49 | if (is_a($currentColumn->getType(), BooleanType::class)) {
50 | continue;
51 | }
52 | $currentColumnName = $currentColumn->getName();
53 |
54 | if (true === \in_array($currentColumnName, $this->columns, true)) {
55 | if (true === (bool) $this->database->executeQuery('SELECT EXISTS (SELECT id FROM tl_plenta_jobs_basic_offer WHERE '.$currentColumnName." = '')")->fetchOne()) {
56 | $shouldRun = true;
57 | break;
58 | }
59 | }
60 | }
61 |
62 | return $shouldRun;
63 | }
64 |
65 | public function run(): MigrationResult
66 | {
67 | $schemaManager = $this->database->getSchemaManager();
68 | $columns = $schemaManager->listTableColumns('tl_plenta_jobs_basic_offer');
69 |
70 | foreach ($columns as $currentColumn) {
71 | $currentColumnName = $currentColumn->getName();
72 |
73 | if (true === \in_array($currentColumnName, $this->columns, true)) {
74 | $this->database
75 | ->executeQuery(
76 | 'UPDATE tl_plenta_jobs_basic_offer SET '.$currentColumnName.' = 0 WHERE '.$currentColumnName." = ''"
77 | )
78 | ;
79 | }
80 | }
81 |
82 | return $this->createResult(
83 | true,
84 | 'All empty boolean values have been changed to 0.'
85 | );
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/Events/Model/FindAllPublishedByTypesAndLocationEvent.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\Events\Model;
14 |
15 | use Contao\ModuleModel;
16 | use Symfony\Contracts\EventDispatcher\Event;
17 |
18 | class FindAllPublishedByTypesAndLocationEvent extends Event
19 | {
20 | public const NAME = 'plenta_jobs_basic.model.find_all_published_by_types_and_location';
21 |
22 | private array $columns;
23 |
24 | private array $values;
25 |
26 | private array $options;
27 |
28 | private bool $applyRequestFilters = false;
29 |
30 | private ?ModuleModel $model = null;
31 |
32 | public function __construct()
33 | {
34 | }
35 |
36 | /**
37 | * @return array
38 | */
39 | public function getColumns(): array
40 | {
41 | return $this->columns;
42 | }
43 |
44 | /**
45 | * @param array $columns
46 | *
47 | * @return FindAllPublishedByTypesAndLocationEvent
48 | */
49 | public function setColumns(array $columns): self
50 | {
51 | $this->columns = $columns;
52 |
53 | return $this;
54 | }
55 |
56 | /**
57 | * @return array
58 | */
59 | public function getValues(): array
60 | {
61 | return $this->values;
62 | }
63 |
64 | /**
65 | * @param array $values
66 | *
67 | * @return FindAllPublishedByTypesAndLocationEvent
68 | */
69 | public function setValues(array $values): self
70 | {
71 | $this->values = $values;
72 |
73 | return $this;
74 | }
75 |
76 | /**
77 | * @return array
78 | */
79 | public function getOptions(): array
80 | {
81 | return $this->options;
82 | }
83 |
84 | /**
85 | * @param array $options
86 | *
87 | * @return FindAllPublishedByTypesAndLocationEvent
88 | */
89 | public function setOptions(array $options): self
90 | {
91 | $this->options = $options;
92 |
93 | return $this;
94 | }
95 |
96 | public function isApplyRequestFilters(): bool
97 | {
98 | return $this->applyRequestFilters;
99 | }
100 |
101 | public function setApplyRequestFilters(bool $applyRequestFilters): FindAllPublishedByTypesAndLocationEvent
102 | {
103 | $this->applyRequestFilters = $applyRequestFilters;
104 | return $this;
105 | }
106 |
107 | public function getModel(): ?ModuleModel
108 | {
109 | return $this->model;
110 | }
111 |
112 | public function setModel(?ModuleModel $model): FindAllPublishedByTypesAndLocationEvent
113 | {
114 | $this->model = $model;
115 | return $this;
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/contao/languages/en/modules.xlf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Jobs (Basic)
7 |
8 |
9 | Jobs
10 |
11 |
12 | Manage job offers
13 |
14 |
15 | Companies
16 |
17 |
18 | Manage companies
19 |
20 |
21 | Locations
22 |
23 |
24 | Locations
25 |
26 |
27 | Manage locations
28 |
29 |
30 | Settings
31 |
32 |
33 | Manage settings
34 |
35 |
36 | Dashboard
37 |
38 |
39 | Support
40 |
41 |
42 | Here you will find support and options for assistance.
43 |
44 |
45 | Jobs (Basic)
46 |
47 |
48 | Job offer list
49 |
50 |
51 | Job offer list
52 |
53 |
54 | Job offer reader
55 |
56 |
57 | Job offer reader
58 |
59 |
60 | Job offer filter
61 |
62 |
63 | Job offer filter
64 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/contao/dca/tl_content.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | use Plenta\ContaoJobsBasic\EventListener\Contao\DCA\JobOfferFields;
14 |
15 | $GLOBALS['TL_DCA']['tl_content']['palettes']['plenta_jobs_basic_job_offer_details'] = '{type_legend},type,plenta_jobs_basic_job_offer_details;{image_legend},size;{expert_legend},guests,cssID;{invisible_legend:hide},invisible,start,stop';
16 | $GLOBALS['TL_DCA']['tl_content']['palettes']['plenta_jobs_basic_job_offer_teaser'] = '{type_legend},type,headline;{plenta_jobs_basic_legend},text,plentaJobsBasicHeadlineTag,plentaJobsBasicJobOffer,plentaJobsBasicJobOfferTeaserParts,plentaJobsBasicNotice;{image_legend},size;{expert_legend},guests,cssID;{invisible_legend:hide},invisible,start,stop';
17 |
18 | $GLOBALS['TL_DCA']['tl_content']['fields']['plenta_jobs_basic_job_offer_details'] = [
19 | 'exclude' => true,
20 | 'inputType' => 'checkboxWizard',
21 | 'options' => [
22 | 'employmentTypeFormatted' => 'Stellenarten',
23 | 'description' => 'Beschreibung',
24 | 'publicationDateFormatted' => 'Veröffentlichungsdatum',
25 | 'title' => 'Titel',
26 | 'addressLocalityFormatted' => 'Arbeitsort',
27 | 'image' => 'Bild',
28 | ],
29 | 'eval' => ['multiple' => true, 'tl_class' => 'clr'],
30 | 'sql' => 'blob NULL',
31 | ];
32 |
33 | $GLOBALS['TL_DCA']['tl_content']['fields']['plentaJobsBasicJobOffer'] = [
34 | 'exclude' => true,
35 | 'inputType' => 'select',
36 | 'foreignKey' => 'tl_plenta_jobs_basic_offer.title',
37 | 'eval' => [
38 | 'chosen' => true,
39 | 'tl_class' => 'clr',
40 | ],
41 | 'sql' => 'int(10) unsigned NOT NULL default 0',
42 | ];
43 |
44 | $GLOBALS['TL_DCA']['tl_content']['fields']['plentaJobsBasicJobOfferTeaserParts'] = [
45 | 'exclude' => true,
46 | 'inputType' => 'checkbox',
47 | 'options' => JobOfferFields::getParts(),
48 | 'eval' => [
49 | 'multiple' => true,
50 | ],
51 | 'reference' => &$GLOBALS['TL_LANG']['MSC']['PLENTA_JOBS']['offerParts'],
52 | 'sql' => "varchar(255) COLLATE ascii_bin NOT NULL default '".serialize(['jobLocation', 'publicationDate', 'employmentType'])."'",
53 | ];
54 |
55 | $GLOBALS['TL_DCA']['tl_content']['fields']['plentaJobsBasicHeadlineTag'] = [
56 | 'exclude' => true,
57 | 'search' => true,
58 | 'inputType' => 'select',
59 | 'options' => ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'div'],
60 | 'eval' => ['maxlength' => 8, 'tl_class' => 'w50 clr'],
61 | 'sql' => "varchar(3) NOT NULL default 'h2' COLLATE ascii_bin",
62 | ];
63 |
64 | $GLOBALS['TL_DCA']['tl_content']['fields']['plentaJobsBasicNotice'] = [
65 | 'inputType' => 'text',
66 | 'exclude' => true,
67 | 'eval' => [
68 | 'maxlength' => 255,
69 | 'tl_class' => 'long',
70 | ],
71 | 'sql' => [
72 | 'type' => 'string',
73 | 'length' => 255,
74 | 'default' => '',
75 | ],
76 | ];
77 |
--------------------------------------------------------------------------------
/contao/languages/de/modules.xlf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Stellenanzeigen
7 |
8 |
9 | Stellenanzeigen
10 |
11 |
12 | Stellenanzeigen verwalten
13 |
14 |
15 | Unternehmen verwalten
16 |
17 |
18 | Unternehmen verwalten
19 |
20 |
21 | Standort
22 |
23 |
24 | Standorte
25 |
26 |
27 | Arbeitsorte verwalten
28 |
29 |
30 | Einstellungen
31 |
32 |
33 | Einstellungen verwalten
34 |
35 |
36 | Zusätzliche Stellenarten verwalten
37 |
38 |
39 | Hier können Sie weitere Stellenarten konfigurieren.
40 |
41 |
42 | Jobs (Basic)
43 |
44 |
45 | Stellenanzeigen Liste
46 |
47 |
48 | Stellenanzeigen Liste
49 |
50 |
51 | Stellenanzeigen Detailansicht
52 |
53 |
54 | Stellenanzeigen Detailansicht
55 |
56 |
57 | Stellenanzeigen Filter
58 |
59 |
60 | Stellenangebot Filter
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/src/Events/JobOfferListBeforeParseTemplateEvent.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\Events;
14 |
15 | use Contao\ModuleModel;
16 | use Contao\Template;
17 | use Plenta\ContaoJobsBasic\Controller\Contao\FrontendModule\JobOfferListController;
18 | use Symfony\Contracts\EventDispatcher\Event;
19 |
20 | class JobOfferListBeforeParseTemplateEvent extends Event
21 | {
22 | public const NAME = 'plenta_jobs_basic.job_offer_list.before_parse_template';
23 |
24 | private Template $template;
25 |
26 | private $jobOffers;
27 |
28 | private JobOfferListController $objModule;
29 |
30 | private ModuleModel $model;
31 |
32 | public function __construct($jobOffers, Template $template, ModuleModel $model, JobOfferListController $objModule)
33 | {
34 | $this->jobOffers = $jobOffers;
35 | $this->template = $template;
36 | $this->model = $model;
37 | $this->objModule = $objModule;
38 | }
39 |
40 | /**
41 | * @return Template
42 | */
43 | public function getTemplate(): Template
44 | {
45 | return $this->template;
46 | }
47 |
48 | /**
49 | * @param Template $template
50 | *
51 | * @return JobOfferListBeforeParseTemplateEvent
52 | */
53 | public function setTemplate(Template $template): self
54 | {
55 | $this->template = $template;
56 |
57 | return $this;
58 | }
59 |
60 | /**
61 | * @return mixed
62 | */
63 | public function getJobOffers()
64 | {
65 | return $this->jobOffers;
66 | }
67 |
68 | /**
69 | * @param mixed $jobOffers
70 | *
71 | * @return JobOfferListBeforeParseTemplateEvent
72 | */
73 | public function setJobOffers($jobOffers)
74 | {
75 | $this->jobOffers = $jobOffers;
76 |
77 | return $this;
78 | }
79 |
80 | /**
81 | * @return JobOfferListController
82 | */
83 | public function getObjModule(): JobOfferListController
84 | {
85 | return $this->objModule;
86 | }
87 |
88 | /**
89 | * @param JobOfferListController $objModule
90 | *
91 | * @return JobOfferListBeforeParseTemplateEvent
92 | */
93 | public function setObjModule(JobOfferListController $objModule): self
94 | {
95 | $this->objModule = $objModule;
96 |
97 | return $this;
98 | }
99 |
100 | /**
101 | * @return ModuleModel
102 | */
103 | public function getModel(): ModuleModel
104 | {
105 | return $this->model;
106 | }
107 |
108 | /**
109 | * @param ModuleModel $model
110 | *
111 | * @return JobOfferListBeforeParseTemplateEvent
112 | */
113 | public function setModel(ModuleModel $model): self
114 | {
115 | $this->model = $model;
116 |
117 | return $this;
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/src/Migration/RenameDatabaseColumnsMigration.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\Migration;
14 |
15 | use Contao\CoreBundle\Migration\AbstractMigration;
16 | use Contao\CoreBundle\Migration\MigrationResult;
17 | use Doctrine\DBAL\Connection;
18 |
19 | class RenameDatabaseColumnsMigration extends AbstractMigration
20 | {
21 | private Connection $database;
22 | private array $oldColumnNames = [
23 | 'plentaJobsMethod' => "varchar(12) NOT NULL default 'POST'",
24 | 'plentaJobsShowButton' => "char(1) NOT NULL default ''",
25 | 'plentaJobsSubmit' => "varchar(255) NOT NULL default ''",
26 | 'plentaJobsShowTypes' => "char(1) NOT NULL default ''",
27 | 'plentaJobsTypesHeadline' => "varchar(255) NOT NULL default ''",
28 | 'plentaJobsShowAllTypes' => "char(1) NOT NULL default ''",
29 | 'plentaJobsShowQuantity' => "char(1) NOT NULL default ''",
30 | 'plentaJobsShowLocations' => "char(1) NOT NULL default ''",
31 | 'plentaJobsLocationsHeadline' => "varchar(255) NOT NULL default ''",
32 | 'plentaJobsShowAllLocations' => "char(1) NOT NULL default ''",
33 | 'plentaJobsShowLocationQuantity' => "char(1) NOT NULL default ''",
34 | ];
35 |
36 | public function __construct(Connection $database)
37 | {
38 | $this->database = $database;
39 | }
40 |
41 | public function shouldRun(): bool
42 | {
43 | $schemaManager = $this->database->getSchemaManager();
44 |
45 | if (!$schemaManager->tablesExist(['tl_module'])) {
46 | return false;
47 | }
48 |
49 | $columns = $schemaManager->listTableColumns('tl_module');
50 | $shouldRun = false;
51 |
52 | foreach ($columns as $currentColumn) {
53 | $currentColumnName = $currentColumn->getName();
54 |
55 | if (true === \array_key_exists($currentColumnName, $this->oldColumnNames)) {
56 | $shouldRun = true;
57 | break;
58 | }
59 | }
60 |
61 | return $shouldRun;
62 | }
63 |
64 | public function run(): MigrationResult
65 | {
66 | $schemaManager = $this->database->getSchemaManager();
67 | $columns = $schemaManager->listTableColumns('tl_module');
68 |
69 | foreach ($columns as $currentColumn) {
70 | $currentColumnName = $currentColumn->getName();
71 |
72 | if (true === \array_key_exists($currentColumnName, $this->oldColumnNames)) {
73 | $newColumnName = str_replace('plentaJobs', 'plentaJobsBasic', $currentColumnName);
74 |
75 | $this->database
76 | ->executeQuery(
77 | 'ALTER TABLE tl_module
78 | CHANGE '.$currentColumnName.' '.$newColumnName.' '.$this->oldColumnNames[$currentColumnName]
79 | )
80 | ;
81 | }
82 | }
83 |
84 | return $this->createResult(
85 | true,
86 | 'All Plenta Jobs Basic Columns have been renamed.'
87 | );
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/Helper/NumberHelper.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\Helper;
14 |
15 | use Symfony\Component\Intl\Currencies;
16 |
17 | class NumberHelper
18 | {
19 | protected string $currency;
20 | protected string $locale;
21 |
22 | public function __construct($currency, $locale)
23 | {
24 | $this->currency = $currency;
25 | $this->locale = $locale;
26 | }
27 |
28 | public function reformatDecimalForDb($value): ?int
29 | {
30 | if (null === $value || '' === $value) {
31 | return null;
32 | }
33 |
34 | $decimalPlaces = Currencies::getFractionDigits($this->currency);
35 | $exp = 10 ** $decimalPlaces;
36 | $threshold = $decimalPlaces + 1;
37 |
38 | // No decimal places
39 | if (false === strpos($value, '.')) {
40 | return (int) ($value * $exp);
41 | }
42 |
43 | $pos = \strlen($value) - strpos($value, '.');
44 |
45 | // Remove dot
46 | $value = str_replace('.', '', $value);
47 |
48 | // One decimal place
49 | if ($pos < $threshold) {
50 | return (int) ($value * (10 ** ($threshold - $pos)));
51 | }
52 |
53 | if ($pos > $threshold) {
54 | $value = substr($value, 0, -($pos - $threshold));
55 | }
56 |
57 | return (int) $value;
58 | }
59 |
60 | public function formatNumberFromDbForDCAField(?string $number): ?string
61 | {
62 | if (null === $number) {
63 | return null;
64 | }
65 |
66 | $decimalPlaces = Currencies::getFractionDigits($this->currency);
67 | $exp = 10 ** $decimalPlaces;
68 |
69 | $thousandSeparator = '';
70 | $decimalSeparator = '.';
71 |
72 | return number_format($number / $exp, $decimalPlaces, $decimalSeparator, $thousandSeparator);
73 | }
74 |
75 | public function formatNumberFromDb(?int $number): ?string
76 | {
77 | if (null === $number) {
78 | return null;
79 | }
80 |
81 | $numberFormatter = \NumberFormatter::create($this->locale, \NumberFormatter::DECIMAL_SEPARATOR_SYMBOL);
82 | $thousandSeparator = $numberFormatter->getSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL);
83 | $decimalSeparator = $numberFormatter->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL);
84 |
85 | $decimalPlaces = Currencies::getFractionDigits($this->currency);
86 | $exp = 10 ** $decimalPlaces;
87 |
88 | return number_format($number / $exp, $decimalPlaces, $decimalSeparator, $thousandSeparator);
89 | }
90 |
91 | public function formatCurrency(?int $number): ?string
92 | {
93 | if (null === $number) {
94 | return null;
95 | }
96 |
97 | $numberFormatter = \NumberFormatter::create($this->locale, \NumberFormatter::CURRENCY);
98 | $decimalPlaces = Currencies::getFractionDigits($this->currency);
99 | $exp = 10 ** $decimalPlaces;
100 | return $numberFormatter->formatCurrency($number / $exp, $this->currency);
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/src/Helper/CountJobsHelper.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\Helper;
14 |
15 | use Contao\ArticleModel;
16 | use Contao\ContentModel;
17 | use Contao\ModuleModel;
18 | use Contao\StringUtil;
19 | use Plenta\ContaoJobsBasic\Contao\Model\PlentaJobsBasicJobLocationModel;
20 | use Plenta\ContaoJobsBasic\Contao\Model\PlentaJobsBasicOfferModel;
21 | use Symfony\Component\HttpFoundation\RequestStack;
22 |
23 | class CountJobsHelper
24 | {
25 | public function __construct(protected RequestStack $requestStack)
26 | {
27 | }
28 |
29 | public function countJobs(bool $filtered = true)
30 | {
31 | $request = $this->requestStack->getCurrentRequest();
32 |
33 | $model = $request->attributes->get('moduleModel');
34 |
35 | if ($model && !$model instanceof ModuleModel) {
36 | $model = ModuleModel::findByPk($model);
37 | }
38 |
39 | if ('plenta_jobs_basic_offer_list' !== $model?->type && 'plenta_jobs_basic_filter' !== $model?->type) {
40 | $model = null;
41 | }
42 |
43 | if (!$model) {
44 | $page = $request->attributes->get('pageModel');
45 |
46 | $articles = ArticleModel::findByPid($page->id);
47 |
48 | foreach ($articles as $article) {
49 | $contents = ContentModel::findPublishedByPidAndTable($article->id, 'tl_article');
50 |
51 | foreach ($contents as $content) {
52 | if ('module' === $content->type) {
53 | $module = ModuleModel::findByPk($content->module);
54 |
55 | if ('plenta_jobs_basic_offer_list' === $module->type) {
56 | $model = $module;
57 | break 2;
58 | }
59 | }
60 | }
61 | }
62 | }
63 |
64 | $types = [];
65 | $locations = [];
66 |
67 | if ($model) {
68 | $types = StringUtil::deserialize($model->plentaJobsBasicEmploymentTypes) ?? [];
69 | $locations = StringUtil::deserialize($model->plentaJobsBasicLocations) ?? [];
70 |
71 | if (empty($locations) && !empty($model->plentaJobsBasicCompanies)) {
72 | $locationObjs = PlentaJobsBasicJobLocationModel::findByMultiplePids(StringUtil::deserialize($model->plentaJobsBasicCompanies, true));
73 |
74 | foreach ($locationObjs as $locationObj) {
75 | $locations[] = $locationObj->id;
76 | }
77 | }
78 | }
79 |
80 | if ($filtered) {
81 | $requestData = $request->query->all();
82 |
83 | $queryTypes = $requestData['types'] ?? [];
84 | $queryLocations = $requestData['location'] ?? [];
85 |
86 | $types = array_merge($types, (is_array($queryTypes) || empty($queryTypes) ? $queryTypes : [$queryTypes]));
87 | $locations = array_merge($locations, (is_array($queryLocations) || empty($queryLocations) ? $queryLocations : [$queryLocations]));
88 |
89 | return PlentaJobsBasicOfferModel::countAllPublishedByTypesAndLocation($types, $locations, true, $model);
90 | }
91 |
92 | return PlentaJobsBasicOfferModel::countAllPublishedByTypesAndLocation($types, $locations, true, $model, false);
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/templates/forms/form_layout.html.twig:
--------------------------------------------------------------------------------
1 | {% use "form_div_layout.html.twig" %}
2 |
3 |
4 | {# Rows #}
5 | {%- block form_row -%}
6 | {% set row_attr = row_attr|merge({class: (row_attr.class|default('') ~ ' widget')|trim}) %}
7 | {% if form.vars.block_prefixes.1 is defined %}
8 | {% set row_attr = row_attr|merge({class: (row_attr.class|default('') ~ ' widget-' ~ form.vars.block_prefixes.1)|trim}) %}
9 | {% endif %}
10 | {% if form.vars.block_prefixes.2 is defined %}
11 | {% if form.vars.block_prefixes.2 != form.vars.unique_block_prefix %}
12 | {% set row_attr = row_attr|merge({class: (row_attr.class|default('') ~ ' widget-' ~ form.vars.block_prefixes.2)|trim}) %}
13 | {% endif %}
14 | {% endif %}
15 | {{- parent() -}}
16 | {%- endblock form_row -%}
17 |
18 | {%- block button_row -%}
19 | {% set row_attr = row_attr|merge({class: (row_attr.class|default('') ~ ' widget')|trim}) %}
20 | {% if form.vars.block_prefixes.1 is defined %}
21 | {% set row_attr = row_attr|merge({class: (row_attr.class|default('') ~ ' widget-' ~ form.vars.block_prefixes.1)|trim}) %}
22 | {% endif %}
23 | {% if form.vars.block_prefixes.2 is defined %}
24 | {% if form.vars.block_prefixes.2 != form.vars.unique_block_prefix %}
25 | {% set row_attr = row_attr|merge({class: (row_attr.class|default('') ~ ' widget-' ~ form.vars.block_prefixes.2)|trim}) %}
26 | {% endif %}
27 | {% endif %}
28 | {{- parent() -}}
29 | {%- endblock button_row -%}
30 |
31 | {# Form label #}
32 | {%- block form_label -%}
33 | {% if label is not same as(false) -%}
34 | {% if not compound -%}
35 | {% set label_attr = label_attr|merge({'for': id}) %}
36 | {%- endif -%}
37 | {% if required -%}
38 | {% set label_attr = label_attr|merge({'class': (label_attr.class|default('') ~ ' required')|trim}) %}
39 | {%- endif -%}
40 | {% if label is empty -%}
41 | {%- if label_format is not empty -%}
42 | {% set label = label_format|replace({
43 | '%name%': name,
44 | '%id%': id,
45 | }) %}
46 | {%- else -%}
47 | {% set label = name|humanize %}
48 | {%- endif -%}
49 | {%- endif -%}
50 | <{{ element|default('label') }}{% if label_attr %}{% with { attr: label_attr } %}{{ block('attributes') }}{% endwith %}{% endif %}>
51 | {% if form.vars.required ?? false %}
52 | {{ 'MSC.mandatory'|trans({}, 'contao_default') }}
53 | {% endif %}
54 | {%- if translation_domain is same as(false) -%}
55 | {{- label|raw -}}
56 | {%- else -%}
57 | {{- label|trans({}, translation_domain)|raw -}}
58 | {%- endif -%}
59 | {% if form.vars.required ?? false %}*{% endif %}
60 | {{ element|default('label') }}>
61 | {%- endif -%}
62 | {%- endblock form_label -%}
63 |
64 |
65 | {# Form errors #}
66 | {%- block form_errors -%}
67 | {%- if errors|length > 0 -%}
68 | {%- for error in errors -%}
69 | {{ error.message }}
70 | {%- endfor -%}
71 | {%- endif -%}
72 | {%- endblock form_errors -%}
73 |
74 | {%- block choice_widget_expanded -%}
75 |
83 | {%- endblock choice_widget_expanded -%}
84 |
85 | {% block html_widget %}
86 | {{ html|raw }}
87 | {% endblock %}
--------------------------------------------------------------------------------
/src/Controller/Contao/ContentElement/PlentaJobsBasicJobOfferTeaserController.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\Controller\Contao\ContentElement;
14 |
15 | use Contao\ContentModel;
16 | use Contao\CoreBundle\Controller\ContentElement\AbstractContentElementController;
17 | use Contao\CoreBundle\DependencyInjection\Attribute\AsContentElement;
18 | use Contao\CoreBundle\Routing\ScopeMatcher;
19 | use Contao\CoreBundle\ServiceAnnotation\ContentElement;
20 | use Contao\CoreBundle\Twig\FragmentTemplate;
21 | use Contao\StringUtil;
22 | use Contao\Template;
23 | use Contao\FrontendTemplate;
24 | use Plenta\ContaoJobsBasic\Contao\Model\PlentaJobsBasicOfferModel;
25 | use Plenta\ContaoJobsBasic\Helper\MetaFieldsHelper;
26 | use Symfony\Component\HttpFoundation\Request;
27 | use Symfony\Component\HttpFoundation\RequestStack;
28 | use Symfony\Component\HttpFoundation\Response;
29 | use Symfony\Contracts\Translation\TranslatorInterface;
30 |
31 | #[AsContentElement(category: 'plentaJobsBasic')]
32 | class PlentaJobsBasicJobOfferTeaserController extends AbstractContentElementController
33 | {
34 | protected $metaFields;
35 |
36 | public function __construct(
37 | protected MetaFieldsHelper $metaFieldsHelper,
38 | protected TranslatorInterface $translator
39 | ) {
40 | }
41 |
42 | public function getMetaFields(ContentModel $model, PlentaJobsBasicOfferModel $jobOffer): array
43 | {
44 | if (null !== $this->metaFields) {
45 | return $this->metaFields;
46 | }
47 |
48 | $this->metaFields = $this->metaFieldsHelper->getMetaFields($jobOffer, $model->size);
49 |
50 | return $this->metaFields;
51 | }
52 |
53 | public function getResponse(FragmentTemplate $template, ContentModel $model, Request $request): Response
54 | {
55 | $jobOffer = PlentaJobsBasicOfferModel::findPublishedByIdOrAlias($model->plentaJobsBasicJobOffer);
56 |
57 | if (!$jobOffer) {
58 | $headlineData = StringUtil::deserialize($model->headline ?? [] ?: '', true);
59 | $attributesData = StringUtil::deserialize($model->cssID ?? [] ?: '', true);
60 |
61 | $template = new FrontendTemplate('jobs_basic/plenta_jobs_basic_job_offer_empty');
62 | $template->setData([
63 | 'type' => $this->getType(),
64 | 'template' => $template->getName(),
65 | 'data' => $model,
66 | 'element_html_id' => $attributesData[0] ?? null,
67 | 'element_css_classes' => trim($attributesData[1] ?? ''),
68 | 'empty' => $model->plentaJobsBasicNotice ?:
69 | $this->translator->trans('MSC.PLENTA_JOBS.emptyList', [], 'contao_default'),
70 | 'headline' => [
71 | 'text' => $headlineData['value'] ?? '',
72 | 'tag_name' => $headlineData['unit'] ?? 'h1',
73 | ],
74 | ]);
75 |
76 | return $template->getResponse();
77 | }
78 |
79 | $template->jobOffer = $jobOffer;
80 | $parts = StringUtil::deserialize($model->plentaJobsBasicJobOfferTeaserParts);
81 |
82 | if (!\is_array($parts)) {
83 | $parts = [];
84 | }
85 |
86 | $template->parts = $parts;
87 | $template->jobOfferMeta = $this->getMetaFields($model, $jobOffer);
88 | $template->link = $jobOffer->getFrontendUrl($request->getLocale());
89 |
90 | $this->tagResponse('contao.db.tl_plenta_jobs_basic_offer.'.$jobOffer->id);
91 |
92 | return $template->getResponse();
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/Migration/RefactorTranslationsMigration.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\Migration;
14 |
15 | use Contao\CoreBundle\Migration\MigrationResult;
16 | use Doctrine\DBAL\Connection;
17 | use Doctrine\DBAL\Types\TextType;
18 |
19 | class RefactorTranslationsMigration extends \Contao\CoreBundle\Migration\AbstractMigration
20 | {
21 | protected Connection $database;
22 |
23 | public function __construct(Connection $connection)
24 | {
25 | $this->database = $connection;
26 | }
27 |
28 | public function getName(): string
29 | {
30 | return 'Plenta Jobs Basic Bundle 2.0 Update - Job Offer Translations';
31 | }
32 |
33 | public function shouldRun(): bool
34 | {
35 | $schemaManager = $this->database->getSchemaManager();
36 |
37 | if (!$schemaManager->tablesExist(['tl_plenta_jobs_basic_offer_translation', 'tl_plenta_jobs_basic_offer'])) {
38 | return false;
39 | }
40 |
41 | $columns = $schemaManager->listTableColumns('tl_plenta_jobs_basic_offer');
42 |
43 | if (!\array_key_exists('translations', $columns)) {
44 | return false;
45 | }
46 |
47 | foreach ($columns as $column) {
48 | if ('translations' !== $column->getName()) {
49 | continue;
50 | }
51 |
52 | if (!is_a($column->getType(), TextType::class)) {
53 | return false;
54 | }
55 | }
56 |
57 | if (true === (bool) $this->database
58 | ->executeQuery('
59 | SELECT EXISTS(
60 | SELECT id
61 | FROM tl_plenta_jobs_basic_offer_translation
62 | )
63 | ')
64 | ->fetchOne()
65 | ) {
66 | return true;
67 | }
68 |
69 | return false;
70 | }
71 |
72 | public function run(): MigrationResult
73 | {
74 | $offers = $this->database->executeQuery('SELECT * FROM tl_plenta_jobs_basic_offer')->fetchAllAssociative();
75 |
76 | foreach ($offers as $offer) {
77 | $translations = $this->database->prepare('SELECT * FROM tl_plenta_jobs_basic_offer_translation WHERE offer_id = ?')->executeQuery([$offer['id']])->fetchAllAssociative();
78 | if (!empty($translations)) {
79 | $translationsArr = [];
80 | foreach ($translations as $translation) {
81 | $translationArr = [
82 | 'description' => $translation['description'],
83 | 'title' => $translation['title'],
84 | 'alias' => $translation['alias'],
85 | 'language' => $translation['language'],
86 | ];
87 | if (empty($translationsArr)) {
88 | $translationsArr[1] = $translationArr;
89 | } else {
90 | $translationsArr[] = $translationArr;
91 | }
92 | }
93 |
94 | $this->database->prepare('UPDATE tl_plenta_jobs_basic_offer SET translations = ? WHERE id = ?')->executeStatement([serialize($translationsArr), $offer['id']]);
95 | $this->database->prepare('DELETE FROM tl_plenta_jobs_basic_offer_translation WHERE offer_id = ?')->executeStatement([$offer['id']]);
96 | }
97 | }
98 |
99 | return new MigrationResult(true, 'Translations have successfully been moved from entities to serialized arrays.');
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/Events/JobOfferReaderBeforeParseTemplateEvent.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\Events;
14 |
15 | use Contao\ModuleModel;
16 | use Contao\Template;
17 | use Plenta\ContaoJobsBasic\Contao\Model\PlentaJobsBasicOfferModel;
18 | use Plenta\ContaoJobsBasic\Controller\Contao\FrontendModule\JobOfferReaderController;
19 | use Symfony\Contracts\EventDispatcher\Event;
20 |
21 | class JobOfferReaderBeforeParseTemplateEvent extends Event
22 | {
23 | public const NAME = 'plenta_jobs_basic.job_offer_reader.before_parse_template';
24 |
25 | private Template $template;
26 |
27 | private PlentaJobsBasicOfferModel $jobOffer;
28 |
29 | private JobOfferReaderController $objModule;
30 |
31 | private ModuleModel $model;
32 |
33 | private ?string $structuredData;
34 |
35 | public function __construct(PlentaJobsBasicOfferModel $jobOffer, Template $template, ModuleModel $model, JobOfferReaderController $objModule, ?string $structuredData)
36 | {
37 | $this->jobOffer = $jobOffer;
38 | $this->template = $template;
39 | $this->model = $model;
40 | $this->objModule = $objModule;
41 | $this->structuredData = $structuredData;
42 | }
43 |
44 | /**
45 | * @return Template
46 | */
47 | public function getTemplate(): Template
48 | {
49 | return $this->template;
50 | }
51 |
52 | /**
53 | * @param Template $template
54 | *
55 | * @return JobOfferReaderBeforeParseTemplateEvent
56 | */
57 | public function setTemplate(Template $template): self
58 | {
59 | $this->template = $template;
60 |
61 | return $this;
62 | }
63 |
64 | /**
65 | * @return PlentaJobsBasicOfferModel
66 | */
67 | public function getJobOffer(): PlentaJobsBasicOfferModel
68 | {
69 | return $this->jobOffer;
70 | }
71 |
72 | /**
73 | * @param PlentaJobsBasicOfferModel $jobOffer
74 | *
75 | * @return JobOfferReaderBeforeParseTemplateEvent
76 | */
77 | public function setJobOffer(PlentaJobsBasicOfferModel $jobOffer): self
78 | {
79 | $this->jobOffer = $jobOffer;
80 |
81 | return $this;
82 | }
83 |
84 | /**
85 | * @return JobOfferReaderController
86 | */
87 | public function getObjModule(): JobOfferReaderController
88 | {
89 | return $this->objModule;
90 | }
91 |
92 | /**
93 | * @param JobOfferReaderController $objModule
94 | *
95 | * @return JobOfferReaderBeforeParseTemplateEvent
96 | */
97 | public function setObjModule(JobOfferReaderController $objModule): self
98 | {
99 | $this->objModule = $objModule;
100 |
101 | return $this;
102 | }
103 |
104 | /**
105 | * @return ModuleModel
106 | */
107 | public function getModel(): ModuleModel
108 | {
109 | return $this->model;
110 | }
111 |
112 | /**
113 | * @param ModuleModel $model
114 | *
115 | * @return JobOfferReaderBeforeParseTemplateEvent
116 | */
117 | public function setModel(ModuleModel $model): self
118 | {
119 | $this->model = $model;
120 |
121 | return $this;
122 | }
123 |
124 | /**
125 | * @return string|null
126 | */
127 | public function getStructuredData(): ?string
128 | {
129 | return $this->structuredData;
130 | }
131 |
132 | /**
133 | * @param string|null $structuredData
134 | *
135 | * @return JobOfferReaderBeforeParseTemplateEvent
136 | */
137 | public function setStructuredData(?string $structuredData): self
138 | {
139 | $this->structuredData = $structuredData;
140 |
141 | return $this;
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/contao/dca/tl_plenta_jobs_basic_organization.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | use Contao\DC_Table;
14 |
15 | $GLOBALS['TL_DCA']['tl_plenta_jobs_basic_organization'] = [
16 | // Config
17 | 'config' => [
18 | 'dataContainer' => DC_Table::class,
19 | 'ctable' => ['tl_plenta_jobs_basic_job_location'],
20 | 'switchToEdit' => true,
21 | 'enableVersioning' => true,
22 | 'sql' => [
23 | 'keys' => [
24 | 'id' => 'primary',
25 | ],
26 | ],
27 | ],
28 |
29 | 'list' => [
30 | 'sorting' => [
31 | 'mode' => 2,
32 | 'fields' => ['name'],
33 | 'flag' => 1,
34 | 'disableGrouping' => true,
35 | ],
36 | 'label' => [
37 | 'fields' => ['name'],
38 | 'format' => '%s',
39 | ],
40 | 'global_operations' => [
41 | 'back' => [
42 | 'route' => 'Plenta\ContaoJobsBasic\Controller\Contao\BackendModule\SettingsController',
43 | 'label' => &$GLOBALS['TL_LANG']['MSC']['backBT'],
44 | 'icon' => 'back.svg',
45 | ],
46 | 'all' => [
47 | 'href' => 'act=select',
48 | 'class' => 'header_edit_all',
49 | 'attributes' => 'onclick="Backend.getScrollOffset()" accesskey="e"',
50 | ],
51 | ],
52 | 'operations' => [
53 | 'edit',
54 | 'children',
55 | 'delete'
56 | ],
57 | ],
58 |
59 | // Palettes
60 | 'palettes' => [
61 | 'default' => '{organization_legend},name,sameAs;{logo_legend},logo',
62 | ],
63 |
64 | // Fields
65 | 'fields' => [
66 | 'id' => [
67 | 'sql' => [
68 | 'type' => 'integer',
69 | 'unsigned' => true,
70 | 'autoincrement' => true,
71 | ],
72 | ],
73 | 'tstamp' => [
74 | 'sql' => [
75 | 'type' => 'integer',
76 | 'unsigned' => true,
77 | 'default' => 0,
78 | ],
79 | ],
80 | 'name' => [
81 | 'label' => &$GLOBALS['TL_LANG']['tl_plenta_jobs_basic_organization']['name'],
82 | 'exclude' => true,
83 | 'search' => true,
84 | 'inputType' => 'text',
85 | 'default' => '',
86 | 'eval' => [
87 | 'maxlength' => 255,
88 | 'tl_class' => 'w50',
89 | 'mandatory' => true,
90 | ],
91 | 'sql' => [
92 | 'type' => 'string',
93 | 'length' => 255,
94 | 'default' => '',
95 | ],
96 | ],
97 | 'sameAs' => [
98 | 'label' => &$GLOBALS['TL_LANG']['tl_plenta_jobs_basic_organization']['sameAs'],
99 | 'exclude' => true,
100 | 'inputType' => 'text',
101 | 'default' => '',
102 | 'eval' => [
103 | 'maxlength' => 255,
104 | 'tl_class' => 'w50',
105 | 'rgxp' => 'url',
106 | ],
107 | 'sql' => [
108 | 'type' => 'string',
109 | 'length' => 255,
110 | 'default' => '',
111 | ],
112 | ],
113 | 'logo' => [
114 | 'exclude' => true,
115 | 'inputType' => 'fileTree',
116 | 'eval' => [
117 | 'fieldType' => 'radio',
118 | 'filesOnly' => true,
119 | 'extensions' => Contao\Config::get('validImageTypes'),
120 | ],
121 | 'sql' => [
122 | 'type' => 'binary_string',
123 | 'notnull' => false,
124 | 'default' => null,
125 | ],
126 | ],
127 | ],
128 | ];
129 |
--------------------------------------------------------------------------------
/src/Controller/Contao/ContentElement/PlentaJobsBasicJobOfferDetailsController.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\Controller\Contao\ContentElement;
14 |
15 | use Contao\Config;
16 | use Contao\ContentModel;
17 | use Contao\CoreBundle\Controller\ContentElement\AbstractContentElementController;
18 | use Contao\CoreBundle\DependencyInjection\Attribute\AsContentElement;
19 | use Contao\CoreBundle\Routing\ScopeMatcher;
20 | use Contao\CoreBundle\ServiceAnnotation\ContentElement;
21 | use Contao\CoreBundle\Twig\FragmentTemplate;
22 | use Contao\Input;
23 | use Contao\StringUtil;
24 | use Contao\Template;
25 | use Plenta\ContaoJobsBasic\Contao\Model\PlentaJobsBasicOfferModel;
26 | use Plenta\ContaoJobsBasic\Helper\MetaFieldsHelper;
27 | use Symfony\Component\HttpFoundation\Request;
28 | use Symfony\Component\HttpFoundation\RequestStack;
29 | use Symfony\Component\HttpFoundation\Response;
30 |
31 | #[AsContentElement(category: 'plentaJobsBasic')]
32 | class PlentaJobsBasicJobOfferDetailsController extends AbstractContentElementController
33 | {
34 |
35 | protected ?PlentaJobsBasicOfferModel $jobOffer = null;
36 |
37 | protected ?array $metaFields = null;
38 |
39 | public function __construct(
40 | protected MetaFieldsHelper $metaFieldsHelper,
41 | protected RequestStack $requestStack,
42 | protected ScopeMatcher $matcher
43 | ) {
44 | }
45 |
46 | public function getMetaFields(ContentModel $model): array
47 | {
48 | if (null !== $this->metaFields) {
49 | return $this->metaFields;
50 | }
51 |
52 | $this->metaFields = $this->metaFieldsHelper->getMetaFields($this->getJobOffer(), $model->size);
53 |
54 | return $this->metaFields;
55 | }
56 |
57 | public function getJobOffer($language = null): ?PlentaJobsBasicOfferModel
58 | {
59 | if (null !== $this->jobOffer) {
60 | return $this->jobOffer;
61 | }
62 |
63 | $alias = Input::get('auto_item');
64 |
65 | if (null === $alias) {
66 | return null;
67 | }
68 |
69 | $this->jobOffer = PlentaJobsBasicOfferModel::findPublishedByIdOrAlias($alias);
70 |
71 | return $this->jobOffer;
72 | }
73 |
74 | public function renderDetails(string $data, string $class): string
75 | {
76 | if (empty($data)) {
77 | return '';
78 | }
79 |
80 | return ''.$data.'
';
81 | }
82 |
83 | protected function getResponse(FragmentTemplate $template, ContentModel $model, Request $request): Response
84 | {
85 | if ($this->matcher->isFrontendRequest($this->requestStack->getCurrentRequest())) {
86 | if (null === $this->getJobOffer($request->getLocale())) {
87 | return new Response('');
88 | }
89 |
90 | $metaFields = $this->getMetaFields($model);
91 | $template->content = '';
92 |
93 | if (!empty($model->plenta_jobs_basic_job_offer_details)) {
94 | $detailsSelected = StringUtil::deserialize($model->plenta_jobs_basic_job_offer_details);
95 |
96 | foreach ($detailsSelected as $details) {
97 | $cssClass = $details;
98 |
99 | if ('description' === $details) {
100 | $cssClass .= ' ce_text';
101 | }
102 |
103 | $template->content .= $this->renderDetails($metaFields[$details], $cssClass);
104 | }
105 | }
106 |
107 | $template->jobOfferMeta = $this->getMetaFields($model);
108 | $template->jobOffer = $this->getJobOffer();
109 |
110 | $this->tagResponse('contao.db.tl_plenta_jobs_basic_offer.'.$this->jobOffer->id);
111 | }
112 |
113 | return $template->getResponse();
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/EventListener/Contao/InsertTagListener.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\EventListener\Contao;
14 |
15 | use Composer\InstalledVersions;
16 | use Contao\Config;
17 | use Contao\CoreBundle\ServiceAnnotation\Hook;
18 | use Contao\Date;
19 | use Contao\Input;
20 | use Contao\UserModel;
21 | use Plenta\ContaoJobsBasic\Contao\Model\PlentaJobsBasicOfferModel;
22 | use Plenta\ContaoJobsBasic\Helper\MetaFieldsHelper;
23 | use Symfony\Component\HttpFoundation\RequestStack;
24 |
25 | /**
26 | * @Hook("replaceInsertTags")
27 | */
28 | class InsertTagListener
29 | {
30 | public const TAG = 'job';
31 |
32 | protected RequestStack $requestStack;
33 |
34 | protected MetaFieldsHelper $metaFieldsHelper;
35 |
36 | /**
37 | * @var string|null
38 | */
39 | protected ?string $autoItem = null;
40 |
41 | public function __construct(RequestStack $requestStack, MetaFieldsHelper $metaFieldsHelper)
42 | {
43 | $this->requestStack = $requestStack;
44 | $this->metaFieldsHelper = $metaFieldsHelper;
45 | }
46 |
47 | public function __invoke(string $tag)
48 | {
49 | $chunks = explode('::', $tag);
50 |
51 | if (self::TAG !== $chunks[0]) {
52 | return false;
53 | }
54 |
55 | $this->handleAutoItem();
56 |
57 | if (!$this->autoItem) {
58 | return false;
59 | }
60 |
61 | $jobOfferData = PlentaJobsBasicOfferModel::findPublishedByIdOrAlias($this->autoItem);
62 | $language = $this->requestStack->getCurrentRequest()->getLocale();
63 |
64 | if (null !== $jobOfferData) {
65 | $translation = $jobOfferData->getTranslation($language);
66 |
67 | if ('id' === $chunks[1]) {
68 | return (string) $jobOfferData->id;
69 | }
70 |
71 | if ('teasertext' === $chunks[1]) {
72 | return $translation['teaser'] ?? (string) $jobOfferData->teaser;
73 | }
74 | if ('description' === $chunks[1]) {
75 | return $translation['description'] ?? (string) $jobOfferData->description;
76 | }
77 |
78 | if ('title' === $chunks[1]) {
79 | return $translation['title'] ?? (string) $jobOfferData->title;
80 | }
81 |
82 | if ('alias' === $chunks[1]) {
83 | return $translation['alias'] ?? (string) $jobOfferData->alias;
84 | }
85 |
86 | if ('datePosted' === $chunks[1]) {
87 | $objPage = $GLOBALS['objPage'] ?? null;
88 |
89 | return Date::parse($objPage->dateFormat ?? Config::get('dateFormat'), $jobOfferData->datePosted);
90 | }
91 |
92 | if ('author' === $chunks[1] && isset($chunks[2])) {
93 | $author = UserModel::findById((int) $jobOfferData->author);
94 |
95 | if (null !== $author) {
96 | if ('name' === $chunks[2]) {
97 | return (string) $author->name;
98 | }
99 |
100 | if ('email' === $chunks[2]) {
101 | return (string) $author->email;
102 | }
103 |
104 | if ('username' === $chunks[2]) {
105 | return (string) $author->username;
106 | }
107 | }
108 | }
109 |
110 | if ('location' === $chunks[1]) {
111 | return $this->metaFieldsHelper->formatAddressLocality($jobOfferData) ?? '';
112 | }
113 |
114 | if ('locationTitle' === $chunks[1]) {
115 | return $this->metaFieldsHelper->formatAddressLocalityTitle($jobOfferData) ?? '';
116 | }
117 | }
118 |
119 | return false;
120 | }
121 |
122 | public function handleAutoItem(): void
123 | {
124 | if (null === $this->autoItem) {
125 | $this->autoItem = Input::get('auto_item', false, true);
126 | }
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/contao/templates/_new/frontend_module/plenta_jobs_basic_filter.html.twig:
--------------------------------------------------------------------------------
1 | {% extends '@Contao/frontend_module/_base.html.twig' %}
2 | {% set searchable = false %}
3 |
4 | {% block content %}
5 |
6 | {{ form(form) }}
7 |
8 |
115 |
116 | {% endblock %}
117 |
--------------------------------------------------------------------------------
/contao/dca/tl_plenta_jobs_basic_settings_employment_type.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | use Contao\DC_Table;
14 | use Plenta\ContaoJobsBasic\EventListener\Contao\DCA\TlPlentaJobsBasicSettingsEmploymentType;
15 |
16 | $GLOBALS['TL_DCA']['tl_plenta_jobs_basic_settings_employment_type'] = [
17 | 'config' => [
18 | 'dataContainer' => DC_Table::class,
19 | 'switchToEdit' => true,
20 | 'markAsCopy' => 'title',
21 | 'enableVersioning' => true,
22 | 'sql' => [
23 | 'keys' => [
24 | 'id' => 'primary',
25 | ],
26 | ],
27 | ],
28 |
29 | 'list' => [
30 | 'sorting' => [
31 | 'mode' => 1,
32 | 'fields' => ['title'],
33 | 'flag' => 1,
34 | 'panelLayout' => 'filter;search,sort,limit',
35 | ],
36 | 'label' => [
37 | 'fields' => ['title'],
38 | 'showColumns' => false,
39 | ],
40 | 'global_operations' => [
41 | 'back' => [
42 | 'route' => 'Plenta\ContaoJobsBasic\Controller\Contao\BackendModule\SettingsController',
43 | 'label' => &$GLOBALS['TL_LANG']['MSC']['backBT'],
44 | 'icon' => 'back.svg',
45 | ],
46 | 'all' => [
47 | 'href' => 'act=select',
48 | 'class' => 'header_edit_all',
49 | 'attributes' => 'onclick="Backend.getScrollOffset()" accesskey="e"',
50 | ],
51 | ],
52 | 'operations' => [
53 | 'edit' => [
54 | 'href' => 'act=edit',
55 | 'icon' => 'edit.svg',
56 | ],
57 | 'copy' => [
58 | 'href' => 'act=copy',
59 | 'icon' => 'copy.svg',
60 | ],
61 | 'delete' => [
62 | 'href' => 'act=delete',
63 | 'icon' => 'delete.svg',
64 | 'attributes' => 'onclick="if(!confirm(\''.($GLOBALS['TL_LANG']['MSC']['deleteConfirm'] ?? null).'\'))return false;Backend.getScrollOffset()"',
65 | ],
66 | ],
67 | ],
68 |
69 | // Palettes
70 | 'palettes' => [
71 | 'default' => '{settings_legend},title,google_for_jobs_mapping;translation',
72 | ],
73 |
74 | // Fields
75 | 'fields' => [
76 | 'id' => [
77 | 'sql' => [
78 | 'type' => 'integer',
79 | 'unsigned' => true,
80 | 'autoincrement' => true,
81 | ],
82 | ],
83 | 'tstamp' => [
84 | 'sql' => [
85 | 'type' => 'integer',
86 | 'unsigned' => true,
87 | 'default' => 0,
88 | ],
89 | ],
90 | 'title' => [
91 | 'exclude' => true,
92 | 'search' => true,
93 | 'inputType' => 'text',
94 | 'eval' => [
95 | 'maxlength' => 255,
96 | 'tl_class' => 'w50',
97 | 'mandatory' => true,
98 | ],
99 | 'sql' => [
100 | 'type' => 'string',
101 | 'length' => 255,
102 | 'default' => '',
103 | ],
104 | ],
105 | 'google_for_jobs_mapping' => [
106 | 'exclude' => true,
107 | 'inputType' => 'select',
108 | 'options_callback' => [
109 | TlPlentaJobsBasicSettingsEmploymentType::class,
110 | 'googleForJobsMappingOptionsCallback',
111 | ],
112 | 'eval' => [
113 | 'tl_class' => 'w50',
114 | 'mandatory' => true,
115 | ],
116 | 'sql' => [
117 | 'type' => 'string',
118 | 'length' => 32,
119 | 'default' => 'OTHER',
120 | ],
121 | ],
122 | 'translation' => [
123 | 'inputType' => 'metaWizard',
124 | 'eval' => [
125 | 'class' => 'clr',
126 | 'allowHtml' => true,
127 | 'multiple' => true,
128 | 'metaFields' => [
129 | 'title' => 'maxlength="255"',
130 | ],
131 | ],
132 | 'load_callback' => [[
133 | TlPlentaJobsBasicSettingsEmploymentType::class,
134 | 'translationLoadCallback',
135 | ]],
136 | 'save_callback' => [[
137 | TlPlentaJobsBasicSettingsEmploymentType::class,
138 | 'translationSaveCallback',
139 | ]],
140 | 'sql' => 'JSON NULL default NULL',
141 | ],
142 | ],
143 | ];
144 |
--------------------------------------------------------------------------------
/src/Form/Type/JobOfferFilterType.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\Form\Type;
14 |
15 | use Contao\CoreBundle\InsertTag\InsertTagParser;
16 | use Contao\CoreBundle\String\SimpleTokenParser;
17 | use Contao\ModuleModel;
18 | use Contao\StringUtil;
19 | use Plenta\ContaoJobsBasic\Helper\CountJobsHelper;
20 | use Symfony\Component\Form\AbstractType;
21 | use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
22 | use Symfony\Component\Form\Extension\Core\Type\SubmitType;
23 | use Symfony\Component\Form\Extension\Core\Type\TextType;
24 | use Symfony\Component\Form\FormBuilderInterface;
25 | use Symfony\Component\HttpFoundation\RequestStack;
26 | use Symfony\Component\OptionsResolver\OptionsResolver;
27 |
28 | class JobOfferFilterType extends AbstractType
29 | {
30 |
31 | public function __construct(
32 | protected InsertTagParser $insertTagParser,
33 | protected RequestStack $requestStack,
34 | protected CountJobsHelper $countJobsHelper,
35 | protected SimpleTokenParser $simpleTokenParser
36 | ) {
37 | }
38 |
39 | public function buildForm(FormBuilderInterface $builder, array $options): void
40 | {
41 | /** @var ModuleModel $model */
42 | $model = $options['fmd'];
43 | if ($model->plentaJobsBasicShowKeyword) {
44 | $builder->add('keywordHeadline', HtmlType::class, [
45 | 'html' => $this->getHeadlineHtml($model->plentaJobsBasicKeywordHeadline, 'keyword'),
46 | 'priority' => 110,
47 | ]);
48 | $builder->add('keyword', TextType::class, [
49 | 'label' => false,
50 | 'required' => false,
51 | 'row_attr' => [
52 | 'class' => 'widget-text',
53 | ],
54 | 'priority' => 109,
55 | ]);
56 | }
57 | if ($model->plentaJobsBasicShowTypes) {
58 | $builder->add('typesHeadline', HtmlType::class, [
59 | 'html' => $this->getHeadlineHtml($model->plentaJobsBasicTypesHeadline, 'jobTypes'),
60 | 'priority' => 100,
61 | ]);
62 | $builder->add('types', ChoiceType::class, [
63 | 'choices' => array_flip($options['types']),
64 | 'multiple' => true,
65 | 'expanded' => true,
66 | 'label' => false,
67 | 'required' => false,
68 | 'row_attr' => [
69 | 'class' => 'widget-checkbox',
70 | ],
71 | 'data' => $this->requestStack->getMainRequest()->get('types', []),
72 | 'priority' => 99,
73 | ]);
74 | }
75 |
76 | if ($model->plentaJobsBasicShowLocations) {
77 | $builder->add('locationsHeadline', HtmlType::class, [
78 | 'html' => $this->getHeadlineHtml($model->plentaJobsBasicLocationsHeadline, 'jobLocation'),
79 | 'priority' => 90,
80 | ]);
81 | $builder->add('location', ChoiceType::class, [
82 | 'choices' => array_flip($options['locations']),
83 | 'multiple' => !$model->plentaJobsBasicDisableMultipleLocations,
84 | 'expanded' => true,
85 | 'label' => false,
86 | 'required' => false,
87 | 'row_attr' => [
88 | 'class' => 'widget-checkbox',
89 | ],
90 | 'placeholder' => 'MSC.PLENTA_JOBS.filterForm.locationPlaceholder',
91 | 'translation_domain' => 'contao_default',
92 | 'data' => $this->requestStack->getMainRequest()->get('location', $model->plentaJobsBasicDisableMultipleLocations ? '' : []),
93 | 'priority' => 89,
94 | ]);
95 | }
96 |
97 | if ($model->plentaJobsBasicShowButton) {
98 | $builder->add('submit', SubmitType::class, [
99 | 'label' => $model->plentaJobsBasicDynamicButton ? $this->simpleTokenParser->parse($model->plentaJobsBasicSubmit, ['count' => $this->countJobsHelper->countJobs()]) : $model->plentaJobsBasicSubmit,
100 | 'priority' => 0,
101 | ]);
102 | }
103 | }
104 |
105 | public function configureOptions(OptionsResolver $resolver): void
106 | {
107 | $resolver->setDefaults([
108 | 'fmd' => null,
109 | 'types' => [],
110 | 'locations' => [],
111 | 'method' => 'GET',
112 | 'csrf_protection' => false,
113 | ]);
114 | }
115 |
116 | public function getHeadlineHtml(?string $content, string $type): string
117 | {
118 | if (empty($content)) {
119 | return '';
120 | }
121 |
122 | $return = '';
123 | $return .= $this->insertTagParser->replace($content);
124 | $return .= '
';
125 |
126 | return $return;
127 | }
128 |
129 | public function getBlockPrefix()
130 | {
131 | return '';
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/contao/languages/de/default.xlf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Vollzeit
7 |
8 |
9 | Teilzeit
10 |
11 |
12 | Befristet
13 |
14 |
15 | Leiharbeit
16 |
17 |
18 | Praktikum
19 |
20 |
21 | Ehrenamt
22 |
23 |
24 | Tageweise
25 |
26 |
27 | Andere
28 |
29 |
30 | Jobs (Basic)
31 |
32 |
33 | Details zum Jobangebot
34 |
35 |
36 | Details zum Jobangebot
37 |
38 |
39 | Stellenanzeigen-Teaser
40 |
41 |
42 | Stellenanzeigen-Teaser
43 |
44 |
45 | Es sind momentan keine Stellenanzeigen vorhanden.
46 |
47 |
48 | Dokumentation
49 |
50 |
51 | Fehler melden
52 |
53 |
56 |
57 | Add-ons
58 |
59 |
60 | Homeoffice
61 |
62 |
63 | Remote in
64 |
65 |
66 | Stellentitel
67 |
68 |
69 | Stellenbild
70 |
71 |
72 | Stellenbeschreibung
73 |
74 |
75 | Stellenart
76 |
77 |
78 | Bewerbungsfrist
79 |
80 |
81 | Arbeitsort
82 |
83 |
84 | Teaser-Text
85 |
86 |
87 | Veröffentlichungsdatum
88 |
89 |
90 | Unternehmen
91 |
92 |
93 | Zurück-Link
94 |
95 |
96 | Inhaltselemente
97 |
98 |
99 | Gehalt
100 |
101 |
102 | Teaser-Text
103 |
104 |
105 | Eintrittsdatum
106 |
107 |
108 | Alle Standorte
109 |
110 |
111 | ab sofort
112 |
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/contao/languages/de/tl_plenta_jobs_basic_job_location.xlf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Adressdaten
7 |
8 |
9 | Straße/Hausnummer
10 |
11 |
12 | Bitte geben Sie die Straße und Hausnummer ein.
13 |
14 |
15 | Stadt
16 |
17 |
18 | Bitte geben Sie die Stadt ein.
19 |
20 |
21 | Bundesland/Kanton
22 |
23 |
24 | Bitte geben Sie das Bundesland oder den Kanton ein.
25 |
26 |
27 | Postleitzahl
28 |
29 |
30 | Bitte geben Sie Postleitzahl ein.
31 |
32 |
33 | Land
34 |
35 |
36 | Bitte wählen Sie ein Land aus.
37 |
38 |
39 | Firmenlogo
40 |
41 |
42 | Neuer Standort
43 |
44 |
45 | Einen neuen Standorte anlegen
46 |
47 |
48 | Ortsbeschränkungen
49 |
50 |
51 | Legen Sie für jeden möglichen Ort einen Eintrag an.
52 |
53 |
54 | Verwaltungseinheit
55 |
56 |
57 | Bitte wählen Sie die Art der Verwaltungseinheit aus der Liste aus.
58 |
59 |
60 | Name
61 |
62 |
63 | Bitte geben Sie den Namen der Verwaltungseinheit an (z.B. Deutschland).
64 |
65 |
66 | Land
67 |
68 |
69 | Bundesland/Kanton
70 |
71 |
72 | Stadt
73 |
74 |
75 | Schulbezirk
76 |
77 |
78 | Standort-Art
79 |
80 |
81 | Bitte wählen Sie eine Art aus der Liste aus.
82 |
83 |
84 | Vor Ort
85 |
86 |
87 | Homeoffice
88 |
89 |
90 | Typen-Einstellungen
91 |
92 |
93 | Standort-Einstellungen
94 |
95 |
96 | Titel
97 |
98 |
99 | Bitte geben Sie einen Titel ein.
100 |
101 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/src/Migration/MoveRemoteJobsMigration.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\Migration;
14 |
15 | use Contao\CoreBundle\Migration\MigrationResult;
16 | use Contao\StringUtil;
17 | use Contao\System;
18 | use Doctrine\DBAL\Connection;
19 | use Plenta\ContaoJobsBasic\GoogleForJobs\GoogleForJobs;
20 |
21 | class MoveRemoteJobsMigration extends \Contao\CoreBundle\Migration\AbstractMigration
22 | {
23 | protected Connection $database;
24 |
25 | public function __construct(Connection $connection)
26 | {
27 | $this->database = $connection;
28 | }
29 |
30 | public function getName(): string
31 | {
32 | return 'Plenta Jobs Basic Bundle 2.0 Update - Remote jobs';
33 | }
34 |
35 | public function shouldRun(): bool
36 | {
37 | $schemaManager = $this->database->getSchemaManager();
38 |
39 | if (!$schemaManager->tablesExist(['tl_plenta_jobs_basic_offer', 'tl_plenta_jobs_basic_job_location'])) {
40 | return false;
41 | }
42 |
43 | $columns = $schemaManager->listTableColumns('tl_plenta_jobs_basic_offer');
44 | $columnsLocation = $schemaManager->listTableColumns('tl_plenta_jobs_basic_job_location');
45 |
46 | if (!isset($columns['isremote']) || !isset($columnsLocation['requirementtype'])) {
47 | return false;
48 | }
49 |
50 | if (true === (bool) $this->database
51 | ->executeQuery("
52 | SELECT EXISTS(
53 | SELECT id
54 | FROM tl_plenta_jobs_basic_offer
55 | WHERE
56 | isRemote = '1'
57 | )
58 | ")
59 | ->fetchOne()
60 | ) {
61 | return true;
62 | }
63 |
64 | return false;
65 | }
66 |
67 | public function run(): MigrationResult
68 | {
69 | $offers = $this->database->executeQuery("SELECT * FROM tl_plenta_jobs_basic_offer WHERE isRemote = '1'")->fetchAllAssociative();
70 | $rootPage = $this->database->executeQuery("SELECT * FROM tl_page WHERE type = 'root' AND fallback = '1' LIMIT 1")->fetchAssociative();
71 | System::loadLanguageFile('countries', $rootPage['language'] ?? 'en');
72 |
73 | foreach ($offers as $offer) {
74 | $locations = StringUtil::deserialize($offer['jobLocation']);
75 | $newLocation = null;
76 | foreach ($locations as $location) {
77 | $locationArr = $this->database->prepare('SELECT * FROM tl_plenta_jobs_basic_job_location WHERE id = ?')->executeQuery([$location])->fetchAssociative();
78 | if ('Telecommute' === $locationArr['jobTypeLocation']) {
79 | $newLocation = null;
80 | break;
81 | }
82 | if (null !== $newLocation) {
83 | continue;
84 | }
85 |
86 | $requirements = StringUtil::deserialize($offer['applicantLocationRequirements']);
87 | if (!\is_array($requirements)) {
88 | $requirements = [];
89 | }
90 |
91 | $requirements = array_filter($requirements, fn ($item) => \in_array($item['key'], GoogleForJobs::ALLOWED_TYPES, true));
92 |
93 | if (empty($requirements)) {
94 | $requirements = [
95 | [
96 | 'key' => 'Country',
97 | 'value' => $GLOBALS['TL_LANG']['CNT'][$locationArr['addressCountry']],
98 | ],
99 | ];
100 | }
101 |
102 | foreach ($requirements as $requirement) {
103 | $remoteLocation = $this->database->prepare('SELECT * FROM tl_plenta_jobs_basic_job_location WHERE jobTypeLocation = ? AND pid = ? AND requirementType = ? AND requirementValue = ?')->executeQuery(['Telecommute', $locationArr['pid'], $requirement['key'], $requirement['value']])->fetchAssociative();
104 | if ($remoteLocation) {
105 | $newLocation = $remoteLocation['id'];
106 | } else {
107 | $this->database->insert('tl_plenta_jobs_basic_job_location', [
108 | 'pid' => $locationArr['pid'],
109 | 'requirementType' => $requirement['key'],
110 | 'requirementValue' => $requirement['value'],
111 | 'tstamp' => time(),
112 | 'jobTypeLocation' => 'Telecommute',
113 | ]);
114 | $newLocation = $this->database->lastInsertId();
115 | }
116 | $locations[] = $newLocation;
117 | }
118 | }
119 |
120 | if (null !== $newLocation) {
121 | $this->database->update(
122 | 'tl_plenta_jobs_basic_offer',
123 | [
124 | 'isRemote' => 0,
125 | 'applicantLocationRequirements' => null,
126 | 'jobLocation' => serialize($locations),
127 | ],
128 | [
129 | 'id' => $offer['id'],
130 | ]
131 | );
132 | }
133 | }
134 |
135 | return new MigrationResult(true, 'Remote jobs have successfully been migrated to having designated remote job locations.');
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/src/Helper/EmploymentType.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\Helper;
14 |
15 | use Composer\InstalledVersions;
16 | use Plenta\ContaoJobsBasic\Contao\Model\PlentaJobsBasicSettingsEmploymentTypeModel;
17 | use Symfony\Component\HttpFoundation\RequestStack;
18 | use Symfony\Contracts\Translation\LocaleAwareInterface;
19 |
20 | class EmploymentType
21 | {
22 | protected LocaleAwareInterface $translator;
23 | protected RequestStack $requestStack;
24 | protected string $customEmploymentTypePrefix = 'CUSTOM_';
25 | private ?array $customEmploymentTypes = null;
26 |
27 | public function __construct(
28 | LocaleAwareInterface $translator,
29 | RequestStack $requestStack
30 | ) {
31 | $this->translator = $translator;
32 | $this->requestStack = $requestStack;
33 | }
34 |
35 | public function getGoogleForJobsEmploymentTypes(): array
36 | {
37 | return [
38 | 'FULL_TIME',
39 | 'PART_TIME',
40 | 'CONTRACTOR',
41 | 'TEMPORARY',
42 | 'INTERN',
43 | 'VOLUNTEER',
44 | 'PER_DIEM',
45 | 'OTHER',
46 | ];
47 | }
48 |
49 | public function getCustomEmploymentTypes(): array
50 | {
51 | $customEmploymentTypes = $this->getAndSetCustomEmploymentTypes();
52 | $return = [];
53 |
54 | if (empty($customEmploymentTypes)) {
55 | return $return;
56 | }
57 |
58 | foreach ($customEmploymentTypes as $customEmploymentType) {
59 | $return[] = $this->customEmploymentTypePrefix.$customEmploymentType->id;
60 | }
61 |
62 | return $return;
63 | }
64 |
65 | /**
66 | * @return PlentaJobsBasicSettingsEmploymentTypeModel[]
67 | */
68 | public function getAndSetCustomEmploymentTypes()
69 | {
70 | if (null === $this->customEmploymentTypes) {
71 | $employmentTypes = [];
72 | $objEmploymentTypes = PlentaJobsBasicSettingsEmploymentTypeModel::findAll();
73 | if ($objEmploymentTypes) {
74 | foreach ($objEmploymentTypes as $employmentType) {
75 | $employmentTypes[$employmentType->id] = $employmentType;
76 | }
77 | }
78 | $this->customEmploymentTypes = $employmentTypes;
79 | }
80 |
81 | return $this->customEmploymentTypes;
82 | }
83 |
84 | /**
85 | * @return string[]
86 | */
87 | public function getEmploymentTypes(): array
88 | {
89 | return array_merge(
90 | $this->getGoogleForJobsEmploymentTypes(),
91 | $this->getCustomEmploymentTypes()
92 | );
93 | }
94 |
95 | public function getEmploymentTypeName(string $employmentType): ?string
96 | {
97 | if (0 === strpos($employmentType, $this->customEmploymentTypePrefix)) {
98 | $translation = $this->getEmploymentTypeNameFromDatabase($employmentType);
99 | } else {
100 | $translation = $this->getEmploymentTypeNameFromTranslator($employmentType);
101 | }
102 |
103 | return $translation;
104 | }
105 |
106 | public function getEmploymentTypeNameFromDatabase(string $identifier): ?string
107 | {
108 | /** @var PlentaJobsBasicSettingsEmploymentTypeModel[] $employmentTypes */
109 | $employmentTypes = $this->getAndSetCustomEmploymentTypes();
110 |
111 | if (empty($employmentTypes)) {
112 | return null;
113 | }
114 |
115 | $employmentTypeId = str_replace($this->customEmploymentTypePrefix, '', $identifier);
116 |
117 | if (false === isset($employmentTypes[$employmentTypeId])) {
118 | return null;
119 | }
120 |
121 | $request = $this->requestStack->getCurrentRequest();
122 |
123 | $language = substr(
124 | $request->getLocale(),
125 | 0,
126 | 2
127 | );
128 |
129 | if (!is_null($employmentTypes[$employmentTypeId]->translation) &&
130 | false === empty($translatedTitle = json_decode($employmentTypes[$employmentTypeId]->translation, true)[$language]['title'] ?? null)) {
131 | $translation = $translatedTitle;
132 | } else {
133 | $translation = $employmentTypes[$employmentTypeId]->title;
134 | }
135 |
136 | return $translation;
137 | }
138 |
139 | public function getEmploymentTypeNameFromTranslator(string $employmentType): ?string
140 | {
141 | $translation = $this->translator->trans(
142 | 'MSC.PLENTA_JOBS.'.$employmentType,
143 | [],
144 | 'contao_default'
145 | );
146 |
147 | if ($translation === 'MSC.PLENTA_JOBS.'.$employmentType) {
148 | return null;
149 | }
150 |
151 | return $translation;
152 | }
153 |
154 | public function getEmploymentTypesFormatted(?array $employmentTypes): string
155 | {
156 | if (null === $employmentTypes) {
157 | return '';
158 | }
159 |
160 | $employmentTypesTemp = [];
161 |
162 | foreach ($employmentTypes as $employmentType) {
163 | $employmentTypesTemp[] = $this->getEmploymentTypeName($employmentType);
164 | }
165 |
166 | return implode(', ', array_filter($employmentTypesTemp));
167 | }
168 |
169 | public function getCustomEmploymentTypePrefix(): string
170 | {
171 | return $this->customEmploymentTypePrefix;
172 | }
173 |
174 | public function getMappedEmploymentTypesForGoogleForJobs(array $employmentTypesUnmapped): array
175 | {
176 | $employmentTypes = $this->getAndSetCustomEmploymentTypes();
177 | $return = [];
178 |
179 | foreach ($employmentTypesUnmapped as $employmentTypeUnmapped) {
180 | if (0 === strpos($employmentTypeUnmapped, $this->customEmploymentTypePrefix)) {
181 | $employmentTypeId = str_replace($this->customEmploymentTypePrefix, '', $employmentTypeUnmapped);
182 |
183 | if (false === isset($employmentTypes[$employmentTypeId])) {
184 | continue;
185 | }
186 |
187 | $return[] = $employmentTypes[$employmentTypeId]->google_for_jobs_mapping;
188 | } else {
189 | $return[] = $employmentTypeUnmapped;
190 | }
191 | }
192 |
193 | return array_unique($return);
194 | }
195 | }
196 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/src/Helper/MetaFieldsHelper.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\Helper;
14 |
15 | use Contao\CoreBundle\File\Metadata;
16 | use Contao\CoreBundle\Image\Studio\Studio;
17 | use Contao\CoreBundle\InsertTag\InsertTagParser;
18 | use Contao\Date;
19 | use Contao\FilesModel;
20 | use Contao\Frontend;
21 | use Contao\FrontendTemplate;
22 | use Contao\StringUtil;
23 | use Contao\System;
24 | use Plenta\ContaoJobsBasic\Contao\Model\PlentaJobsBasicJobLocationModel;
25 | use Plenta\ContaoJobsBasic\Contao\Model\PlentaJobsBasicOfferModel;
26 | use Plenta\ContaoJobsBasic\Contao\Model\PlentaJobsBasicOrganizationModel;
27 | use Symfony\Component\HttpFoundation\RequestStack;
28 | use Symfony\Component\Intl\Countries;
29 | use Symfony\Contracts\Translation\TranslatorInterface;
30 |
31 | class MetaFieldsHelper
32 | {
33 | public function __construct(
34 | protected EmploymentType $employmentTypeHelper,
35 | protected RequestStack $requestStack,
36 | protected InsertTagParser $insertTagParser,
37 | protected TranslatorInterface $translator,
38 | ) {
39 | }
40 |
41 | public function getMetaFields(PlentaJobsBasicOfferModel $jobOffer, $imageSize = null): array
42 | {
43 | $metaFields = [];
44 |
45 | $translation = $jobOffer->getTranslation($this->requestStack->getCurrentRequest()->getLocale());
46 |
47 | $metaFields['publicationDateFormatted'] = Date::parse(Date::getNumericDateFormat(), $jobOffer->datePosted);
48 | $metaFields['employmentTypeFormatted'] = $this->employmentTypeHelper->getEmploymentTypesFormatted(json_decode($jobOffer->employmentType, true));
49 | $metaFields['locationFormatted'] = $this->formatLocation($jobOffer);
50 | $metaFields['addressLocalityFormatted'] = $this->formatAddressLocality($jobOffer);
51 | $metaFields['addressCountryFormatted'] = $this->formatAddressCountry($jobOffer);
52 | $metaFields['title'] = $this->insertTagParser->replace($translation['title'] ?? $jobOffer->title);
53 | $metaFields['description'] = $this->insertTagParser->replace(StringUtil::restoreBasicEntities($translation['description'] ?? $jobOffer->description));
54 | $metaFields['alias'] = $translation['alias'] ?? $jobOffer->alias;
55 | $metaFields['company'] = $this->formatCompany($jobOffer);
56 | $metaFields['teaser'] = $this->insertTagParser->replace($translation['teaser'] ?? $jobOffer->teaser ?? '');
57 |
58 | if ($jobOffer->entryDate) {
59 | $metaFields['entryDateFormatted'] = Date::parse(Date::getNumericDateFormat(), $jobOffer->entryDate);
60 | } elseif ($jobOffer->startNow) {
61 | $metaFields['entryDateFormatted'] = $this->translator->trans('MSC.PLENTA_JOBS.startNow', [], 'contao_default');
62 | }
63 |
64 | if ($imageSize && $jobOffer->addImage) {
65 | $file = FilesModel::findByUuid(StringUtil::binToUuid($jobOffer->singleSRC));
66 | if ($file) {
67 | $tpl = new FrontendTemplate('jobs_basic_reader_parts/plenta_jobs_basic_reader_image');
68 | $meta = [];
69 | if ($jobOffer->overwriteMeta) {
70 | $request = System::getContainer()->get('request_stack')->getCurrentRequest();
71 | if ($request && System::getContainer()->get('contao.routing.scope_matcher')->isBackendRequest($request)) {
72 | $language = $request->getLocale();
73 | } else {
74 | global $objPage;
75 | $language = $objPage->language;
76 | }
77 | $arrMeta = Frontend::getMetaData($file->meta, $language);
78 | $meta = [
79 | 'alt' => $jobOffer->alt ?: ($arrMeta['alt'] ?? ''),
80 | 'imageTitle' => $jobOffer->imageTitle ?: ($arrMeta['title'] ?? ''),
81 | 'imageUrl' => $jobOffer->imageUrl ?: ($arrMeta['link'] ?? ''),
82 | 'caption' => $jobOffer->caption ?: ($arrMeta['caption'] ?? ''),
83 | ];
84 | }
85 | $data = [
86 | 'id' => null,
87 | 'singleSRC' => $jobOffer->singleSRC,
88 | 'sortBy' => 'custom',
89 | 'fullsize' => false,
90 | 'size' => $imageSize,
91 | ];
92 |
93 | if (!empty($meta)) {
94 | $data['overwriteMeta'] = true;
95 | $data['alt'] = $meta['alt'] ?? '';
96 | $data['imageTitle'] = $meta['imageTitle'] ?? '';
97 | $data['imageUrl'] = $meta['imageUrl'] ?? '';
98 | $data['caption'] = $meta['caption'] ?? '';
99 | }
100 |
101 | $tpl->data = $data;
102 |
103 | $metaFields['image'] = $tpl->parse();
104 | }
105 | }
106 |
107 | if (!isset($metaFields['image'])) {
108 | $metaFields['image'] = '';
109 | }
110 |
111 | return $metaFields;
112 | }
113 |
114 | public function formatLocation(PlentaJobsBasicOfferModel $jobOffer): string
115 | {
116 | return $this->formatAddressLocality($jobOffer);
117 | }
118 |
119 | public function formatAddressLocality(PlentaJobsBasicOfferModel $jobOffer): string
120 | {
121 | $locationsTemp = [];
122 |
123 | $locations = StringUtil::deserialize($jobOffer->jobLocation);
124 |
125 | foreach ($locations as $location) {
126 | $objLocation = PlentaJobsBasicJobLocationModel::findByPk($location);
127 | $name = 'onPremise' === $objLocation->jobTypeLocation ? $objLocation->addressLocality : ('Country' === $objLocation->requirementType ? $objLocation->requirementValue : null);
128 | if (!\in_array($name, $locationsTemp, true)) {
129 | $locationsTemp[] = $name;
130 | }
131 | }
132 |
133 | return implode(', ', $locationsTemp);
134 | }
135 |
136 | public function formatAddressCountry(PlentaJobsBasicOfferModel $jobOffer): string
137 | {
138 | $countriesTemp = [];
139 | $locations = StringUtil::deserialize($jobOffer->jobLocation);
140 |
141 | System::loadLanguageFile('countries');
142 |
143 | foreach ($locations as $location) {
144 | $objLocation = PlentaJobsBasicJobLocationModel::findByPk($location);
145 | $name = 'onPremise' === $objLocation->jobTypeLocation ? Countries::getName($objLocation->addressCountry) : ('Country' === $objLocation->requirementType ? $objLocation->requirementValue : null);
146 | if ($name && !\in_array($name, $countriesTemp, true)) {
147 | $countriesTemp[] = $name;
148 | }
149 | }
150 |
151 | return implode(', ', $countriesTemp);
152 | }
153 |
154 | public function formatCompany(PlentaJobsBasicOfferModel $jobOffer): string
155 | {
156 | $company = [];
157 | $locations = StringUtil::deserialize($jobOffer->jobLocation);
158 | foreach ($locations as $location) {
159 | $objLocation = PlentaJobsBasicJobLocationModel::findByPk($location);
160 | if (!\in_array($objLocation->pid, $company, true)) {
161 | $company[$objLocation->pid] = PlentaJobsBasicOrganizationModel::findByPk($objLocation->pid)->name;
162 | }
163 | }
164 |
165 | return implode(', ', $company);
166 | }
167 |
168 | public function formatAddressLocalityTitle(PlentaJobsBasicOfferModel $jobOffer): string
169 | {
170 | $locationsTemp = [];
171 |
172 | $locations = StringUtil::deserialize($jobOffer->jobLocation);
173 |
174 | foreach ($locations as $location) {
175 | $objLocation = PlentaJobsBasicJobLocationModel::findByPk($location);
176 | $name = 'onPremise' === $objLocation->jobTypeLocation ? $objLocation->title : '';
177 | if (!\in_array($name, $locationsTemp, true)) {
178 | $locationsTemp[] = $name;
179 | }
180 | }
181 |
182 | return implode(', ', $locationsTemp);
183 | }
184 | }
185 |
--------------------------------------------------------------------------------
/contao/dca/tl_plenta_jobs_basic_job_location.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | use Contao\DC_Table;
14 | use Contao\System;
15 | use Plenta\ContaoJobsBasic\EventListener\Contao\DCA\TlPlentaJobsBasicJobLocation;
16 | use Plenta\ContaoJobsBasic\GoogleForJobs\GoogleForJobs;
17 |
18 | $GLOBALS['TL_DCA']['tl_plenta_jobs_basic_job_location'] = [
19 | // Config
20 | 'config' => [
21 | 'dataContainer' => DC_Table::class,
22 | 'ptable' => 'tl_plenta_jobs_basic_organization',
23 | 'switchToEdit' => true,
24 | 'enableVersioning' => true,
25 | 'sql' => [
26 | 'keys' => [
27 | 'id' => 'primary',
28 | ],
29 | ],
30 | ],
31 |
32 | 'list' => [
33 | 'sorting' => [
34 | 'mode' => 4,
35 | 'flag' => 1,
36 | 'fields' => ['pid'],
37 | 'headerFields' => ['name'],
38 | 'child_record_callback' => [TlPlentaJobsBasicJobLocation::class, 'listLocations'],
39 | 'child_record_class' => 'no_padding',
40 | 'panelLayout' => 'filter;sort,search,limit',
41 | 'disableGrouping' => true,
42 | ],
43 | 'label' => [
44 | 'fields' => [
45 | 'streetAddress',
46 | 'postalCode',
47 | 'addressLocality',
48 | ],
49 | 'format' => '%s, %s %s',
50 | ],
51 | 'global_operations' => [
52 | 'all' => [
53 | 'href' => 'act=select',
54 | 'class' => 'header_edit_all',
55 | 'attributes' => 'onclick="Backend.getScrollOffset()" accesskey="e"',
56 | ],
57 | ],
58 | 'operations' => [
59 | 'edit' => [
60 | 'label' => &$GLOBALS['TL_LANG']['tl_jobs_product_config']['edit'],
61 | 'href' => 'act=edit',
62 | 'icon' => 'edit.svg',
63 | ],
64 | 'delete' => [
65 | 'href' => 'act=delete',
66 | 'icon' => 'delete.svg',
67 | 'attributes' => 'onclick="if(!confirm(\''.($GLOBALS['TL_LANG']['MSC']['deleteConfirm'] ?? null).'\'))return false;Backend.getScrollOffset()"',
68 | ],
69 | 'show' => [
70 | 'href' => 'act=show',
71 | 'icon' => 'show.svg',
72 | ],
73 | ],
74 | ],
75 |
76 | // Palettes
77 | 'palettes' => [
78 | '__selector__' => ['jobTypeLocation'],
79 | 'default' => '{type_legend},jobTypeLocation,title',
80 | 'onPremise' => '{type_legend},jobTypeLocation,title;{address_legend},streetAddress,postalCode,addressLocality,addressRegion,addressCountry',
81 | 'Telecommute' => '{type_legend},jobTypeLocation,title;{location_legend},requirementType,requirementValue',
82 | ],
83 |
84 | // Fields
85 | 'fields' => [
86 | 'id' => [
87 | 'search' => true,
88 | 'sql' => [
89 | 'type' => 'integer',
90 | 'unsigned' => true,
91 | 'autoincrement' => true,
92 | ],
93 | ],
94 | 'pid' => [
95 | 'foreignKey' => 'tl_plenta_jobs_basic_organization.name',
96 | 'relation' => [
97 | 'type' => 'belongsTo',
98 | 'load' => 'lazy',
99 | ],
100 | 'sql' => [
101 | 'type' => 'integer',
102 | 'unsigned' => true,
103 | 'notnull' => false,
104 | ],
105 | ],
106 | 'tstamp' => [
107 | 'sorting' => true,
108 | 'flag' => 6,
109 | 'sql' => [
110 | 'type' => 'integer',
111 | 'unsigned' => true,
112 | 'default' => 0,
113 | ],
114 | ],
115 | 'streetAddress' => [
116 | 'exclude' => true,
117 | 'search' => true,
118 | 'inputType' => 'text',
119 | 'default' => '',
120 | 'eval' => [
121 | 'maxlength' => 255,
122 | 'tl_class' => 'w50',
123 | ],
124 | 'sql' => [
125 | 'type' => 'string',
126 | 'length' => 255,
127 | 'default' => '',
128 | ],
129 | ],
130 | 'postalCode' => [
131 | 'exclude' => true,
132 | 'search' => true,
133 | 'inputType' => 'text',
134 | 'default' => '',
135 | 'eval' => [
136 | 'mandatory' => true,
137 | 'maxlength' => 32,
138 | 'tl_class' => 'w50 clr',
139 | ],
140 | 'sql' => [
141 | 'type' => 'string',
142 | 'length' => 32,
143 | 'default' => '',
144 | ],
145 | ],
146 | 'addressLocality' => [
147 | 'exclude' => true,
148 | 'sorting' => true,
149 | 'search' => true,
150 | 'flag' => 5,
151 | 'inputType' => 'text',
152 | 'default' => '',
153 | 'eval' => [
154 | 'mandatory' => true,
155 | 'maxlength' => 255,
156 | 'tl_class' => 'w50',
157 | ],
158 | 'sql' => [
159 | 'type' => 'string',
160 | 'length' => 255,
161 | 'default' => '',
162 | ],
163 | ],
164 | 'addressRegion' => [
165 | 'exclude' => true,
166 | 'search' => true,
167 | 'inputType' => 'text',
168 | 'default' => '',
169 | 'eval' => [
170 | 'maxlength' => 255,
171 | 'tl_class' => 'w50 clr',
172 | ],
173 | 'sql' => [
174 | 'type' => 'string',
175 | 'length' => 255,
176 | 'default' => '',
177 | ],
178 | ],
179 | 'addressCountry' => [
180 | 'exclude' => true,
181 | 'inputType' => 'select',
182 | 'options' => System::getContainer()->get('contao.intl.countries')->getCountries(),
183 | 'eval' => [
184 | 'mandatory' => true,
185 | 'includeBlankOption' => true,
186 | 'chosen' => true,
187 | 'tl_class' => 'w50',
188 | ],
189 | 'sql' => [
190 | 'type' => 'string',
191 | 'length' => 2,
192 | 'default' => '',
193 | ],
194 | ],
195 | 'jobTypeLocation' => [
196 | 'exclude' => true,
197 | 'inputType' => 'select',
198 | 'options' => ['onPremise', 'Telecommute'],
199 | 'reference' => &$GLOBALS['TL_LANG']['tl_plenta_jobs_basic_job_location']['jobTypeLocationOptions'],
200 | 'eval' => [
201 | 'submitOnChange' => true,
202 | ],
203 | 'sql' => [
204 | 'type' => 'string',
205 | 'length' => 32,
206 | 'default' => 'onPremise',
207 | ],
208 | ],
209 | 'requirementType' => [
210 | 'exclude' => true,
211 | 'inputType' => 'select',
212 | 'options' => GoogleForJobs::ALLOWED_TYPES,
213 | 'eval' => ['tl_class' => 'w50', 'mandatory' => true],
214 | 'reference' => &$GLOBALS['TL_LANG']['tl_plenta_jobs_basic_job_location']['administrativeAreas'],
215 | 'sql' => [
216 | 'type' => 'string',
217 | 'length' => 32,
218 | 'default' => '',
219 | ],
220 | ],
221 | 'requirementValue' => [
222 | 'exclude' => true,
223 | 'inputType' => 'text',
224 | 'eval' => ['mandatory' => true, 'tl_class' => 'w50'],
225 | 'sql' => [
226 | 'type' => 'string',
227 | 'length' => 255,
228 | 'default' => '',
229 | ],
230 | ],
231 | 'title' => [
232 | 'exclude' => true,
233 | 'search' => true,
234 | 'inputType' => 'text',
235 | 'eval' => ['tl_class' => 'w50'],
236 | 'sql' => [
237 | 'type' => 'string',
238 | 'length' => 255,
239 | 'default' => '',
240 | ],
241 | ],
242 | ],
243 | ];
244 |
--------------------------------------------------------------------------------
/src/EventListener/Contao/DCA/TlPlentaJobsBasicOffer.php:
--------------------------------------------------------------------------------
1 |
10 | * @link https://github.com/plenta/
11 | */
12 |
13 | namespace Plenta\ContaoJobsBasic\EventListener\Contao\DCA;
14 |
15 | use Composer\InstalledVersions;
16 | use Contao\CoreBundle\Slug\Slug;
17 | use Contao\CoreBundle\Util\PackageUtil;
18 | use Contao\DataContainer;
19 | use Contao\Input;
20 | use Contao\Message;
21 | use Contao\PageModel;
22 | use Contao\StringUtil;
23 | use Contao\System;
24 | use Exception;
25 | use Plenta\ContaoJobsBasic\Contao\Model\PlentaJobsBasicJobLocationModel;
26 | use Plenta\ContaoJobsBasic\Contao\Model\PlentaJobsBasicOfferModel;
27 | use Plenta\ContaoJobsBasic\Helper\EmploymentType;
28 | use Plenta\ContaoJobsBasic\Helper\NumberHelper;
29 | use Symfony\Component\HttpFoundation\RequestStack;
30 | use Twig\Environment as TwigEnvironment;
31 |
32 | class TlPlentaJobsBasicOffer
33 | {
34 | protected EmploymentType $employmentTypeHelper;
35 |
36 | protected Slug $slugGenerator;
37 |
38 | protected RequestStack $requestStack;
39 |
40 | protected TwigEnvironment $twig;
41 |
42 | public function __construct(
43 | EmploymentType $employmentTypeHelper,
44 | Slug $slugGenerator,
45 | RequestStack $requestStack,
46 | TwigEnvironment $twig
47 | ) {
48 | $this->employmentTypeHelper = $employmentTypeHelper;
49 | $this->slugGenerator = $slugGenerator;
50 | $this->requestStack = $requestStack;
51 | $this->twig = $twig;
52 | }
53 |
54 | /**
55 | * @param mixed $varValue
56 | *
57 | * @throws Exception
58 | */
59 | public function aliasSaveCallback($varValue, DataContainer $dc): string
60 | {
61 | $lang = null;
62 |
63 | if ('alias' === $dc->inputName) {
64 | $title = $dc->activeRecord->title;
65 | $aliasExists = fn (string $alias): bool => PlentaJobsBasicOfferModel::doesAliasExist($alias, (int) $dc->activeRecord->id);
66 | } else {
67 | $index = str_replace('translations__alias__', '', $dc->inputName);
68 | $title = Input::post('translations__title__'.$index);
69 | $lang = Input::post('translations__language__'.$index);
70 | $aliasExists = fn (string $alias): bool => PlentaJobsBasicOfferModel::doesAliasExist($alias) || PlentaJobsBasicOfferModel::doesAliasExist($alias, (int) $dc->activeRecord->id, $lang);
71 | }
72 |
73 | if (empty($varValue)) {
74 | $rootPages = PageModel::findPublishedRootPages();
75 | $fallback = null;
76 | $rootPage = null;
77 |
78 | foreach ($rootPages as $rootP) {
79 | if ($rootP->fallback) {
80 | $fallback = $rootP->id;
81 | }
82 |
83 | if ($dc->inputName === 'alias' && $rootP->fallback) {
84 | $rootPage = $fallback;
85 | break;
86 | }
87 |
88 | if ($rootP->language === $lang) {
89 | $rootPage = $rootP->id;
90 | break;
91 | }
92 | }
93 |
94 | if (empty($rootPage)) {
95 | $rootPage = $fallback;
96 | }
97 |
98 | $varValue = $this->slugGenerator->generate(
99 | $title,
100 | $rootPage,
101 | $aliasExists
102 | );
103 | } elseif (preg_match('/^[1-9]\d*$/', $varValue)) {
104 | throw new Exception(sprintf($GLOBALS['TL_LANG']['ERR']['aliasNumeric'], $varValue));
105 | } elseif ($aliasExists($varValue)) {
106 | throw new Exception(sprintf($GLOBALS['TL_LANG']['ERR']['aliasExists'], $varValue));
107 | }
108 |
109 | return $varValue;
110 | }
111 |
112 | public function jobLocationOptionsCallback(): array
113 | {
114 | $jobLocations = PlentaJobsBasicJobLocationModel::findAll();
115 |
116 | $return = [];
117 | foreach ($jobLocations as $jobLocation) {
118 | $return[$jobLocation->id] = $jobLocation->getRelated('pid')->name.': ';
119 |
120 | if ($jobLocation->title) {
121 | $return[$jobLocation->id] .= $jobLocation->title;
122 | }
123 | elseif ('onPremise' === $jobLocation->jobTypeLocation) {
124 | $return[$jobLocation->id] .= $jobLocation->streetAddress;
125 |
126 | if ('' !== $jobLocation->addressLocality) {
127 | $return[$jobLocation->id] .= ($jobLocation->streetAddress ? ', ' : '').$jobLocation->addressLocality;
128 | }
129 | } else {
130 | $return[$jobLocation->id] .= $GLOBALS['TL_LANG']['MSC']['PLENTA_JOBS']['remote'].' ['.$jobLocation->requirementValue.']';
131 | }
132 | }
133 |
134 | return $return;
135 | }
136 |
137 | public function employmentTypeOptionsCallback(): array
138 | {
139 | $employmentTypes = $this->employmentTypeHelper->getEmploymentTypes();
140 |
141 | $return = [];
142 | foreach ($employmentTypes as $employmentType) {
143 | $return[$employmentType] = $this->employmentTypeHelper->getEmploymentTypeName($employmentType);
144 | }
145 |
146 | return $return;
147 | }
148 |
149 | public function employmentTypeSaveCallback($value, DataContainer $dc): string
150 | {
151 | $value = StringUtil::deserialize($value);
152 |
153 | return json_encode($value);
154 | }
155 |
156 | public function employmentTypeLoadCallback($value, DataContainer $dc): string
157 | {
158 | if (null === $value) {
159 | return serialize([]);
160 | }
161 |
162 | return serialize(json_decode($value));
163 | }
164 |
165 | public function saveCallbackGlobal(DataContainer $dc): void
166 | {
167 | // Front end call
168 | if (!$dc instanceof DataContainer) {
169 | return;
170 | }
171 |
172 | if (!$dc->activeRecord) {
173 | return;
174 | }
175 |
176 | if (null === $dc->activeRecord->datePosted && !empty(Input::post('published'))) {
177 | $offer = PlentaJobsBasicOfferModel::findByPk($dc->activeRecord->id);
178 | $offer->datePosted = time();
179 | $offer->save();
180 | }
181 | }
182 |
183 | public function salaryOnLoad($value, DataContainer $dc): string
184 | {
185 | $numberHelper = new NumberHelper($dc->activeRecord->salaryCurrency, $this->requestStack->getCurrentRequest()->getLocale());
186 | $value = $numberHelper->formatNumberFromDbForDCAField((string) $value);
187 |
188 | return $value;
189 | }
190 |
191 | public function salaryOnSave($value, DataContainer $dc): int
192 | {
193 | $numberHelper = new NumberHelper($dc->activeRecord->salaryCurrency, $this->requestStack->getCurrentRequest()->getLocale());
194 |
195 | return $numberHelper->reformatDecimalForDb($value);
196 | }
197 |
198 | public function onShowInfoCallback(DataContainer $dc = null): void
199 | {
200 | $GLOBALS['TL_CSS'][] = 'bundles/plentacontaojobsbasic/dashboard.css';
201 | $info = $this->twig->render('@PlentaContaoJobsBasic/be_plenta_info.html.twig', [
202 | 'version' => InstalledVersions::getVersion('plenta/contao-jobs-basic-bundle'),
203 | ]);
204 |
205 | Message::addRaw($info);
206 | }
207 |
208 | public function getLanguages(): array
209 | {
210 | return System::getContainer()->get('contao.intl.locales')->getLanguages();
211 | }
212 |
213 | public function labelCallback(array $row, string $label, DataContainer $dc, array $labels): string
214 | {
215 | $jobLocations = [];
216 | $locations = $this->jobLocationOptionsCallback();
217 | $locationsArr = StringUtil::deserialize($row['jobLocation']);
218 | foreach ($locationsArr as $location) {
219 | $jobLocations[] = $locations[$location];
220 | }
221 |
222 | $jobEmploymentTypes = [];
223 | $employmentTypes = $this->employmentTypeOptionsCallback();
224 | $typesArr = StringUtil::deserialize($this->employmentTypeLoadCallback($row['employmentType'], $dc));
225 | foreach ($typesArr as $type) {
226 | $jobEmploymentTypes[] = $employmentTypes[$type];
227 | }
228 |
229 | $label = ''.$row['title'].'
';
230 | $label .= implode(' | ', $jobLocations);
231 | $label .= ' | '.implode(', ', $jobEmploymentTypes);
232 |
233 | return $label;
234 | }
235 |
236 | public function getSerpUrl(PlentaJobsBasicOfferModel $offer): string
237 | {
238 | $strSuffix = '/';
239 |
240 | return sprintf(preg_replace('/%(?!s)/', '%%', $strSuffix), $offer->alias ?: $offer->id);
241 | }
242 | }
243 |
--------------------------------------------------------------------------------