diff --git a/src/Configuration/StringAccessibleConfigTrait.php b/src/Configuration/StringAccessibleConfigTrait.php index 9f9f7bd7..d8e564c0 100644 --- a/src/Configuration/StringAccessibleConfigTrait.php +++ b/src/Configuration/StringAccessibleConfigTrait.php @@ -26,9 +26,6 @@ trait StringAccessibleConfigTrait */ protected $initialized = false; - /** - * @param array $settings - */ public function __construct(ConfigLoaderInterface $repository, array $settings) { $this->repository = $repository; @@ -120,4 +117,45 @@ trait StringAccessibleConfigTrait return $config[$search]; } + + /** + * @return bool + */ + public function offsetExists($offset) + { + try { + $this->find($offset); + } catch (\Exception $ex) { + return false; + } + + return true; + } + + /** + * @return mixed + */ + public function offsetGet($offset) + { + return $this->find($offset); + } + + /** + * @param mixed $offset + * @param mixed $value + * @throws \BadMethodCallException + */ + public function offsetSet($offset, $value) + { + throw new \BadMethodCallException('SystemBundleConfiguration does not support offsetSet()'); + } + + /** + * @param mixed $offset + * @throws \BadMethodCallException + */ + public function offsetUnset($offset) + { + throw new \BadMethodCallException('SystemBundleConfiguration does not support offsetUnset()'); + } } diff --git a/src/Configuration/ThemeConfiguration.php b/src/Configuration/ThemeConfiguration.php index 195df9d3..ed648140 100644 --- a/src/Configuration/ThemeConfiguration.php +++ b/src/Configuration/ThemeConfiguration.php @@ -9,7 +9,7 @@ namespace App\Configuration; -class ThemeConfiguration implements SystemBundleConfiguration +class ThemeConfiguration implements SystemBundleConfiguration, \ArrayAccess { use StringAccessibleConfigTrait; diff --git a/src/Controller/SystemConfigurationController.php b/src/Controller/SystemConfigurationController.php index 8645a1e6..8372df6e 100644 --- a/src/Controller/SystemConfigurationController.php +++ b/src/Controller/SystemConfigurationController.php @@ -312,6 +312,30 @@ class SystemConfigurationController extends AbstractController ->setType(TextType::class) ->setConstraints([new DateTime(['format' => 'H:i']), new NotNull()]), ]), + (new SystemConfigurationModel()) + ->setSection(SystemConfigurationModel::SECTION_BRANDING) + ->setConfiguration([ + (new Configuration()) + ->setName('theme.branding.logo') + ->setTranslationDomain('system-configuration') + ->setRequired(false) + ->setType(TextType::class), + (new Configuration()) + ->setName('theme.branding.company') + ->setTranslationDomain('system-configuration') + ->setRequired(false) + ->setType(TextType::class), + (new Configuration()) + ->setName('theme.branding.mini') + ->setTranslationDomain('system-configuration') + ->setRequired(false) + ->setType(TextType::class), + (new Configuration()) + ->setName('theme.branding.title') + ->setTranslationDomain('system-configuration') + ->setRequired(false) + ->setType(TextType::class), + ]), ]; } } diff --git a/src/DependencyInjection/Compiler/TwigContextCompilerPass.php b/src/DependencyInjection/Compiler/TwigContextCompilerPass.php index 93b6404c..84168297 100644 --- a/src/DependencyInjection/Compiler/TwigContextCompilerPass.php +++ b/src/DependencyInjection/Compiler/TwigContextCompilerPass.php @@ -9,6 +9,7 @@ namespace App\DependencyInjection\Compiler; +use App\Configuration\ThemeConfiguration; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -24,8 +25,8 @@ class TwigContextCompilerPass implements CompilerPassInterface public function process(ContainerBuilder $container) { $twig = $container->getDefinition('twig'); - $theme = $container->getParameter('kimai.theme'); + $theme = $container->getDefinition(ThemeConfiguration::class); $twig->addMethodCall('addGlobal', ['kimai_context', $theme]); if ($container->hasDefinition('twig.loader.native_filesystem')) { diff --git a/src/Form/Model/Configuration.php b/src/Form/Model/Configuration.php index 71df4b10..646c458a 100644 --- a/src/Form/Model/Configuration.php +++ b/src/Form/Model/Configuration.php @@ -33,6 +33,10 @@ final class Configuration * @var string */ private $type; + /** + * @var array + */ + private $options = []; /** * @var bool */ @@ -155,4 +159,16 @@ final class Configuration return $this; } + + public function getOptions(): array + { + return $this->options; + } + + public function setOptions(array $options): Configuration + { + $this->options = $options; + + return $this; + } } diff --git a/src/Form/Model/SystemConfiguration.php b/src/Form/Model/SystemConfiguration.php index a7367145..e9a2b468 100644 --- a/src/Form/Model/SystemConfiguration.php +++ b/src/Form/Model/SystemConfiguration.php @@ -16,6 +16,7 @@ class SystemConfiguration public const SECTION_FORM_USER = 'form_user'; public const SECTION_THEME = 'theme'; public const SECTION_CALENDAR = 'calendar'; + public const SECTION_BRANDING = 'branding'; /** * @var string diff --git a/src/Form/Type/SystemConfigurationType.php b/src/Form/Type/SystemConfigurationType.php index fd4ce8dc..1111f35b 100644 --- a/src/Form/Type/SystemConfigurationType.php +++ b/src/Form/Type/SystemConfigurationType.php @@ -54,13 +54,15 @@ class SystemConfigurationType extends AbstractType $type = HiddenType::class; } - $event->getForm()->add('value', $type, [ + $options = [ 'label' => 'label.' . ($preference->getLabel() ?? $preference->getName()), 'constraints' => $preference->getConstraints(), 'required' => $required, 'disabled' => !$preference->isEnabled(), 'translation_domain' => $preference->getTranslationDomain(), - ]); + ]; + + $event->getForm()->add('value', $type, array_merge($options, $preference->getOptions())); } ); $builder->add('name', HiddenType::class); diff --git a/src/Repository/ConfigurationRepository.php b/src/Repository/ConfigurationRepository.php index 4884e479..4ec36217 100644 --- a/src/Repository/ConfigurationRepository.php +++ b/src/Repository/ConfigurationRepository.php @@ -47,6 +47,11 @@ class ConfigurationRepository extends EntityRepository implements ConfigLoaderIn $entity = $this->findOneBy(['name' => $configuration->getName()]); $value = $configuration->getValue(); + if (null === $value && null !== $entity) { + $em->remove($entity); + continue; + } + if (null === $entity) { $entity = new Configuration(); $entity->setName($configuration->getName()); diff --git a/tests/Controller/SystemConfigurationControllerTest.php b/tests/Controller/SystemConfigurationControllerTest.php index 67914636..0da9e63a 100644 --- a/tests/Controller/SystemConfigurationControllerTest.php +++ b/tests/Controller/SystemConfigurationControllerTest.php @@ -53,6 +53,7 @@ class SystemConfigurationControllerTest extends ControllerBaseTest ['form[name=system_configuration_form_form_user]', $this->createUrl('/admin/system-config/update/form_user')], ['form[name=system_configuration_form_theme]', $this->createUrl('/admin/system-config/update/theme')], ['form[name=system_configuration_form_calendar]', $this->createUrl('/admin/system-config/update/calendar')], + ['form[name=system_configuration_form_branding]', $this->createUrl('/admin/system-config/update/branding')], ]; } diff --git a/translations/system-configuration.de.xliff b/translations/system-configuration.de.xliff index 8836a3f2..ff713ec2 100644 --- a/translations/system-configuration.de.xliff +++ b/translations/system-configuration.de.xliff @@ -98,6 +98,26 @@ label.calendar.visibleHours.end Ende des sichtbaren Zeitbereichs + + branding + Markendarstellung + + + + label.theme.branding.company + Unternehmen + + + label.theme.branding.mini + Mini Logo (zugeklappte Seitenleiste) + + + label.theme.branding.title + Browser Titel + diff --git a/translations/system-configuration.en.xliff b/translations/system-configuration.en.xliff index 3f8a1ddb..afc5beca 100644 --- a/translations/system-configuration.en.xliff +++ b/translations/system-configuration.en.xliff @@ -98,6 +98,26 @@ label.calendar.visibleHours.end End of visible time range + + branding + Branding + + + + label.theme.branding.company + Company + + + label.theme.branding.mini + Mini Logo (collapsed sidebar) + + + label.theme.branding.title + Browser Title +