From e69654eba12cd9fca0fe15a8547c5fa0a6ffb1a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6rner?= Date: Tue, 5 Aug 2025 16:06:16 +0200 Subject: [PATCH 01/23] update dependencies --- composer.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.json b/composer.json index 3eb27a1..ee951fe 100644 --- a/composer.json +++ b/composer.json @@ -24,12 +24,12 @@ "issues": "https://github.com/heimrichhannot/contao-multilingual-fields-bundle/issues" }, "require": { - "php": "^7.4 || ^8.0", - "contao/core-bundle": "^4.9", - "heimrichhannot/contao-utils-bundle": "^2.241", - "symfony/config": "^4.4 || ^5.0", - "symfony/dependency-injection": "^4.4 || ^5.0", - "symfony/http-foundation": "^4.4 || ^5.0" + "php": "^8.1", + "contao/core-bundle": "^4.13 || ^5.0", + "heimrichhannot/contao-utils-bundle": "^2.241 || ^3.0", + "symfony/config": "^5.4 || ^6.0 || ^7.0", + "symfony/dependency-injection": "^5.4 || ^6.0 || ^7.0", + "symfony/http-foundation": "^5.4 || ^6.0 || ^7.0" }, "autoload": { "psr-4": { From 495cf316f1770905df27d1335a4e637695a06138 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6rner?= Date: Wed, 6 Aug 2025 07:40:27 +0200 Subject: [PATCH 02/23] current working state --- .../Contao/LoadDataContainerListener.php | 107 +++++++++--------- src/Util/DcaUtil.php | 57 ++++++++++ 2 files changed, 113 insertions(+), 51 deletions(-) create mode 100644 src/Util/DcaUtil.php diff --git a/src/EventListener/Contao/LoadDataContainerListener.php b/src/EventListener/Contao/LoadDataContainerListener.php index b45b3dc..f991127 100644 --- a/src/EventListener/Contao/LoadDataContainerListener.php +++ b/src/EventListener/Contao/LoadDataContainerListener.php @@ -8,6 +8,7 @@ namespace HeimrichHannot\MultilingualFieldsBundle\EventListener\Contao; +use Contao\CoreBundle\Slug\Slug; use Contao\StringUtil; use Contao\CoreBundle\DataContainer\PaletteManipulator; use Contao\CoreBundle\Intl\Locales; @@ -15,12 +16,10 @@ use Contao\Database; use Contao\DataContainer; use HeimrichHannot\MultilingualFieldsBundle\EventListener\DataContainer\LanguageEditSwitchButtonCallback; +use HeimrichHannot\MultilingualFieldsBundle\Util\DcaUtil; use HeimrichHannot\MultilingualFieldsBundle\Util\MultilingualFieldsUtil; -use HeimrichHannot\UtilsBundle\Dca\DcaUtil; use HeimrichHannot\UtilsBundle\StaticUtil\SUtils; -use HeimrichHannot\UtilsBundle\Util\Utils; use Symfony\Component\HttpFoundation\RequestStack; -use Symfony\Contracts\Translation\TranslatorInterface; /** * @Hook("loadDataContainer", priority=-256) @@ -35,32 +34,23 @@ class LoadDataContainerListener * @var array */ protected $bundleConfig; - /** - * @var DcaUtil - */ - protected $dcaUtil; protected MultilingualFieldsUtil $multilingualFieldsUtil; - private Utils $utils; private RequestStack $requestStack; private Locales $locales; - private TranslatorInterface $translator; public function __construct( array $bundleConfig, MultilingualFieldsUtil $multilingualFieldsUtil, - DcaUtil $dcaUtil, - Utils $utils, RequestStack $requestStack, Locales $locales, - TranslatorInterface $translator - ) { + private readonly Slug $slug, + private readonly DcaUtil $dcaUtil, + ) + { $this->bundleConfig = $bundleConfig; $this->multilingualFieldsUtil = $multilingualFieldsUtil; - $this->dcaUtil = $dcaUtil; - $this->utils = $utils; $this->requestStack = $requestStack; $this->locales = $locales; - $this->translator = $translator; } public function __invoke($table): void @@ -105,8 +95,8 @@ protected function initConfig($table) } foreach ($languages as $language) { - $translatedFieldname = $language.'_'.$field; - $selectorField = $language.'_translate_'.$field; + $translatedFieldname = $language . '_' . $field; + $selectorField = $language . '_translate_' . $field; $fieldDca = $dca['fields'][$field]; // adjust the label @@ -116,7 +106,7 @@ protected function initConfig($table) $label = $GLOBALS['TL_LANG'][$table][$field]; } - $translatedLabel[0] = ((string) $label[0]).' ('.$GLOBALS['TL_LANG']['LNG'][$language].')'; + $translatedLabel[0] = ((string)$label[0]) . ' (' . $GLOBALS['TL_LANG']['LNG'][$language] . ')'; $translatedLabel[1] = $label[1]; // release the reference @@ -135,29 +125,7 @@ protected function initConfig($table) $dca['fields'][$translatedFieldname]['eval']['tl_class'] = 'long clr'; } - // alias field? - $isAliasField = $fieldConfig['is_alias_field'] ?? false; - $aliasBaseField = $fieldConfig['alias_base_field'] ?? false; - - if ($isAliasField && $aliasBaseField) { - $dca['fields'][$translatedFieldname]['save_callback'] = [ - function ($value, DataContainer $dc) use ($translatedFieldname, $table, $language, $aliasBaseField) { - $baseFieldValue = $dc->activeRecord->{$language.'_translate_'.$aliasBaseField} ? - $dc->activeRecord->{$language.'_'.$aliasBaseField} : $dc->activeRecord->{$aliasBaseField}; - - return $this->dcaUtil->generateAlias( - $value, - $dc->id, - $table, - $baseFieldValue, - true, - [ - 'aliasField' => $translatedFieldname, - ] - ); - }, - ]; - } + $this->handleAliasField($dca, $fieldConfig, $translatedFieldname, $language); // add the original fields as readonly $readOnlyFields[] = $field; @@ -261,7 +229,9 @@ function ($value, DataContainer $dc) use ($translatedFieldname, $table, $languag } } - $paletteName = $this->dcaUtil->getCurrentPaletteName($table, (int) $dc->id) ?: 'default'; + + + $paletteName = $dc->getPalette() ?: 'default'; // create palette for editing the fields if ($isEditMode) { @@ -285,8 +255,8 @@ function ($value, DataContainer $dc) use ($translatedFieldname, $table, $languag } // sub palette field and selector in palette? if (!($selector = $this->dcaUtil->getSubPaletteFieldSelector($originalField, $table)) || - !\in_array($selector, StringUtil::trimsplit('[;,]', $dc->getPalette())) - ) { + !\in_array($selector, StringUtil::trimsplit('[;,]', $dc->getPalette())) + ) { $paletteManipulator->removeField($originalField); continue; @@ -315,14 +285,14 @@ function ($value, DataContainer $dc) use ($translatedFieldname, $table, $languag $paletteManipulator->applyToPalette($paletteName, $table); - $dca['palettes'][$paletteName] = 'mf_editLanguages;'.$dca['palettes'][$paletteName]; + $dca['palettes'][$paletteName] = 'mf_editLanguages;' . $dca['palettes'][$paletteName]; } else { - if ('tl_content' === $table) { - $dca['palettes'][$paletteName] = ($this->multilingualFieldsUtil->hasContentLanguageField($dc->id) ? 'mf_language,' : ''). - 'mf_editLanguages;'.$dca['palettes'][$paletteName]; - } else { - $dca['palettes'][$paletteName] = 'mf_editLanguages;'.$dca['palettes'][$paletteName]; + $pm = PaletteManipulator::create(); + if ('tl_content' === $table && $this->multilingualFieldsUtil->hasContentLanguageField($dc->id)) { + $pm->addField('mf_language', null, PaletteManipulator::POSITION_BEFORE); } + $pm->addField('mf_editLanguages', null, PaletteManipulator::POSITION_BEFORE); + $pm->applyToPalette($paletteName, $table); } }; } @@ -358,4 +328,39 @@ protected function addContentLanguageField(): void 'sql' => "varchar(5) NOT NULL default ''", ]; } + + private function handleAliasField(array &$dca, array $fieldConfig, string $translatedFieldName, string $language): void + { + if (!($fieldConfig['is_alias_field'] ?? false) || empty($fieldConfig['alias_base_field'])) { + return; + } + + $aliasBaseField = $fieldConfig['alias_base_field']; + $slug = $this->slug; + + $dca['fields'][$translatedFieldName]['save_callback'] = [ + function ($value, DataContainer $dc) use ($translatedFieldName, $language, $aliasBaseField, $slug) { + + $baseFieldValue = $dc->activeRecord->{$language . '_translate_' . $aliasBaseField} + ? $dc->activeRecord->{$language . '_' . $aliasBaseField} + : $dc->activeRecord->{$aliasBaseField}; + + $aliasExists = (static fn(string $alias): bool => Database::getInstance() + ->prepare("SELECT id FROM $dc->table WHERE $translatedFieldName=? AND id!=?") + ->execute($alias, $dc->id) + ->numRows > 0); + + // Generate an alias if there is none + if (!$value) { + $value = $slug->generate($baseFieldValue, [], $aliasExists); + } elseif (preg_match('/^[1-9]\d*$/', (string)$value)) { + throw new \Exception(sprintf($GLOBALS['TL_LANG']['ERR']['aliasNumeric'], $value)); + } elseif ($aliasExists($value)) { + throw new \Exception(sprintf($GLOBALS['TL_LANG']['ERR']['aliasExists'], $value)); + } + + return $value; + } + ]; + } } diff --git a/src/Util/DcaUtil.php b/src/Util/DcaUtil.php new file mode 100644 index 0000000..24b66e8 --- /dev/null +++ b/src/Util/DcaUtil.php @@ -0,0 +1,57 @@ + &$data) { + if (!empty($fields)) { + if (!\in_array($field, $fields)) { + continue; + } + } elseif (\in_array($field, $skipFields)) { + continue; + } + + switch ($data['inputType']) { + case 'checkbox': + case 'radio': + case 'radioTable': + $data['eval']['disabled'] = true; + + break; + + case 'select': + case 'imageSize': + $data['eval']['readonly'] = true; + $data['eval']['class'] = 'readonly'; + + break; + + case 'fileTree': + case 'metaWizard': + case 'tagsinput': + $data['eval']['readonly'] = true; + $data['eval']['tl_class'] = $data['eval']['tl_class'].' readonly'; + + break; + + case 'multiColumnEditor': + $data['eval']['readonly'] = true; + + $this->setFieldsToReadOnly($data['eval']['multiColumnEditor'], $config); + + break; + + default: + $data['eval']['readonly'] = true; + break; + } + } + } +} \ No newline at end of file From bac277975b33ca8d3037f7533e99cc395cd2f491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6rner?= Date: Wed, 6 Aug 2025 07:56:31 +0200 Subject: [PATCH 03/23] make bundle installable within contao 5 --- src/Util/MultilingualFieldsUtil.php | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/Util/MultilingualFieldsUtil.php b/src/Util/MultilingualFieldsUtil.php index c0e13cd..b6b8bd2 100644 --- a/src/Util/MultilingualFieldsUtil.php +++ b/src/Util/MultilingualFieldsUtil.php @@ -12,23 +12,14 @@ use Contao\ContentModel; use Contao\Controller; use Contao\Model; -use HeimrichHannot\UtilsBundle\Model\ModelUtil; class MultilingualFieldsUtil { - /** - * @var array - */ - protected $bundleConfig; - /** - * @var ModelUtil - */ - protected $modelUtil; - public function __construct(array $bundleConfig, ModelUtil $modelUtil) + public function __construct( + protected array $bundleConfig + ) { - $this->bundleConfig = $bundleConfig; - $this->modelUtil = $modelUtil; } public function isTranslatable(string $table) From 0fa42eb5743fc02d614c03cef9929f1e3c57eaba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6rner?= Date: Wed, 6 Aug 2025 10:20:39 +0200 Subject: [PATCH 04/23] make edit work --- composer.json | 4 +- config/services.yaml | 3 + src/DependencyInjection/Configuration.php | 2 +- .../Contao/LoadDataContainerListener.php | 143 +++--------------- .../DataContainer/ConfigOnPaletteListener.php | 78 ++++++++++ src/Multilingual/MultilingualField.php | 26 ++++ src/Multilingual/MultilingualTable.php | 26 ++++ src/Multilingual/TableBuilder.php | 44 ++++++ 8 files changed, 198 insertions(+), 128 deletions(-) create mode 100644 src/EventListener/DataContainer/ConfigOnPaletteListener.php create mode 100644 src/Multilingual/MultilingualField.php create mode 100644 src/Multilingual/MultilingualTable.php create mode 100644 src/Multilingual/TableBuilder.php diff --git a/composer.json b/composer.json index ee951fe..f144bde 100644 --- a/composer.json +++ b/composer.json @@ -24,8 +24,8 @@ "issues": "https://github.com/heimrichhannot/contao-multilingual-fields-bundle/issues" }, "require": { - "php": "^8.1", - "contao/core-bundle": "^4.13 || ^5.0", + "php": "^8.2", + "contao/core-bundle": "^5.3", "heimrichhannot/contao-utils-bundle": "^2.241 || ^3.0", "symfony/config": "^5.4 || ^6.0 || ^7.0", "symfony/dependency-injection": "^5.4 || ^6.0 || ^7.0", diff --git a/config/services.yaml b/config/services.yaml index 2a6d1a8..3bdb560 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -5,3 +5,6 @@ services: autowire: true bind: $bundleConfig: '%huh_multilingual_fields%' + + HeimrichHannot\MultilingualFieldsBundle\Multilingual\TableBuilder: + autowire: true diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index a2f90a1..cd93d10 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -13,7 +13,7 @@ class Configuration implements ConfigurationInterface { - const ROOT_ID = 'huh_multilingual_fields'; + public const ROOT_ID = 'huh_multilingual_fields'; /** * {@inheritdoc} diff --git a/src/EventListener/Contao/LoadDataContainerListener.php b/src/EventListener/Contao/LoadDataContainerListener.php index f991127..186cbcd 100644 --- a/src/EventListener/Contao/LoadDataContainerListener.php +++ b/src/EventListener/Contao/LoadDataContainerListener.php @@ -9,16 +9,14 @@ namespace HeimrichHannot\MultilingualFieldsBundle\EventListener\Contao; use Contao\CoreBundle\Slug\Slug; -use Contao\StringUtil; -use Contao\CoreBundle\DataContainer\PaletteManipulator; use Contao\CoreBundle\Intl\Locales; use Contao\CoreBundle\ServiceAnnotation\Hook; use Contao\Database; use Contao\DataContainer; +use HeimrichHannot\MultilingualFieldsBundle\EventListener\DataContainer\ConfigOnPaletteListener; use HeimrichHannot\MultilingualFieldsBundle\EventListener\DataContainer\LanguageEditSwitchButtonCallback; use HeimrichHannot\MultilingualFieldsBundle\Util\DcaUtil; use HeimrichHannot\MultilingualFieldsBundle\Util\MultilingualFieldsUtil; -use HeimrichHannot\UtilsBundle\StaticUtil\SUtils; use Symfony\Component\HttpFoundation\RequestStack; /** @@ -26,31 +24,20 @@ */ class LoadDataContainerListener { - const EDIT_LANGUAGES_PARAM = 'edit_languages'; + public const EDIT_LANGUAGES_PARAM = 'edit_languages'; protected static $processedTables = []; - /** - * @var array - */ - protected $bundleConfig; - protected MultilingualFieldsUtil $multilingualFieldsUtil; - private RequestStack $requestStack; - private Locales $locales; - public function __construct( - array $bundleConfig, - MultilingualFieldsUtil $multilingualFieldsUtil, - RequestStack $requestStack, - Locales $locales, - private readonly Slug $slug, - private readonly DcaUtil $dcaUtil, + private array $bundleConfig, + protected MultilingualFieldsUtil $multilingualFieldsUtil, + private readonly RequestStack $requestStack, + private readonly Locales $locales, + private readonly Slug $slug, + private readonly DcaUtil $dcaUtil, ) { $this->bundleConfig = $bundleConfig; - $this->multilingualFieldsUtil = $multilingualFieldsUtil; - $this->requestStack = $requestStack; - $this->locales = $locales; } public function __invoke($table): void @@ -81,7 +68,7 @@ protected function initConfig($table) $dca = &$GLOBALS['TL_DCA'][$table]; - $isEditMode = $request ? $request->query->get(static::EDIT_LANGUAGES_PARAM, false) : false; + $isEditMode = $request && $request->query->get(static::EDIT_LANGUAGES_PARAM, false); // add translated fields $paletteData = []; @@ -98,6 +85,9 @@ protected function initConfig($table) $translatedFieldname = $language . '_' . $field; $selectorField = $language . '_translate_' . $field; $fieldDca = $dca['fields'][$field]; + $languageName = $this->locales->getLocales(null)[$language] ?? $language; + + // adjust the label if (isset($dca['fields'][$field]['label'])) { @@ -106,7 +96,7 @@ protected function initConfig($table) $label = $GLOBALS['TL_LANG'][$table][$field]; } - $translatedLabel[0] = ((string)$label[0]) . ' (' . $GLOBALS['TL_LANG']['LNG'][$language] . ')'; + $translatedLabel[0] = ((string)$label[0]) . ' (' . $languageName . ')'; $translatedLabel[1] = $label[1]; // release the reference @@ -155,8 +145,10 @@ protected function initConfig($table) // add the selector $dca['fields'][$selectorField] = [ 'label' => [ - sprintf($GLOBALS['TL_LANG']['MSC']['multilingualFieldsBundle']['mf_translateField'][0], - $GLOBALS['TL_LANG']['LNG'][$language]), + sprintf( + $GLOBALS['TL_LANG']['MSC']['multilingualFieldsBundle']['mf_translateField'][0], + $languageName + ), $GLOBALS['TL_LANG']['MSC']['multilingualFieldsBundle']['mf_translateField'][1], ], 'exclude' => true, @@ -174,17 +166,6 @@ protected function initConfig($table) if ($isEditMode && 0 === array_search($language, $languages)) { $dca['fields'][$selectorField]['eval']['tl_class'] .= ' clr'; } - - // add the subpalette - $dca['palettes']['__selector__'][] = $selectorField; - $dca['subpalettes'][$selectorField] = $translatedFieldname; - - // add field to palette data - if (!isset($paletteData[$field])) { - $paletteData[$field] = []; - } - - $paletteData[$field][] = $selectorField; } } @@ -201,100 +182,12 @@ protected function initConfig($table) } // add language switch - $langId = $isEditMode ? 'MSC.multilingualFieldsBundle.mf_closeEditLanguages' : 'MSC.multilingualFieldsBundle.mf_editLanguages'; $dca['fields']['mf_editLanguages'] = [ 'inputType' => 'mf_editLanguages', 'input_field_callback' => [LanguageEditSwitchButtonCallback::class, '__invoke'], ]; - // create onload callback for the palette generation - $dca['config']['onload_callback'][] = function (DataContainer $dc = null) use ($isEditMode, $paletteData, $config, $table, &$dca) { - if (null === $dc || !$dc->id) { - return; - } - - // check sql condition - if (isset($config['sql_condition'])) { - $sqlCondition = $config['sql_condition']; - $sqlConditionValues = $config['sql_condition_values'] ?? []; - - $values = array_merge([$dc->id], $sqlConditionValues); - - $check = Database::getInstance()->prepare("SELECT id FROM $table WHERE id=? AND $sqlCondition")->limit(1); - - $check = \call_user_func_array([$check, 'execute'], $values); - - if ($check->numRows < 1) { - return; - } - } - - - - $paletteName = $dc->getPalette() ?: 'default'; - - // create palette for editing the fields - if ($isEditMode) { - $paletteManipulator = PaletteManipulator::create(); - - $translatableFields = $this->multilingualFieldsUtil->getTranslatableFields($table); - - // remove untranslatable fields - foreach ($dca['fields'] as $field => $data) { - if (!\in_array($field, $translatableFields) && !isset($data['eval']['translationConfig']) && !isset($data['eval']['translatedField'])) { - $paletteManipulator->removeField($field); - } - } - - foreach ($paletteData as $originalField => $fields) { - if (!\in_array($originalField, StringUtil::trimsplit('[;,]', $dc->getPalette()))) { - if (!$this->dcaUtil->isSubPaletteField($originalField, $table)) { - $paletteManipulator->removeField($originalField); - - continue; - } - // sub palette field and selector in palette? - if (!($selector = $this->dcaUtil->getSubPaletteFieldSelector($originalField, $table)) || - !\in_array($selector, StringUtil::trimsplit('[;,]', $dc->getPalette())) - ) { - $paletteManipulator->removeField($originalField); - - continue; - } - - // add the sub palette field as an ordinary field - if (isset($paletteData[$selector]) && \is_array($paletteData[$selector])) { - $selector = $paletteData[$selector][\count($paletteData[$selector]) - 1]; - } - - $paletteManipulator->addField($originalField, $selector); - } - - $lastInsertedField = $originalField; - - foreach ($fields as $field) { - $paletteManipulator->addField($field, $lastInsertedField); - - $lastInsertedField = $field; - } - - // remove selector behavior - unset($dca['fields'][$originalField]['eval']['submitOnChange']); - SUtils::array()::removeValue($originalField, $dca['palettes']['__selector__']); - } - - $paletteManipulator->applyToPalette($paletteName, $table); - - $dca['palettes'][$paletteName] = 'mf_editLanguages;' . $dca['palettes'][$paletteName]; - } else { - $pm = PaletteManipulator::create(); - if ('tl_content' === $table && $this->multilingualFieldsUtil->hasContentLanguageField($dc->id)) { - $pm->addField('mf_language', null, PaletteManipulator::POSITION_BEFORE); - } - $pm->addField('mf_editLanguages', null, PaletteManipulator::POSITION_BEFORE); - $pm->applyToPalette($paletteName, $table); - } - }; + $dca['config']['onpalette_callback'][] = [ConfigOnPaletteListener::class, '__invoke']; } protected function addContentLanguageField(): void diff --git a/src/EventListener/DataContainer/ConfigOnPaletteListener.php b/src/EventListener/DataContainer/ConfigOnPaletteListener.php new file mode 100644 index 0000000..72c6558 --- /dev/null +++ b/src/EventListener/DataContainer/ConfigOnPaletteListener.php @@ -0,0 +1,78 @@ +requestStack->getCurrentRequest() + ?->query->get(LoadDataContainerListener::EDIT_LANGUAGES_PARAM, false) ?? false; + + return match ($isEditMode) { + true => $this->buildEditPalette($palette, $dc), + false => $this->addEditFields($palette, $dc), + }; + } + + private function addEditFields(string $palette, DataContainer $dc): string + { + $prependPalette = 'mf_editLanguages;'; + + if ('tl_content' === $dc->table && $this->fieldsUtil->hasContentLanguageField($dc->id)) { + $prependPalette = 'mf_language,' . $prependPalette; + } + return $prependPalette.$palette; + } + + private function buildEditPalette(string $originalPalette, DataContainer $dc): string + { + $mlTable = $this->tableBuilder->buildTableFor($dc->table); + if (!$mlTable) { + return $originalPalette; + } + + $paletteFields = StringUtil::trimsplit('[;,]', $originalPalette); + $paletteManipulator = PaletteManipulator::create(); + + foreach ($paletteFields as $paletteField) { + if (str_starts_with($paletteField, '{')) { + continue; + } + $mlField = $mlTable->getField($paletteField); + if (!$mlField) { + $paletteManipulator->removeField($paletteField); + continue; + } + + foreach ($mlTable->languages as $language) { + $selectorFieldName = $mlField->getSelectorFieldNameFor($language); + $paletteManipulator->addField($selectorFieldName, $paletteField); + if ($dc->getCurrentRecord()[$selectorFieldName] ?? false) { + $paletteManipulator->addField($mlField->getFieldNameFor($language), $selectorFieldName); + } + } + $paletteManipulator->removeField($paletteField); + } + + $palette = $paletteManipulator->applyToString($originalPalette); + + return 'mf_editLanguages;' . $palette; + } +} \ No newline at end of file diff --git a/src/Multilingual/MultilingualField.php b/src/Multilingual/MultilingualField.php new file mode 100644 index 0000000..65325c8 --- /dev/null +++ b/src/Multilingual/MultilingualField.php @@ -0,0 +1,26 @@ +fieldname = $fieldConfig['name']; + } + + public function getFieldNameFor(string $language): string + { + return $language . '_' . $this->fieldname; + } + + public function getSelectorFieldNameFor(string $language): string + { + return $language . '_translate_' . $this->fieldname; + + } +} \ No newline at end of file diff --git a/src/Multilingual/MultilingualTable.php b/src/Multilingual/MultilingualTable.php new file mode 100644 index 0000000..2534e45 --- /dev/null +++ b/src/Multilingual/MultilingualTable.php @@ -0,0 +1,26 @@ +fields; + } + + public function getField(string $field): ?MultilingualField + { + return $this->fields[$field] ?? null; + } +} \ No newline at end of file diff --git a/src/Multilingual/TableBuilder.php b/src/Multilingual/TableBuilder.php new file mode 100644 index 0000000..ff0d5c6 --- /dev/null +++ b/src/Multilingual/TableBuilder.php @@ -0,0 +1,44 @@ +parameterBag->has(Configuration::ROOT_ID)) { + return null; + } + + $bundleConfig = $this->parameterBag->get(Configuration::ROOT_ID); + if (empty($bundleConfig['data_containers'][$table]) || !\is_array($bundleConfig['data_containers'][$table])) { + return null; + } + + $dca = &$GLOBALS['TL_DCA'][$table]; + + $fields = []; + foreach ($bundleConfig['data_containers'][$table]['fields'] as $fieldConfig) { + $field = new MultilingualField($fieldConfig); + $fields[$field->fieldname] = $field; + } + + if (empty($fields)) { + return null; + } + + return new MultilingualTable( + $fields, + $bundleConfig['languages'] ?? [], + ); + } +} \ No newline at end of file From c005825e7f374106389396b140e5de3944d47acd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6rner?= Date: Wed, 6 Aug 2025 10:30:52 +0200 Subject: [PATCH 05/23] run rector --- rector.php | 6 ++---- src/Util/DcaUtil.php | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/rector.php b/rector.php index b24ec35..b66b529 100644 --- a/rector.php +++ b/rector.php @@ -13,15 +13,13 @@ return RectorConfig::configure() ->withPaths([ __DIR__ . '/src', - // __DIR__ . '/contao', - + __DIR__ . '/contao', ]) ->withRules([ AddVoidReturnTypeWhereNoReturnRector::class, # In Vorbereitung für PHP 8.4: - ExplicitNullableParamTypeRector::class + ExplicitNullableParamTypeRector::class ]) - ->withImportNames( importShortClasses: false, removeUnusedImports: true diff --git a/src/Util/DcaUtil.php b/src/Util/DcaUtil.php index 24b66e8..d4970d8 100644 --- a/src/Util/DcaUtil.php +++ b/src/Util/DcaUtil.php @@ -4,7 +4,7 @@ class DcaUtil { - public function setFieldsToReadOnly(&$dca, array $config = []) + public function setFieldsToReadOnly(&$dca, array $config = []): void { $skipFields = $config['skipFields'] ?? []; $fields = $config['fields'] ?? []; From 7c2e645f417f1be0f6b8e5502737486b3f1ddbf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6rner?= Date: Wed, 6 Aug 2025 10:33:59 +0200 Subject: [PATCH 06/23] more rector magic --- composer.json | 8 ++++---- rector.php | 6 +++--- src/DependencyInjection/Configuration.php | 2 +- .../Contao/IsVisibleElementListener.php | 17 ++++------------- .../Contao/LoadDataContainerListener.php | 6 ++---- .../Contao/ReplaceInsertTagsListener.php | 12 ++++-------- .../Contao/SqlGetFromDcaListener.php | 6 ++---- .../LanguageEditSwitchButtonCallback.php | 13 +------------ 8 files changed, 21 insertions(+), 49 deletions(-) diff --git a/composer.json b/composer.json index f144bde..a72fe82 100644 --- a/composer.json +++ b/composer.json @@ -26,10 +26,10 @@ "require": { "php": "^8.2", "contao/core-bundle": "^5.3", - "heimrichhannot/contao-utils-bundle": "^2.241 || ^3.0", - "symfony/config": "^5.4 || ^6.0 || ^7.0", - "symfony/dependency-injection": "^5.4 || ^6.0 || ^7.0", - "symfony/http-foundation": "^5.4 || ^6.0 || ^7.0" + "heimrichhannot/contao-utils-bundle": "^3.0", + "symfony/config": "^6.4 || ^7.0", + "symfony/dependency-injection": "^6.4 || ^7.0", + "symfony/http-foundation": "^6.4 || ^7.0" }, "autoload": { "psr-4": { diff --git a/rector.php b/rector.php index b66b529..dbd9b25 100644 --- a/rector.php +++ b/rector.php @@ -25,12 +25,12 @@ removeUnusedImports: true ) ->withSets([ - LevelSetList::UP_TO_PHP_74, + LevelSetList::UP_TO_PHP_80, SymfonySetList::SYMFONY_44, SymfonySetList::SYMFONY_CONSTRUCTOR_INJECTION, // # Erst mit Symfony 6 (Contao 5) nutzen: // // SymfonySetList::ANNOTATIONS_TO_ATTRIBUTES, - ContaoLevelSetList::UP_TO_CONTAO_49, + ContaoLevelSetList::UP_TO_CONTAO_413, ContaoSetList::FQCN, -// ContaoSetList::ANNOTATIONS_TO_ATTRIBUTES, + ContaoSetList::ANNOTATIONS_TO_ATTRIBUTES, ]); \ No newline at end of file diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index cd93d10..1031829 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -18,7 +18,7 @@ class Configuration implements ConfigurationInterface /** * {@inheritdoc} */ - public function getConfigTreeBuilder() + public function getConfigTreeBuilder(): TreeBuilder { $treeBuilder = new TreeBuilder(static::ROOT_ID); diff --git a/src/EventListener/Contao/IsVisibleElementListener.php b/src/EventListener/Contao/IsVisibleElementListener.php index 82650ab..f4c5158 100644 --- a/src/EventListener/Contao/IsVisibleElementListener.php +++ b/src/EventListener/Contao/IsVisibleElementListener.php @@ -8,24 +8,15 @@ namespace HeimrichHannot\MultilingualFieldsBundle\EventListener\Contao; -use Contao\CoreBundle\ServiceAnnotation\Hook; +use Contao\CoreBundle\DependencyInjection\Attribute\AsHook; use HeimrichHannot\MultilingualFieldsBundle\Util\MultilingualFieldsUtil; use HeimrichHannot\UtilsBundle\Util\Utils; -/** - * @Hook("isVisibleElement") - */ +#[AsHook('isVisibleElement')] class IsVisibleElementListener { - protected MultilingualFieldsUtil $multilingualFieldsUtil; - private Utils $utils; - - public function __construct( - MultilingualFieldsUtil $multilingualFieldsUtil, - Utils $utils - ) { - $this->multilingualFieldsUtil = $multilingualFieldsUtil; - $this->utils = $utils; + public function __construct(protected MultilingualFieldsUtil $multilingualFieldsUtil, private Utils $utils) + { } public function __invoke($element, $return) diff --git a/src/EventListener/Contao/LoadDataContainerListener.php b/src/EventListener/Contao/LoadDataContainerListener.php index 186cbcd..0dff3de 100644 --- a/src/EventListener/Contao/LoadDataContainerListener.php +++ b/src/EventListener/Contao/LoadDataContainerListener.php @@ -8,9 +8,9 @@ namespace HeimrichHannot\MultilingualFieldsBundle\EventListener\Contao; +use Contao\CoreBundle\DependencyInjection\Attribute\AsHook; use Contao\CoreBundle\Slug\Slug; use Contao\CoreBundle\Intl\Locales; -use Contao\CoreBundle\ServiceAnnotation\Hook; use Contao\Database; use Contao\DataContainer; use HeimrichHannot\MultilingualFieldsBundle\EventListener\DataContainer\ConfigOnPaletteListener; @@ -19,9 +19,7 @@ use HeimrichHannot\MultilingualFieldsBundle\Util\MultilingualFieldsUtil; use Symfony\Component\HttpFoundation\RequestStack; -/** - * @Hook("loadDataContainer", priority=-256) - */ +#[AsHook('loadDataContainer', priority: -256)] class LoadDataContainerListener { public const EDIT_LANGUAGES_PARAM = 'edit_languages'; diff --git a/src/EventListener/Contao/ReplaceInsertTagsListener.php b/src/EventListener/Contao/ReplaceInsertTagsListener.php index 8b74b68..4dd2f96 100644 --- a/src/EventListener/Contao/ReplaceInsertTagsListener.php +++ b/src/EventListener/Contao/ReplaceInsertTagsListener.php @@ -8,15 +8,13 @@ namespace HeimrichHannot\MultilingualFieldsBundle\EventListener\Contao; +use Contao\CoreBundle\DependencyInjection\Attribute\AsHook; use Contao\Controller; use Contao\CoreBundle\Framework\ContaoFramework; -use Contao\CoreBundle\ServiceAnnotation\Hook; use HeimrichHannot\MultilingualFieldsBundle\Util\MultilingualFieldsUtil; use HeimrichHannot\UtilsBundle\Util\Utils; -/** - * @Hook("replaceInsertTags") - */ +#[AsHook('replaceInsertTags')] class ReplaceInsertTagsListener { /** @@ -27,23 +25,21 @@ class ReplaceInsertTagsListener * @var ContaoFramework */ protected $framework; - private Utils $utils; public function __construct( ContaoFramework $framework, MultilingualFieldsUtil $multilingualFieldsUtil, - Utils $utils + private Utils $utils ) { $this->framework = $framework; $this->multilingualFieldsUtil = $multilingualFieldsUtil; - $this->utils = $utils; } public function __invoke($tag) { $tagData = explode('::', $tag); - if (0 !== strpos($tagData[0], 'mf')) { + if (!str_starts_with($tagData[0], 'mf')) { return false; } diff --git a/src/EventListener/Contao/SqlGetFromDcaListener.php b/src/EventListener/Contao/SqlGetFromDcaListener.php index e3dfc3e..e6d3467 100644 --- a/src/EventListener/Contao/SqlGetFromDcaListener.php +++ b/src/EventListener/Contao/SqlGetFromDcaListener.php @@ -8,12 +8,10 @@ namespace HeimrichHannot\MultilingualFieldsBundle\EventListener\Contao; -use Contao\CoreBundle\ServiceAnnotation\Hook; +use Contao\CoreBundle\DependencyInjection\Attribute\AsHook; use HeimrichHannot\MultilingualFieldsBundle\Util\MultilingualFieldsUtil; -/** - * @Hook("sqlGetFromDca") - */ +#[AsHook('sqlGetFromDca')] class SqlGetFromDcaListener { /** diff --git a/src/EventListener/DataContainer/LanguageEditSwitchButtonCallback.php b/src/EventListener/DataContainer/LanguageEditSwitchButtonCallback.php index 8406ee1..caf1764 100644 --- a/src/EventListener/DataContainer/LanguageEditSwitchButtonCallback.php +++ b/src/EventListener/DataContainer/LanguageEditSwitchButtonCallback.php @@ -10,19 +10,8 @@ class LanguageEditSwitchButtonCallback { - private Utils $utils; - private RequestStack $requestStack; - private TranslatorInterface $translator; - - public function __construct( - Utils $utils, - RequestStack $requestStack, - TranslatorInterface $translator - ) + public function __construct(private Utils $utils, private RequestStack $requestStack, private TranslatorInterface $translator) { - $this->utils = $utils; - $this->requestStack = $requestStack; - $this->translator = $translator; } public function __invoke(DataContainer $dc, string $label): string From fafb4c54b412f9b2c97ef5b4cab9b52c6f2335f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6rner?= Date: Wed, 6 Aug 2025 10:43:55 +0200 Subject: [PATCH 07/23] fix phpstan reports --- phpstan.neon | 8 ++++---- .../Contao/LoadDataContainerListener.php | 7 ++++--- .../Contao/ReplaceInsertTagsListener.php | 19 ++++++++++--------- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index 08ccddf..3931af5 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,10 +1,10 @@ parameters: - level: 1 + level: 2 paths: - src -# - contao -# universalObjectCratesClasses: -# - Contao\Model + - contao + universalObjectCratesClasses: + - Contao\Model # - Contao\Template includes: - vendor/phpstan/phpstan-symfony/extension.neon \ No newline at end of file diff --git a/src/EventListener/Contao/LoadDataContainerListener.php b/src/EventListener/Contao/LoadDataContainerListener.php index 0dff3de..bf5bffc 100644 --- a/src/EventListener/Contao/LoadDataContainerListener.php +++ b/src/EventListener/Contao/LoadDataContainerListener.php @@ -231,10 +231,11 @@ private function handleAliasField(array &$dca, array $fieldConfig, string $trans $dca['fields'][$translatedFieldName]['save_callback'] = [ function ($value, DataContainer $dc) use ($translatedFieldName, $language, $aliasBaseField, $slug) { + $currentRecord = $dc->getCurrentRecord(); - $baseFieldValue = $dc->activeRecord->{$language . '_translate_' . $aliasBaseField} - ? $dc->activeRecord->{$language . '_' . $aliasBaseField} - : $dc->activeRecord->{$aliasBaseField}; + $baseFieldValue = $currentRecord[$language . '_translate_' . $aliasBaseField] + ? $currentRecord[$language . '_' . $aliasBaseField] + : $currentRecord[$aliasBaseField]; $aliasExists = (static fn(string $alias): bool => Database::getInstance() ->prepare("SELECT id FROM $dc->table WHERE $translatedFieldName=? AND id!=?") diff --git a/src/EventListener/Contao/ReplaceInsertTagsListener.php b/src/EventListener/Contao/ReplaceInsertTagsListener.php index 4dd2f96..7ff3f04 100644 --- a/src/EventListener/Contao/ReplaceInsertTagsListener.php +++ b/src/EventListener/Contao/ReplaceInsertTagsListener.php @@ -11,6 +11,10 @@ use Contao\CoreBundle\DependencyInjection\Attribute\AsHook; use Contao\Controller; use Contao\CoreBundle\Framework\ContaoFramework; +use Contao\CoreBundle\InsertTag\InsertTagParser; +use Contao\FaqModel; +use Contao\NewsModel; +use HeimrichHannot\EventRegistrationBundle\Model\CalendarEventsModel; use HeimrichHannot\MultilingualFieldsBundle\Util\MultilingualFieldsUtil; use HeimrichHannot\UtilsBundle\Util\Utils; @@ -20,16 +24,17 @@ class ReplaceInsertTagsListener /** * @var MultilingualFieldsUtil */ - protected $multilingualFieldsUtil; + protected MultilingualFieldsUtil $multilingualFieldsUtil; /** * @var ContaoFramework */ - protected $framework; + protected ContaoFramework $framework; public function __construct( ContaoFramework $framework, MultilingualFieldsUtil $multilingualFieldsUtil, - private Utils $utils + private Utils $utils, + private readonly InsertTagParser $insertTagParser, ) { $this->framework = $framework; $this->multilingualFieldsUtil = $multilingualFieldsUtil; @@ -67,9 +72,7 @@ public function __invoke($tag) } if (!$this->multilingualFieldsUtil->isTranslatable($table)) { - return $this->framework->getAdapter(Controller::class)->replaceInsertTags( - '{{'.$type.'_url::'.$entityObj->id.'}}', false - ); + return $this->insertTagParser->replace('{{'.$type.'_url::'.$entityObj->id.'}}'); } if (empty($GLOBALS['TL_DCA'][$table])) { @@ -83,9 +86,7 @@ public function __invoke($tag) return false; } - $url = $this->framework->getAdapter(Controller::class)->replaceInsertTags( - '{{changelanguage_link_url::'.$archive->jumpTo.'::'.$language.'}}' - ); + $url = $this->insertTagParser->replace('{{changelanguage_link_url::'.$archive->jumpTo.'::'.$language.'}}'); // alias $entityObj = $this->multilingualFieldsUtil->translateModel($table, $entityObj, $language); From 2e7450f9e4b18771a0c3ee4ae2b344e480ef436e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6rner?= Date: Wed, 6 Aug 2025 10:46:33 +0200 Subject: [PATCH 08/23] run ecs --- ecs.php | 72 +++++++++---------- src/ContaoManager/Plugin.php | 2 +- src/DependencyInjection/Configuration.php | 3 - ...mrichHannotMultilingualFieldsExtension.php | 3 - .../Contao/IsVisibleElementListener.php | 10 +-- .../Contao/LoadDataContainerListener.php | 38 +++++----- .../Contao/ReplaceInsertTagsListener.php | 30 +++----- .../Contao/SqlGetFromDcaListener.php | 3 - .../DataContainer/ConfigOnPaletteListener.php | 10 +-- .../LanguageEditSwitchButtonCallback.php | 9 ++- src/Multilingual/MultilingualField.php | 6 +- src/Multilingual/MultilingualTable.php | 5 +- src/Multilingual/TableBuilder.php | 5 +- src/Util/DcaUtil.php | 4 +- src/Util/MultilingualFieldsUtil.php | 20 +++--- 15 files changed, 99 insertions(+), 121 deletions(-) diff --git a/ecs.php b/ecs.php index 1284f50..cb909d7 100644 --- a/ecs.php +++ b/ecs.php @@ -1,37 +1,37 @@ withPaths([ -// __DIR__ . '/src', -// // __DIR__ . '/contao', -// -// ]) -// -// // add a single rule -// ->withRules([ -// NoUnusedImportsFixer::class, -// BracesPositionFixer::class, -// ]) -// // add sets - group of rules -// ->withPreparedSets( -// arrays: true, -// comments: true, -// docblocks: true, -// spaces: true, -// namespaces: true, -// ) -// ->withPhpCsFixerSets(symfony: true) -// ->withSkip([ -// NotOperatorWithSuccessorSpaceFixer::class, -// MethodChainingIndentationFixer::class => [ -// '*/DependencyInjection/Configuration.php', -// ], -// ]); \ No newline at end of file + +declare(strict_types=1); + +use PhpCsFixer\Fixer\Basic\BracesPositionFixer; +use PhpCsFixer\Fixer\Import\NoUnusedImportsFixer; +use PhpCsFixer\Fixer\Operator\NotOperatorWithSuccessorSpaceFixer; +use PhpCsFixer\Fixer\Whitespace\MethodChainingIndentationFixer; +use Symplify\EasyCodingStandard\Config\ECSConfig; + +return ECSConfig::configure() + ->withPaths([ + __DIR__ . '/src', + // __DIR__ . '/contao', + + ]) + + // add a single rule + ->withRules([ + NoUnusedImportsFixer::class, + BracesPositionFixer::class, + ]) + // add sets - group of rules + ->withPreparedSets( + arrays: true, + comments: true, + docblocks: true, + spaces: true, + namespaces: true, + ) + ->withPhpCsFixerSets(symfony: true) + ->withSkip([ + NotOperatorWithSuccessorSpaceFixer::class, + MethodChainingIndentationFixer::class => [ + '*/DependencyInjection/Configuration.php', + ], + ]); \ No newline at end of file diff --git a/src/ContaoManager/Plugin.php b/src/ContaoManager/Plugin.php index 9b2a7c3..1c3c09a 100644 --- a/src/ContaoManager/Plugin.php +++ b/src/ContaoManager/Plugin.php @@ -35,7 +35,7 @@ public function getBundles(ParserInterface $parser): array return [ BundleConfig::create(HeimrichHannotMultilingualFieldsBundle::class) - ->setLoadAfter($loadAfter) + ->setLoadAfter($loadAfter), ]; } diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 1031829..2b8067f 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -15,9 +15,6 @@ class Configuration implements ConfigurationInterface { public const ROOT_ID = 'huh_multilingual_fields'; - /** - * {@inheritdoc} - */ public function getConfigTreeBuilder(): TreeBuilder { $treeBuilder = new TreeBuilder(static::ROOT_ID); diff --git a/src/DependencyInjection/HeimrichHannotMultilingualFieldsExtension.php b/src/DependencyInjection/HeimrichHannotMultilingualFieldsExtension.php index 91bfc0a..f416e72 100644 --- a/src/DependencyInjection/HeimrichHannotMultilingualFieldsExtension.php +++ b/src/DependencyInjection/HeimrichHannotMultilingualFieldsExtension.php @@ -13,9 +13,6 @@ class HeimrichHannotMultilingualFieldsExtension extends Extension { - /** - * {@inheritdoc} - */ public function load(array $configs, ContainerBuilder $container): void { $configuration = new Configuration(); diff --git a/src/EventListener/Contao/IsVisibleElementListener.php b/src/EventListener/Contao/IsVisibleElementListener.php index f4c5158..f527028 100644 --- a/src/EventListener/Contao/IsVisibleElementListener.php +++ b/src/EventListener/Contao/IsVisibleElementListener.php @@ -15,8 +15,10 @@ #[AsHook('isVisibleElement')] class IsVisibleElementListener { - public function __construct(protected MultilingualFieldsUtil $multilingualFieldsUtil, private Utils $utils) - { + public function __construct( + protected MultilingualFieldsUtil $multilingualFieldsUtil, + private Utils $utils, + ) { } public function __invoke($element, $return) @@ -32,11 +34,11 @@ public function __invoke($element, $return) // adjust fields if ($this->multilingualFieldsUtil->isTranslatable('tl_content')) { foreach ($this->multilingualFieldsUtil->getTranslatableFields('tl_content') as $field) { - if (!$element->{$GLOBALS['TL_LANGUAGE'].'_translate_'.$field}) { + if (!$element->{$GLOBALS['TL_LANGUAGE'] . '_translate_' . $field}) { continue; } - $element->{$field} = $element->{$GLOBALS['TL_LANGUAGE'].'_'.$field}; + $element->{$field} = $element->{$GLOBALS['TL_LANGUAGE'] . '_' . $field}; } } diff --git a/src/EventListener/Contao/LoadDataContainerListener.php b/src/EventListener/Contao/LoadDataContainerListener.php index bf5bffc..49be947 100644 --- a/src/EventListener/Contao/LoadDataContainerListener.php +++ b/src/EventListener/Contao/LoadDataContainerListener.php @@ -9,8 +9,8 @@ namespace HeimrichHannot\MultilingualFieldsBundle\EventListener\Contao; use Contao\CoreBundle\DependencyInjection\Attribute\AsHook; -use Contao\CoreBundle\Slug\Slug; use Contao\CoreBundle\Intl\Locales; +use Contao\CoreBundle\Slug\Slug; use Contao\Database; use Contao\DataContainer; use HeimrichHannot\MultilingualFieldsBundle\EventListener\DataContainer\ConfigOnPaletteListener; @@ -27,14 +27,13 @@ class LoadDataContainerListener protected static $processedTables = []; public function __construct( - private array $bundleConfig, + private array $bundleConfig, protected MultilingualFieldsUtil $multilingualFieldsUtil, - private readonly RequestStack $requestStack, - private readonly Locales $locales, - private readonly Slug $slug, - private readonly DcaUtil $dcaUtil, - ) - { + private readonly RequestStack $requestStack, + private readonly Locales $locales, + private readonly Slug $slug, + private readonly DcaUtil $dcaUtil, + ) { $this->bundleConfig = $bundleConfig; } @@ -85,8 +84,6 @@ protected function initConfig($table) $fieldDca = $dca['fields'][$field]; $languageName = $this->locales->getLocales(null)[$language] ?? $language; - - // adjust the label if (isset($dca['fields'][$field]['label'])) { $label = $dca['fields'][$field]['label']; @@ -94,7 +91,7 @@ protected function initConfig($table) $label = $GLOBALS['TL_LANG'][$table][$field]; } - $translatedLabel[0] = ((string)$label[0]) . ' (' . $languageName . ')'; + $translatedLabel[0] = ((string) $label[0]) . ' (' . $languageName . ')'; $translatedLabel[1] = $label[1]; // release the reference @@ -203,7 +200,12 @@ protected function addContentLanguageField(): void 'exclude' => true, 'filter' => true, 'inputType' => 'select', - 'eval' => ['includeBlankOption' => true, 'chosen' => true, 'rgxp' => 'locale', 'tl_class' => 'w50'], + 'eval' => [ + 'includeBlankOption' => true, + 'chosen' => true, + 'rgxp' => 'locale', + 'tl_class' => 'w50', + ], 'options_callback' => static function () use ($multilingualFieldsUtil, $locales) { $languages = $locales->getLocales(null, true); $options = []; @@ -237,22 +239,22 @@ function ($value, DataContainer $dc) use ($translatedFieldName, $language, $alia ? $currentRecord[$language . '_' . $aliasBaseField] : $currentRecord[$aliasBaseField]; - $aliasExists = (static fn(string $alias): bool => Database::getInstance() - ->prepare("SELECT id FROM $dc->table WHERE $translatedFieldName=? AND id!=?") - ->execute($alias, $dc->id) - ->numRows > 0); + $aliasExists = (static fn (string $alias): bool => Database::getInstance() + ->prepare("SELECT id FROM $dc->table WHERE $translatedFieldName=? AND id!=?") + ->execute($alias, $dc->id) + ->numRows > 0); // Generate an alias if there is none if (!$value) { $value = $slug->generate($baseFieldValue, [], $aliasExists); - } elseif (preg_match('/^[1-9]\d*$/', (string)$value)) { + } elseif (preg_match('/^[1-9]\d*$/', (string) $value)) { throw new \Exception(sprintf($GLOBALS['TL_LANG']['ERR']['aliasNumeric'], $value)); } elseif ($aliasExists($value)) { throw new \Exception(sprintf($GLOBALS['TL_LANG']['ERR']['aliasExists'], $value)); } return $value; - } + }, ]; } } diff --git a/src/EventListener/Contao/ReplaceInsertTagsListener.php b/src/EventListener/Contao/ReplaceInsertTagsListener.php index 7ff3f04..2b7275a 100644 --- a/src/EventListener/Contao/ReplaceInsertTagsListener.php +++ b/src/EventListener/Contao/ReplaceInsertTagsListener.php @@ -8,36 +8,22 @@ namespace HeimrichHannot\MultilingualFieldsBundle\EventListener\Contao; -use Contao\CoreBundle\DependencyInjection\Attribute\AsHook; use Contao\Controller; +use Contao\CoreBundle\DependencyInjection\Attribute\AsHook; use Contao\CoreBundle\Framework\ContaoFramework; use Contao\CoreBundle\InsertTag\InsertTagParser; -use Contao\FaqModel; -use Contao\NewsModel; -use HeimrichHannot\EventRegistrationBundle\Model\CalendarEventsModel; use HeimrichHannot\MultilingualFieldsBundle\Util\MultilingualFieldsUtil; use HeimrichHannot\UtilsBundle\Util\Utils; #[AsHook('replaceInsertTags')] class ReplaceInsertTagsListener { - /** - * @var MultilingualFieldsUtil - */ - protected MultilingualFieldsUtil $multilingualFieldsUtil; - /** - * @var ContaoFramework - */ - protected ContaoFramework $framework; - public function __construct( - ContaoFramework $framework, - MultilingualFieldsUtil $multilingualFieldsUtil, + protected ContaoFramework $framework, + protected MultilingualFieldsUtil $multilingualFieldsUtil, private Utils $utils, private readonly InsertTagParser $insertTagParser, ) { - $this->framework = $framework; - $this->multilingualFieldsUtil = $multilingualFieldsUtil; } public function __invoke($tag) @@ -61,7 +47,7 @@ public function __invoke($tag) if ('event' === $type) { $table = 'tl_calendar_events'; } else { - $table = 'tl_'.$type; + $table = 'tl_' . $type; } $entity = $tagData[1]; @@ -72,7 +58,7 @@ public function __invoke($tag) } if (!$this->multilingualFieldsUtil->isTranslatable($table)) { - return $this->insertTagParser->replace('{{'.$type.'_url::'.$entityObj->id.'}}'); + return $this->insertTagParser->replace('{{' . $type . '_url::' . $entityObj->id . '}}'); } if (empty($GLOBALS['TL_DCA'][$table])) { @@ -82,16 +68,16 @@ public function __invoke($tag) $ptable = $dca['config']['ptable'] ?? null; - if (!$ptable || null === ($archive = $this->utils->model()->findOneModelInstanceBy($ptable, [$ptable.'.id=?'], [$entityObj->pid]))) { + if (!$ptable || null === ($archive = $this->utils->model()->findOneModelInstanceBy($ptable, [$ptable . '.id=?'], [$entityObj->pid]))) { return false; } - $url = $this->insertTagParser->replace('{{changelanguage_link_url::'.$archive->jumpTo.'::'.$language.'}}'); + $url = $this->insertTagParser->replace('{{changelanguage_link_url::' . $archive->jumpTo . '::' . $language . '}}'); // alias $entityObj = $this->multilingualFieldsUtil->translateModel($table, $entityObj, $language); - $url .= '/'.$entityObj->alias; + $url .= '/' . $entityObj->alias; return $url; } diff --git a/src/EventListener/Contao/SqlGetFromDcaListener.php b/src/EventListener/Contao/SqlGetFromDcaListener.php index e6d3467..ac1b41c 100644 --- a/src/EventListener/Contao/SqlGetFromDcaListener.php +++ b/src/EventListener/Contao/SqlGetFromDcaListener.php @@ -19,9 +19,6 @@ class SqlGetFromDcaListener */ protected $multilingualFieldsUtil; - /** - * SqlGetFromDcaListener constructor. - */ public function __construct(MultilingualFieldsUtil $multilingualFieldsUtil) { $this->multilingualFieldsUtil = $multilingualFieldsUtil; diff --git a/src/EventListener/DataContainer/ConfigOnPaletteListener.php b/src/EventListener/DataContainer/ConfigOnPaletteListener.php index 72c6558..463f6a5 100644 --- a/src/EventListener/DataContainer/ConfigOnPaletteListener.php +++ b/src/EventListener/DataContainer/ConfigOnPaletteListener.php @@ -16,13 +16,12 @@ public function __construct( private readonly RequestStack $requestStack, private readonly MultilingualFieldsUtil $fieldsUtil, private readonly TableBuilder $tableBuilder, - ) - { + ) { } public function __invoke(string $palette, DataContainer $dc): string { - $isEditMode = (bool)$this->requestStack->getCurrentRequest() + $isEditMode = (bool) $this->requestStack->getCurrentRequest() ?->query->get(LoadDataContainerListener::EDIT_LANGUAGES_PARAM, false) ?? false; return match ($isEditMode) { @@ -38,7 +37,8 @@ private function addEditFields(string $palette, DataContainer $dc): string if ('tl_content' === $dc->table && $this->fieldsUtil->hasContentLanguageField($dc->id)) { $prependPalette = 'mf_language,' . $prependPalette; } - return $prependPalette.$palette; + + return $prependPalette . $palette; } private function buildEditPalette(string $originalPalette, DataContainer $dc): string @@ -75,4 +75,4 @@ private function buildEditPalette(string $originalPalette, DataContainer $dc): s return 'mf_editLanguages;' . $palette; } -} \ No newline at end of file +} diff --git a/src/EventListener/DataContainer/LanguageEditSwitchButtonCallback.php b/src/EventListener/DataContainer/LanguageEditSwitchButtonCallback.php index caf1764..626e2e7 100644 --- a/src/EventListener/DataContainer/LanguageEditSwitchButtonCallback.php +++ b/src/EventListener/DataContainer/LanguageEditSwitchButtonCallback.php @@ -10,8 +10,11 @@ class LanguageEditSwitchButtonCallback { - public function __construct(private Utils $utils, private RequestStack $requestStack, private TranslatorInterface $translator) - { + public function __construct( + private Utils $utils, + private RequestStack $requestStack, + private TranslatorInterface $translator, + ) { } public function __invoke(DataContainer $dc, string $label): string @@ -44,4 +47,4 @@ public function __invoke(DataContainer $dc, string $label): string $text ); } -} \ No newline at end of file +} diff --git a/src/Multilingual/MultilingualField.php b/src/Multilingual/MultilingualField.php index 65325c8..4d879db 100644 --- a/src/Multilingual/MultilingualField.php +++ b/src/Multilingual/MultilingualField.php @@ -8,8 +8,7 @@ class MultilingualField public function __construct( private readonly array $fieldConfig, - ) - { + ) { $this->fieldname = $fieldConfig['name']; } @@ -21,6 +20,5 @@ public function getFieldNameFor(string $language): string public function getSelectorFieldNameFor(string $language): string { return $language . '_translate_' . $this->fieldname; - } -} \ No newline at end of file +} diff --git a/src/Multilingual/MultilingualTable.php b/src/Multilingual/MultilingualTable.php index 2534e45..3d3c53c 100644 --- a/src/Multilingual/MultilingualTable.php +++ b/src/Multilingual/MultilingualTable.php @@ -7,8 +7,7 @@ class MultilingualTable public function __construct( private readonly array $fields = [], public readonly array $languages = [], - ) - { + ) { } /** @@ -23,4 +22,4 @@ public function getField(string $field): ?MultilingualField { return $this->fields[$field] ?? null; } -} \ No newline at end of file +} diff --git a/src/Multilingual/TableBuilder.php b/src/Multilingual/TableBuilder.php index ff0d5c6..2147569 100644 --- a/src/Multilingual/TableBuilder.php +++ b/src/Multilingual/TableBuilder.php @@ -9,8 +9,7 @@ class TableBuilder { public function __construct( private readonly ParameterBagInterface $parameterBag, - ) - { + ) { } public function buildTableFor(string $table): ?MultilingualTable @@ -41,4 +40,4 @@ public function buildTableFor(string $table): ?MultilingualTable $bundleConfig['languages'] ?? [], ); } -} \ No newline at end of file +} diff --git a/src/Util/DcaUtil.php b/src/Util/DcaUtil.php index d4970d8..42befa6 100644 --- a/src/Util/DcaUtil.php +++ b/src/Util/DcaUtil.php @@ -37,7 +37,7 @@ public function setFieldsToReadOnly(&$dca, array $config = []): void case 'metaWizard': case 'tagsinput': $data['eval']['readonly'] = true; - $data['eval']['tl_class'] = $data['eval']['tl_class'].' readonly'; + $data['eval']['tl_class'] = $data['eval']['tl_class'] . ' readonly'; break; @@ -54,4 +54,4 @@ public function setFieldsToReadOnly(&$dca, array $config = []): void } } } -} \ No newline at end of file +} diff --git a/src/Util/MultilingualFieldsUtil.php b/src/Util/MultilingualFieldsUtil.php index b6b8bd2..1d1d420 100644 --- a/src/Util/MultilingualFieldsUtil.php +++ b/src/Util/MultilingualFieldsUtil.php @@ -8,18 +8,16 @@ namespace HeimrichHannot\MultilingualFieldsBundle\Util; -use Contao\Model\Collection; use Contao\ContentModel; use Contao\Controller; use Contao\Model; +use Contao\Model\Collection; class MultilingualFieldsUtil { - public function __construct( - protected array $bundleConfig - ) - { + protected array $bundleConfig, + ) { } public function isTranslatable(string $table) @@ -33,7 +31,7 @@ public function getTranslatableFields(string $table) return false; } - return array_map(fn($row) => $row['name'], $this->bundleConfig['data_containers'][$table]['fields']); + return array_map(fn ($row) => $row['name'], $this->bundleConfig['data_containers'][$table]['fields']); } public function translateModel(string $table, Model $model, string $language = ''): ?Model @@ -45,11 +43,11 @@ public function translateModel(string $table, Model $model, string $language = ' $language = $language ?: $GLOBALS['TL_LANGUAGE']; foreach ($translatableFields as $field) { - if (!$model->{$language.'_translate_'.$field}) { + if (!$model->{$language . '_translate_' . $field}) { continue; } - $model->{$field} = $model->{$language.'_'.$field}; + $model->{$field} = $model->{$language . '_' . $field}; } return $model; @@ -71,7 +69,7 @@ public function translateModels(string $table, Collection $models, string $langu public function getRenderedMultilingualContentElements(Collection $models): string { - return implode('', array_map(fn($element) => Controller::getContentElement($element), $this->translateModels('tl_content', $models))); + return implode('', array_map(fn ($element) => Controller::getContentElement($element), $this->translateModels('tl_content', $models))); } public function hasContentLanguageField($element = null): bool @@ -81,8 +79,8 @@ public function hasContentLanguageField($element = null): bool } if (null !== ($element = ContentModel::findByPk($element))) { - if (!isset($this->bundleConfig['content_language_select']['types']) || !\is_array($this->bundleConfig['content_language_select']['types']) || - empty($this->bundleConfig['content_language_select']['types'])) { + if (!isset($this->bundleConfig['content_language_select']['types']) || !\is_array($this->bundleConfig['content_language_select']['types']) + || empty($this->bundleConfig['content_language_select']['types'])) { return true; } From 63819e150343a734ee5f91e4be5dd72878f3528a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6rner?= Date: Wed, 6 Aug 2025 10:53:16 +0200 Subject: [PATCH 09/23] run and raise rector --- rector.php | 18 +++++++++++------- .../Contao/IsVisibleElementListener.php | 2 +- .../Contao/ReplaceInsertTagsListener.php | 4 ++-- .../DataContainer/ConfigOnPaletteListener.php | 2 +- .../LanguageEditSwitchButtonCallback.php | 6 +++--- 5 files changed, 18 insertions(+), 14 deletions(-) diff --git a/rector.php b/rector.php index dbd9b25..262429e 100644 --- a/rector.php +++ b/rector.php @@ -22,15 +22,19 @@ ]) ->withImportNames( importShortClasses: false, - removeUnusedImports: true + removeUnusedImports: true, + ) + ->withAttributesSets( + all: true, + ) + ->withComposerBased( + twig: true, + doctrine: true, + symfony: true, ) ->withSets([ - LevelSetList::UP_TO_PHP_80, - SymfonySetList::SYMFONY_44, - SymfonySetList::SYMFONY_CONSTRUCTOR_INJECTION, -// # Erst mit Symfony 6 (Contao 5) nutzen: -// // SymfonySetList::ANNOTATIONS_TO_ATTRIBUTES, - ContaoLevelSetList::UP_TO_CONTAO_413, + LevelSetList::UP_TO_PHP_82, + ContaoLevelSetList::UP_TO_CONTAO_53, ContaoSetList::FQCN, ContaoSetList::ANNOTATIONS_TO_ATTRIBUTES, ]); \ No newline at end of file diff --git a/src/EventListener/Contao/IsVisibleElementListener.php b/src/EventListener/Contao/IsVisibleElementListener.php index f527028..d459e76 100644 --- a/src/EventListener/Contao/IsVisibleElementListener.php +++ b/src/EventListener/Contao/IsVisibleElementListener.php @@ -17,7 +17,7 @@ class IsVisibleElementListener { public function __construct( protected MultilingualFieldsUtil $multilingualFieldsUtil, - private Utils $utils, + private readonly Utils $utils, ) { } diff --git a/src/EventListener/Contao/ReplaceInsertTagsListener.php b/src/EventListener/Contao/ReplaceInsertTagsListener.php index 2b7275a..55a43da 100644 --- a/src/EventListener/Contao/ReplaceInsertTagsListener.php +++ b/src/EventListener/Contao/ReplaceInsertTagsListener.php @@ -21,14 +21,14 @@ class ReplaceInsertTagsListener public function __construct( protected ContaoFramework $framework, protected MultilingualFieldsUtil $multilingualFieldsUtil, - private Utils $utils, + private readonly Utils $utils, private readonly InsertTagParser $insertTagParser, ) { } public function __invoke($tag) { - $tagData = explode('::', $tag); + $tagData = explode('::', (string) $tag); if (!str_starts_with($tagData[0], 'mf')) { return false; diff --git a/src/EventListener/DataContainer/ConfigOnPaletteListener.php b/src/EventListener/DataContainer/ConfigOnPaletteListener.php index 463f6a5..5276797 100644 --- a/src/EventListener/DataContainer/ConfigOnPaletteListener.php +++ b/src/EventListener/DataContainer/ConfigOnPaletteListener.php @@ -52,7 +52,7 @@ private function buildEditPalette(string $originalPalette, DataContainer $dc): s $paletteManipulator = PaletteManipulator::create(); foreach ($paletteFields as $paletteField) { - if (str_starts_with($paletteField, '{')) { + if (str_starts_with((string) $paletteField, '{')) { continue; } $mlField = $mlTable->getField($paletteField); diff --git a/src/EventListener/DataContainer/LanguageEditSwitchButtonCallback.php b/src/EventListener/DataContainer/LanguageEditSwitchButtonCallback.php index 626e2e7..65a1e77 100644 --- a/src/EventListener/DataContainer/LanguageEditSwitchButtonCallback.php +++ b/src/EventListener/DataContainer/LanguageEditSwitchButtonCallback.php @@ -11,9 +11,9 @@ class LanguageEditSwitchButtonCallback { public function __construct( - private Utils $utils, - private RequestStack $requestStack, - private TranslatorInterface $translator, + private readonly Utils $utils, + private readonly RequestStack $requestStack, + private readonly TranslatorInterface $translator, ) { } From dea01d358fc71134eb7fd9dbfaa2d70a3cd79309 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6rner?= Date: Wed, 6 Aug 2025 11:23:16 +0200 Subject: [PATCH 10/23] adjustments to service configuration --- config/services.yaml | 14 ++++++++++---- .../Contao/LoadDataContainerListener.php | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/config/services.yaml b/config/services.yaml index 3bdb560..d741437 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -1,10 +1,16 @@ services: + _defaults: + autowire: true + HeimrichHannot\MultilingualFieldsBundle\: resource: '../src/{EventListener,Util}/*' - public: true - autowire: true + autoconfigure: true bind: $bundleConfig: '%huh_multilingual_fields%' - HeimrichHannot\MultilingualFieldsBundle\Multilingual\TableBuilder: - autowire: true + HeimrichHannot\MultilingualFieldsBundle\EventListener\DataContainer\: + resource: '../src/EventListener/DataContainer/*' + autoconfigure: true + public: true + + HeimrichHannot\MultilingualFieldsBundle\Multilingual\TableBuilder: ~ \ No newline at end of file diff --git a/src/EventListener/Contao/LoadDataContainerListener.php b/src/EventListener/Contao/LoadDataContainerListener.php index 49be947..4052999 100644 --- a/src/EventListener/Contao/LoadDataContainerListener.php +++ b/src/EventListener/Contao/LoadDataContainerListener.php @@ -149,7 +149,7 @@ protected function initConfig($table) 'exclude' => true, 'inputType' => 'checkbox', 'eval' => [ - 'tl_class' => 'w50 translate-checkbox', + 'tl_class' => 'w50', 'submitOnChange' => true, 'translationField' => $translatedFieldname, 'translatedField' => $field, From c59d359bb001b2bf6c210c45f28718c9a6064c83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6rner?= Date: Wed, 6 Aug 2025 11:23:29 +0200 Subject: [PATCH 11/23] update asset stack --- .../js/contao-multilingual-fields-bundle.js | 2 +- .../contao-multilingual-fields-bundle.be.scss | 18 +--- package.json | 8 +- .../contao-multilingual-fields-bundle.css | 70 +++++++++++++++- .../contao-multilingual-fields-bundle.js | 83 ++++++++++++++++++- 5 files changed, 161 insertions(+), 20 deletions(-) diff --git a/assets/js/contao-multilingual-fields-bundle.js b/assets/js/contao-multilingual-fields-bundle.js index af34558..50606b2 100644 --- a/assets/js/contao-multilingual-fields-bundle.js +++ b/assets/js/contao-multilingual-fields-bundle.js @@ -1,4 +1,4 @@ -require('../scss/contao-multilingual-fields-bundle.be.scss'); +import '../scss/contao-multilingual-fields-bundle.be.scss'; function moveEditButton() { let widget = document.getElementById('mf_language_edit_switch_button_widget'); diff --git a/assets/scss/contao-multilingual-fields-bundle.be.scss b/assets/scss/contao-multilingual-fields-bundle.be.scss index 4f630ca..be7b153 100644 --- a/assets/scss/contao-multilingual-fields-bundle.be.scss +++ b/assets/scss/contao-multilingual-fields-bundle.be.scss @@ -1,4 +1,4 @@ -@import 'mixins/readonly'; +@use 'mixins/readonly' as readonly; .tl_tbox.nolegend { #mf_language_edit_switch_button_widget { @@ -47,13 +47,13 @@ // readonly controls .tl_chosen, select, .tl_submit { &.readonly, .readonly & { - @include readonly-widget(); + @include readonly.readonly-widget(); } } .widget.readonly { .tl_metawizard .tl_text, .tl_select, .mce-tinymce { - @include readonly-widget(); + @include readonly.readonly-widget(); } .mce-tinymce { @@ -62,15 +62,3 @@ } } } - -// checkboxes -.translate-checkbox { - margin-bottom: 3.5rem; - - & + [id^="sub_"] > .widget { - margin-top: -3.5rem; - margin-bottom: 3.5rem; - clear: both; - float: left; - } -} diff --git a/package.json b/package.json index b877894..5c468d7 100644 --- a/package.json +++ b/package.json @@ -3,9 +3,13 @@ "version": "1.0.0", "dependencies": {}, "devDependencies": { - "@symfony/webpack-encore": "^1.1", + "@babel/core": "^7.20.0", + "@babel/preset-env": "^7.20.0", + "webpack": "^5.72.0", + "webpack-cli": "^5.0.1", + "@symfony/webpack-encore": "^5.0", "sass": "^1.32.8", - "sass-loader": "^11.0.0" + "sass-loader": "^16.0.1" }, "scripts": { "build-dev": "yarn encore dev", diff --git a/public/assets/contao-multilingual-fields-bundle.css b/public/assets/contao-multilingual-fields-bundle.css index 48b99ef..f1275b2 100644 --- a/public/assets/contao-multilingual-fields-bundle.css +++ b/public/assets/contao-multilingual-fields-bundle.css @@ -1 +1,69 @@ -.tl_tbox.nolegend #mf_language_edit_switch_button_widget{margin-top:20px;min-height:unset}.tl_tbox.nolegend #mf_language_edit_switch_button{background-color:#eee;background-position:5px;background-size:20px 20px;border:1px solid #aaa;border-radius:2px;box-sizing:border-box;cursor:pointer;height:30px;min-height:unset;padding:7px 12px 7px 28px;transition:background .2s ease}.tl_tbox.nolegend #mf_language_edit_switch_button:hover{background-color:#ddd;color:#333}#mf_language_edit_switch_button{background-image:url(/bundles/heimrichhannotmultilingualfields/assets/images/pencil.33ceff4e.svg);background-position:0;background-repeat:no-repeat;background-size:contain;float:left;padding:1px 0 1px 23px}#mf_language_edit_switch_button.close{background-image:url(/bundles/heimrichhannotmultilingualfields/assets/images/pencil-off.ba7989e0.svg)}.readonly .tl_chosen,.readonly .tl_submit,.readonly select,.tl_chosen.readonly,.tl_submit.readonly,select.readonly{background-color:#f9f9f9;border:1px solid #c8c8c8;color:#bbb;cursor:not-allowed}.readonly .tl_chosen *,.readonly .tl_submit *,.readonly select *,.tl_chosen.readonly *,.tl_submit.readonly *,select.readonly *{cursor:not-allowed}.readonly .tl_chosen span,.readonly .tl_submit span,.readonly select span,.tl_chosen.readonly span,.tl_submit.readonly span,select.readonly span{color:#bbb}.widget.readonly .mce-tinymce,.widget.readonly .tl_metawizard .tl_text,.widget.readonly .tl_select{background-color:#f9f9f9;border:1px solid #c8c8c8;color:#bbb;cursor:not-allowed}.widget.readonly .mce-tinymce *,.widget.readonly .tl_metawizard .tl_text *,.widget.readonly .tl_select *{cursor:not-allowed}.widget.readonly .mce-tinymce span,.widget.readonly .tl_metawizard .tl_text span,.widget.readonly .tl_select span{color:#bbb}.widget.readonly .mce-tinymce .mce-edit-area{opacity:.7}.translate-checkbox{margin-bottom:3.5rem}.translate-checkbox+[id^=sub_]>.widget{clear:both;float:left;margin-bottom:3.5rem;margin-top:-3.5rem} \ No newline at end of file +/*!**********************************************************************************************************************************************************************************************************************************************************************************************************!*\ + !*** css ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[4].oneOf[1].use[1]!./node_modules/resolve-url-loader/index.js??ruleSet[1].rules[4].oneOf[1].use[2]!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[4].oneOf[1].use[3]!./assets/scss/contao-multilingual-fields-bundle.be.scss ***! + \**********************************************************************************************************************************************************************************************************************************************************************************************************/ +.tl_tbox.nolegend #mf_language_edit_switch_button_widget { + min-height: unset; + margin-top: 20px; +} +.tl_tbox.nolegend #mf_language_edit_switch_button { + min-height: unset; + padding-top: 13px; + height: 30px; + padding: 7px 12px; + padding: 7px 12px 7px 28px; + background-size: 20px 20px; + background-position: 5px center; + border: 1px solid #aaa; + border-radius: 2px; + box-sizing: border-box; + cursor: pointer; + background-color: #eee; + transition: background 0.2s ease; +} +.tl_tbox.nolegend #mf_language_edit_switch_button:hover { + background-color: #ddd; + color: #333; +} + +#mf_language_edit_switch_button { + float: left; + padding: 1px 0 1px 23px; + background-position: left center; + background-repeat: no-repeat; + background-size: contain; + background-image: url(/bundles/heimrichhannotmultilingualfields/assets/images/pencil.33ceff4e.svg); +} +#mf_language_edit_switch_button.close { + background-image: url(/bundles/heimrichhannotmultilingualfields/assets/images/pencil-off.ba7989e0.svg); +} + +.tl_chosen.readonly, .readonly .tl_chosen, select.readonly, .readonly select, .tl_submit.readonly, .readonly .tl_submit { + color: #bbb; + background-color: #f9f9f9; + border: 1px solid #c8c8c8; + cursor: not-allowed; +} +.tl_chosen.readonly *, .readonly .tl_chosen *, select.readonly *, .readonly select *, .tl_submit.readonly *, .readonly .tl_submit * { + cursor: not-allowed; +} +.tl_chosen.readonly span, .readonly .tl_chosen span, select.readonly span, .readonly select span, .tl_submit.readonly span, .readonly .tl_submit span { + color: #bbb; +} + +.widget.readonly .tl_metawizard .tl_text, .widget.readonly .tl_select, .widget.readonly .mce-tinymce { + color: #bbb; + background-color: #f9f9f9; + border: 1px solid #c8c8c8; + cursor: not-allowed; +} +.widget.readonly .tl_metawizard .tl_text *, .widget.readonly .tl_select *, .widget.readonly .mce-tinymce * { + cursor: not-allowed; +} +.widget.readonly .tl_metawizard .tl_text span, .widget.readonly .tl_select span, .widget.readonly .mce-tinymce span { + color: #bbb; +} +.widget.readonly .mce-tinymce .mce-edit-area { + opacity: 0.7; +} + +/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29udGFvLW11bHRpbGluZ3VhbC1maWVsZHMtYnVuZGxlLmNzcyIsIm1hcHBpbmdzIjoiOzs7QUFHRTtFQUNFO0VBQ0E7QUFGSjtBQUtFO0VBQ0U7RUFDQTtFQUNBO0VBQ0E7RUFFQTtFQUNBO0VBQ0E7RUFFQTtFQUNBO0VBQ0E7RUFDQTtFQUNBO0VBQ0E7QUFMSjtBQU9JO0VBQ0U7RUFDQTtBQUxOOztBQVVBO0VBQ0U7RUFDQTtFQUNBO0VBQ0E7RUFDQTtFQUVBO0FBUkY7QUFVRTtFQUNFO0FBUko7O0FBY0U7RUMvQ0E7RUFDQTtFQUNBO0VBQ0E7QURxQ0Y7QUNuQ0U7RUFDRTtBRHFDSjtBQ2xDRTtFQUNFO0FEb0NKOztBQU9FO0VDckRBO0VBQ0E7RUFDQTtFQUNBO0FEa0RGO0FDaERFO0VBQ0U7QURrREo7QUMvQ0U7RUFDRTtBRGlESjtBQURJO0VBQ0U7QUFHTixDIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vY29udGFvLW11bHRpbGluZ3VhbC1maWVsZHMtYnVuZGxlLy4vYXNzZXRzL3Njc3MvY29udGFvLW11bHRpbGluZ3VhbC1maWVsZHMtYnVuZGxlLmJlLnNjc3MiLCJ3ZWJwYWNrOi8vY29udGFvLW11bHRpbGluZ3VhbC1maWVsZHMtYnVuZGxlLy4vYXNzZXRzL3Njc3MvbWl4aW5zL19yZWFkb25seS5zY3NzIl0sInNvdXJjZXNDb250ZW50IjpbIkB1c2UgJ21peGlucy9yZWFkb25seScgYXMgcmVhZG9ubHk7XG5cbi50bF90Ym94Lm5vbGVnZW5kIHtcbiAgI21mX2xhbmd1YWdlX2VkaXRfc3dpdGNoX2J1dHRvbl93aWRnZXQge1xuICAgIG1pbi1oZWlnaHQ6IHVuc2V0O1xuICAgIG1hcmdpbi10b3A6IDIwcHg7XG4gIH1cblxuICAjbWZfbGFuZ3VhZ2VfZWRpdF9zd2l0Y2hfYnV0dG9uIHtcbiAgICBtaW4taGVpZ2h0OiB1bnNldDtcbiAgICBwYWRkaW5nLXRvcDogMTNweDtcbiAgICBoZWlnaHQ6IDMwcHg7XG4gICAgcGFkZGluZzogN3B4IDEycHg7XG5cbiAgICBwYWRkaW5nOiA3cHggMTJweCA3cHggMjhweDtcbiAgICBiYWNrZ3JvdW5kLXNpemU6IDIwcHggMjBweDtcbiAgICBiYWNrZ3JvdW5kLXBvc2l0aW9uOiA1cHggY2VudGVyO1xuXG4gICAgYm9yZGVyOiAxcHggc29saWQgI2FhYTtcbiAgICBib3JkZXItcmFkaXVzOiAycHg7XG4gICAgYm94LXNpemluZzogYm9yZGVyLWJveDtcbiAgICBjdXJzb3I6IHBvaW50ZXI7XG4gICAgYmFja2dyb3VuZC1jb2xvcjogI2VlZTtcbiAgICB0cmFuc2l0aW9uOiBiYWNrZ3JvdW5kIC4ycyBlYXNlO1xuXG4gICAgJjpob3ZlciB7XG4gICAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjZGRkO1xuICAgICAgY29sb3I6ICMzMzM7XG4gICAgfVxuICB9XG59XG5cbiNtZl9sYW5ndWFnZV9lZGl0X3N3aXRjaF9idXR0b24ge1xuICBmbG9hdDogbGVmdDtcbiAgcGFkZGluZzogMXB4IDAgMXB4IDIzcHg7XG4gIGJhY2tncm91bmQtcG9zaXRpb246IGxlZnQgY2VudGVyO1xuICBiYWNrZ3JvdW5kLXJlcGVhdDogbm8tcmVwZWF0O1xuICBiYWNrZ3JvdW5kLXNpemU6IGNvbnRhaW47XG5cbiAgYmFja2dyb3VuZC1pbWFnZTogdXJsKC4uL2ltZy9wZW5jaWwuc3ZnKTtcblxuICAmLmNsb3NlIHtcbiAgICBiYWNrZ3JvdW5kLWltYWdlOiB1cmwoLi4vaW1nL3BlbmNpbC1vZmYuc3ZnKTtcbiAgfVxufVxuXG4vLyByZWFkb25seSBjb250cm9sc1xuLnRsX2Nob3Nlbiwgc2VsZWN0LCAudGxfc3VibWl0IHtcbiAgJi5yZWFkb25seSwgLnJlYWRvbmx5ICYge1xuICAgIEBpbmNsdWRlIHJlYWRvbmx5LnJlYWRvbmx5LXdpZGdldCgpO1xuICB9XG59XG5cbi53aWRnZXQucmVhZG9ubHkge1xuICAudGxfbWV0YXdpemFyZCAudGxfdGV4dCwgLnRsX3NlbGVjdCwgLm1jZS10aW55bWNlIHtcbiAgICBAaW5jbHVkZSByZWFkb25seS5yZWFkb25seS13aWRnZXQoKTtcbiAgfVxuXG4gIC5tY2UtdGlueW1jZSB7XG4gICAgLm1jZS1lZGl0LWFyZWEge1xuICAgICAgb3BhY2l0eTogMC43O1xuICAgIH1cbiAgfVxufVxuIiwiQG1peGluIHJlYWRvbmx5LXdpZGdldCgpIHtcbiAgY29sb3I6ICNiYmI7XG4gIGJhY2tncm91bmQtY29sb3I6ICNmOWY5Zjk7XG4gIGJvcmRlcjogMXB4IHNvbGlkICNjOGM4Yzg7XG4gIGN1cnNvcjogbm90LWFsbG93ZWQ7XG5cbiAgKiB7XG4gICAgY3Vyc29yOiBub3QtYWxsb3dlZDtcbiAgfVxuXG4gIHNwYW4ge1xuICAgIGNvbG9yOiAjYmJiO1xuICB9XG59XG4iXSwibmFtZXMiOltdLCJzb3VyY2VSb290IjoiIn0=*/ \ No newline at end of file diff --git a/public/assets/contao-multilingual-fields-bundle.js b/public/assets/contao-multilingual-fields-bundle.js index bd64d01..fd8a94b 100644 --- a/public/assets/contao-multilingual-fields-bundle.js +++ b/public/assets/contao-multilingual-fields-bundle.js @@ -1 +1,82 @@ -(()=>{var e={807:(e,t,n)=>{"use strict";n.r(t)}},t={};function n(o){var r=t[o];if(void 0!==r)return r.exports;var d=t[o]={exports:{}};return e[o](d,d.exports,n),d.exports}n.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n(807),document.addEventListener("DOMContentLoaded",(function(){var e=document.getElementById("mf_language_edit_switch_button_widget"),t=e.getElementsByTagName("a")[0],n=document.getElementById("tl_buttons"),o=e.parentElement;t&&n&&(n.appendChild(t),o&&o.remove())}))})(); \ No newline at end of file +/******/ (() => { // webpackBootstrap +/******/ "use strict"; +/******/ var __webpack_modules__ = ({ + +/***/ "./assets/scss/contao-multilingual-fields-bundle.be.scss": +/*!***************************************************************!*\ + !*** ./assets/scss/contao-multilingual-fields-bundle.be.scss ***! + \***************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +// extracted by mini-css-extract-plugin + + +/***/ }) + +/******/ }); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/make namespace object */ +/******/ (() => { +/******/ // define __esModule on exports +/******/ __webpack_require__.r = (exports) => { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ })(); +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; +// This entry needs to be wrapped in an IIFE because it needs to be isolated against other modules in the chunk. +(() => { +/*!********************************************************!*\ + !*** ./assets/js/contao-multilingual-fields-bundle.js ***! + \********************************************************/ +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var _scss_contao_multilingual_fields_bundle_be_scss__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../scss/contao-multilingual-fields-bundle.be.scss */ "./assets/scss/contao-multilingual-fields-bundle.be.scss"); + +function moveEditButton() { + var widget = document.getElementById('mf_language_edit_switch_button_widget'); + var element = widget.getElementsByTagName('a')[0]; + var buttons = document.getElementById('tl_buttons'); + var elemParent = widget.parentElement; + if (element && buttons) { + buttons.appendChild(element); + if (elemParent) { + elemParent.remove(); + } + } +} +document.addEventListener('DOMContentLoaded', moveEditButton); +})(); + +/******/ })() +; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29udGFvLW11bHRpbGluZ3VhbC1maWVsZHMtYnVuZGxlLmpzIiwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7O0FBQUE7Ozs7Ozs7VUNBQTtVQUNBOztVQUVBO1VBQ0E7VUFDQTtVQUNBO1VBQ0E7VUFDQTtVQUNBO1VBQ0E7VUFDQTtVQUNBO1VBQ0E7VUFDQTtVQUNBOztVQUVBO1VBQ0E7O1VBRUE7VUFDQTtVQUNBOzs7OztXQ3RCQTtXQUNBO1dBQ0E7V0FDQSx1REFBdUQsaUJBQWlCO1dBQ3hFO1dBQ0EsZ0RBQWdELGFBQWE7V0FDN0QsRTs7Ozs7Ozs7Ozs7O0FDTjJEO0FBRTNELFNBQVNBLGNBQWNBLENBQUEsRUFBRztFQUN0QixJQUFJQyxNQUFNLEdBQUdDLFFBQVEsQ0FBQ0MsY0FBYyxDQUFDLHVDQUF1QyxDQUFDO0VBQzdFLElBQUlDLE9BQU8sR0FBR0gsTUFBTSxDQUFDSSxvQkFBb0IsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUM7RUFDakQsSUFBSUMsT0FBTyxHQUFHSixRQUFRLENBQUNDLGNBQWMsQ0FBQyxZQUFZLENBQUM7RUFDbkQsSUFBSUksVUFBVSxHQUFHTixNQUFNLENBQUNPLGFBQWE7RUFFckMsSUFBSUosT0FBTyxJQUFJRSxPQUFPLEVBQUU7SUFDcEJBLE9BQU8sQ0FBQ0csV0FBVyxDQUFDTCxPQUFPLENBQUM7SUFDNUIsSUFBSUcsVUFBVSxFQUFFO01BQ1pBLFVBQVUsQ0FBQ0csTUFBTSxDQUFDLENBQUM7SUFDdkI7RUFDSjtBQUNKO0FBRUFSLFFBQVEsQ0FBQ1MsZ0JBQWdCLENBQUMsa0JBQWtCLEVBQUVYLGNBQWMsQ0FBQyxDIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vY29udGFvLW11bHRpbGluZ3VhbC1maWVsZHMtYnVuZGxlLy4vYXNzZXRzL3Njc3MvY29udGFvLW11bHRpbGluZ3VhbC1maWVsZHMtYnVuZGxlLmJlLnNjc3M/ZDEyZSIsIndlYnBhY2s6Ly9jb250YW8tbXVsdGlsaW5ndWFsLWZpZWxkcy1idW5kbGUvd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vY29udGFvLW11bHRpbGluZ3VhbC1maWVsZHMtYnVuZGxlL3dlYnBhY2svcnVudGltZS9tYWtlIG5hbWVzcGFjZSBvYmplY3QiLCJ3ZWJwYWNrOi8vY29udGFvLW11bHRpbGluZ3VhbC1maWVsZHMtYnVuZGxlLy4vYXNzZXRzL2pzL2NvbnRhby1tdWx0aWxpbmd1YWwtZmllbGRzLWJ1bmRsZS5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyIvLyBleHRyYWN0ZWQgYnkgbWluaS1jc3MtZXh0cmFjdC1wbHVnaW5cbmV4cG9ydCB7fTsiLCIvLyBUaGUgbW9kdWxlIGNhY2hlXG52YXIgX193ZWJwYWNrX21vZHVsZV9jYWNoZV9fID0ge307XG5cbi8vIFRoZSByZXF1aXJlIGZ1bmN0aW9uXG5mdW5jdGlvbiBfX3dlYnBhY2tfcmVxdWlyZV9fKG1vZHVsZUlkKSB7XG5cdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuXHR2YXIgY2FjaGVkTW9kdWxlID0gX193ZWJwYWNrX21vZHVsZV9jYWNoZV9fW21vZHVsZUlkXTtcblx0aWYgKGNhY2hlZE1vZHVsZSAhPT0gdW5kZWZpbmVkKSB7XG5cdFx0cmV0dXJuIGNhY2hlZE1vZHVsZS5leHBvcnRzO1xuXHR9XG5cdC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpXG5cdHZhciBtb2R1bGUgPSBfX3dlYnBhY2tfbW9kdWxlX2NhY2hlX19bbW9kdWxlSWRdID0ge1xuXHRcdC8vIG5vIG1vZHVsZS5pZCBuZWVkZWRcblx0XHQvLyBubyBtb2R1bGUubG9hZGVkIG5lZWRlZFxuXHRcdGV4cG9ydHM6IHt9XG5cdH07XG5cblx0Ly8gRXhlY3V0ZSB0aGUgbW9kdWxlIGZ1bmN0aW9uXG5cdF9fd2VicGFja19tb2R1bGVzX19bbW9kdWxlSWRdKG1vZHVsZSwgbW9kdWxlLmV4cG9ydHMsIF9fd2VicGFja19yZXF1aXJlX18pO1xuXG5cdC8vIFJldHVybiB0aGUgZXhwb3J0cyBvZiB0aGUgbW9kdWxlXG5cdHJldHVybiBtb2R1bGUuZXhwb3J0cztcbn1cblxuIiwiLy8gZGVmaW5lIF9fZXNNb2R1bGUgb24gZXhwb3J0c1xuX193ZWJwYWNrX3JlcXVpcmVfXy5yID0gKGV4cG9ydHMpID0+IHtcblx0aWYodHlwZW9mIFN5bWJvbCAhPT0gJ3VuZGVmaW5lZCcgJiYgU3ltYm9sLnRvU3RyaW5nVGFnKSB7XG5cdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFN5bWJvbC50b1N0cmluZ1RhZywgeyB2YWx1ZTogJ01vZHVsZScgfSk7XG5cdH1cblx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsICdfX2VzTW9kdWxlJywgeyB2YWx1ZTogdHJ1ZSB9KTtcbn07IiwiaW1wb3J0ICcuLi9zY3NzL2NvbnRhby1tdWx0aWxpbmd1YWwtZmllbGRzLWJ1bmRsZS5iZS5zY3NzJztcblxuZnVuY3Rpb24gbW92ZUVkaXRCdXR0b24oKSB7XG4gICAgbGV0IHdpZGdldCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdtZl9sYW5ndWFnZV9lZGl0X3N3aXRjaF9idXR0b25fd2lkZ2V0Jyk7XG4gICAgbGV0IGVsZW1lbnQgPSB3aWRnZXQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoJ2EnKVswXTtcbiAgICBsZXQgYnV0dG9ucyA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd0bF9idXR0b25zJyk7XG4gICAgbGV0IGVsZW1QYXJlbnQgPSB3aWRnZXQucGFyZW50RWxlbWVudDtcblxuICAgIGlmIChlbGVtZW50ICYmIGJ1dHRvbnMpIHtcbiAgICAgICAgYnV0dG9ucy5hcHBlbmRDaGlsZChlbGVtZW50KTtcbiAgICAgICAgaWYgKGVsZW1QYXJlbnQpIHtcbiAgICAgICAgICAgIGVsZW1QYXJlbnQucmVtb3ZlKCk7XG4gICAgICAgIH1cbiAgICB9XG59XG5cbmRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ0RPTUNvbnRlbnRMb2FkZWQnLCBtb3ZlRWRpdEJ1dHRvbik7Il0sIm5hbWVzIjpbIm1vdmVFZGl0QnV0dG9uIiwid2lkZ2V0IiwiZG9jdW1lbnQiLCJnZXRFbGVtZW50QnlJZCIsImVsZW1lbnQiLCJnZXRFbGVtZW50c0J5VGFnTmFtZSIsImJ1dHRvbnMiLCJlbGVtUGFyZW50IiwicGFyZW50RWxlbWVudCIsImFwcGVuZENoaWxkIiwicmVtb3ZlIiwiYWRkRXZlbnRMaXN0ZW5lciJdLCJzb3VyY2VSb290IjoiIn0= \ No newline at end of file From d6ca39f6a34752e10acf917ba3da17d48ee2ce6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6rner?= Date: Wed, 6 Aug 2025 11:59:49 +0200 Subject: [PATCH 12/23] make conditions working again, styling adjustments --- assets/img/pencil-off.svg | 2 +- assets/img/pencil.svg | 2 +- .../contao-multilingual-fields-bundle.be.scss | 17 +++- .../contao-multilingual-fields-bundle.css | 70 +--------------- .../contao-multilingual-fields-bundle.js | 83 +------------------ public/assets/images/pencil-off.1f8c3c00.svg | 1 + public/assets/images/pencil-off.a6bd717c.svg | 1 + public/assets/images/pencil.d40bcf51.svg | 1 + public/assets/images/pencil.e8555421.svg | 1 + public/assets/manifest.json | 4 +- .../DataContainer/ConfigOnPaletteListener.php | 11 ++- src/Multilingual/MultilingualTable.php | 40 +++++++-- src/Multilingual/TableBuilder.php | 16 +++- 13 files changed, 76 insertions(+), 173 deletions(-) create mode 100644 public/assets/images/pencil-off.1f8c3c00.svg create mode 100644 public/assets/images/pencil-off.a6bd717c.svg create mode 100644 public/assets/images/pencil.d40bcf51.svg create mode 100644 public/assets/images/pencil.e8555421.svg diff --git a/assets/img/pencil-off.svg b/assets/img/pencil-off.svg index 41cbd71..b3ab2e1 100644 --- a/assets/img/pencil-off.svg +++ b/assets/img/pencil-off.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/img/pencil.svg b/assets/img/pencil.svg index 8d5e050..00677f0 100644 --- a/assets/img/pencil.svg +++ b/assets/img/pencil.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/scss/contao-multilingual-fields-bundle.be.scss b/assets/scss/contao-multilingual-fields-bundle.be.scss index be7b153..423f32a 100644 --- a/assets/scss/contao-multilingual-fields-bundle.be.scss +++ b/assets/scss/contao-multilingual-fields-bundle.be.scss @@ -31,11 +31,20 @@ } #mf_language_edit_switch_button { - float: left; - padding: 1px 0 1px 23px; - background-position: left center; + background-color: transparent; + background-position: 0; background-repeat: no-repeat; - background-size: contain; + border: none; + display: inline-block; + margin-left: 0px; + padding: 3px 0 3px 23px; + + float: left; + + //padding: 1px 0 1px 23px; + //background-position: left center; + //background-repeat: no-repeat; + //background-size: contain; background-image: url(../img/pencil.svg); diff --git a/public/assets/contao-multilingual-fields-bundle.css b/public/assets/contao-multilingual-fields-bundle.css index f1275b2..1636efc 100644 --- a/public/assets/contao-multilingual-fields-bundle.css +++ b/public/assets/contao-multilingual-fields-bundle.css @@ -1,69 +1 @@ -/*!**********************************************************************************************************************************************************************************************************************************************************************************************************!*\ - !*** css ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[4].oneOf[1].use[1]!./node_modules/resolve-url-loader/index.js??ruleSet[1].rules[4].oneOf[1].use[2]!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[4].oneOf[1].use[3]!./assets/scss/contao-multilingual-fields-bundle.be.scss ***! - \**********************************************************************************************************************************************************************************************************************************************************************************************************/ -.tl_tbox.nolegend #mf_language_edit_switch_button_widget { - min-height: unset; - margin-top: 20px; -} -.tl_tbox.nolegend #mf_language_edit_switch_button { - min-height: unset; - padding-top: 13px; - height: 30px; - padding: 7px 12px; - padding: 7px 12px 7px 28px; - background-size: 20px 20px; - background-position: 5px center; - border: 1px solid #aaa; - border-radius: 2px; - box-sizing: border-box; - cursor: pointer; - background-color: #eee; - transition: background 0.2s ease; -} -.tl_tbox.nolegend #mf_language_edit_switch_button:hover { - background-color: #ddd; - color: #333; -} - -#mf_language_edit_switch_button { - float: left; - padding: 1px 0 1px 23px; - background-position: left center; - background-repeat: no-repeat; - background-size: contain; - background-image: url(/bundles/heimrichhannotmultilingualfields/assets/images/pencil.33ceff4e.svg); -} -#mf_language_edit_switch_button.close { - background-image: url(/bundles/heimrichhannotmultilingualfields/assets/images/pencil-off.ba7989e0.svg); -} - -.tl_chosen.readonly, .readonly .tl_chosen, select.readonly, .readonly select, .tl_submit.readonly, .readonly .tl_submit { - color: #bbb; - background-color: #f9f9f9; - border: 1px solid #c8c8c8; - cursor: not-allowed; -} -.tl_chosen.readonly *, .readonly .tl_chosen *, select.readonly *, .readonly select *, .tl_submit.readonly *, .readonly .tl_submit * { - cursor: not-allowed; -} -.tl_chosen.readonly span, .readonly .tl_chosen span, select.readonly span, .readonly select span, .tl_submit.readonly span, .readonly .tl_submit span { - color: #bbb; -} - -.widget.readonly .tl_metawizard .tl_text, .widget.readonly .tl_select, .widget.readonly .mce-tinymce { - color: #bbb; - background-color: #f9f9f9; - border: 1px solid #c8c8c8; - cursor: not-allowed; -} -.widget.readonly .tl_metawizard .tl_text *, .widget.readonly .tl_select *, .widget.readonly .mce-tinymce * { - cursor: not-allowed; -} -.widget.readonly .tl_metawizard .tl_text span, .widget.readonly .tl_select span, .widget.readonly .mce-tinymce span { - color: #bbb; -} -.widget.readonly .mce-tinymce .mce-edit-area { - opacity: 0.7; -} - -/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29udGFvLW11bHRpbGluZ3VhbC1maWVsZHMtYnVuZGxlLmNzcyIsIm1hcHBpbmdzIjoiOzs7QUFHRTtFQUNFO0VBQ0E7QUFGSjtBQUtFO0VBQ0U7RUFDQTtFQUNBO0VBQ0E7RUFFQTtFQUNBO0VBQ0E7RUFFQTtFQUNBO0VBQ0E7RUFDQTtFQUNBO0VBQ0E7QUFMSjtBQU9JO0VBQ0U7RUFDQTtBQUxOOztBQVVBO0VBQ0U7RUFDQTtFQUNBO0VBQ0E7RUFDQTtFQUVBO0FBUkY7QUFVRTtFQUNFO0FBUko7O0FBY0U7RUMvQ0E7RUFDQTtFQUNBO0VBQ0E7QURxQ0Y7QUNuQ0U7RUFDRTtBRHFDSjtBQ2xDRTtFQUNFO0FEb0NKOztBQU9FO0VDckRBO0VBQ0E7RUFDQTtFQUNBO0FEa0RGO0FDaERFO0VBQ0U7QURrREo7QUMvQ0U7RUFDRTtBRGlESjtBQURJO0VBQ0U7QUFHTixDIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vY29udGFvLW11bHRpbGluZ3VhbC1maWVsZHMtYnVuZGxlLy4vYXNzZXRzL3Njc3MvY29udGFvLW11bHRpbGluZ3VhbC1maWVsZHMtYnVuZGxlLmJlLnNjc3MiLCJ3ZWJwYWNrOi8vY29udGFvLW11bHRpbGluZ3VhbC1maWVsZHMtYnVuZGxlLy4vYXNzZXRzL3Njc3MvbWl4aW5zL19yZWFkb25seS5zY3NzIl0sInNvdXJjZXNDb250ZW50IjpbIkB1c2UgJ21peGlucy9yZWFkb25seScgYXMgcmVhZG9ubHk7XG5cbi50bF90Ym94Lm5vbGVnZW5kIHtcbiAgI21mX2xhbmd1YWdlX2VkaXRfc3dpdGNoX2J1dHRvbl93aWRnZXQge1xuICAgIG1pbi1oZWlnaHQ6IHVuc2V0O1xuICAgIG1hcmdpbi10b3A6IDIwcHg7XG4gIH1cblxuICAjbWZfbGFuZ3VhZ2VfZWRpdF9zd2l0Y2hfYnV0dG9uIHtcbiAgICBtaW4taGVpZ2h0OiB1bnNldDtcbiAgICBwYWRkaW5nLXRvcDogMTNweDtcbiAgICBoZWlnaHQ6IDMwcHg7XG4gICAgcGFkZGluZzogN3B4IDEycHg7XG5cbiAgICBwYWRkaW5nOiA3cHggMTJweCA3cHggMjhweDtcbiAgICBiYWNrZ3JvdW5kLXNpemU6IDIwcHggMjBweDtcbiAgICBiYWNrZ3JvdW5kLXBvc2l0aW9uOiA1cHggY2VudGVyO1xuXG4gICAgYm9yZGVyOiAxcHggc29saWQgI2FhYTtcbiAgICBib3JkZXItcmFkaXVzOiAycHg7XG4gICAgYm94LXNpemluZzogYm9yZGVyLWJveDtcbiAgICBjdXJzb3I6IHBvaW50ZXI7XG4gICAgYmFja2dyb3VuZC1jb2xvcjogI2VlZTtcbiAgICB0cmFuc2l0aW9uOiBiYWNrZ3JvdW5kIC4ycyBlYXNlO1xuXG4gICAgJjpob3ZlciB7XG4gICAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjZGRkO1xuICAgICAgY29sb3I6ICMzMzM7XG4gICAgfVxuICB9XG59XG5cbiNtZl9sYW5ndWFnZV9lZGl0X3N3aXRjaF9idXR0b24ge1xuICBmbG9hdDogbGVmdDtcbiAgcGFkZGluZzogMXB4IDAgMXB4IDIzcHg7XG4gIGJhY2tncm91bmQtcG9zaXRpb246IGxlZnQgY2VudGVyO1xuICBiYWNrZ3JvdW5kLXJlcGVhdDogbm8tcmVwZWF0O1xuICBiYWNrZ3JvdW5kLXNpemU6IGNvbnRhaW47XG5cbiAgYmFja2dyb3VuZC1pbWFnZTogdXJsKC4uL2ltZy9wZW5jaWwuc3ZnKTtcblxuICAmLmNsb3NlIHtcbiAgICBiYWNrZ3JvdW5kLWltYWdlOiB1cmwoLi4vaW1nL3BlbmNpbC1vZmYuc3ZnKTtcbiAgfVxufVxuXG4vLyByZWFkb25seSBjb250cm9sc1xuLnRsX2Nob3Nlbiwgc2VsZWN0LCAudGxfc3VibWl0IHtcbiAgJi5yZWFkb25seSwgLnJlYWRvbmx5ICYge1xuICAgIEBpbmNsdWRlIHJlYWRvbmx5LnJlYWRvbmx5LXdpZGdldCgpO1xuICB9XG59XG5cbi53aWRnZXQucmVhZG9ubHkge1xuICAudGxfbWV0YXdpemFyZCAudGxfdGV4dCwgLnRsX3NlbGVjdCwgLm1jZS10aW55bWNlIHtcbiAgICBAaW5jbHVkZSByZWFkb25seS5yZWFkb25seS13aWRnZXQoKTtcbiAgfVxuXG4gIC5tY2UtdGlueW1jZSB7XG4gICAgLm1jZS1lZGl0LWFyZWEge1xuICAgICAgb3BhY2l0eTogMC43O1xuICAgIH1cbiAgfVxufVxuIiwiQG1peGluIHJlYWRvbmx5LXdpZGdldCgpIHtcbiAgY29sb3I6ICNiYmI7XG4gIGJhY2tncm91bmQtY29sb3I6ICNmOWY5Zjk7XG4gIGJvcmRlcjogMXB4IHNvbGlkICNjOGM4Yzg7XG4gIGN1cnNvcjogbm90LWFsbG93ZWQ7XG5cbiAgKiB7XG4gICAgY3Vyc29yOiBub3QtYWxsb3dlZDtcbiAgfVxuXG4gIHNwYW4ge1xuICAgIGNvbG9yOiAjYmJiO1xuICB9XG59XG4iXSwibmFtZXMiOltdLCJzb3VyY2VSb290IjoiIn0=*/ \ No newline at end of file +.tl_tbox.nolegend #mf_language_edit_switch_button_widget{margin-top:20px;min-height:unset}.tl_tbox.nolegend #mf_language_edit_switch_button{background-color:#eee;background-position:5px;background-size:20px 20px;border:1px solid #aaa;border-radius:2px;box-sizing:border-box;cursor:pointer;height:30px;min-height:unset;padding:7px 12px 7px 28px;transition:background .2s ease}.tl_tbox.nolegend #mf_language_edit_switch_button:hover{background-color:#ddd;color:#333}#mf_language_edit_switch_button{background-color:transparent;background-image:url(/bundles/heimrichhannotmultilingualfields/assets/images/pencil.e8555421.svg);background-position:0;background-repeat:no-repeat;border:none;display:inline-block;float:left;margin-left:0;padding:3px 0 3px 23px}#mf_language_edit_switch_button.close{background-image:url(/bundles/heimrichhannotmultilingualfields/assets/images/pencil-off.a6bd717c.svg)}.readonly .tl_chosen,.readonly .tl_submit,.readonly select,.tl_chosen.readonly,.tl_submit.readonly,select.readonly{background-color:#f9f9f9;border:1px solid #c8c8c8;color:#bbb;cursor:not-allowed}.readonly .tl_chosen *,.readonly .tl_submit *,.readonly select *,.tl_chosen.readonly *,.tl_submit.readonly *,select.readonly *{cursor:not-allowed}.readonly .tl_chosen span,.readonly .tl_submit span,.readonly select span,.tl_chosen.readonly span,.tl_submit.readonly span,select.readonly span{color:#bbb}.widget.readonly .mce-tinymce,.widget.readonly .tl_metawizard .tl_text,.widget.readonly .tl_select{background-color:#f9f9f9;border:1px solid #c8c8c8;color:#bbb;cursor:not-allowed}.widget.readonly .mce-tinymce *,.widget.readonly .tl_metawizard .tl_text *,.widget.readonly .tl_select *{cursor:not-allowed}.widget.readonly .mce-tinymce span,.widget.readonly .tl_metawizard .tl_text span,.widget.readonly .tl_select span{color:#bbb}.widget.readonly .mce-tinymce .mce-edit-area{opacity:.7} \ No newline at end of file diff --git a/public/assets/contao-multilingual-fields-bundle.js b/public/assets/contao-multilingual-fields-bundle.js index fd8a94b..4277dc2 100644 --- a/public/assets/contao-multilingual-fields-bundle.js +++ b/public/assets/contao-multilingual-fields-bundle.js @@ -1,82 +1 @@ -/******/ (() => { // webpackBootstrap -/******/ "use strict"; -/******/ var __webpack_modules__ = ({ - -/***/ "./assets/scss/contao-multilingual-fields-bundle.be.scss": -/*!***************************************************************!*\ - !*** ./assets/scss/contao-multilingual-fields-bundle.be.scss ***! - \***************************************************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -__webpack_require__.r(__webpack_exports__); -// extracted by mini-css-extract-plugin - - -/***/ }) - -/******/ }); -/************************************************************************/ -/******/ // The module cache -/******/ var __webpack_module_cache__ = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ // Check if module is in cache -/******/ var cachedModule = __webpack_module_cache__[moduleId]; -/******/ if (cachedModule !== undefined) { -/******/ return cachedModule.exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = __webpack_module_cache__[moduleId] = { -/******/ // no module.id needed -/******/ // no module.loaded needed -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/************************************************************************/ -/******/ /* webpack/runtime/make namespace object */ -/******/ (() => { -/******/ // define __esModule on exports -/******/ __webpack_require__.r = (exports) => { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ })(); -/******/ -/************************************************************************/ -var __webpack_exports__ = {}; -// This entry needs to be wrapped in an IIFE because it needs to be isolated against other modules in the chunk. -(() => { -/*!********************************************************!*\ - !*** ./assets/js/contao-multilingual-fields-bundle.js ***! - \********************************************************/ -__webpack_require__.r(__webpack_exports__); -/* harmony import */ var _scss_contao_multilingual_fields_bundle_be_scss__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../scss/contao-multilingual-fields-bundle.be.scss */ "./assets/scss/contao-multilingual-fields-bundle.be.scss"); - -function moveEditButton() { - var widget = document.getElementById('mf_language_edit_switch_button_widget'); - var element = widget.getElementsByTagName('a')[0]; - var buttons = document.getElementById('tl_buttons'); - var elemParent = widget.parentElement; - if (element && buttons) { - buttons.appendChild(element); - if (elemParent) { - elemParent.remove(); - } - } -} -document.addEventListener('DOMContentLoaded', moveEditButton); -})(); - -/******/ })() -; -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29udGFvLW11bHRpbGluZ3VhbC1maWVsZHMtYnVuZGxlLmpzIiwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7O0FBQUE7Ozs7Ozs7VUNBQTtVQUNBOztVQUVBO1VBQ0E7VUFDQTtVQUNBO1VBQ0E7VUFDQTtVQUNBO1VBQ0E7VUFDQTtVQUNBO1VBQ0E7VUFDQTtVQUNBOztVQUVBO1VBQ0E7O1VBRUE7VUFDQTtVQUNBOzs7OztXQ3RCQTtXQUNBO1dBQ0E7V0FDQSx1REFBdUQsaUJBQWlCO1dBQ3hFO1dBQ0EsZ0RBQWdELGFBQWE7V0FDN0QsRTs7Ozs7Ozs7Ozs7O0FDTjJEO0FBRTNELFNBQVNBLGNBQWNBLENBQUEsRUFBRztFQUN0QixJQUFJQyxNQUFNLEdBQUdDLFFBQVEsQ0FBQ0MsY0FBYyxDQUFDLHVDQUF1QyxDQUFDO0VBQzdFLElBQUlDLE9BQU8sR0FBR0gsTUFBTSxDQUFDSSxvQkFBb0IsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUM7RUFDakQsSUFBSUMsT0FBTyxHQUFHSixRQUFRLENBQUNDLGNBQWMsQ0FBQyxZQUFZLENBQUM7RUFDbkQsSUFBSUksVUFBVSxHQUFHTixNQUFNLENBQUNPLGFBQWE7RUFFckMsSUFBSUosT0FBTyxJQUFJRSxPQUFPLEVBQUU7SUFDcEJBLE9BQU8sQ0FBQ0csV0FBVyxDQUFDTCxPQUFPLENBQUM7SUFDNUIsSUFBSUcsVUFBVSxFQUFFO01BQ1pBLFVBQVUsQ0FBQ0csTUFBTSxDQUFDLENBQUM7SUFDdkI7RUFDSjtBQUNKO0FBRUFSLFFBQVEsQ0FBQ1MsZ0JBQWdCLENBQUMsa0JBQWtCLEVBQUVYLGNBQWMsQ0FBQyxDIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vY29udGFvLW11bHRpbGluZ3VhbC1maWVsZHMtYnVuZGxlLy4vYXNzZXRzL3Njc3MvY29udGFvLW11bHRpbGluZ3VhbC1maWVsZHMtYnVuZGxlLmJlLnNjc3M/ZDEyZSIsIndlYnBhY2s6Ly9jb250YW8tbXVsdGlsaW5ndWFsLWZpZWxkcy1idW5kbGUvd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vY29udGFvLW11bHRpbGluZ3VhbC1maWVsZHMtYnVuZGxlL3dlYnBhY2svcnVudGltZS9tYWtlIG5hbWVzcGFjZSBvYmplY3QiLCJ3ZWJwYWNrOi8vY29udGFvLW11bHRpbGluZ3VhbC1maWVsZHMtYnVuZGxlLy4vYXNzZXRzL2pzL2NvbnRhby1tdWx0aWxpbmd1YWwtZmllbGRzLWJ1bmRsZS5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyIvLyBleHRyYWN0ZWQgYnkgbWluaS1jc3MtZXh0cmFjdC1wbHVnaW5cbmV4cG9ydCB7fTsiLCIvLyBUaGUgbW9kdWxlIGNhY2hlXG52YXIgX193ZWJwYWNrX21vZHVsZV9jYWNoZV9fID0ge307XG5cbi8vIFRoZSByZXF1aXJlIGZ1bmN0aW9uXG5mdW5jdGlvbiBfX3dlYnBhY2tfcmVxdWlyZV9fKG1vZHVsZUlkKSB7XG5cdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuXHR2YXIgY2FjaGVkTW9kdWxlID0gX193ZWJwYWNrX21vZHVsZV9jYWNoZV9fW21vZHVsZUlkXTtcblx0aWYgKGNhY2hlZE1vZHVsZSAhPT0gdW5kZWZpbmVkKSB7XG5cdFx0cmV0dXJuIGNhY2hlZE1vZHVsZS5leHBvcnRzO1xuXHR9XG5cdC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpXG5cdHZhciBtb2R1bGUgPSBfX3dlYnBhY2tfbW9kdWxlX2NhY2hlX19bbW9kdWxlSWRdID0ge1xuXHRcdC8vIG5vIG1vZHVsZS5pZCBuZWVkZWRcblx0XHQvLyBubyBtb2R1bGUubG9hZGVkIG5lZWRlZFxuXHRcdGV4cG9ydHM6IHt9XG5cdH07XG5cblx0Ly8gRXhlY3V0ZSB0aGUgbW9kdWxlIGZ1bmN0aW9uXG5cdF9fd2VicGFja19tb2R1bGVzX19bbW9kdWxlSWRdKG1vZHVsZSwgbW9kdWxlLmV4cG9ydHMsIF9fd2VicGFja19yZXF1aXJlX18pO1xuXG5cdC8vIFJldHVybiB0aGUgZXhwb3J0cyBvZiB0aGUgbW9kdWxlXG5cdHJldHVybiBtb2R1bGUuZXhwb3J0cztcbn1cblxuIiwiLy8gZGVmaW5lIF9fZXNNb2R1bGUgb24gZXhwb3J0c1xuX193ZWJwYWNrX3JlcXVpcmVfXy5yID0gKGV4cG9ydHMpID0+IHtcblx0aWYodHlwZW9mIFN5bWJvbCAhPT0gJ3VuZGVmaW5lZCcgJiYgU3ltYm9sLnRvU3RyaW5nVGFnKSB7XG5cdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFN5bWJvbC50b1N0cmluZ1RhZywgeyB2YWx1ZTogJ01vZHVsZScgfSk7XG5cdH1cblx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsICdfX2VzTW9kdWxlJywgeyB2YWx1ZTogdHJ1ZSB9KTtcbn07IiwiaW1wb3J0ICcuLi9zY3NzL2NvbnRhby1tdWx0aWxpbmd1YWwtZmllbGRzLWJ1bmRsZS5iZS5zY3NzJztcblxuZnVuY3Rpb24gbW92ZUVkaXRCdXR0b24oKSB7XG4gICAgbGV0IHdpZGdldCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdtZl9sYW5ndWFnZV9lZGl0X3N3aXRjaF9idXR0b25fd2lkZ2V0Jyk7XG4gICAgbGV0IGVsZW1lbnQgPSB3aWRnZXQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoJ2EnKVswXTtcbiAgICBsZXQgYnV0dG9ucyA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd0bF9idXR0b25zJyk7XG4gICAgbGV0IGVsZW1QYXJlbnQgPSB3aWRnZXQucGFyZW50RWxlbWVudDtcblxuICAgIGlmIChlbGVtZW50ICYmIGJ1dHRvbnMpIHtcbiAgICAgICAgYnV0dG9ucy5hcHBlbmRDaGlsZChlbGVtZW50KTtcbiAgICAgICAgaWYgKGVsZW1QYXJlbnQpIHtcbiAgICAgICAgICAgIGVsZW1QYXJlbnQucmVtb3ZlKCk7XG4gICAgICAgIH1cbiAgICB9XG59XG5cbmRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ0RPTUNvbnRlbnRMb2FkZWQnLCBtb3ZlRWRpdEJ1dHRvbik7Il0sIm5hbWVzIjpbIm1vdmVFZGl0QnV0dG9uIiwid2lkZ2V0IiwiZG9jdW1lbnQiLCJnZXRFbGVtZW50QnlJZCIsImVsZW1lbnQiLCJnZXRFbGVtZW50c0J5VGFnTmFtZSIsImJ1dHRvbnMiLCJlbGVtUGFyZW50IiwicGFyZW50RWxlbWVudCIsImFwcGVuZENoaWxkIiwicmVtb3ZlIiwiYWRkRXZlbnRMaXN0ZW5lciJdLCJzb3VyY2VSb290IjoiIn0= \ No newline at end of file +(()=>{"use strict";document.addEventListener("DOMContentLoaded",function(){var e=document.getElementById("mf_language_edit_switch_button_widget"),t=e.getElementsByTagName("a")[0],n=document.getElementById("tl_buttons"),d=e.parentElement;t&&n&&(n.appendChild(t),d&&d.remove())})})(); \ No newline at end of file diff --git a/public/assets/images/pencil-off.1f8c3c00.svg b/public/assets/images/pencil-off.1f8c3c00.svg new file mode 100644 index 0000000..429f3c2 --- /dev/null +++ b/public/assets/images/pencil-off.1f8c3c00.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/assets/images/pencil-off.a6bd717c.svg b/public/assets/images/pencil-off.a6bd717c.svg new file mode 100644 index 0000000..b3ab2e1 --- /dev/null +++ b/public/assets/images/pencil-off.a6bd717c.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/assets/images/pencil.d40bcf51.svg b/public/assets/images/pencil.d40bcf51.svg new file mode 100644 index 0000000..d4d7095 --- /dev/null +++ b/public/assets/images/pencil.d40bcf51.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/assets/images/pencil.e8555421.svg b/public/assets/images/pencil.e8555421.svg new file mode 100644 index 0000000..00677f0 --- /dev/null +++ b/public/assets/images/pencil.e8555421.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/assets/manifest.json b/public/assets/manifest.json index bcbe0a2..ce58377 100644 --- a/public/assets/manifest.json +++ b/public/assets/manifest.json @@ -1,6 +1,6 @@ { "bundles/heimrichhannotmultilingualfields/assets/contao-multilingual-fields-bundle.css": "/bundles/heimrichhannotmultilingualfields/assets/contao-multilingual-fields-bundle.css", "bundles/heimrichhannotmultilingualfields/assets/contao-multilingual-fields-bundle.js": "/bundles/heimrichhannotmultilingualfields/assets/contao-multilingual-fields-bundle.js", - "bundles/heimrichhannotmultilingualfields/assets/images/pencil-off.svg": "/bundles/heimrichhannotmultilingualfields/assets/images/pencil-off.ba7989e0.svg", - "bundles/heimrichhannotmultilingualfields/assets/images/pencil.svg": "/bundles/heimrichhannotmultilingualfields/assets/images/pencil.33ceff4e.svg" + "bundles/heimrichhannotmultilingualfields/assets/images/pencil-off.svg": "/bundles/heimrichhannotmultilingualfields/assets/images/pencil-off.a6bd717c.svg", + "bundles/heimrichhannotmultilingualfields/assets/images/pencil.svg": "/bundles/heimrichhannotmultilingualfields/assets/images/pencil.e8555421.svg" } \ No newline at end of file diff --git a/src/EventListener/DataContainer/ConfigOnPaletteListener.php b/src/EventListener/DataContainer/ConfigOnPaletteListener.php index 5276797..199762b 100644 --- a/src/EventListener/DataContainer/ConfigOnPaletteListener.php +++ b/src/EventListener/DataContainer/ConfigOnPaletteListener.php @@ -6,6 +6,7 @@ use Contao\DataContainer; use Contao\StringUtil; use HeimrichHannot\MultilingualFieldsBundle\EventListener\Contao\LoadDataContainerListener; +use HeimrichHannot\MultilingualFieldsBundle\Multilingual\MultilingualTable; use HeimrichHannot\MultilingualFieldsBundle\Multilingual\TableBuilder; use HeimrichHannot\MultilingualFieldsBundle\Util\MultilingualFieldsUtil; use Symfony\Component\HttpFoundation\RequestStack; @@ -21,11 +22,16 @@ public function __construct( public function __invoke(string $palette, DataContainer $dc): string { + $mlTable = $this->tableBuilder->buildTableFor($dc->table); + if (!$mlTable || !$mlTable->conditionMet($dc->id)) { + return $palette; + } + $isEditMode = (bool) $this->requestStack->getCurrentRequest() ?->query->get(LoadDataContainerListener::EDIT_LANGUAGES_PARAM, false) ?? false; return match ($isEditMode) { - true => $this->buildEditPalette($palette, $dc), + true => $this->buildEditPalette($palette, $dc, $mlTable), false => $this->addEditFields($palette, $dc), }; } @@ -41,9 +47,8 @@ private function addEditFields(string $palette, DataContainer $dc): string return $prependPalette . $palette; } - private function buildEditPalette(string $originalPalette, DataContainer $dc): string + private function buildEditPalette(string $originalPalette, DataContainer $dc, MultilingualTable $mlTable): string { - $mlTable = $this->tableBuilder->buildTableFor($dc->table); if (!$mlTable) { return $originalPalette; } diff --git a/src/Multilingual/MultilingualTable.php b/src/Multilingual/MultilingualTable.php index 3d3c53c..a9dd75e 100644 --- a/src/Multilingual/MultilingualTable.php +++ b/src/Multilingual/MultilingualTable.php @@ -2,24 +2,48 @@ namespace HeimrichHannot\MultilingualFieldsBundle\Multilingual; +use Contao\Database; + class MultilingualTable { + /** + * @internal Use TableBuilder to create instances of this class. + */ public function __construct( - private readonly array $fields = [], + public readonly string $table, + /** + * @var MultilingualField[] + */ + public readonly array $fields = [], + private readonly array $config = [], public readonly array $languages = [], ) { } - /** - * @return MultilingualField[] - */ - public function getFields(): array + public function getField(string $field): ?MultilingualField { - return $this->fields; + return $this->fields[$field] ?? null; } - public function getField(string $field): ?MultilingualField + public function conditionMet(int $id): bool { - return $this->fields[$field] ?? null; + if (empty($this->config['sql_condition'])) { + return true; + } + + $sqlCondition = $this->config['sql_condition']; + $sqlConditionValues = $this->config['sql_condition_values'] ?? []; + + $values = array_merge([$id], $sqlConditionValues); + + $query = "SELECT id FROM {$this->table} WHERE id=? AND $sqlCondition"; + + $result = Database::getInstance() + ->prepare($query) + ->limit(1) + ->execute(...$values); + ; + + return $result->numRows > 0; } } diff --git a/src/Multilingual/TableBuilder.php b/src/Multilingual/TableBuilder.php index 2147569..d3a07b6 100644 --- a/src/Multilingual/TableBuilder.php +++ b/src/Multilingual/TableBuilder.php @@ -7,6 +7,8 @@ class TableBuilder { + private array $tableCache = []; + public function __construct( private readonly ParameterBagInterface $parameterBag, ) { @@ -14,6 +16,10 @@ public function __construct( public function buildTableFor(string $table): ?MultilingualTable { + if (isset($this->tableCache[$table])) { + return $this->tableCache[$table]; + } + if (!$this->parameterBag->has(Configuration::ROOT_ID)) { return null; } @@ -23,8 +29,6 @@ public function buildTableFor(string $table): ?MultilingualTable return null; } - $dca = &$GLOBALS['TL_DCA'][$table]; - $fields = []; foreach ($bundleConfig['data_containers'][$table]['fields'] as $fieldConfig) { $field = new MultilingualField($fieldConfig); @@ -35,9 +39,15 @@ public function buildTableFor(string $table): ?MultilingualTable return null; } - return new MultilingualTable( + $mlTable = new MultilingualTable( + $table, $fields, + $bundleConfig['data_containers'][$table], $bundleConfig['languages'] ?? [], ); + + $this->tableCache[$table] = $mlTable; + + return $mlTable; } } From fa25f064183f1de038093a2fa8dd01f62757e80f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6rner?= Date: Wed, 6 Aug 2025 14:36:16 +0200 Subject: [PATCH 13/23] add twig function --- README.md | 24 +++++++ config/services.yaml | 2 +- .../Contao/LoadDataContainerListener.php | 4 ++ src/Multilingual/MultilingualField.php | 19 ++++++ src/Multilingual/MultilingualTable.php | 1 + src/Multilingual/TableBuilder.php | 6 +- .../Extension/MultilingualFieldsExtension.php | 18 ++++++ .../Runtime/MultilingualFieldsRuntime.php | 63 +++++++++++++++++++ 8 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 src/Twig/Extension/MultilingualFieldsExtension.php create mode 100644 src/Twig/Runtime/MultilingualFieldsRuntime.php diff --git a/README.md b/README.md index 140daa6..6728279 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,30 @@ This would end up in your DCA being automatically modified the following way: 1. Clear the project's cache (`/var/cache`). 1. Update the database. The new fields should be created now. +## Twig functions + +The following Twig functions are available: + +### `mf_value` +This function returns the value of a translatable field for the current language. If the field is not translated, it returns +the value of the fallback language. + +```twig +{{ mf_value([field], [entity](, [fallback](, [language]))) }} + +# Parameters: +# - field (string): the name of the field to get the value for +# - entity (Model|array): the entity to get the value for (either the model or and an array with the table name and the id of the entity) +# - fallback (string): whether to return the value of the fallback language if the field is not translated (optional, defaults to empty string) +# - language (string): the language to get the value for (optional, defaults to the current language) + +# Example: +{{ mf_value('position', ['tl_member', member.id]) }} +{{ mf_value('position', memberModel) }} +{{ mf_value('position', memberModel, 'Employee', 'en') }} +``` + + ## Insert tags The following new insert tags are available. These take into account the translated jumpTo url and alias. diff --git a/config/services.yaml b/config/services.yaml index d741437..c4759e8 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -3,7 +3,7 @@ services: autowire: true HeimrichHannot\MultilingualFieldsBundle\: - resource: '../src/{EventListener,Util}/*' + resource: '../src/{EventListener,Twig,Util}/*' autoconfigure: true bind: $bundleConfig: '%huh_multilingual_fields%' diff --git a/src/EventListener/Contao/LoadDataContainerListener.php b/src/EventListener/Contao/LoadDataContainerListener.php index 4052999..9531495 100644 --- a/src/EventListener/Contao/LoadDataContainerListener.php +++ b/src/EventListener/Contao/LoadDataContainerListener.php @@ -79,6 +79,10 @@ protected function initConfig($table) } foreach ($languages as $language) { + if ($language === $this->bundleConfig['fallback_language']) { + continue; + } + $translatedFieldname = $language . '_' . $field; $selectorField = $language . '_translate_' . $field; $fieldDca = $dca['fields'][$field]; diff --git a/src/Multilingual/MultilingualField.php b/src/Multilingual/MultilingualField.php index 4d879db..c48550b 100644 --- a/src/Multilingual/MultilingualField.php +++ b/src/Multilingual/MultilingualField.php @@ -8,12 +8,17 @@ class MultilingualField public function __construct( private readonly array $fieldConfig, + private readonly string $fallbackLanguage = 'en', ) { $this->fieldname = $fieldConfig['name']; } public function getFieldNameFor(string $language): string { + if ($language === $this->fallbackLanguage) { + return $this->fieldname; + } + return $language . '_' . $this->fieldname; } @@ -21,4 +26,18 @@ public function getSelectorFieldNameFor(string $language): string { return $language . '_translate_' . $this->fieldname; } + + public function valueFor(array $row, string $language): mixed + { + if ($language !== $this->fallbackLanguage) { + $selectorField = $this->getSelectorFieldNameFor($language); + $fieldName = $this->getFieldNameFor($language); + if (!empty($row[$selectorField]) && isset($row[$fieldName])) { + return $row[$fieldName]; + } + } + + return $row[$this->fieldname] ?? null; + + } } diff --git a/src/Multilingual/MultilingualTable.php b/src/Multilingual/MultilingualTable.php index a9dd75e..c8dcf00 100644 --- a/src/Multilingual/MultilingualTable.php +++ b/src/Multilingual/MultilingualTable.php @@ -17,6 +17,7 @@ public function __construct( public readonly array $fields = [], private readonly array $config = [], public readonly array $languages = [], + public readonly string $fallbackLanguage = 'en', ) { } diff --git a/src/Multilingual/TableBuilder.php b/src/Multilingual/TableBuilder.php index d3a07b6..6573cc5 100644 --- a/src/Multilingual/TableBuilder.php +++ b/src/Multilingual/TableBuilder.php @@ -31,7 +31,10 @@ public function buildTableFor(string $table): ?MultilingualTable $fields = []; foreach ($bundleConfig['data_containers'][$table]['fields'] as $fieldConfig) { - $field = new MultilingualField($fieldConfig); + $field = new MultilingualField( + $fieldConfig, + $bundleConfig['fallback_language'] ?? 'en' + ); $fields[$field->fieldname] = $field; } @@ -44,6 +47,7 @@ public function buildTableFor(string $table): ?MultilingualTable $fields, $bundleConfig['data_containers'][$table], $bundleConfig['languages'] ?? [], + $bundleConfig['fallback_language'] ?? 'en', ); $this->tableCache[$table] = $mlTable; diff --git a/src/Twig/Extension/MultilingualFieldsExtension.php b/src/Twig/Extension/MultilingualFieldsExtension.php new file mode 100644 index 0000000..7c972a3 --- /dev/null +++ b/src/Twig/Extension/MultilingualFieldsExtension.php @@ -0,0 +1,18 @@ +fetchModel($entity); + if (!$entity) { + return $fallback; + } + + $mfTable = $this->tableBuilder->buildTableFor($entity::getTable()); + + if (!$mfTable || !$mfTable->conditionMet($entity->id)) { + return $entity->{$field} ?? $fallback; + } + + $mfField = $mfTable->getField($field); + if (!$mfField) { + return $entity->{$field} ?? $fallback; + } + + $language = $language ?? $this->requestStack->getCurrentRequest()?->getLocale(); + + if (!$language) { + return $entity->{$field} ?? $fallback; + } + + return $mfField->valueFor($entity->row(), $language) ?? $fallback; + } + + private function fetchModel(mixed $entity): ?Model + { + if ($entity instanceof Model) { + return $entity; + } + + $table = $entity[0] ?? null; + $id = $entity[1] ?? null; + + if (!\is_string($table) || !\is_numeric($id)) { + throw new \InvalidArgumentException('Invalid entity format. Expected [table, id].'); + } + + return $this->utils->model()->findModelInstanceByPk($table, (int) $id); + + } +} \ No newline at end of file From 2b1fd5584753c3125c7a1ac9663bd848872c1d55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6rner?= Date: Wed, 6 Aug 2025 14:37:51 +0200 Subject: [PATCH 14/23] run code tools --- src/Multilingual/MultilingualField.php | 1 - src/Multilingual/MultilingualTable.php | 3 +-- src/Twig/Extension/MultilingualFieldsExtension.php | 3 +-- src/Twig/Runtime/MultilingualFieldsRuntime.php | 5 ++--- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/Multilingual/MultilingualField.php b/src/Multilingual/MultilingualField.php index c48550b..8db6ca3 100644 --- a/src/Multilingual/MultilingualField.php +++ b/src/Multilingual/MultilingualField.php @@ -38,6 +38,5 @@ public function valueFor(array $row, string $language): mixed } return $row[$this->fieldname] ?? null; - } } diff --git a/src/Multilingual/MultilingualTable.php b/src/Multilingual/MultilingualTable.php index c8dcf00..8f266fc 100644 --- a/src/Multilingual/MultilingualTable.php +++ b/src/Multilingual/MultilingualTable.php @@ -7,7 +7,7 @@ class MultilingualTable { /** - * @internal Use TableBuilder to create instances of this class. + * @internal use TableBuilder to create instances of this class */ public function __construct( public readonly string $table, @@ -43,7 +43,6 @@ public function conditionMet(int $id): bool ->prepare($query) ->limit(1) ->execute(...$values); - ; return $result->numRows > 0; } diff --git a/src/Twig/Extension/MultilingualFieldsExtension.php b/src/Twig/Extension/MultilingualFieldsExtension.php index 7c972a3..0b05c74 100644 --- a/src/Twig/Extension/MultilingualFieldsExtension.php +++ b/src/Twig/Extension/MultilingualFieldsExtension.php @@ -14,5 +14,4 @@ public function getFunctions() new TwigFunction('mf_value', [MultilingualFieldsRuntime::class, 'mfFieldValue']), ]; } - -} \ No newline at end of file +} diff --git a/src/Twig/Runtime/MultilingualFieldsRuntime.php b/src/Twig/Runtime/MultilingualFieldsRuntime.php index 4026c8d..7a470f7 100644 --- a/src/Twig/Runtime/MultilingualFieldsRuntime.php +++ b/src/Twig/Runtime/MultilingualFieldsRuntime.php @@ -35,7 +35,7 @@ public function mfFieldValue(string $field, Model|array $entity, string $fallbac return $entity->{$field} ?? $fallback; } - $language = $language ?? $this->requestStack->getCurrentRequest()?->getLocale(); + $language ??= $this->requestStack->getCurrentRequest()?->getLocale(); if (!$language) { return $entity->{$field} ?? $fallback; @@ -58,6 +58,5 @@ private function fetchModel(mixed $entity): ?Model } return $this->utils->model()->findModelInstanceByPk($table, (int) $id); - } -} \ No newline at end of file +} From d3c4dc64aad3ee8bba519ce51ecd2cd5bbd41943 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6rner?= Date: Wed, 6 Aug 2025 14:44:39 +0200 Subject: [PATCH 15/23] update locales --- contao/languages/en/default.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/contao/languages/en/default.php b/contao/languages/en/default.php index 9df9065..ce21703 100644 --- a/contao/languages/en/default.php +++ b/contao/languages/en/default.php @@ -11,7 +11,9 @@ /* * Misc */ -$lang['mf_editLanguages'] = ' Edit languages'; -$lang['mf_closeEditLanguages'] = ' Close language editor'; +$lang['mf_editLanguages'] = 'Edit languages'; +$lang['mf_closeEditLanguages'] = 'Close language editor'; $lang['mf_translateField'][0] = 'Translate (%s)'; $lang['mf_translateField'][1] = 'Click this option in order to translate the field for the given language.'; +$lang['mf_language'][0] = 'Restrict to language (empty = always shown)'; +$lang['mf_language'][1] = 'Select the language that must be given in order for the content element to be displayed.'; From d7f7da6b95119768395dc5f1359c0f0e6d2e95f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6rner?= Date: Wed, 6 Aug 2025 14:45:39 +0200 Subject: [PATCH 16/23] cleanup --- src/ContaoManager/Plugin.php | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/ContaoManager/Plugin.php b/src/ContaoManager/Plugin.php index 1c3c09a..787084b 100644 --- a/src/ContaoManager/Plugin.php +++ b/src/ContaoManager/Plugin.php @@ -11,25 +11,16 @@ use Contao\CoreBundle\ContaoCoreBundle; use Contao\ManagerPlugin\Bundle\BundlePluginInterface; use Contao\ManagerPlugin\Bundle\Config\BundleConfig; -use Contao\ManagerPlugin\Bundle\Config\ConfigInterface; use Contao\ManagerPlugin\Bundle\Parser\ParserInterface; use Contao\ManagerPlugin\Config\ConfigPluginInterface; use HeimrichHannot\MultilingualFieldsBundle\HeimrichHannotMultilingualFieldsBundle; -use MadeYourDay\RockSolidCustomElements\RockSolidCustomElementsBundle; use Symfony\Component\Config\Loader\LoaderInterface; class Plugin implements BundlePluginInterface, ConfigPluginInterface { - /** - * Gets a list of autoload configurations for this bundle. - * - * @return ConfigInterface[] - */ public function getBundles(ParserInterface $parser): array { $loadAfter = [ - // @phpstan-ignore class.notFound - RockSolidCustomElementsBundle::class, ContaoCoreBundle::class, ]; @@ -39,9 +30,6 @@ public function getBundles(ParserInterface $parser): array ]; } - /** - * Allows a plugin to load container configuration. - */ public function registerContainerConfiguration(LoaderInterface $loader, array $managerConfig): void { $loader->load('@HeimrichHannotMultilingualFieldsBundle/config/services.yaml'); From ed2144a38c75031803a16c2080635e2695956cbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6rner?= Date: Wed, 6 Aug 2025 14:59:07 +0200 Subject: [PATCH 17/23] code enhancements to isVisibleElement listener --- .../Contao/IsVisibleElementListener.php | 35 ++++++++++++------- src/Multilingual/MultilingualField.php | 8 ++++- src/Multilingual/TableBuilder.php | 2 ++ 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/EventListener/Contao/IsVisibleElementListener.php b/src/EventListener/Contao/IsVisibleElementListener.php index d459e76..040a158 100644 --- a/src/EventListener/Contao/IsVisibleElementListener.php +++ b/src/EventListener/Contao/IsVisibleElementListener.php @@ -8,9 +8,13 @@ namespace HeimrichHannot\MultilingualFieldsBundle\EventListener\Contao; +use Contao\ContentModel; use Contao\CoreBundle\DependencyInjection\Attribute\AsHook; +use Contao\Model; +use HeimrichHannot\MultilingualFieldsBundle\Multilingual\TableBuilder; use HeimrichHannot\MultilingualFieldsBundle\Util\MultilingualFieldsUtil; use HeimrichHannot\UtilsBundle\Util\Utils; +use Symfony\Component\HttpFoundation\RequestStack; #[AsHook('isVisibleElement')] class IsVisibleElementListener @@ -18,30 +22,35 @@ class IsVisibleElementListener public function __construct( protected MultilingualFieldsUtil $multilingualFieldsUtil, private readonly Utils $utils, + private readonly RequestStack $requestStack, + private readonly TableBuilder $tableBuilder, ) { } - public function __invoke($element, $return) + public function __invoke(Model $element, bool $isVisible): bool { - if ($this->utils->container()->isBackend()) { - return $return; + if ($this->utils->container()->isBackend() || !($element instanceof ContentModel)) { + return $isVisible; } - if ($this->multilingualFieldsUtil->hasContentLanguageField($element->id) && $element->mf_language && $element->mf_language !== $GLOBALS['TL_LANGUAGE']) { + $language = $this->requestStack->getCurrentRequest()?->getLocale() ?: 'en'; + + if ($this->multilingualFieldsUtil->hasContentLanguageField($element->id) + && $element->mf_language + && $element->mf_language !== $language + ) { return false; } - // adjust fields - if ($this->multilingualFieldsUtil->isTranslatable('tl_content')) { - foreach ($this->multilingualFieldsUtil->getTranslatableFields('tl_content') as $field) { - if (!$element->{$GLOBALS['TL_LANGUAGE'] . '_translate_' . $field}) { - continue; - } + $mfTable = $this->tableBuilder->buildTableFor($element::getTable()); + if (!$mfTable) { + return $isVisible; + } - $element->{$field} = $element->{$GLOBALS['TL_LANGUAGE'] . '_' . $field}; - } + foreach ($mfTable->fields as $field) { + $element->{$field->fieldname} = $field->valueFor($element, $language); } - return $return; + return $isVisible; } } diff --git a/src/Multilingual/MultilingualField.php b/src/Multilingual/MultilingualField.php index 8db6ca3..73348a3 100644 --- a/src/Multilingual/MultilingualField.php +++ b/src/Multilingual/MultilingualField.php @@ -2,6 +2,8 @@ namespace HeimrichHannot\MultilingualFieldsBundle\Multilingual; +use Contao\Model; + class MultilingualField { public readonly string $fieldname; @@ -27,8 +29,12 @@ public function getSelectorFieldNameFor(string $language): string return $language . '_translate_' . $this->fieldname; } - public function valueFor(array $row, string $language): mixed + public function valueFor(array|Model $row, string $language): mixed { + if ($row instanceof Model) { + $row = $row->row(); + } + if ($language !== $this->fallbackLanguage) { $selectorField = $this->getSelectorFieldNameFor($language); $fieldName = $this->getFieldNameFor($language); diff --git a/src/Multilingual/TableBuilder.php b/src/Multilingual/TableBuilder.php index 6573cc5..42c751d 100644 --- a/src/Multilingual/TableBuilder.php +++ b/src/Multilingual/TableBuilder.php @@ -26,6 +26,7 @@ public function buildTableFor(string $table): ?MultilingualTable $bundleConfig = $this->parameterBag->get(Configuration::ROOT_ID); if (empty($bundleConfig['data_containers'][$table]) || !\is_array($bundleConfig['data_containers'][$table])) { + $this->tableCache[$table] = null; return null; } @@ -39,6 +40,7 @@ public function buildTableFor(string $table): ?MultilingualTable } if (empty($fields)) { + $this->tableCache[$table] = null; return null; } From 7ec42e25415a0e43c6b1660c1a23797ea106fa00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6rner?= Date: Wed, 6 Aug 2025 14:59:51 +0200 Subject: [PATCH 18/23] rename method --- src/EventListener/Contao/IsVisibleElementListener.php | 2 +- src/EventListener/DataContainer/ConfigOnPaletteListener.php | 2 +- src/Multilingual/TableBuilder.php | 2 +- src/Twig/Runtime/MultilingualFieldsRuntime.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/EventListener/Contao/IsVisibleElementListener.php b/src/EventListener/Contao/IsVisibleElementListener.php index 040a158..a99cb06 100644 --- a/src/EventListener/Contao/IsVisibleElementListener.php +++ b/src/EventListener/Contao/IsVisibleElementListener.php @@ -42,7 +42,7 @@ public function __invoke(Model $element, bool $isVisible): bool return false; } - $mfTable = $this->tableBuilder->buildTableFor($element::getTable()); + $mfTable = $this->tableBuilder->buildFor($element::getTable()); if (!$mfTable) { return $isVisible; } diff --git a/src/EventListener/DataContainer/ConfigOnPaletteListener.php b/src/EventListener/DataContainer/ConfigOnPaletteListener.php index 199762b..e3124f9 100644 --- a/src/EventListener/DataContainer/ConfigOnPaletteListener.php +++ b/src/EventListener/DataContainer/ConfigOnPaletteListener.php @@ -22,7 +22,7 @@ public function __construct( public function __invoke(string $palette, DataContainer $dc): string { - $mlTable = $this->tableBuilder->buildTableFor($dc->table); + $mlTable = $this->tableBuilder->buildFor($dc->table); if (!$mlTable || !$mlTable->conditionMet($dc->id)) { return $palette; } diff --git a/src/Multilingual/TableBuilder.php b/src/Multilingual/TableBuilder.php index 42c751d..0c84021 100644 --- a/src/Multilingual/TableBuilder.php +++ b/src/Multilingual/TableBuilder.php @@ -14,7 +14,7 @@ public function __construct( ) { } - public function buildTableFor(string $table): ?MultilingualTable + public function buildFor(string $table): ?MultilingualTable { if (isset($this->tableCache[$table])) { return $this->tableCache[$table]; diff --git a/src/Twig/Runtime/MultilingualFieldsRuntime.php b/src/Twig/Runtime/MultilingualFieldsRuntime.php index 7a470f7..6de3d44 100644 --- a/src/Twig/Runtime/MultilingualFieldsRuntime.php +++ b/src/Twig/Runtime/MultilingualFieldsRuntime.php @@ -24,7 +24,7 @@ public function mfFieldValue(string $field, Model|array $entity, string $fallbac return $fallback; } - $mfTable = $this->tableBuilder->buildTableFor($entity::getTable()); + $mfTable = $this->tableBuilder->buildFor($entity::getTable()); if (!$mfTable || !$mfTable->conditionMet($entity->id)) { return $entity->{$field} ?? $fallback; From 3c81d4f4044da1ba778de6f6cf94fae099a598a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6rner?= Date: Wed, 6 Aug 2025 15:50:54 +0200 Subject: [PATCH 19/23] fix class on edit layout --- src/EventListener/Contao/LoadDataContainerListener.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/EventListener/Contao/LoadDataContainerListener.php b/src/EventListener/Contao/LoadDataContainerListener.php index 9531495..0005dac 100644 --- a/src/EventListener/Contao/LoadDataContainerListener.php +++ b/src/EventListener/Contao/LoadDataContainerListener.php @@ -34,7 +34,6 @@ public function __construct( private readonly Slug $slug, private readonly DcaUtil $dcaUtil, ) { - $this->bundleConfig = $bundleConfig; } public function __invoke($table): void @@ -110,9 +109,12 @@ protected function initConfig($table) // copy the field $dca['fields'][$translatedFieldname] = $fieldDca; + $classes = $dca['fields'][$translatedFieldname]['eval']['tl_class'] ?? ''; + $classes .= ' clr'; if (isset($dca['fields'][$translatedFieldname]['eval']['rte'])) { - $dca['fields'][$translatedFieldname]['eval']['tl_class'] = 'long clr'; + $classes .= ' long'; } + $dca['fields'][$translatedFieldname]['eval']['tl_class'] = $classes; $this->handleAliasField($dca, $fieldConfig, $translatedFieldname, $language); From b670cca87f53039a8b31e89bfbc15bd48041593e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6rner?= Date: Wed, 6 Aug 2025 16:44:08 +0200 Subject: [PATCH 20/23] enhance edit view --- assets/scss/contao-multilingual-fields-bundle.be.scss | 4 ++++ public/assets/contao-multilingual-fields-bundle.css | 2 +- src/EventListener/Contao/LoadDataContainerListener.php | 3 +-- src/EventListener/DataContainer/ConfigOnPaletteListener.php | 1 - 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/assets/scss/contao-multilingual-fields-bundle.be.scss b/assets/scss/contao-multilingual-fields-bundle.be.scss index 423f32a..db70c2b 100644 --- a/assets/scss/contao-multilingual-fields-bundle.be.scss +++ b/assets/scss/contao-multilingual-fields-bundle.be.scss @@ -71,3 +71,7 @@ } } } + +fieldset.tl_box > .widget.mf-origin-field:not(:first-of-type) { + padding-top: 2rem; +} \ No newline at end of file diff --git a/public/assets/contao-multilingual-fields-bundle.css b/public/assets/contao-multilingual-fields-bundle.css index 1636efc..734b960 100644 --- a/public/assets/contao-multilingual-fields-bundle.css +++ b/public/assets/contao-multilingual-fields-bundle.css @@ -1 +1 @@ -.tl_tbox.nolegend #mf_language_edit_switch_button_widget{margin-top:20px;min-height:unset}.tl_tbox.nolegend #mf_language_edit_switch_button{background-color:#eee;background-position:5px;background-size:20px 20px;border:1px solid #aaa;border-radius:2px;box-sizing:border-box;cursor:pointer;height:30px;min-height:unset;padding:7px 12px 7px 28px;transition:background .2s ease}.tl_tbox.nolegend #mf_language_edit_switch_button:hover{background-color:#ddd;color:#333}#mf_language_edit_switch_button{background-color:transparent;background-image:url(/bundles/heimrichhannotmultilingualfields/assets/images/pencil.e8555421.svg);background-position:0;background-repeat:no-repeat;border:none;display:inline-block;float:left;margin-left:0;padding:3px 0 3px 23px}#mf_language_edit_switch_button.close{background-image:url(/bundles/heimrichhannotmultilingualfields/assets/images/pencil-off.a6bd717c.svg)}.readonly .tl_chosen,.readonly .tl_submit,.readonly select,.tl_chosen.readonly,.tl_submit.readonly,select.readonly{background-color:#f9f9f9;border:1px solid #c8c8c8;color:#bbb;cursor:not-allowed}.readonly .tl_chosen *,.readonly .tl_submit *,.readonly select *,.tl_chosen.readonly *,.tl_submit.readonly *,select.readonly *{cursor:not-allowed}.readonly .tl_chosen span,.readonly .tl_submit span,.readonly select span,.tl_chosen.readonly span,.tl_submit.readonly span,select.readonly span{color:#bbb}.widget.readonly .mce-tinymce,.widget.readonly .tl_metawizard .tl_text,.widget.readonly .tl_select{background-color:#f9f9f9;border:1px solid #c8c8c8;color:#bbb;cursor:not-allowed}.widget.readonly .mce-tinymce *,.widget.readonly .tl_metawizard .tl_text *,.widget.readonly .tl_select *{cursor:not-allowed}.widget.readonly .mce-tinymce span,.widget.readonly .tl_metawizard .tl_text span,.widget.readonly .tl_select span{color:#bbb}.widget.readonly .mce-tinymce .mce-edit-area{opacity:.7} \ No newline at end of file +.tl_tbox.nolegend #mf_language_edit_switch_button_widget{margin-top:20px;min-height:unset}.tl_tbox.nolegend #mf_language_edit_switch_button{background-color:#eee;background-position:5px;background-size:20px 20px;border:1px solid #aaa;border-radius:2px;box-sizing:border-box;cursor:pointer;height:30px;min-height:unset;padding:7px 12px 7px 28px;transition:background .2s ease}.tl_tbox.nolegend #mf_language_edit_switch_button:hover{background-color:#ddd;color:#333}#mf_language_edit_switch_button{background-color:transparent;background-image:url(/bundles/heimrichhannotmultilingualfields/assets/images/pencil.e8555421.svg);background-position:0;background-repeat:no-repeat;border:none;display:inline-block;float:left;margin-left:0;padding:3px 0 3px 23px}#mf_language_edit_switch_button.close{background-image:url(/bundles/heimrichhannotmultilingualfields/assets/images/pencil-off.a6bd717c.svg)}.readonly .tl_chosen,.readonly .tl_submit,.readonly select,.tl_chosen.readonly,.tl_submit.readonly,select.readonly{background-color:#f9f9f9;border:1px solid #c8c8c8;color:#bbb;cursor:not-allowed}.readonly .tl_chosen *,.readonly .tl_submit *,.readonly select *,.tl_chosen.readonly *,.tl_submit.readonly *,select.readonly *{cursor:not-allowed}.readonly .tl_chosen span,.readonly .tl_submit span,.readonly select span,.tl_chosen.readonly span,.tl_submit.readonly span,select.readonly span{color:#bbb}.widget.readonly .mce-tinymce,.widget.readonly .tl_metawizard .tl_text,.widget.readonly .tl_select{background-color:#f9f9f9;border:1px solid #c8c8c8;color:#bbb;cursor:not-allowed}.widget.readonly .mce-tinymce *,.widget.readonly .tl_metawizard .tl_text *,.widget.readonly .tl_select *{cursor:not-allowed}.widget.readonly .mce-tinymce span,.widget.readonly .tl_metawizard .tl_text span,.widget.readonly .tl_select span{color:#bbb}.widget.readonly .mce-tinymce .mce-edit-area{opacity:.7}fieldset.tl_box>.widget.mf-origin-field:not(:first-of-type){padding-top:2rem} \ No newline at end of file diff --git a/src/EventListener/Contao/LoadDataContainerListener.php b/src/EventListener/Contao/LoadDataContainerListener.php index 0005dac..1c7bf11 100644 --- a/src/EventListener/Contao/LoadDataContainerListener.php +++ b/src/EventListener/Contao/LoadDataContainerListener.php @@ -67,7 +67,6 @@ protected function initConfig($table) $isEditMode = $request && $request->query->get(static::EDIT_LANGUAGES_PARAM, false); // add translated fields - $paletteData = []; $readOnlyFields = []; foreach ($config['fields'] as $fieldConfig) { @@ -126,7 +125,7 @@ protected function initConfig($table) if ($isEditMode) { // put to next line - $dca['fields'][$field]['eval']['tl_class'] .= ' clr'; + $dca['fields'][$field]['eval']['tl_class'] .= ' clr mf-origin-field'; unset($dca['fields'][$translatedFieldname]['eval']['submitOnChange']); } diff --git a/src/EventListener/DataContainer/ConfigOnPaletteListener.php b/src/EventListener/DataContainer/ConfigOnPaletteListener.php index e3124f9..2885c3e 100644 --- a/src/EventListener/DataContainer/ConfigOnPaletteListener.php +++ b/src/EventListener/DataContainer/ConfigOnPaletteListener.php @@ -73,7 +73,6 @@ private function buildEditPalette(string $originalPalette, DataContainer $dc, Mu $paletteManipulator->addField($mlField->getFieldNameFor($language), $selectorFieldName); } } - $paletteManipulator->removeField($paletteField); } $palette = $paletteManipulator->applyToString($originalPalette); From 8f6362e7bbfedc13b1f022a6efdbf88819b430e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6rner?= Date: Wed, 6 Aug 2025 17:29:30 +0200 Subject: [PATCH 21/23] add basic calendar reader support --- src/EventListener/CalendarEventsListener.php | 47 ++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/EventListener/CalendarEventsListener.php diff --git a/src/EventListener/CalendarEventsListener.php b/src/EventListener/CalendarEventsListener.php new file mode 100644 index 0000000..13dfc7f --- /dev/null +++ b/src/EventListener/CalendarEventsListener.php @@ -0,0 +1,47 @@ +getName(), 'event_')) { + return; + } + if (!($template->calendar instanceof CalendarModel)) { + return; + } + + $mlTable = $this->tableBuilder->buildFor(CalendarEventsModel::getTable()); + if (!$mlTable) { + return; + } + + $calendarEvent = CalendarEventsModel::findByPk($template->id); + if (!$calendarEvent) { + return; + } + + $language = $this->requestStack->getCurrentRequest()?->getLocale() ?? 'en'; + + foreach ($mlTable->fields as $mlField) { + $template->{$mlField->fieldname} = $mlField->valueFor($calendarEvent->row(), $language); + } + } +} \ No newline at end of file From 5a504f22134a9e6fe4683ae509a0597c63227eb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6rner?= Date: Thu, 7 Aug 2025 09:53:59 +0200 Subject: [PATCH 22/23] add better fallbacks for multilingual field valueFor method --- src/Multilingual/MultilingualField.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Multilingual/MultilingualField.php b/src/Multilingual/MultilingualField.php index 73348a3..8543ff7 100644 --- a/src/Multilingual/MultilingualField.php +++ b/src/Multilingual/MultilingualField.php @@ -29,12 +29,16 @@ public function getSelectorFieldNameFor(string $language): string return $language . '_translate_' . $this->fieldname; } - public function valueFor(array|Model $row, string $language): mixed + public function valueFor(array|Model $row, ?string $language = null, mixed $fallback = null): mixed { if ($row instanceof Model) { $row = $row->row(); } + if (null === $language) { + $language = $this->fallbackLanguage; + } + if ($language !== $this->fallbackLanguage) { $selectorField = $this->getSelectorFieldNameFor($language); $fieldName = $this->getFieldNameFor($language); @@ -43,6 +47,6 @@ public function valueFor(array|Model $row, string $language): mixed } } - return $row[$this->fieldname] ?? null; + return $fallback ?: $row[$this->fieldname] ?? null; } } From 8ed9401dff274e9c9eb5e8dc726fa7417a5eee91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6rner?= Date: Fri, 8 Aug 2025 09:27:46 +0200 Subject: [PATCH 23/23] fix issues with calendars and changelanguage --- src/EventListener/CalendarEventsListener.php | 75 ++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/src/EventListener/CalendarEventsListener.php b/src/EventListener/CalendarEventsListener.php index 13dfc7f..0e2fbba 100644 --- a/src/EventListener/CalendarEventsListener.php +++ b/src/EventListener/CalendarEventsListener.php @@ -4,10 +4,14 @@ use Contao\CalendarModel; use Contao\CoreBundle\DependencyInjection\Attribute\AsHook; +use Contao\Input; +use Contao\PageModel; use Contao\Template; use HeimrichHannot\EventRegistrationBundle\Model\CalendarEventsModel; use HeimrichHannot\MultilingualFieldsBundle\Multilingual\TableBuilder; use Symfony\Component\HttpFoundation\RequestStack; +use Terminal42\ChangeLanguage\Event\ChangelanguageNavigationEvent; +use Terminal42\ChangeLanguage\EventListener\Navigation\NavigationHandlerInterface; class CalendarEventsListener { @@ -44,4 +48,75 @@ public function onParseTemplate(Template $template): void $template->{$mlField->fieldname} = $mlField->valueFor($calendarEvent->row(), $language); } } + + #[AsHook('changelanguageNavigation', priority: -1)] + public function onChangelanguageNavigation(ChangelanguageNavigationEvent $event): void + { + $current = $this->findCurrent(); + + if (null === $current) { + return; + } + + $navigationItem = $event->getNavigationItem(); + + if ($navigationItem->isCurrentPage()) { + return; + } + + /** @var CalendarModel|null $parent */ + $parent = $current->getRelated('pid'); + if (null === $parent) { + return; + } + + + if (0 !== (int) $parent->master) { + return; + } + + $targetPage = $navigationItem->getTargetPage(); + + if (null === $targetPage) { + return; + } + + $event->getUrlParameterBag()->setUrlAttribute($this->getUrlKey(), $current->alias ?: $current->id); + $event->getNavigationItem()->setTitle($current->title); + $event->getNavigationItem()->setPageTitle($current->pageTitle); + } + + protected function findCurrent(): ?\Contao\CalendarEventsModel + { + $alias = $this->getAutoItem(); + + if ('' === $alias) { + return null; + } + + return CalendarEventsModel::findByAlias($alias); + } + + protected function getAutoItem(): string + { + $strKey = $this->getUrlKey(); + + if ( + !isset($GLOBALS['TL_CONFIG']['useAutoItem']) + || ( + $GLOBALS['TL_CONFIG']['useAutoItem'] + && isset($GLOBALS['TL_AUTO_ITEM']) + && \in_array($strKey, $GLOBALS['TL_AUTO_ITEM'], true) + ) + ) { + $strKey = 'auto_item'; + } + + return (string) Input::get($strKey, false, true); + } + + protected function getUrlKey(): string + { + return isset($GLOBALS['TL_CONFIG']['useAutoItem']) ? 'events' : 'auto_item'; + } } \ No newline at end of file