Next major version 2 with PHP 8.1, Symfony 6, Tabler UI, 2FA ... (#2902)
This commit is contained in:
@@ -1,92 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Configuration;
|
||||
|
||||
/**
|
||||
* @deprecated since 1.11 - use SystemConfiguration instead
|
||||
*/
|
||||
class CalendarConfiguration implements SystemBundleConfiguration
|
||||
{
|
||||
private $configuration;
|
||||
|
||||
public function __construct(SystemConfiguration $configuration)
|
||||
{
|
||||
$this->configuration = $configuration;
|
||||
}
|
||||
|
||||
public function find(string $key)
|
||||
{
|
||||
if (strpos($key, $this->getPrefix() . '.') === false) {
|
||||
$key = $this->getPrefix() . '.' . $key;
|
||||
}
|
||||
|
||||
return $this->configuration->find($key);
|
||||
}
|
||||
|
||||
public function getPrefix(): string
|
||||
{
|
||||
return 'calendar';
|
||||
}
|
||||
|
||||
public function getBusinessDays(): array
|
||||
{
|
||||
return $this->configuration->getCalendarBusinessDays();
|
||||
}
|
||||
|
||||
public function getBusinessTimeBegin(): string
|
||||
{
|
||||
return $this->configuration->getCalendarBusinessTimeBegin();
|
||||
}
|
||||
|
||||
public function getBusinessTimeEnd(): string
|
||||
{
|
||||
return $this->configuration->getCalendarBusinessTimeEnd();
|
||||
}
|
||||
|
||||
public function getTimeframeBegin(): string
|
||||
{
|
||||
return $this->configuration->getCalendarTimeframeBegin();
|
||||
}
|
||||
|
||||
public function getTimeframeEnd(): string
|
||||
{
|
||||
return $this->configuration->getCalendarTimeframeEnd();
|
||||
}
|
||||
|
||||
public function getDayLimit(): int
|
||||
{
|
||||
return $this->configuration->getCalendarDayLimit();
|
||||
}
|
||||
|
||||
public function isShowWeekNumbers(): bool
|
||||
{
|
||||
return $this->configuration->isCalendarShowWeekNumbers();
|
||||
}
|
||||
|
||||
public function isShowWeekends(): bool
|
||||
{
|
||||
return $this->configuration->isCalendarShowWeekends();
|
||||
}
|
||||
|
||||
public function getGoogleApiKey(): ?string
|
||||
{
|
||||
return $this->configuration->getCalendarGoogleApiKey();
|
||||
}
|
||||
|
||||
public function getGoogleSources(): ?array
|
||||
{
|
||||
return $this->configuration->getCalendarGoogleSources();
|
||||
}
|
||||
|
||||
public function getSlotDuration(): string
|
||||
{
|
||||
return $this->configuration->getCalendarSlotDuration();
|
||||
}
|
||||
}
|
||||
@@ -14,8 +14,13 @@ use App\Entity\Configuration;
|
||||
interface ConfigLoaderInterface
|
||||
{
|
||||
/**
|
||||
* @param null|string $prefix
|
||||
* @param string $name
|
||||
* @return ?Configuration
|
||||
*/
|
||||
public function getConfiguration(string $name): ?Configuration;
|
||||
|
||||
/**
|
||||
* @return Configuration[]
|
||||
*/
|
||||
public function getConfiguration(?string $prefix = null): array;
|
||||
public function getConfigurations(): array;
|
||||
}
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Configuration;
|
||||
|
||||
/**
|
||||
* @deprecated will be removed with 2.0, use SystemConfiguration instead
|
||||
*/
|
||||
class FormConfiguration implements SystemBundleConfiguration
|
||||
{
|
||||
private $configuration;
|
||||
|
||||
public function __construct(SystemConfiguration $configuration)
|
||||
{
|
||||
$this->configuration = $configuration;
|
||||
}
|
||||
|
||||
public function find(string $key)
|
||||
{
|
||||
if (strpos($key, $this->getPrefix() . '.') === false) {
|
||||
$key = $this->getPrefix() . '.' . $key;
|
||||
}
|
||||
|
||||
return $this->configuration->find($key);
|
||||
}
|
||||
|
||||
public function getPrefix(): string
|
||||
{
|
||||
return 'defaults';
|
||||
}
|
||||
|
||||
public function getCustomerDefaultTimezone(): ?string
|
||||
{
|
||||
return $this->configuration->getCustomerDefaultTimezone();
|
||||
}
|
||||
|
||||
public function getCustomerDefaultCurrency(): string
|
||||
{
|
||||
return $this->configuration->getCustomerDefaultCurrency();
|
||||
}
|
||||
|
||||
public function getCustomerDefaultCountry(): string
|
||||
{
|
||||
return $this->configuration->getCustomerDefaultCountry();
|
||||
}
|
||||
|
||||
public function getUserDefaultTimezone(): ?string
|
||||
{
|
||||
return $this->configuration->getUserDefaultTimezone();
|
||||
}
|
||||
|
||||
public function getUserDefaultTheme(): ?string
|
||||
{
|
||||
return $this->configuration->getUserDefaultTheme();
|
||||
}
|
||||
|
||||
public function getUserDefaultLanguage(): string
|
||||
{
|
||||
return $this->configuration->getUserDefaultLanguage();
|
||||
}
|
||||
|
||||
public function getUserDefaultCurrency(): string
|
||||
{
|
||||
return $this->configuration->getUserDefaultCurrency();
|
||||
}
|
||||
}
|
||||
@@ -11,11 +11,8 @@ namespace App\Configuration;
|
||||
|
||||
final class LdapConfiguration
|
||||
{
|
||||
private $configuration;
|
||||
|
||||
public function __construct(SystemConfiguration $configuration)
|
||||
public function __construct(private SystemConfiguration $configuration)
|
||||
{
|
||||
$this->configuration = $configuration;
|
||||
}
|
||||
|
||||
public function isActivated(): bool
|
||||
@@ -25,16 +22,16 @@ final class LdapConfiguration
|
||||
|
||||
public function getRoleParameters(): array
|
||||
{
|
||||
return $this->configuration->getLdapRoleParameters();
|
||||
return $this->configuration->findArray('ldap.role');
|
||||
}
|
||||
|
||||
public function getUserParameters(): array
|
||||
{
|
||||
return $this->configuration->getLdapUserParameters();
|
||||
return $this->configuration->findArray('ldap.user');
|
||||
}
|
||||
|
||||
public function getConnectionParameters(): array
|
||||
{
|
||||
return $this->configuration->getLdapConnectionParameters();
|
||||
return $this->configuration->findArray('ldap.connection');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,12 @@
|
||||
|
||||
namespace App\Configuration;
|
||||
|
||||
use App\Utils\MomentFormatConverter;
|
||||
use App\Constants;
|
||||
|
||||
final class LanguageFormattings
|
||||
final class LocaleService
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $settings;
|
||||
/**
|
||||
* @var MomentFormatConverter
|
||||
*/
|
||||
private $momentFormatter;
|
||||
|
||||
public function __construct(array $languageSettings)
|
||||
public function __construct(private array $languageSettings)
|
||||
{
|
||||
$this->settings = $languageSettings;
|
||||
$this->momentFormatter = new MomentFormatConverter();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -33,31 +22,19 @@ final class LanguageFormattings
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getAvailableLanguages(): array
|
||||
public function getAllLocales(): array
|
||||
{
|
||||
return array_keys($this->settings);
|
||||
return array_keys($this->languageSettings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the format which is used by the form component to handle date values.
|
||||
*
|
||||
* @param string $locale
|
||||
* @return string
|
||||
*/
|
||||
public function getDateTypeFormat(string $locale): string
|
||||
public function isKnownLocale(string $language): bool
|
||||
{
|
||||
return $this->getConfig('date_type', $locale);
|
||||
return \in_array($language, $this->getAllLocales());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the format which is used by the Javascript component to handle date values.
|
||||
*
|
||||
* @param string $locale
|
||||
* @return string
|
||||
*/
|
||||
public function getDatePickerFormat(string $locale): string
|
||||
public function getDefaultLocale(): string
|
||||
{
|
||||
return $this->momentFormatter->convert($this->getDateTypeFormat($locale));
|
||||
return Constants::DEFAULT_LOCALE;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,7 +67,7 @@ final class LanguageFormattings
|
||||
*/
|
||||
public function getDateTimeFormat(string $locale): string
|
||||
{
|
||||
return $this->getConfig('date_time', $locale);
|
||||
return $this->getDateFormat($locale) . ' ' . $this->getTimeFormat($locale);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,24 +78,31 @@ final class LanguageFormattings
|
||||
*/
|
||||
public function getDurationFormat(string $locale): string
|
||||
{
|
||||
return $this->getConfig('duration', $locale);
|
||||
return '%h:%m';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param string $locale
|
||||
* @return string
|
||||
*/
|
||||
private function getConfig(string $key, string $locale): string
|
||||
public function isRightToLeft(string $locale): bool
|
||||
{
|
||||
if (!isset($this->settings[$locale])) {
|
||||
return $this->getConfig('rtl', $locale);
|
||||
}
|
||||
|
||||
public function is24Hour(string $locale): bool
|
||||
{
|
||||
$format = $this->getTimeFormat($locale);
|
||||
|
||||
return stripos($format, 'a') === false;
|
||||
}
|
||||
|
||||
private function getConfig(string $key, string $locale): string|bool
|
||||
{
|
||||
if (!isset($this->languageSettings[$locale])) {
|
||||
throw new \InvalidArgumentException(sprintf('Unknown locale given: %s', $locale));
|
||||
}
|
||||
|
||||
if (!isset($this->settings[$locale][$key])) {
|
||||
if (!isset($this->languageSettings[$locale][$key])) {
|
||||
throw new \InvalidArgumentException(sprintf('Unknown setting for locale %s: %s', $locale, $key));
|
||||
}
|
||||
|
||||
return $this->settings[$locale][$key];
|
||||
return $this->languageSettings[$locale][$key];
|
||||
}
|
||||
}
|
||||
@@ -11,14 +11,8 @@ namespace App\Configuration;
|
||||
|
||||
final class MailConfiguration
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $mailFrom;
|
||||
|
||||
public function __construct(string $mailFrom)
|
||||
public function __construct(private string $mailFrom)
|
||||
{
|
||||
$this->mailFrom = $mailFrom;
|
||||
}
|
||||
|
||||
public function getFromAddress(): ?string
|
||||
|
||||
@@ -14,11 +14,8 @@ namespace App\Configuration;
|
||||
*/
|
||||
final class SamlConfiguration implements SamlConfigurationInterface
|
||||
{
|
||||
private $configuration;
|
||||
|
||||
public function __construct(SystemConfiguration $configuration)
|
||||
public function __construct(private SystemConfiguration $configuration)
|
||||
{
|
||||
$this->configuration = $configuration;
|
||||
}
|
||||
|
||||
public function isActivated(): bool
|
||||
@@ -31,19 +28,29 @@ final class SamlConfiguration implements SamlConfigurationInterface
|
||||
return $this->configuration->getSamlTitle();
|
||||
}
|
||||
|
||||
public function getProvider(): string
|
||||
{
|
||||
return $this->configuration->getSamlProvider();
|
||||
}
|
||||
|
||||
public function getAttributeMapping(): array
|
||||
{
|
||||
return $this->configuration->getSamlAttributeMapping();
|
||||
return $this->configuration->findArray('saml.mapping');
|
||||
}
|
||||
|
||||
public function getRolesAttribute(): ?string
|
||||
{
|
||||
return $this->configuration->getSamlRolesAttribute();
|
||||
$attr = $this->configuration->find('saml.roles.attribute');
|
||||
if (empty($attr)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (string) $attr;
|
||||
}
|
||||
|
||||
public function getRolesMapping(): array
|
||||
{
|
||||
return $this->configuration->getSamlRolesMapping();
|
||||
return $this->configuration->findArray('saml.roles.mapping');
|
||||
}
|
||||
|
||||
public function isRolesResetOnLogin(): bool
|
||||
@@ -53,6 +60,6 @@ final class SamlConfiguration implements SamlConfigurationInterface
|
||||
|
||||
public function getConnection(): array
|
||||
{
|
||||
return $this->configuration->getSamlConnection();
|
||||
return $this->configuration->findArray('saml.connection');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ interface SamlConfigurationInterface
|
||||
|
||||
public function getTitle(): string;
|
||||
|
||||
public function getProvider(): string;
|
||||
|
||||
public function getAttributeMapping(): array;
|
||||
|
||||
public function getRolesAttribute(): ?string;
|
||||
|
||||
@@ -1,212 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Configuration;
|
||||
|
||||
use App\Entity\Configuration;
|
||||
|
||||
/**
|
||||
* @internal do NOT use this trait, but access your configs via SystemConfiguration
|
||||
*/
|
||||
trait StringAccessibleConfigTrait
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $settings;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $original;
|
||||
/**
|
||||
* @var ConfigLoaderInterface
|
||||
*/
|
||||
protected $repository;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $initialized = false;
|
||||
|
||||
public function __construct(ConfigLoaderInterface $repository, array $settings)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
$this->original = $this->settings = $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ConfigLoaderInterface $repository
|
||||
* @return Configuration[]
|
||||
*/
|
||||
protected function getConfigurations(ConfigLoaderInterface $repository): array
|
||||
{
|
||||
return $repository->getConfiguration($this->getPrefix());
|
||||
}
|
||||
|
||||
protected function prepare()
|
||||
{
|
||||
if ($this->initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->getConfigurations($this->repository) as $configuration) {
|
||||
$this->set($configuration->getName(), $configuration->getValue());
|
||||
}
|
||||
|
||||
$this->initialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function getPrefix(): string;
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function default(string $key)
|
||||
{
|
||||
$key = $this->prepareSearchKey($key);
|
||||
|
||||
return $this->get($key, $this->original);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @return string|int|bool|float|null|array
|
||||
*/
|
||||
public function find(string $key)
|
||||
{
|
||||
$this->prepare();
|
||||
$key = $this->prepareSearchKey($key);
|
||||
|
||||
return $this->get($key, $this->settings);
|
||||
}
|
||||
|
||||
private function prepareSearchKey(string $key): string
|
||||
{
|
||||
$prefix = $this->getPrefix() . '.';
|
||||
$length = \strlen($prefix);
|
||||
|
||||
if (substr($key, 0, $length) === $prefix) {
|
||||
$key = substr($key, $length);
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param array $config
|
||||
* @return mixed
|
||||
*/
|
||||
private function get(string $key, array $config)
|
||||
{
|
||||
$keys = explode('.', $key);
|
||||
$search = array_shift($keys);
|
||||
|
||||
if (!\array_key_exists($search, $config)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (\is_array($config[$search]) && !empty($keys)) {
|
||||
return $this->get(implode('.', $keys), $config[$search]);
|
||||
}
|
||||
|
||||
return $config[$search];
|
||||
}
|
||||
|
||||
public function has(string $key): bool
|
||||
{
|
||||
$this->prepare();
|
||||
$key = $this->prepareSearchKey($key);
|
||||
|
||||
$keys = explode('.', $key);
|
||||
$search = array_shift($keys);
|
||||
|
||||
if (!\array_key_exists($search, $this->settings)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function offsetExists($offset): bool
|
||||
{
|
||||
return $this->has($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $offset
|
||||
* @return array|bool|float|int|string|null
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->find($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
* @param mixed $value
|
||||
* @throws \BadMethodCallException
|
||||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
$this->set($offset, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
* @throws \BadMethodCallException
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
throw new \BadMethodCallException('SystemBundleConfiguration does not support offsetUnset()');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an array item to a given value using "dot" notation.
|
||||
*
|
||||
* If no key is given to the method, the entire array will be replaced.
|
||||
*
|
||||
* @see https://github.com/divineomega/array_undot
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function set(string $key, $value): array
|
||||
{
|
||||
$array = &$this->settings;
|
||||
$keys = explode('.', $key);
|
||||
while (\count($keys) > 1) {
|
||||
$key = array_shift($keys);
|
||||
if (!isset($array[$key]) || !\is_array($array[$key])) {
|
||||
$array[$key] = [];
|
||||
}
|
||||
|
||||
$array = &$array[$key];
|
||||
}
|
||||
|
||||
$k = array_shift($keys);
|
||||
|
||||
if (\array_key_exists($k, $array)) {
|
||||
if (\is_bool($array[$k])) {
|
||||
$value = (bool) $value;
|
||||
} elseif (\is_int($array[$k])) {
|
||||
$value = (int) $value;
|
||||
}
|
||||
}
|
||||
|
||||
$array[$k] = $value;
|
||||
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Configuration;
|
||||
|
||||
interface SystemBundleConfiguration
|
||||
{
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getPrefix(): string;
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function find(string $key);
|
||||
}
|
||||
@@ -9,21 +9,152 @@
|
||||
|
||||
namespace App\Configuration;
|
||||
|
||||
class SystemConfiguration implements SystemBundleConfiguration
|
||||
final class SystemConfiguration
|
||||
{
|
||||
use StringAccessibleConfigTrait;
|
||||
private bool $initialized = false;
|
||||
|
||||
public function getPrefix(): string
|
||||
public function __construct(private ConfigLoaderInterface $repository, private ?array $settings)
|
||||
{
|
||||
return 'kimai';
|
||||
}
|
||||
|
||||
protected function getConfigurations(ConfigLoaderInterface $repository): array
|
||||
private function prepare(): void
|
||||
{
|
||||
return $repository->getConfiguration();
|
||||
if ($this->initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->repository->getConfigurations() as $configuration) {
|
||||
$this->set($configuration->getName(), $configuration->getValue());
|
||||
}
|
||||
|
||||
$this->initialized = true;
|
||||
}
|
||||
|
||||
// ========== Login form ==========
|
||||
/**
|
||||
* Set an array item to a given value using "dot" notation.
|
||||
*
|
||||
* If no key is given to the method, the entire array will be replaced.
|
||||
*
|
||||
* @see https://github.com/divineomega/array_undot
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
private function set(string $key, $value): void
|
||||
{
|
||||
if (\array_key_exists($key, $this->settings)) {
|
||||
if (\is_bool($this->settings[$key])) {
|
||||
$value = (bool) $value;
|
||||
} elseif (\is_int($this->settings[$key])) {
|
||||
$value = (int) $value;
|
||||
}
|
||||
}
|
||||
$this->settings[$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @return string|int|bool|float|null
|
||||
*/
|
||||
public function find(string $key): string|int|bool|float|null
|
||||
{
|
||||
$this->prepare();
|
||||
|
||||
if (\array_key_exists($key, $this->settings)) {
|
||||
return $this->settings[$key];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should be avoided if possible, use plain keys instead.
|
||||
*
|
||||
* @param string $key
|
||||
* @return array
|
||||
*/
|
||||
public function findArray(string $key): array
|
||||
{
|
||||
$this->prepare();
|
||||
|
||||
$result = array_filter($this->settings, function ($settingName) use ($key): bool {
|
||||
return str_starts_with($settingName, $key);
|
||||
}, ARRAY_FILTER_USE_KEY);
|
||||
|
||||
$replaced = [];
|
||||
foreach ($result as $settingName => $value) {
|
||||
if (\is_bool($this->settings[$settingName])) {
|
||||
$value = (bool) $value;
|
||||
} elseif (\is_int($this->settings[$settingName])) {
|
||||
$value = (int) $value;
|
||||
}
|
||||
|
||||
$baseName = str_replace($key . '.', '', $settingName);
|
||||
|
||||
$keys = explode('.', $baseName);
|
||||
$array = &$replaced;
|
||||
while (\count($keys) > 1) {
|
||||
$search = array_shift($keys);
|
||||
/* @phpstan-ignore-next-line */
|
||||
if (!\array_key_exists($search, $array) || !\is_array($array[$search])) {
|
||||
$array[$search] = [];
|
||||
}
|
||||
|
||||
$array = &$array[$search];
|
||||
}
|
||||
$array[array_shift($keys)] = $value;
|
||||
}
|
||||
|
||||
return $replaced;
|
||||
}
|
||||
|
||||
public function has(string $key): bool
|
||||
{
|
||||
$this->prepare();
|
||||
|
||||
if (\array_key_exists($key, $this->settings)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$result = array_filter($this->settings, function ($settingName) use ($key): bool {
|
||||
return str_starts_with($settingName, $key);
|
||||
}, ARRAY_FILTER_USE_KEY);
|
||||
|
||||
return \count($result) > 0;
|
||||
}
|
||||
|
||||
// ========== Array access methods ==========
|
||||
|
||||
public function offsetExists($offset): bool
|
||||
{
|
||||
return $this->has($offset);
|
||||
}
|
||||
|
||||
public function offsetGet($offset): mixed
|
||||
{
|
||||
return $this->find($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
* @param mixed $value
|
||||
* @throws \BadMethodCallException
|
||||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
$this->set($offset, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
* @throws \BadMethodCallException
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
throw new \BadMethodCallException('SystemBundleConfiguration does not support offsetUnset()');
|
||||
}
|
||||
|
||||
// ========== Authentication configurations ==========
|
||||
|
||||
public function isLoginFormActive(): bool
|
||||
{
|
||||
@@ -41,6 +172,10 @@ class SystemConfiguration implements SystemBundleConfiguration
|
||||
|
||||
public function isSelfRegistrationActive(): bool
|
||||
{
|
||||
if (!$this->isLoginFormActive()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (bool) $this->find('user.registration');
|
||||
}
|
||||
|
||||
@@ -56,11 +191,13 @@ class SystemConfiguration implements SystemBundleConfiguration
|
||||
|
||||
public function isPasswordResetActive(): bool
|
||||
{
|
||||
if (!$this->isLoginFormActive()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (bool) $this->find('user.password_reset');
|
||||
}
|
||||
|
||||
// ========== SAML configurations ==========
|
||||
|
||||
public function isSamlActive(): bool
|
||||
{
|
||||
return (bool) $this->find('saml.activate');
|
||||
@@ -71,19 +208,9 @@ class SystemConfiguration implements SystemBundleConfiguration
|
||||
return (string) $this->find('saml.title');
|
||||
}
|
||||
|
||||
public function getSamlAttributeMapping(): array
|
||||
public function getSamlProvider(): ?string
|
||||
{
|
||||
return (array) $this->find('saml.mapping');
|
||||
}
|
||||
|
||||
public function getSamlRolesAttribute(): ?string
|
||||
{
|
||||
return (string) $this->find('saml.roles.attribute');
|
||||
}
|
||||
|
||||
public function getSamlRolesMapping(): array
|
||||
{
|
||||
return (array) $this->find('saml.roles.mapping');
|
||||
return $this->find('saml.provider');
|
||||
}
|
||||
|
||||
public function isSamlRolesResetOnLogin(): bool
|
||||
@@ -91,40 +218,13 @@ class SystemConfiguration implements SystemBundleConfiguration
|
||||
return (bool) $this->find('saml.roles.resetOnLogin');
|
||||
}
|
||||
|
||||
public function getSamlConnection(): array
|
||||
{
|
||||
return (array) $this->find('saml.connection');
|
||||
}
|
||||
|
||||
// ========== LDAP configurations ==========
|
||||
|
||||
public function isLdapActive(): bool
|
||||
{
|
||||
return (bool) $this->find('ldap.activate');
|
||||
}
|
||||
|
||||
public function getLdapRoleParameters(): array
|
||||
{
|
||||
return (array) $this->find('ldap.role');
|
||||
}
|
||||
|
||||
public function getLdapUserParameters(): array
|
||||
{
|
||||
return (array) $this->find('ldap.user');
|
||||
}
|
||||
|
||||
public function getLdapConnectionParameters(): array
|
||||
{
|
||||
return (array) $this->find('ldap.connection');
|
||||
}
|
||||
|
||||
// ========== Calendar configurations ==========
|
||||
|
||||
public function getCalendarBusinessDays(): array
|
||||
{
|
||||
return (array) $this->find('calendar.businessHours.days');
|
||||
}
|
||||
|
||||
public function getCalendarBusinessTimeBegin(): string
|
||||
{
|
||||
return (string) $this->find('calendar.businessHours.begin');
|
||||
@@ -165,9 +265,9 @@ class SystemConfiguration implements SystemBundleConfiguration
|
||||
return $this->find('calendar.google.api_key');
|
||||
}
|
||||
|
||||
public function getCalendarGoogleSources(): ?array
|
||||
public function getCalendarGoogleSources(): array
|
||||
{
|
||||
return $this->find('calendar.google.sources');
|
||||
return $this->findArray('calendar.google.sources');
|
||||
}
|
||||
|
||||
public function getCalendarSlotDuration(): string
|
||||
@@ -272,18 +372,16 @@ class SystemConfiguration implements SystemBundleConfiguration
|
||||
return (bool) $this->find('timesheet.markdown_content');
|
||||
}
|
||||
|
||||
public function isTimesheetRequiresActivity(): bool
|
||||
{
|
||||
return (bool) $this->find('timesheet.rules.require_activity');
|
||||
}
|
||||
|
||||
public function getTimesheetActiveEntriesHardLimit(): int
|
||||
{
|
||||
return (int) $this->find('timesheet.active_entries.hard_limit');
|
||||
}
|
||||
|
||||
public function getTimesheetActiveEntriesSoftLimit(): int
|
||||
{
|
||||
@trigger_error('The configuration timesheet.active_entries.soft_limit is deprecated since 1.15', E_USER_DEPRECATED);
|
||||
|
||||
return $this->getTimesheetActiveEntriesHardLimit();
|
||||
}
|
||||
|
||||
public function getTimesheetDefaultRoundingDays(): string
|
||||
{
|
||||
return (string) $this->find('timesheet.rounding.default.days');
|
||||
@@ -309,32 +407,7 @@ class SystemConfiguration implements SystemBundleConfiguration
|
||||
return (int) $this->find('timesheet.rounding.default.duration');
|
||||
}
|
||||
|
||||
public function getTimesheetLockdownPeriodStart(): string
|
||||
{
|
||||
return (string) $this->find('timesheet.rules.lockdown_period_start');
|
||||
}
|
||||
|
||||
public function getTimesheetLockdownPeriodEnd(): string
|
||||
{
|
||||
return (string) $this->find('timesheet.rules.lockdown_period_end');
|
||||
}
|
||||
|
||||
public function getTimesheetLockdownGracePeriod(): string
|
||||
{
|
||||
return (string) $this->find('timesheet.rules.lockdown_grace_period');
|
||||
}
|
||||
|
||||
public function getTimesheetLockdownTimeZone(): ?string
|
||||
{
|
||||
return $this->find('timesheet.rules.lockdown_period_timezone');
|
||||
}
|
||||
|
||||
public function isTimesheetLockdownActive(): bool
|
||||
{
|
||||
return !empty($this->find('timesheet.rules.lockdown_period_start')) && !empty($this->find('timesheet.rules.lockdown_period_end'));
|
||||
}
|
||||
|
||||
private function getIncrement(string $key, int $fallback, int $min = 1): ?int
|
||||
private function getIncrement(string $key, int $fallback, int $min = 1): int
|
||||
{
|
||||
$config = $this->find($key);
|
||||
|
||||
@@ -344,27 +417,22 @@ class SystemConfiguration implements SystemBundleConfiguration
|
||||
|
||||
$config = (int) $config;
|
||||
|
||||
return $config < $min ? null : $config;
|
||||
return max($config, $min);
|
||||
}
|
||||
|
||||
public function getTimesheetIncrementDuration(): ?int
|
||||
public function getTimesheetIncrementDuration(): int
|
||||
{
|
||||
return $this->getIncrement('timesheet.duration_increment', $this->getTimesheetDefaultRoundingDuration(), 1);
|
||||
return $this->getIncrement('timesheet.duration_increment', $this->getTimesheetDefaultRoundingDuration(), 0);
|
||||
}
|
||||
|
||||
public function getTimesheetIncrementBegin(): ?int
|
||||
public function getTimesheetIncrementMinutes(): int
|
||||
{
|
||||
return $this->getIncrement('timesheet.time_increment', $this->getTimesheetDefaultRoundingBegin(), 0);
|
||||
}
|
||||
|
||||
public function getTimesheetIncrementEnd(): ?int
|
||||
{
|
||||
return $this->getIncrement('timesheet.time_increment', $this->getTimesheetDefaultRoundingEnd(), 0);
|
||||
return $this->getIncrement('timesheet.time_increment', $this->getTimesheetDefaultRoundingDuration(), 0);
|
||||
}
|
||||
|
||||
public function getQuickEntriesRecentAmount(): int
|
||||
{
|
||||
return $this->getIncrement('quick_entry.recent_activities', 5, 0) ?? 5;
|
||||
return $this->getIncrement('quick_entry.recent_activities', 5, 5);
|
||||
}
|
||||
|
||||
// ========== Company configurations ==========
|
||||
@@ -382,14 +450,9 @@ class SystemConfiguration implements SystemBundleConfiguration
|
||||
|
||||
// ========== Theme configurations ==========
|
||||
|
||||
public function isThemeColorsLimited(): bool
|
||||
public function isShowAbout(): bool
|
||||
{
|
||||
return (bool) $this->find('theme.colors_limited');
|
||||
}
|
||||
|
||||
public function isThemeRandomColors(): bool
|
||||
{
|
||||
return (bool) $this->find('theme.random_colors');
|
||||
return (bool) $this->find('theme.show_about');
|
||||
}
|
||||
|
||||
public function isThemeAllowAvatarUrls(): bool
|
||||
@@ -397,11 +460,6 @@ class SystemConfiguration implements SystemBundleConfiguration
|
||||
return (bool) $this->find('theme.avatar_url');
|
||||
}
|
||||
|
||||
public function getThemeAutocompleteCharacters(): int
|
||||
{
|
||||
return (int) $this->find('theme.autocomplete_chars');
|
||||
}
|
||||
|
||||
public function getThemeColorChoices(): ?string
|
||||
{
|
||||
$config = $this->find('theme.color_choices');
|
||||
@@ -409,19 +467,7 @@ class SystemConfiguration implements SystemBundleConfiguration
|
||||
return $config;
|
||||
}
|
||||
|
||||
return $this->default('theme.color_choices');
|
||||
}
|
||||
|
||||
// ========== Branding configurations ==========
|
||||
|
||||
public function getBrandingTitle(): ?string
|
||||
{
|
||||
return $this->find('theme.branding.title');
|
||||
}
|
||||
|
||||
public function isAllowTagCreation(): bool
|
||||
{
|
||||
return (bool) $this->find('theme.tags_create');
|
||||
return 'Silver|#c0c0c0';
|
||||
}
|
||||
|
||||
// ========== Projects ==========
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Configuration;
|
||||
|
||||
/**
|
||||
* @internal might be deprecated in the future, use SystemConfiguration instead
|
||||
*/
|
||||
final class ThemeConfiguration implements \ArrayAccess
|
||||
{
|
||||
private $systemConfiguration;
|
||||
|
||||
public function __construct(SystemConfiguration $systemConfiguration)
|
||||
{
|
||||
$this->systemConfiguration = $systemConfiguration;
|
||||
}
|
||||
|
||||
public function offsetExists($offset): bool
|
||||
{
|
||||
return $this->systemConfiguration->has('theme.' . $offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->systemConfiguration->find('theme.' . $offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
* @param mixed $value
|
||||
* @throws \BadMethodCallException
|
||||
*/
|
||||
public function offsetSet($offset, $value): void
|
||||
{
|
||||
throw new \BadMethodCallException('ThemeConfiguration does not support offsetSet()');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
* @throws \BadMethodCallException
|
||||
*/
|
||||
public function offsetUnset($offset): void
|
||||
{
|
||||
throw new \BadMethodCallException('ThemeConfiguration does not support offsetUnset()');
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.15
|
||||
*/
|
||||
public function isAllowTagCreation(): bool
|
||||
{
|
||||
return (bool) $this->offsetGet('tags_create');
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.15
|
||||
*/
|
||||
public function getTitle(): ?string
|
||||
{
|
||||
$title = $this->offsetGet('branding.title');
|
||||
if (null === $title) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (string) $title;
|
||||
}
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Configuration;
|
||||
|
||||
/**
|
||||
* @deprecated since 1.13, use SystemConfiguration instead
|
||||
*/
|
||||
class TimesheetConfiguration implements SystemBundleConfiguration
|
||||
{
|
||||
private $configuration;
|
||||
|
||||
public function __construct(SystemConfiguration $configuration)
|
||||
{
|
||||
$this->configuration = $configuration;
|
||||
}
|
||||
|
||||
public function find(string $key)
|
||||
{
|
||||
if (strpos($key, $this->getPrefix() . '.') === false) {
|
||||
$key = $this->getPrefix() . '.' . $key;
|
||||
}
|
||||
|
||||
return $this->configuration->find($key);
|
||||
}
|
||||
|
||||
public function getPrefix(): string
|
||||
{
|
||||
return 'timesheet';
|
||||
}
|
||||
|
||||
public function isAllowFutureTimes(): bool
|
||||
{
|
||||
return $this->configuration->isTimesheetAllowFutureTimes();
|
||||
}
|
||||
|
||||
public function isAllowOverlappingRecords(): bool
|
||||
{
|
||||
return $this->configuration->isTimesheetAllowOverlappingRecords();
|
||||
}
|
||||
|
||||
public function getTrackingMode(): string
|
||||
{
|
||||
return $this->configuration->getTimesheetTrackingMode();
|
||||
}
|
||||
|
||||
public function getDefaultBeginTime(): string
|
||||
{
|
||||
return $this->configuration->getTimesheetDefaultBeginTime();
|
||||
}
|
||||
|
||||
public function isMarkdownEnabled(): bool
|
||||
{
|
||||
return $this->configuration->isTimesheetMarkdownEnabled();
|
||||
}
|
||||
|
||||
public function getActiveEntriesHardLimit(): int
|
||||
{
|
||||
return $this->configuration->getTimesheetActiveEntriesHardLimit();
|
||||
}
|
||||
|
||||
public function getActiveEntriesSoftLimit(): int
|
||||
{
|
||||
return $this->configuration->getTimesheetActiveEntriesSoftLimit();
|
||||
}
|
||||
|
||||
public function getDefaultRoundingDays(): string
|
||||
{
|
||||
return $this->configuration->getTimesheetDefaultRoundingDays();
|
||||
}
|
||||
|
||||
public function getDefaultRoundingMode(): string
|
||||
{
|
||||
return $this->configuration->getTimesheetDefaultRoundingMode();
|
||||
}
|
||||
|
||||
public function getDefaultRoundingBegin(): int
|
||||
{
|
||||
return $this->configuration->getTimesheetDefaultRoundingBegin();
|
||||
}
|
||||
|
||||
public function getDefaultRoundingEnd(): int
|
||||
{
|
||||
return $this->configuration->getTimesheetDefaultRoundingEnd();
|
||||
}
|
||||
|
||||
public function getDefaultRoundingDuration(): int
|
||||
{
|
||||
return $this->configuration->getTimesheetDefaultRoundingDuration();
|
||||
}
|
||||
|
||||
public function getLockdownPeriodStart(): string
|
||||
{
|
||||
return $this->configuration->getTimesheetLockdownPeriodStart();
|
||||
}
|
||||
|
||||
public function getLockdownPeriodEnd(): string
|
||||
{
|
||||
return $this->configuration->getTimesheetLockdownPeriodEnd();
|
||||
}
|
||||
|
||||
public function getLockdownGracePeriod(): string
|
||||
{
|
||||
return $this->configuration->getTimesheetLockdownGracePeriod();
|
||||
}
|
||||
|
||||
public function isLockdownActive(): bool
|
||||
{
|
||||
return $this->configuration->isTimesheetLockdownActive();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user