Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f93ed0d0c6 | |||
| a6440c74a2 |
@@ -1,216 +0,0 @@
|
|||||||
Create a Contao 5.7 bundle (PHP 8.4)
|
|
||||||
|
|
||||||
Vendor: MummertMedia
|
|
||||||
Bundle: EventManagerBundle
|
|
||||||
Composer name: mummert-media/eventmanager-bundle
|
|
||||||
Type: contao-bundle
|
|
||||||
|
|
||||||
This bundle is internal only.
|
|
||||||
|
|
||||||
IMPORTANT:
|
|
||||||
Only implement backend functionality.
|
|
||||||
No frontend logic.
|
|
||||||
No controllers.
|
|
||||||
No services.
|
|
||||||
No access logic.
|
|
||||||
No event listeners.
|
|
||||||
No future planning.
|
|
||||||
Only backend entities and relations.
|
|
||||||
|
|
||||||
====================================================
|
|
||||||
GOAL
|
|
||||||
====================================================
|
|
||||||
|
|
||||||
Extend Contao backend with:
|
|
||||||
|
|
||||||
1) New entity: tl_organization named Organisationen, just german translation
|
|
||||||
2) New entity: tl_location named Veranstaltungsorte, just german translation
|
|
||||||
3) Join table: tl_member_organization
|
|
||||||
4) Join table: tl_calendar_events_organization
|
|
||||||
5) Extend tl_calendar_events with:
|
|
||||||
- location_id (exactly one location) with dropdown in Backend (title ASC)
|
|
||||||
- organization assignment (multiple via join table)
|
|
||||||
6) Extend tl_member so that:
|
|
||||||
- A member can belong to multiple organizations
|
|
||||||
7) Extend tl_organization so that:
|
|
||||||
- Multiple members can be assigned to it
|
|
||||||
|
|
||||||
Everything manageable via Contao backend only.
|
|
||||||
|
|
||||||
====================================================
|
|
||||||
DATABASE TABLES
|
|
||||||
====================================================
|
|
||||||
|
|
||||||
Use Contao DCA SQL definitions (DBAL 4 compatible).
|
|
||||||
Do NOT use serialized fields.
|
|
||||||
Do NOT use string length values like "255".
|
|
||||||
Use integer values.
|
|
||||||
|
|
||||||
------------------------------------
|
|
||||||
tl_organization + add german translation
|
|
||||||
------------------------------------
|
|
||||||
|
|
||||||
id int unsigned auto_increment
|
|
||||||
pid int unsigned default 0
|
|
||||||
tstamp int unsigned default 0
|
|
||||||
dateAdded int unsigned default 0
|
|
||||||
title varchar(255)
|
|
||||||
alias varchar(128)
|
|
||||||
logo binary(16) nullable
|
|
||||||
street varchar(255)
|
|
||||||
street2 varchar(255)
|
|
||||||
postal varchar(255)
|
|
||||||
city varchar(255)
|
|
||||||
state varchar(255)
|
|
||||||
country varchar(2)
|
|
||||||
phone varchar(64)
|
|
||||||
email varchar(255)
|
|
||||||
website varchar(255)
|
|
||||||
lat decimal(10,8)
|
|
||||||
lng decimal(11,8)
|
|
||||||
description text nullable
|
|
||||||
published char(1)
|
|
||||||
isExternal char(1)
|
|
||||||
type varchar(32)
|
|
||||||
|
|
||||||
------------------------------------
|
|
||||||
tl_location + add german translation
|
|
||||||
------------------------------------
|
|
||||||
|
|
||||||
id int unsigned auto_increment
|
|
||||||
tstamp int unsigned default 0
|
|
||||||
dateAdded int unsigned default 0
|
|
||||||
title varchar(255)
|
|
||||||
alias varchar(128)
|
|
||||||
description text nullable
|
|
||||||
street varchar(255)
|
|
||||||
street2 varchar(255)
|
|
||||||
postal varchar(255)
|
|
||||||
city varchar(255)
|
|
||||||
state varchar(255)
|
|
||||||
country varchar(2) default 'de'
|
|
||||||
lat decimal(10,8)
|
|
||||||
lng decimal(11,8)
|
|
||||||
image binary(16) nullable
|
|
||||||
published char(1)
|
|
||||||
|
|
||||||
------------------------------------
|
|
||||||
tl_member_organization
|
|
||||||
------------------------------------
|
|
||||||
|
|
||||||
id int unsigned auto_increment
|
|
||||||
tstamp int unsigned default 0
|
|
||||||
member_id int unsigned
|
|
||||||
organization_id int unsigned
|
|
||||||
|
|
||||||
------------------------------------
|
|
||||||
tl_calendar_events_organization
|
|
||||||
------------------------------------
|
|
||||||
|
|
||||||
id int unsigned auto_increment
|
|
||||||
tstamp int unsigned default 0
|
|
||||||
event_id int unsigned
|
|
||||||
organization_id int unsigned
|
|
||||||
|
|
||||||
====================================================
|
|
||||||
DCA REQUIREMENTS
|
|
||||||
====================================================
|
|
||||||
|
|
||||||
1) tl_organization:
|
|
||||||
- Backend list view
|
|
||||||
- Editable fields
|
|
||||||
- Published toggle
|
|
||||||
- Alias field
|
|
||||||
- Type as select field with static options:
|
|
||||||
accommodation, shopping, culture
|
|
||||||
- Add multi-relation field for members
|
|
||||||
(store relation in tl_member_organization)
|
|
||||||
|
|
||||||
2) tl_location:
|
|
||||||
- Backend list view
|
|
||||||
- Editable fields
|
|
||||||
- Published toggle
|
|
||||||
- Alias field
|
|
||||||
|
|
||||||
3) tl_calendar_events:
|
|
||||||
- Add field location_id (select, mandatory)
|
|
||||||
foreignKey: tl_location.title
|
|
||||||
- Add multi-organization relation
|
|
||||||
(store relation in tl_calendar_events_organization)
|
|
||||||
|
|
||||||
4) tl_member:
|
|
||||||
- Add multi-organization relation
|
|
||||||
(store relation in tl_member_organization)
|
|
||||||
|
|
||||||
====================================================
|
|
||||||
RELATION HANDLING
|
|
||||||
====================================================
|
|
||||||
|
|
||||||
Use proper many-to-many handling via join tables.
|
|
||||||
Use relation definitions in DCA.
|
|
||||||
Do NOT use serialized arrays.
|
|
||||||
Do NOT store IDs as comma-separated strings.
|
|
||||||
|
|
||||||
====================================================
|
|
||||||
BACKEND INTEGRATION
|
|
||||||
====================================================
|
|
||||||
|
|
||||||
Register backend modules for:
|
|
||||||
|
|
||||||
- Organisationen
|
|
||||||
- Veranstaltungsorte
|
|
||||||
|
|
||||||
Under backend group content
|
|
||||||
|
|
||||||
|
|
||||||
Use standard Contao backend module registration.
|
|
||||||
|
|
||||||
====================================================
|
|
||||||
PROJECT STRUCTURE
|
|
||||||
====================================================
|
|
||||||
|
|
||||||
Generate:
|
|
||||||
|
|
||||||
src/
|
|
||||||
MummertMediaEventManagerBundle.php
|
|
||||||
DependencyInjection/
|
|
||||||
MummertMediaEventManagerExtension.php
|
|
||||||
Contao/
|
|
||||||
Manager/
|
|
||||||
Plugin.php
|
|
||||||
|
|
||||||
contao/
|
|
||||||
dca/
|
|
||||||
tl_organization.php
|
|
||||||
tl_location.php
|
|
||||||
tl_member.php
|
|
||||||
tl_calendar_events.php
|
|
||||||
config/
|
|
||||||
config.php
|
|
||||||
|
|
||||||
config/
|
|
||||||
services.yaml
|
|
||||||
|
|
||||||
composer.json
|
|
||||||
|
|
||||||
====================================================
|
|
||||||
QUALITY RULES
|
|
||||||
====================================================
|
|
||||||
|
|
||||||
- declare(strict_types=1);
|
|
||||||
- PHP 8.4
|
|
||||||
- DBAL 4 compatible SQL
|
|
||||||
- no deprecated Contao 4 syntax
|
|
||||||
- no frontend logic
|
|
||||||
- no controllers
|
|
||||||
- no services
|
|
||||||
- no placeholders
|
|
||||||
- complete working DCA
|
|
||||||
- ready to install via path repository
|
|
||||||
|
|
||||||
====================================================
|
|
||||||
OUTPUT
|
|
||||||
====================================================
|
|
||||||
|
|
||||||
Generate full file contents for all required files.
|
|
||||||
Code must be production-ready.
|
|
||||||
+10
-3
@@ -9,9 +9,10 @@
|
|||||||
"eventmanager"
|
"eventmanager"
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^8.4",
|
"php": "^8.3",
|
||||||
"contao/core-bundle": "^5.7",
|
"contao/core-bundle": "^5.7",
|
||||||
"contao/manager-plugin": "^2.0"
|
"contao/manager-plugin": "^2.0",
|
||||||
|
"numero2/contao-tags": "^0.5"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
@@ -21,5 +22,11 @@
|
|||||||
"extra": {
|
"extra": {
|
||||||
"contao-manager-plugin": "MummertMedia\\EventManagerBundle\\Contao\\Manager\\Plugin"
|
"contao-manager-plugin": "MummertMedia\\EventManagerBundle\\Contao\\Manager\\Plugin"
|
||||||
},
|
},
|
||||||
"prefer-stable": true
|
"prefer-stable": true,
|
||||||
|
"config": {
|
||||||
|
"allow-plugins": {
|
||||||
|
"contao-components/installer": true,
|
||||||
|
"contao/manager-plugin": true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ $GLOBALS['TL_DCA']['tl_calendar_events']['config']['onload_callback'][] = static
|
|||||||
|
|
||||||
PaletteManipulator::create()
|
PaletteManipulator::create()
|
||||||
->addLegend('organization_legend', 'details_legend', PaletteManipulator::POSITION_AFTER)
|
->addLegend('organization_legend', 'details_legend', PaletteManipulator::POSITION_AFTER)
|
||||||
->addField(['location_id', 'type', 'organizations'], 'organization_legend', PaletteManipulator::POSITION_APPEND)
|
->addField(['location_id', 'tags', 'organizations'], 'organization_legend', PaletteManipulator::POSITION_APPEND)
|
||||||
->applyToPalette((string) $paletteName, 'tl_calendar_events');
|
->applyToPalette((string) $paletteName, 'tl_calendar_events');
|
||||||
|
|
||||||
PaletteManipulator::create()
|
PaletteManipulator::create()
|
||||||
@@ -71,14 +71,18 @@ $GLOBALS['TL_DCA']['tl_calendar_events']['fields']['location_id'] = [
|
|||||||
'sql' => ['type' => 'integer', 'unsigned' => true, 'default' => 0],
|
'sql' => ['type' => 'integer', 'unsigned' => true, 'default' => 0],
|
||||||
];
|
];
|
||||||
|
|
||||||
$GLOBALS['TL_DCA']['tl_calendar_events']['fields']['type'] = [
|
$GLOBALS['TL_DCA']['tl_calendar_events']['fields']['tags'] = [
|
||||||
'label' => &$GLOBALS['TL_LANG']['tl_calendar_events']['type'],
|
'label' => &$GLOBALS['TL_LANG']['tl_calendar_events']['tags'],
|
||||||
'exclude' => true,
|
'exclude' => true,
|
||||||
|
'filter' => true,
|
||||||
'inputType' => 'select',
|
'inputType' => 'select',
|
||||||
'options' => ['accommodation', 'shopping', 'culture'],
|
'foreignKey' => 'tl_tags.tag',
|
||||||
'reference' => &$GLOBALS['TL_LANG']['tl_calendar_events']['type_options'],
|
'options_callback' => ['numero2_tags.listener.data_container.tags', 'getTagOptions'],
|
||||||
'eval' => ['multiple' => true, 'chosen' => true, 'includeBlankOption' => false, 'tl_class' => 'w50'],
|
'load_callback' => [['numero2_tags.listener.data_container.tags', 'loadTags']],
|
||||||
|
'save_callback' => [['numero2_tags.listener.data_container.tags', 'saveTags']],
|
||||||
|
'eval' => ['multiple' => true, 'size' => 8, 'tl_class' => 'w50 tags', 'chosen' => true, 'groupTagsByField' => true, 'tagGroup' => 'event_type'],
|
||||||
'sql' => ['type' => 'blob', 'notnull' => false],
|
'sql' => ['type' => 'blob', 'notnull' => false],
|
||||||
|
'relation' => ['type' => 'hasMany', 'load' => 'eager'],
|
||||||
];
|
];
|
||||||
|
|
||||||
$GLOBALS['TL_DCA']['tl_calendar_events']['fields']['organizations'] = [
|
$GLOBALS['TL_DCA']['tl_calendar_events']['fields']['organizations'] = [
|
||||||
|
|||||||
@@ -2,10 +2,13 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Contao\Database;
|
||||||
|
use Contao\StringUtil;
|
||||||
|
|
||||||
$GLOBALS['TL_DCA']['tl_module']['palettes']['member_organizations'] = '{title_legend},name,headline,type;{eventmanager_legend},editPage;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID';
|
$GLOBALS['TL_DCA']['tl_module']['palettes']['member_organizations'] = '{title_legend},name,headline,type;{eventmanager_legend},editPage;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID';
|
||||||
$GLOBALS['TL_DCA']['tl_module']['palettes']['member_events'] = '{title_legend},name,headline,type;{eventmanager_legend},editPage;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID';
|
$GLOBALS['TL_DCA']['tl_module']['palettes']['member_events'] = '{title_legend},name,headline,type;{eventmanager_legend},editPage;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID';
|
||||||
$GLOBALS['TL_DCA']['tl_module']['palettes']['organization_edit'] = '{title_legend},name,headline,type;{eventmanager_legend},listPage,logoFolder;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID';
|
$GLOBALS['TL_DCA']['tl_module']['palettes']['organization_edit'] = '{title_legend},name,headline,type;{eventmanager_legend},listPage,logoFolder,organizationTypeTags;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID';
|
||||||
$GLOBALS['TL_DCA']['tl_module']['palettes']['event_edit'] = '{title_legend},name,headline,type;{eventmanager_legend},listPage,eventFolder,termsPage,frontendAuthorId,frontendArchiveId;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID';
|
$GLOBALS['TL_DCA']['tl_module']['palettes']['event_edit'] = '{title_legend},name,headline,type;{eventmanager_legend},listPage,eventFolder,termsPage,frontendAuthorId,frontendArchiveId,eventTypeTags;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID';
|
||||||
|
|
||||||
$GLOBALS['TL_DCA']['tl_module']['fields']['editPage'] = [
|
$GLOBALS['TL_DCA']['tl_module']['fields']['editPage'] = [
|
||||||
'label' => &$GLOBALS['TL_LANG']['tl_module']['editPage'],
|
'label' => &$GLOBALS['TL_LANG']['tl_module']['editPage'],
|
||||||
@@ -62,3 +65,57 @@ $GLOBALS['TL_DCA']['tl_module']['fields']['frontendArchiveId'] = [
|
|||||||
'eval' => ['mandatory' => true, 'rgxp' => 'digit', 'maxlength' => 10, 'tl_class' => 'w50'],
|
'eval' => ['mandatory' => true, 'rgxp' => 'digit', 'maxlength' => 10, 'tl_class' => 'w50'],
|
||||||
'sql' => ['type' => 'integer', 'unsigned' => true, 'default' => 0],
|
'sql' => ['type' => 'integer', 'unsigned' => true, 'default' => 0],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
$GLOBALS['TL_DCA']['tl_module']['fields']['organizationTypeTags'] = [
|
||||||
|
'label' => &$GLOBALS['TL_LANG']['tl_module']['organizationTypeTags'],
|
||||||
|
'exclude' => true,
|
||||||
|
'inputType' => 'checkbox',
|
||||||
|
'options_callback' => static function () {
|
||||||
|
$rows = Database::getInstance()
|
||||||
|
->prepare('SELECT DISTINCT t.id, t.tag FROM tl_tags t LEFT JOIN tl_tags_rel r ON r.tag_id=t.id AND r.ptable=? AND r.field=? ORDER BY t.tag ASC')
|
||||||
|
->execute('tl_organization', 'tags')
|
||||||
|
->fetchAllAssoc();
|
||||||
|
|
||||||
|
$options = [];
|
||||||
|
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$options[(int) $row['id']] = (string) $row['tag'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $options;
|
||||||
|
},
|
||||||
|
'eval' => ['multiple' => true, 'tl_class' => 'clr'],
|
||||||
|
'sql' => ['type' => 'blob', 'notnull' => false],
|
||||||
|
'save_callback' => [
|
||||||
|
static function ($value): array {
|
||||||
|
return array_values(array_unique(array_map('intval', StringUtil::deserialize($value, true))));
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$GLOBALS['TL_DCA']['tl_module']['fields']['eventTypeTags'] = [
|
||||||
|
'label' => &$GLOBALS['TL_LANG']['tl_module']['eventTypeTags'],
|
||||||
|
'exclude' => true,
|
||||||
|
'inputType' => 'checkbox',
|
||||||
|
'options_callback' => static function () {
|
||||||
|
$rows = Database::getInstance()
|
||||||
|
->prepare('SELECT DISTINCT t.id, t.tag FROM tl_tags t LEFT JOIN tl_tags_rel r ON r.tag_id=t.id AND r.ptable=? AND r.field=? ORDER BY t.tag ASC')
|
||||||
|
->execute('tl_calendar_events', 'tags')
|
||||||
|
->fetchAllAssoc();
|
||||||
|
|
||||||
|
$options = [];
|
||||||
|
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$options[(int) $row['id']] = (string) $row['tag'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $options;
|
||||||
|
},
|
||||||
|
'eval' => ['multiple' => true, 'tl_class' => 'clr'],
|
||||||
|
'sql' => ['type' => 'blob', 'notnull' => false],
|
||||||
|
'save_callback' => [
|
||||||
|
static function ($value): array {
|
||||||
|
return array_values(array_unique(array_map('intval', StringUtil::deserialize($value, true))));
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ $GLOBALS['TL_DCA']['tl_organization'] = [
|
|||||||
],
|
],
|
||||||
'palettes' => [
|
'palettes' => [
|
||||||
'__selector__' => [],
|
'__selector__' => [],
|
||||||
'default' => '{title_legend},title,alias,type,isExternal,logo;{address_legend},street,street2,postal,city,state,country;{contact_legend},phone,email,website;{geo_legend},lat,lng;{description_legend},description;{relation_legend},members;{publish_legend},published',
|
'default' => '{title_legend},title,alias,tags,isExternal,logo;{address_legend},street,street2,postal,city,state,country;{contact_legend},phone,email,website;{geo_legend},lat,lng;{description_legend},description;{relation_legend},members;{publish_legend},published',
|
||||||
],
|
],
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'id' => [
|
'id' => [
|
||||||
@@ -211,14 +211,17 @@ $GLOBALS['TL_DCA']['tl_organization'] = [
|
|||||||
'eval' => ['tl_class' => 'w50'],
|
'eval' => ['tl_class' => 'w50'],
|
||||||
'sql' => ['type' => 'string', 'length' => 1, 'fixed' => true, 'default' => ''],
|
'sql' => ['type' => 'string', 'length' => 1, 'fixed' => true, 'default' => ''],
|
||||||
],
|
],
|
||||||
'type' => [
|
'tags' => [
|
||||||
'label' => &$GLOBALS['TL_LANG']['tl_organization']['type'],
|
'label' => &$GLOBALS['TL_LANG']['tl_organization']['tags'],
|
||||||
'exclude' => true,
|
'exclude' => true,
|
||||||
'inputType' => 'select',
|
'inputType' => 'select',
|
||||||
'options' => ['accommodation', 'shopping', 'culture'],
|
'foreignKey' => 'tl_tags.tag',
|
||||||
'reference' => &$GLOBALS['TL_LANG']['tl_organization']['type_options'],
|
'options_callback' => ['numero2_tags.listener.data_container.tags', 'getTagOptions'],
|
||||||
'eval' => ['multiple' => true, 'chosen' => true, 'includeBlankOption' => false, 'tl_class' => 'w50'],
|
'load_callback' => [['numero2_tags.listener.data_container.tags', 'loadTags']],
|
||||||
|
'save_callback' => [['numero2_tags.listener.data_container.tags', 'saveTags']],
|
||||||
|
'eval' => ['multiple' => true, 'size' => 8, 'tl_class' => 'w50 tags', 'chosen' => true, 'groupTagsByField' => true, 'tagGroup' => 'organization_type'],
|
||||||
'sql' => ['type' => 'blob', 'notnull' => false],
|
'sql' => ['type' => 'blob', 'notnull' => false],
|
||||||
|
'relation' => ['type' => 'hasMany', 'load' => 'eager'],
|
||||||
],
|
],
|
||||||
'members' => [
|
'members' => [
|
||||||
'label' => &$GLOBALS['TL_LANG']['tl_organization']['members'],
|
'label' => &$GLOBALS['TL_LANG']['tl_organization']['members'],
|
||||||
|
|||||||
@@ -4,13 +4,9 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
$GLOBALS['TL_LANG']['tl_calendar_events']['organization_legend'] = 'Organisationen und Ort';
|
$GLOBALS['TL_LANG']['tl_calendar_events']['organization_legend'] = 'Organisationen und Ort';
|
||||||
$GLOBALS['TL_LANG']['tl_calendar_events']['location_id'] = ['Veranstaltungsort', 'Zugeordneter Veranstaltungsort'];
|
$GLOBALS['TL_LANG']['tl_calendar_events']['location_id'] = ['Veranstaltungsort', 'Zugeordneter Veranstaltungsort'];
|
||||||
$GLOBALS['TL_LANG']['tl_calendar_events']['type'] = ['Typ', 'Mehrere Typen auswählbar.'];
|
$GLOBALS['TL_LANG']['tl_calendar_events']['tags'] = ['Typen', 'Vorhandene Tags für Veranstaltungstypen auswählen.'];
|
||||||
$GLOBALS['TL_LANG']['tl_calendar_events']['organizations'] = ['Organisationen', 'Zugeordnete Organisationen'];
|
$GLOBALS['TL_LANG']['tl_calendar_events']['organizations'] = ['Organisationen', 'Zugeordnete Organisationen'];
|
||||||
$GLOBALS['TL_LANG']['tl_calendar_events']['photographer'] = ['Urheber/Fotograf', 'Die Angabe des Urhebers ist notwendig. Ihnen muss eine Genehmigung zur Verwendung des Bildes vorliegen.'];
|
$GLOBALS['TL_LANG']['tl_calendar_events']['photographer'] = ['Urheber/Fotograf', 'Die Angabe des Urhebers ist notwendig. Ihnen muss eine Genehmigung zur Verwendung des Bildes vorliegen.'];
|
||||||
$GLOBALS['TL_LANG']['tl_calendar_events']['isSoldOut'] = ['Ausverkauft', 'Diese Veranstaltung ist ausverkauft.'];
|
$GLOBALS['TL_LANG']['tl_calendar_events']['isSoldOut'] = ['Ausverkauft', 'Diese Veranstaltung ist ausverkauft.'];
|
||||||
$GLOBALS['TL_LANG']['tl_calendar_events']['isCanceled'] = ['Abgesagt', 'Diese Veranstaltung wurde abgesagt.'];
|
$GLOBALS['TL_LANG']['tl_calendar_events']['isCanceled'] = ['Abgesagt', 'Diese Veranstaltung wurde abgesagt.'];
|
||||||
$GLOBALS['TL_LANG']['tl_calendar_events']['termsAccepted'] = ['Nutzungsbedingungen akzeptiert', 'Die Nutzungsbedingungen wurden akzeptiert.'];
|
$GLOBALS['TL_LANG']['tl_calendar_events']['termsAccepted'] = ['Nutzungsbedingungen akzeptiert', 'Die Nutzungsbedingungen wurden akzeptiert.'];
|
||||||
|
|
||||||
$GLOBALS['TL_LANG']['tl_calendar_events']['type_options']['accommodation'] = 'Unterkunft';
|
|
||||||
$GLOBALS['TL_LANG']['tl_calendar_events']['type_options']['shopping'] = 'Shopping';
|
|
||||||
$GLOBALS['TL_LANG']['tl_calendar_events']['type_options']['culture'] = 'Kultur';
|
|
||||||
|
|||||||
@@ -10,3 +10,5 @@ $GLOBALS['TL_LANG']['tl_module']['eventFolder'] = ['Event-Ordner', 'Bitte wähle
|
|||||||
$GLOBALS['TL_LANG']['tl_module']['termsPage'] = ['Seite mit Nutzungsbedingungen', 'Optional: Seite mit den Nutzungsbedingungen, die im Frontend beim Zustimmungs-Label verlinkt wird.'];
|
$GLOBALS['TL_LANG']['tl_module']['termsPage'] = ['Seite mit Nutzungsbedingungen', 'Optional: Seite mit den Nutzungsbedingungen, die im Frontend beim Zustimmungs-Label verlinkt wird.'];
|
||||||
$GLOBALS['TL_LANG']['tl_module']['frontendAuthorId'] = ['Backend Benutzer ID', 'ID des Backend-Benutzers, der als Autor für frontendseitig angelegte Events gesetzt wird.'];
|
$GLOBALS['TL_LANG']['tl_module']['frontendAuthorId'] = ['Backend Benutzer ID', 'ID des Backend-Benutzers, der als Autor für frontendseitig angelegte Events gesetzt wird.'];
|
||||||
$GLOBALS['TL_LANG']['tl_module']['frontendArchiveId'] = ['ID des Newsarchivs', 'Archiv-ID (pid), in das frontendseitig angelegte Events gespeichert werden.'];
|
$GLOBALS['TL_LANG']['tl_module']['frontendArchiveId'] = ['ID des Newsarchivs', 'Archiv-ID (pid), in das frontendseitig angelegte Events gespeichert werden.'];
|
||||||
|
$GLOBALS['TL_LANG']['tl_module']['organizationTypeTags'] = ['Anzuzeigende Organisationstypen', 'Optional: Begrenzen Sie die im Frontend anzeigbaren Organisationstyp-Tags. Leer = alle.'];
|
||||||
|
$GLOBALS['TL_LANG']['tl_module']['eventTypeTags'] = ['Anzuzeigende Veranstaltungstypen', 'Optional: Begrenzen Sie die im Frontend anzeigbaren Veranstaltungstyp-Tags. Leer = alle.'];
|
||||||
|
|||||||
@@ -19,13 +19,9 @@ $GLOBALS['TL_LANG']['tl_organization']['lng'] = ['Längengrad', 'Längengrad'];
|
|||||||
$GLOBALS['TL_LANG']['tl_organization']['description'] = ['Beschreibung', 'Beschreibung'];
|
$GLOBALS['TL_LANG']['tl_organization']['description'] = ['Beschreibung', 'Beschreibung'];
|
||||||
$GLOBALS['TL_LANG']['tl_organization']['published'] = ['Veröffentlicht', 'Organisation veröffentlichen'];
|
$GLOBALS['TL_LANG']['tl_organization']['published'] = ['Veröffentlicht', 'Organisation veröffentlichen'];
|
||||||
$GLOBALS['TL_LANG']['tl_organization']['isExternal'] = ['Extern', 'Externe Organisation'];
|
$GLOBALS['TL_LANG']['tl_organization']['isExternal'] = ['Extern', 'Externe Organisation'];
|
||||||
$GLOBALS['TL_LANG']['tl_organization']['type'] = ['Typ', 'Mehrere Typen auswählbar.'];
|
$GLOBALS['TL_LANG']['tl_organization']['tags'] = ['Typen', 'Vorhandene Tags für Organisationstypen auswählen.'];
|
||||||
$GLOBALS['TL_LANG']['tl_organization']['members'] = ['Mitglieder', 'Zugeordnete Mitglieder'];
|
$GLOBALS['TL_LANG']['tl_organization']['members'] = ['Mitglieder', 'Zugeordnete Mitglieder'];
|
||||||
|
|
||||||
$GLOBALS['TL_LANG']['tl_organization']['type_options']['accommodation'] = 'Unterkunft';
|
|
||||||
$GLOBALS['TL_LANG']['tl_organization']['type_options']['shopping'] = 'Shopping';
|
|
||||||
$GLOBALS['TL_LANG']['tl_organization']['type_options']['culture'] = 'Kultur';
|
|
||||||
|
|
||||||
$GLOBALS['TL_LANG']['tl_organization']['title_legend'] = 'Titel';
|
$GLOBALS['TL_LANG']['tl_organization']['title_legend'] = 'Titel';
|
||||||
$GLOBALS['TL_LANG']['tl_organization']['address_legend'] = 'Adresse';
|
$GLOBALS['TL_LANG']['tl_organization']['address_legend'] = 'Adresse';
|
||||||
$GLOBALS['TL_LANG']['tl_organization']['contact_legend'] = 'Kontakt';
|
$GLOBALS['TL_LANG']['tl_organization']['contact_legend'] = 'Kontakt';
|
||||||
|
|||||||
@@ -4,13 +4,9 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
$GLOBALS['TL_LANG']['tl_calendar_events']['organization_legend'] = 'Organizations and location';
|
$GLOBALS['TL_LANG']['tl_calendar_events']['organization_legend'] = 'Organizations and location';
|
||||||
$GLOBALS['TL_LANG']['tl_calendar_events']['location_id'] = ['Location', 'Assigned location'];
|
$GLOBALS['TL_LANG']['tl_calendar_events']['location_id'] = ['Location', 'Assigned location'];
|
||||||
$GLOBALS['TL_LANG']['tl_calendar_events']['type'] = ['Type', 'Multiple types can be selected.'];
|
$GLOBALS['TL_LANG']['tl_calendar_events']['tags'] = ['Types', 'Select existing tags for event types.'];
|
||||||
$GLOBALS['TL_LANG']['tl_calendar_events']['organizations'] = ['Organizations', 'Assigned organizations'];
|
$GLOBALS['TL_LANG']['tl_calendar_events']['organizations'] = ['Organizations', 'Assigned organizations'];
|
||||||
$GLOBALS['TL_LANG']['tl_calendar_events']['photographer'] = ['Author/Photographer', 'Please provide the image author/photographer and make sure you have permission to use the image.'];
|
$GLOBALS['TL_LANG']['tl_calendar_events']['photographer'] = ['Author/Photographer', 'Please provide the image author/photographer and make sure you have permission to use the image.'];
|
||||||
$GLOBALS['TL_LANG']['tl_calendar_events']['isSoldOut'] = ['Sold out', 'This event is sold out.'];
|
$GLOBALS['TL_LANG']['tl_calendar_events']['isSoldOut'] = ['Sold out', 'This event is sold out.'];
|
||||||
$GLOBALS['TL_LANG']['tl_calendar_events']['isCanceled'] = ['Canceled', 'This event has been canceled.'];
|
$GLOBALS['TL_LANG']['tl_calendar_events']['isCanceled'] = ['Canceled', 'This event has been canceled.'];
|
||||||
$GLOBALS['TL_LANG']['tl_calendar_events']['termsAccepted'] = ['Terms accepted', 'The terms of use have been accepted.'];
|
$GLOBALS['TL_LANG']['tl_calendar_events']['termsAccepted'] = ['Terms accepted', 'The terms of use have been accepted.'];
|
||||||
|
|
||||||
$GLOBALS['TL_LANG']['tl_calendar_events']['type_options']['accommodation'] = 'Accommodation';
|
|
||||||
$GLOBALS['TL_LANG']['tl_calendar_events']['type_options']['shopping'] = 'Shopping';
|
|
||||||
$GLOBALS['TL_LANG']['tl_calendar_events']['type_options']['culture'] = 'Culture';
|
|
||||||
|
|||||||
@@ -10,3 +10,5 @@ $GLOBALS['TL_LANG']['tl_module']['eventFolder'] = ['Event folder', 'Please selec
|
|||||||
$GLOBALS['TL_LANG']['tl_module']['termsPage'] = ['Terms page', 'Optional: page containing the terms of use linked from the frontend consent label.'];
|
$GLOBALS['TL_LANG']['tl_module']['termsPage'] = ['Terms page', 'Optional: page containing the terms of use linked from the frontend consent label.'];
|
||||||
$GLOBALS['TL_LANG']['tl_module']['frontendAuthorId'] = ['Backend user ID', 'Backend user ID that should be set as author for events created from the frontend.'];
|
$GLOBALS['TL_LANG']['tl_module']['frontendAuthorId'] = ['Backend user ID', 'Backend user ID that should be set as author for events created from the frontend.'];
|
||||||
$GLOBALS['TL_LANG']['tl_module']['frontendArchiveId'] = ['News archive ID', 'Archive ID (pid) where events created from the frontend should be stored.'];
|
$GLOBALS['TL_LANG']['tl_module']['frontendArchiveId'] = ['News archive ID', 'Archive ID (pid) where events created from the frontend should be stored.'];
|
||||||
|
$GLOBALS['TL_LANG']['tl_module']['organizationTypeTags'] = ['Displayed organization types', 'Optional: Limit which organization type tags can be selected in the frontend. Empty = all.'];
|
||||||
|
$GLOBALS['TL_LANG']['tl_module']['eventTypeTags'] = ['Displayed event types', 'Optional: Limit which event type tags can be selected in the frontend. Empty = all.'];
|
||||||
|
|||||||
@@ -19,13 +19,9 @@ $GLOBALS['TL_LANG']['tl_organization']['lng'] = ['Longitude', 'Longitude'];
|
|||||||
$GLOBALS['TL_LANG']['tl_organization']['description'] = ['Description', 'Description'];
|
$GLOBALS['TL_LANG']['tl_organization']['description'] = ['Description', 'Description'];
|
||||||
$GLOBALS['TL_LANG']['tl_organization']['published'] = ['Published', 'Publish organization'];
|
$GLOBALS['TL_LANG']['tl_organization']['published'] = ['Published', 'Publish organization'];
|
||||||
$GLOBALS['TL_LANG']['tl_organization']['isExternal'] = ['External', 'External organization'];
|
$GLOBALS['TL_LANG']['tl_organization']['isExternal'] = ['External', 'External organization'];
|
||||||
$GLOBALS['TL_LANG']['tl_organization']['type'] = ['Type', 'Multiple types can be selected.'];
|
$GLOBALS['TL_LANG']['tl_organization']['tags'] = ['Types', 'Select existing tags for organization types.'];
|
||||||
$GLOBALS['TL_LANG']['tl_organization']['members'] = ['Members', 'Assigned members'];
|
$GLOBALS['TL_LANG']['tl_organization']['members'] = ['Members', 'Assigned members'];
|
||||||
|
|
||||||
$GLOBALS['TL_LANG']['tl_organization']['type_options']['accommodation'] = 'Accommodation';
|
|
||||||
$GLOBALS['TL_LANG']['tl_organization']['type_options']['shopping'] = 'Shopping';
|
|
||||||
$GLOBALS['TL_LANG']['tl_organization']['type_options']['culture'] = 'Culture';
|
|
||||||
|
|
||||||
$GLOBALS['TL_LANG']['tl_organization']['title_legend'] = 'Title';
|
$GLOBALS['TL_LANG']['tl_organization']['title_legend'] = 'Title';
|
||||||
$GLOBALS['TL_LANG']['tl_organization']['address_legend'] = 'Address';
|
$GLOBALS['TL_LANG']['tl_organization']['address_legend'] = 'Address';
|
||||||
$GLOBALS['TL_LANG']['tl_organization']['contact_legend'] = 'Contact';
|
$GLOBALS['TL_LANG']['tl_organization']['contact_legend'] = 'Contact';
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
{{ form_row(form.organization_ids) }}
|
{{ form_row(form.organization_ids) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{ form_row(form.location_id) }}
|
{{ form_row(form.location_id) }}
|
||||||
{{ form_row(form.type) }}
|
{{ form_row(form.tags) }}
|
||||||
{{ form_row(form.teaser) }}
|
{{ form_row(form.teaser) }}
|
||||||
{{ form_row(form.description) }}
|
{{ form_row(form.description) }}
|
||||||
{{ form_row(form.url) }}
|
{{ form_row(form.url) }}
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
(function () {
|
(function () {
|
||||||
const locationSelect = document.querySelector('select.js-location-choice');
|
const locationSelect = document.querySelector('select.js-location-choice');
|
||||||
const organizationSelect = document.querySelector('select.js-organization-choice');
|
const organizationSelect = document.querySelector('select.js-organization-choice');
|
||||||
const typeSelect = document.querySelector('select.js-event-type-choice');
|
const typeSelect = document.querySelector('select.js-event-tags-choice');
|
||||||
|
|
||||||
if (organizationSelect && typeof window.Choices === 'function') {
|
if (organizationSelect && typeof window.Choices === 'function') {
|
||||||
new window.Choices(organizationSelect, {
|
new window.Choices(organizationSelect, {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
(function () {
|
(function () {
|
||||||
const typeSelect = document.querySelector('select.js-organization-type-choice');
|
const typeSelect = document.querySelector('select.js-organization-tags-choice');
|
||||||
|
|
||||||
if (typeSelect && typeof window.Choices === 'function') {
|
if (typeSelect && typeof window.Choices === 'function') {
|
||||||
new window.Choices(typeSelect, {
|
new window.Choices(typeSelect, {
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ class EventEditController extends AbstractFrontendModuleController
|
|||||||
'startTime' => null,
|
'startTime' => null,
|
||||||
'endTime' => null,
|
'endTime' => null,
|
||||||
'location_id' => 0,
|
'location_id' => 0,
|
||||||
'type' => null,
|
'tags' => null,
|
||||||
'teaser' => '',
|
'teaser' => '',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'url' => '',
|
'url' => '',
|
||||||
@@ -88,9 +88,11 @@ class EventEditController extends AbstractFrontendModuleController
|
|||||||
}
|
}
|
||||||
|
|
||||||
$memberOrganizationIds = $this->eventRepository->getOrganizationIdsForMember((int) $user->id);
|
$memberOrganizationIds = $this->eventRepository->getOrganizationIdsForMember((int) $user->id);
|
||||||
|
$allowedEventTagIds = array_values(array_unique(array_map('intval', StringUtil::deserialize($model->eventTypeTags ?? null, true))));
|
||||||
$organizationChoices = $this->eventRepository->getOrganizationChoicesForMember((int) $user->id);
|
$organizationChoices = $this->eventRepository->getOrganizationChoicesForMember((int) $user->id);
|
||||||
$showOrganization = count($memberOrganizationIds) > 1;
|
$showOrganization = count($memberOrganizationIds) > 1;
|
||||||
$currentOrganizationIds = $this->eventRepository->getOrganizationIdsForEvent($eventId);
|
$currentOrganizationIds = $this->eventRepository->getOrganizationIdsForEvent($eventId);
|
||||||
|
$currentTagIds = $this->eventRepository->getTagIdsForEvent($eventId);
|
||||||
|
|
||||||
$formData = [
|
$formData = [
|
||||||
'title' => (string) ($event['title'] ?? ''),
|
'title' => (string) ($event['title'] ?? ''),
|
||||||
@@ -100,7 +102,7 @@ class EventEditController extends AbstractFrontendModuleController
|
|||||||
'startTime' => ('1' === (string) ($event['addTime'] ?? '') && !empty($event['startTime'])) ? date('H:i', (int) $event['startTime']) : '',
|
'startTime' => ('1' === (string) ($event['addTime'] ?? '') && !empty($event['startTime'])) ? date('H:i', (int) $event['startTime']) : '',
|
||||||
'endTime' => $this->resolveFormEndTime($event),
|
'endTime' => $this->resolveFormEndTime($event),
|
||||||
'location_id' => (int) ($event['location_id'] ?? 0),
|
'location_id' => (int) ($event['location_id'] ?? 0),
|
||||||
'type' => StringUtil::deserialize($event['type'] ?? null, true),
|
'tags' => $currentTagIds,
|
||||||
'teaser' => (string) ($event['teaser'] ?? ''),
|
'teaser' => (string) ($event['teaser'] ?? ''),
|
||||||
'description' => (string) ($event['description'] ?? ''),
|
'description' => (string) ($event['description'] ?? ''),
|
||||||
'url' => (string) ($event['url'] ?? ''),
|
'url' => (string) ($event['url'] ?? ''),
|
||||||
@@ -135,6 +137,7 @@ class EventEditController extends AbstractFrontendModuleController
|
|||||||
|
|
||||||
$form = $this->createForm(EventType::class, $formData, [
|
$form = $this->createForm(EventType::class, $formData, [
|
||||||
'location_choices' => $this->eventRepository->getLocationChoices(),
|
'location_choices' => $this->eventRepository->getLocationChoices(),
|
||||||
|
'tag_choices' => $this->eventRepository->getTagChoicesForEventType($allowedEventTagIds),
|
||||||
'organization_choices' => $organizationChoices,
|
'organization_choices' => $organizationChoices,
|
||||||
'selected_organization_ids' => $showOrganization ? ($formData['organization_ids'] ?? []) : [],
|
'selected_organization_ids' => $showOrganization ? ($formData['organization_ids'] ?? []) : [],
|
||||||
'show_organization' => $showOrganization,
|
'show_organization' => $showOrganization,
|
||||||
@@ -213,6 +216,14 @@ class EventEditController extends AbstractFrontendModuleController
|
|||||||
$this->eventRepository->update($eventId, $submittedData);
|
$this->eventRepository->update($eventId, $submittedData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$selectedTagIds = array_values(array_unique(array_map('intval', (array) ($submittedData['tags'] ?? []))));
|
||||||
|
|
||||||
|
if ([] !== $allowedEventTagIds) {
|
||||||
|
$selectedTagIds = array_values(array_intersect($selectedTagIds, $allowedEventTagIds));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->eventRepository->assignTagsToEvent($eventId, $selectedTagIds);
|
||||||
|
|
||||||
$useImage = !empty($submittedData['addImage']);
|
$useImage = !empty($submittedData['addImage']);
|
||||||
$removeImage = '1' === (string) $request->request->get('remove_image', '0');
|
$removeImage = '1' === (string) $request->request->get('remove_image', '0');
|
||||||
$uploadedImage = $form->get('eventUpload')->getData();
|
$uploadedImage = $form->get('eventUpload')->getData();
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ class OrganizationEditController extends AbstractFrontendModuleController
|
|||||||
}
|
}
|
||||||
|
|
||||||
$organization = $this->organizationRepository->findById($organizationId);
|
$organization = $this->organizationRepository->findById($organizationId);
|
||||||
|
$allowedOrganizationTagIds = array_values(array_unique(array_map('intval', StringUtil::deserialize($model->organizationTypeTags ?? null, true))));
|
||||||
|
|
||||||
if (null === $organization) {
|
if (null === $organization) {
|
||||||
$template->set('error', 'Organisation nicht gefunden.');
|
$template->set('error', 'Organisation nicht gefunden.');
|
||||||
@@ -77,7 +78,7 @@ class OrganizationEditController extends AbstractFrontendModuleController
|
|||||||
'email' => (string) ($organization['email'] ?? ''),
|
'email' => (string) ($organization['email'] ?? ''),
|
||||||
'website' => (string) ($organization['website'] ?? ''),
|
'website' => (string) ($organization['website'] ?? ''),
|
||||||
'description' => (string) ($organization['description'] ?? ''),
|
'description' => (string) ($organization['description'] ?? ''),
|
||||||
'type' => StringUtil::deserialize($organization['type'] ?? null, true),
|
'tags' => $this->organizationRepository->getTagIdsForOrganization($organizationId),
|
||||||
];
|
];
|
||||||
|
|
||||||
$currentLogoPath = null;
|
$currentLogoPath = null;
|
||||||
@@ -90,13 +91,22 @@ class OrganizationEditController extends AbstractFrontendModuleController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$form = $this->createForm(OrganizationType::class, $formData);
|
$form = $this->createForm(OrganizationType::class, $formData, [
|
||||||
|
'tag_choices' => $this->organizationRepository->getTagChoicesForOrganizationType($allowedOrganizationTagIds),
|
||||||
|
]);
|
||||||
$form->handleRequest($request);
|
$form->handleRequest($request);
|
||||||
|
|
||||||
if ($form->isSubmitted() && $form->isValid()) {
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
$submittedData = (array) $form->getData();
|
$submittedData = (array) $form->getData();
|
||||||
|
|
||||||
$this->organizationRepository->update($organizationId, $submittedData);
|
$this->organizationRepository->update($organizationId, $submittedData);
|
||||||
|
$selectedTagIds = array_values(array_unique(array_map('intval', (array) ($submittedData['tags'] ?? []))));
|
||||||
|
|
||||||
|
if ([] !== $allowedOrganizationTagIds) {
|
||||||
|
$selectedTagIds = array_values(array_intersect($selectedTagIds, $allowedOrganizationTagIds));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->organizationRepository->assignTagsToOrganization($organizationId, $selectedTagIds);
|
||||||
|
|
||||||
$deleteLogo = '1' === (string) $request->request->get('remove_logo', '0');
|
$deleteLogo = '1' === (string) $request->request->get('remove_logo', '0');
|
||||||
$uploadedLogo = $form->get('logoUpload')->getData();
|
$uploadedLogo = $form->get('logoUpload')->getData();
|
||||||
|
|||||||
+8
-10
@@ -39,7 +39,7 @@ class EventType extends AbstractType
|
|||||||
])
|
])
|
||||||
->add('startDate', DateType::class, [
|
->add('startDate', DateType::class, [
|
||||||
'label' => 'Startdatum',
|
'label' => 'Startdatum',
|
||||||
'required' => false,
|
'required' => true,
|
||||||
'widget' => 'single_text',
|
'widget' => 'single_text',
|
||||||
'input' => 'string',
|
'input' => 'string',
|
||||||
'html5' => false,
|
'html5' => false,
|
||||||
@@ -110,7 +110,7 @@ class EventType extends AbstractType
|
|||||||
->add('location_id', ChoiceType::class, [
|
->add('location_id', ChoiceType::class, [
|
||||||
'label' => 'Veranstaltungsort',
|
'label' => 'Veranstaltungsort',
|
||||||
'choices' => $options['location_choices'],
|
'choices' => $options['location_choices'],
|
||||||
'required' => false,
|
'required' => true,
|
||||||
'choice_value' => static fn ($value) => null !== $value ? (string) $value : '',
|
'choice_value' => static fn ($value) => null !== $value ? (string) $value : '',
|
||||||
'placeholder' => 'Bitte auswählen',
|
'placeholder' => 'Bitte auswählen',
|
||||||
'attr' => [
|
'attr' => [
|
||||||
@@ -118,17 +118,13 @@ class EventType extends AbstractType
|
|||||||
'data-placeholder' => 'Veranstaltungsort suchen …',
|
'data-placeholder' => 'Veranstaltungsort suchen …',
|
||||||
],
|
],
|
||||||
])
|
])
|
||||||
->add('type', ChoiceType::class, [
|
->add('tags', ChoiceType::class, [
|
||||||
'label' => 'Typ',
|
'label' => 'Typen',
|
||||||
'choices' => [
|
'choices' => $options['tag_choices'],
|
||||||
'Unterkunft' => 'accommodation',
|
|
||||||
'Einkaufen' => 'shopping',
|
|
||||||
'Kultur' => 'culture',
|
|
||||||
],
|
|
||||||
'multiple' => true,
|
'multiple' => true,
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'attr' => [
|
'attr' => [
|
||||||
'class' => 'js-event-type-choice',
|
'class' => 'js-event-tags-choice',
|
||||||
'data-placeholder' => 'Typen suchen …',
|
'data-placeholder' => 'Typen suchen …',
|
||||||
],
|
],
|
||||||
])
|
])
|
||||||
@@ -191,6 +187,7 @@ class EventType extends AbstractType
|
|||||||
$resolver->setDefaults([
|
$resolver->setDefaults([
|
||||||
'csrf_protection' => true,
|
'csrf_protection' => true,
|
||||||
'location_choices' => [],
|
'location_choices' => [],
|
||||||
|
'tag_choices' => [],
|
||||||
'organization_choices' => [],
|
'organization_choices' => [],
|
||||||
'selected_organization_ids' => [],
|
'selected_organization_ids' => [],
|
||||||
'show_organization' => false,
|
'show_organization' => false,
|
||||||
@@ -198,6 +195,7 @@ class EventType extends AbstractType
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$resolver->setAllowedTypes('location_choices', 'array');
|
$resolver->setAllowedTypes('location_choices', 'array');
|
||||||
|
$resolver->setAllowedTypes('tag_choices', 'array');
|
||||||
$resolver->setAllowedTypes('organization_choices', 'array');
|
$resolver->setAllowedTypes('organization_choices', 'array');
|
||||||
$resolver->setAllowedTypes('selected_organization_ids', 'array');
|
$resolver->setAllowedTypes('selected_organization_ids', 'array');
|
||||||
$resolver->setAllowedTypes('show_organization', 'bool');
|
$resolver->setAllowedTypes('show_organization', 'bool');
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use Symfony\Component\Form\Extension\Core\Type\FileType;
|
|||||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
class OrganizationType extends AbstractType
|
class OrganizationType extends AbstractType
|
||||||
{
|
{
|
||||||
@@ -27,17 +28,13 @@ class OrganizationType extends AbstractType
|
|||||||
->add('email', EmailType::class, ['label' => 'E-Mail', 'required' => false])
|
->add('email', EmailType::class, ['label' => 'E-Mail', 'required' => false])
|
||||||
->add('website', TextType::class, ['label' => 'Webseite', 'required' => false])
|
->add('website', TextType::class, ['label' => 'Webseite', 'required' => false])
|
||||||
->add('description', TextareaType::class, ['label' => 'Beschreibung', 'required' => false])
|
->add('description', TextareaType::class, ['label' => 'Beschreibung', 'required' => false])
|
||||||
->add('type', ChoiceType::class, [
|
->add('tags', ChoiceType::class, [
|
||||||
'label' => 'Typ',
|
'label' => 'Typen',
|
||||||
'choices' => [
|
'choices' => $options['tag_choices'],
|
||||||
'Unterkunft' => 'accommodation',
|
|
||||||
'Einkaufen' => 'shopping',
|
|
||||||
'Kultur' => 'culture',
|
|
||||||
],
|
|
||||||
'multiple' => true,
|
'multiple' => true,
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'attr' => [
|
'attr' => [
|
||||||
'class' => 'js-organization-type-choice',
|
'class' => 'js-organization-tags-choice',
|
||||||
'data-placeholder' => 'Typ auswählen …',
|
'data-placeholder' => 'Typ auswählen …',
|
||||||
],
|
],
|
||||||
])
|
])
|
||||||
@@ -51,4 +48,13 @@ class OrganizationType extends AbstractType
|
|||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function configureOptions(OptionsResolver $resolver): void
|
||||||
|
{
|
||||||
|
$resolver->setDefaults([
|
||||||
|
'tag_choices' => [],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$resolver->setAllowedTypes('tag_choices', 'array');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,6 +131,12 @@ class EventRepository
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$tagIds = $this->getTagIdsForEvent($eventId);
|
||||||
|
|
||||||
|
if ([] !== $tagIds) {
|
||||||
|
$this->assignTagsToEvent($newEventId, $tagIds);
|
||||||
|
}
|
||||||
|
|
||||||
return $newEventId;
|
return $newEventId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,6 +263,12 @@ class EventRepository
|
|||||||
|
|
||||||
public function delete(int $eventId): void
|
public function delete(int $eventId): void
|
||||||
{
|
{
|
||||||
|
$this->connection->delete(
|
||||||
|
'tl_tags_rel',
|
||||||
|
['ptable' => 'tl_calendar_events', 'field' => 'tags', 'pid' => $eventId],
|
||||||
|
['pid' => ParameterType::INTEGER],
|
||||||
|
);
|
||||||
|
|
||||||
$this->connection->delete(
|
$this->connection->delete(
|
||||||
'tl_calendar_events_organization',
|
'tl_calendar_events_organization',
|
||||||
['event_id' => $eventId],
|
['event_id' => $eventId],
|
||||||
@@ -324,6 +336,73 @@ class EventRepository
|
|||||||
return array_values(array_unique(array_map('intval', $rows)));
|
return array_values(array_unique(array_map('intval', $rows)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, int>
|
||||||
|
*/
|
||||||
|
public function getTagChoicesForEventType(array $allowedTagIds = []): array
|
||||||
|
{
|
||||||
|
$rows = $this->connection->createQueryBuilder()
|
||||||
|
->select('DISTINCT t.id', 't.tag')
|
||||||
|
->from('tl_tags', 't')
|
||||||
|
->innerJoin('t', 'tl_tags_rel', 'r', 'r.tag_id = t.id')
|
||||||
|
->where('r.ptable = :ptable')
|
||||||
|
->andWhere('r.field = :field')
|
||||||
|
->setParameter('ptable', 'tl_calendar_events')
|
||||||
|
->setParameter('field', 'tags')
|
||||||
|
->orderBy('t.tag', 'ASC')
|
||||||
|
->executeQuery()
|
||||||
|
->fetchAllAssociative();
|
||||||
|
|
||||||
|
if ([] === $rows) {
|
||||||
|
$rows = $this->connection->createQueryBuilder()
|
||||||
|
->select('t.id', 't.tag')
|
||||||
|
->from('tl_tags', 't')
|
||||||
|
->orderBy('t.tag', 'ASC')
|
||||||
|
->executeQuery()
|
||||||
|
->fetchAllAssociative();
|
||||||
|
}
|
||||||
|
|
||||||
|
$choices = [];
|
||||||
|
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$choices[(string) ($row['tag'] ?? '')] = (int) $row['id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$allowedTagIds = array_values(array_unique(array_map('intval', $allowedTagIds)));
|
||||||
|
|
||||||
|
if ([] !== $allowedTagIds) {
|
||||||
|
$choices = array_filter(
|
||||||
|
$choices,
|
||||||
|
static fn (int $id): bool => in_array($id, $allowedTagIds, true),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $choices;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return array<int> */
|
||||||
|
public function getTagIdsForEvent(int $eventId): array
|
||||||
|
{
|
||||||
|
if ($eventId <= 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$ids = $this->connection->createQueryBuilder()
|
||||||
|
->select('tag_id')
|
||||||
|
->from('tl_tags_rel')
|
||||||
|
->where('ptable = :ptable')
|
||||||
|
->andWhere('field = :field')
|
||||||
|
->andWhere('pid = :pid')
|
||||||
|
->setParameter('ptable', 'tl_calendar_events')
|
||||||
|
->setParameter('field', 'tags')
|
||||||
|
->setParameter('pid', $eventId, ParameterType::INTEGER)
|
||||||
|
->orderBy('tag_id', 'ASC')
|
||||||
|
->executeQuery()
|
||||||
|
->fetchFirstColumn();
|
||||||
|
|
||||||
|
return array_values(array_unique(array_map('intval', $ids)));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array<string, int>
|
* @return array<string, int>
|
||||||
*/
|
*/
|
||||||
@@ -496,6 +575,41 @@ class EventRepository
|
|||||||
return $eventId;
|
return $eventId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @param array<int|string> $tagIds */
|
||||||
|
public function assignTagsToEvent(int $eventId, array $tagIds): void
|
||||||
|
{
|
||||||
|
$this->connection->delete(
|
||||||
|
'tl_tags_rel',
|
||||||
|
['ptable' => 'tl_calendar_events', 'field' => 'tags', 'pid' => $eventId],
|
||||||
|
['pid' => ParameterType::INTEGER],
|
||||||
|
);
|
||||||
|
|
||||||
|
$tagIds = array_values(array_unique(array_map('intval', $tagIds)));
|
||||||
|
|
||||||
|
if ([] === $tagIds) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$allowedTagIds = $this->getAllowedTagIdsForEvents();
|
||||||
|
$tagIds = array_values(array_intersect($tagIds, $allowedTagIds));
|
||||||
|
|
||||||
|
foreach ($tagIds as $tagId) {
|
||||||
|
$this->connection->insert(
|
||||||
|
'tl_tags_rel',
|
||||||
|
[
|
||||||
|
'tag_id' => $tagId,
|
||||||
|
'pid' => $eventId,
|
||||||
|
'ptable' => 'tl_calendar_events',
|
||||||
|
'field' => 'tags',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'tag_id' => ParameterType::INTEGER,
|
||||||
|
'pid' => ParameterType::INTEGER,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<string, mixed> $data
|
* @param array<string, mixed> $data
|
||||||
*
|
*
|
||||||
@@ -533,7 +647,6 @@ class EventRepository
|
|||||||
'addTime' => $addTime ? 1 : 0,
|
'addTime' => $addTime ? 1 : 0,
|
||||||
'startTime' => $startTimeTimestamp,
|
'startTime' => $startTimeTimestamp,
|
||||||
'endTime' => $endTimeTimestamp,
|
'endTime' => $endTimeTimestamp,
|
||||||
'type' => serialize($data['type'] ?? []),
|
|
||||||
'teaser' => $data['teaser'] ?? null,
|
'teaser' => $data['teaser'] ?? null,
|
||||||
'description' => $data['description'] ?? null,
|
'description' => $data['description'] ?? null,
|
||||||
'url' => $url,
|
'url' => $url,
|
||||||
@@ -616,4 +729,30 @@ class EventRepository
|
|||||||
$types,
|
$types,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @return array<int> */
|
||||||
|
private function getAllowedTagIdsForEvents(): array
|
||||||
|
{
|
||||||
|
$ids = $this->connection->createQueryBuilder()
|
||||||
|
->select('DISTINCT r.tag_id')
|
||||||
|
->from('tl_tags_rel', 'r')
|
||||||
|
->where('r.ptable = :ptable')
|
||||||
|
->andWhere('r.field = :field')
|
||||||
|
->setParameter('ptable', 'tl_calendar_events')
|
||||||
|
->setParameter('field', 'tags')
|
||||||
|
->executeQuery()
|
||||||
|
->fetchFirstColumn();
|
||||||
|
|
||||||
|
$ids = array_values(array_unique(array_map('intval', $ids)));
|
||||||
|
|
||||||
|
if ([] !== $ids) {
|
||||||
|
return $ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_values(array_unique(array_map('intval', $this->connection->createQueryBuilder()
|
||||||
|
->select('id')
|
||||||
|
->from('tl_tags')
|
||||||
|
->executeQuery()
|
||||||
|
->fetchFirstColumn())));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,7 +79,6 @@ class OrganizationRepository
|
|||||||
'email' => $data['email'] ?? '',
|
'email' => $data['email'] ?? '',
|
||||||
'website' => $data['website'] ?? '',
|
'website' => $data['website'] ?? '',
|
||||||
'description' => $data['description'] ?? null,
|
'description' => $data['description'] ?? null,
|
||||||
'type' => serialize($data['type'] ?? []),
|
|
||||||
'tstamp' => time(),
|
'tstamp' => time(),
|
||||||
],
|
],
|
||||||
['id' => $organizationId],
|
['id' => $organizationId],
|
||||||
@@ -109,4 +108,132 @@ class OrganizationRepository
|
|||||||
$types,
|
$types,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, int>
|
||||||
|
*/
|
||||||
|
public function getTagChoicesForOrganizationType(array $allowedTagIds = []): array
|
||||||
|
{
|
||||||
|
$rows = $this->connection->createQueryBuilder()
|
||||||
|
->select('DISTINCT t.id', 't.tag')
|
||||||
|
->from('tl_tags', 't')
|
||||||
|
->innerJoin('t', 'tl_tags_rel', 'r', 'r.tag_id = t.id')
|
||||||
|
->where('r.ptable = :ptable')
|
||||||
|
->andWhere('r.field = :field')
|
||||||
|
->setParameter('ptable', 'tl_organization')
|
||||||
|
->setParameter('field', 'tags')
|
||||||
|
->orderBy('t.tag', 'ASC')
|
||||||
|
->executeQuery()
|
||||||
|
->fetchAllAssociative();
|
||||||
|
|
||||||
|
if ([] === $rows) {
|
||||||
|
$rows = $this->connection->createQueryBuilder()
|
||||||
|
->select('t.id', 't.tag')
|
||||||
|
->from('tl_tags', 't')
|
||||||
|
->orderBy('t.tag', 'ASC')
|
||||||
|
->executeQuery()
|
||||||
|
->fetchAllAssociative();
|
||||||
|
}
|
||||||
|
|
||||||
|
$choices = [];
|
||||||
|
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$choices[(string) ($row['tag'] ?? '')] = (int) $row['id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$allowedTagIds = array_values(array_unique(array_map('intval', $allowedTagIds)));
|
||||||
|
|
||||||
|
if ([] !== $allowedTagIds) {
|
||||||
|
$choices = array_filter(
|
||||||
|
$choices,
|
||||||
|
static fn (int $id): bool => in_array($id, $allowedTagIds, true),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $choices;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return array<int> */
|
||||||
|
public function getTagIdsForOrganization(int $organizationId): array
|
||||||
|
{
|
||||||
|
if ($organizationId <= 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$ids = $this->connection->createQueryBuilder()
|
||||||
|
->select('tag_id')
|
||||||
|
->from('tl_tags_rel')
|
||||||
|
->where('ptable = :ptable')
|
||||||
|
->andWhere('field = :field')
|
||||||
|
->andWhere('pid = :pid')
|
||||||
|
->setParameter('ptable', 'tl_organization')
|
||||||
|
->setParameter('field', 'tags')
|
||||||
|
->setParameter('pid', $organizationId, ParameterType::INTEGER)
|
||||||
|
->orderBy('tag_id', 'ASC')
|
||||||
|
->executeQuery()
|
||||||
|
->fetchFirstColumn();
|
||||||
|
|
||||||
|
return array_values(array_unique(array_map('intval', $ids)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param array<int|string> $tagIds */
|
||||||
|
public function assignTagsToOrganization(int $organizationId, array $tagIds): void
|
||||||
|
{
|
||||||
|
$this->connection->delete(
|
||||||
|
'tl_tags_rel',
|
||||||
|
['ptable' => 'tl_organization', 'field' => 'tags', 'pid' => $organizationId],
|
||||||
|
['pid' => ParameterType::INTEGER],
|
||||||
|
);
|
||||||
|
|
||||||
|
$tagIds = array_values(array_unique(array_map('intval', $tagIds)));
|
||||||
|
|
||||||
|
if ([] === $tagIds) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$allowedTagIds = $this->getAllowedTagIdsForOrganization();
|
||||||
|
$tagIds = array_values(array_intersect($tagIds, $allowedTagIds));
|
||||||
|
|
||||||
|
foreach ($tagIds as $tagId) {
|
||||||
|
$this->connection->insert(
|
||||||
|
'tl_tags_rel',
|
||||||
|
[
|
||||||
|
'tag_id' => $tagId,
|
||||||
|
'pid' => $organizationId,
|
||||||
|
'ptable' => 'tl_organization',
|
||||||
|
'field' => 'tags',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'tag_id' => ParameterType::INTEGER,
|
||||||
|
'pid' => ParameterType::INTEGER,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return array<int> */
|
||||||
|
private function getAllowedTagIdsForOrganization(): array
|
||||||
|
{
|
||||||
|
$ids = $this->connection->createQueryBuilder()
|
||||||
|
->select('DISTINCT r.tag_id')
|
||||||
|
->from('tl_tags_rel', 'r')
|
||||||
|
->where('r.ptable = :ptable')
|
||||||
|
->andWhere('r.field = :field')
|
||||||
|
->setParameter('ptable', 'tl_organization')
|
||||||
|
->setParameter('field', 'tags')
|
||||||
|
->executeQuery()
|
||||||
|
->fetchFirstColumn();
|
||||||
|
|
||||||
|
$ids = array_values(array_unique(array_map('intval', $ids)));
|
||||||
|
|
||||||
|
if ([] !== $ids) {
|
||||||
|
return $ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_values(array_unique(array_map('intval', $this->connection->createQueryBuilder()
|
||||||
|
->select('id')
|
||||||
|
->from('tl_tags')
|
||||||
|
->executeQuery()
|
||||||
|
->fetchFirstColumn())));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user