Release 2.7.0 (#4506)

* added api URL for simpler integration
* allow to request password change upon next login
* make ModifiedAt timesheet independent
* improved plugin api
* replace kernel calls with AutoconfigureTag and TaggedIterator attributes
* new translation keys (e.g. for days)
* support setting min and max date on date and daterange pickers
This commit is contained in:
Kevin Papst
2023-12-26 14:49:11 +01:00
committed by GitHub
parent f86eca05ae
commit ad645f5b58
72 changed files with 378 additions and 735 deletions

View File

@@ -11,42 +11,38 @@ namespace App\Plugin;
final class Plugin
{
private string $id;
private string $path;
private ?PluginMetadata $metadata = null;
public function __construct(PluginInterface $bundle)
public function __construct(private readonly PluginInterface $bundle)
{
$this->id = $bundle->getName();
$this->path = $bundle->getPath();
}
public function getMetadata(): ?PluginMetadata
public function getMetadata(): PluginMetadata
{
if ($this->metadata === null) {
$this->metadata = new PluginMetadata($this->getPath());
}
return $this->metadata;
}
public function setMetadata(PluginMetadata $metadata): void
{
$this->metadata = $metadata;
}
public function getPath(): string
{
return $this->path;
return $this->bundle->getPath();
}
public function getName(): string
{
if ($this->metadata !== null && $this->metadata->getName() !== null) {
return $this->metadata->getName();
$meta = $this->getMetadata();
if ($meta->getName() !== null) {
return $meta->getName();
}
return $this->id;
return $this->getId();
}
public function getId(): string
{
return $this->id;
return $this->bundle->getName();
}
}

View File

@@ -9,6 +9,9 @@
namespace App\Plugin;
use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;
#[AutoconfigureTag]
interface PluginInterface
{
public function getName(): string;

View File

@@ -9,23 +9,23 @@
namespace App\Plugin;
use Symfony\Component\DependencyInjection\Attribute\TaggedIterator;
final class PluginManager
{
/**
* @var array<Plugin>|null
*/
private ?array $plugins = null;
/**
* @var iterable<PluginInterface>
*/
private iterable $bundles;
/**
* @param iterable<PluginInterface> $plugins
* @param iterable<PluginInterface> $bundles
*/
public function __construct(iterable $plugins)
public function __construct(
#[TaggedIterator(PluginInterface::class)]
private readonly iterable $bundles
)
{
$this->bundles = $plugins;
}
/**
@@ -46,29 +46,27 @@ final class PluginManager
return $this->plugins;
}
public function hasPlugin(string $name): bool
{
foreach ($this->bundles as $plugin) {
if ($plugin->getName() === $name) {
return true;
}
}
return false;
}
public function getPlugin(string $name): ?Plugin
{
$plugins = $this->getPlugins();
foreach ($plugins as $plugin) {
if ($plugin->getName() === $name) {
if ($plugin->getId() === $name) {
return $plugin;
}
}
return null;
}
/**
* Call this method and pass a plugin, to set its metadata.
* This is not pre-filled by default, as it would mean to parse several composer.json on each request.
*
* @param Plugin $plugin
* @throws \Exception
*/
public function loadMetadata(Plugin $plugin): void
{
$meta = PluginMetadata::loadFromComposer($plugin->getPath());
$plugin->setMetadata($meta);
}
}

View File

@@ -20,11 +20,9 @@ class PluginMetadata
private ?string $name = null;
/**
* @param string $path
* @return PluginMetadata
* @throws \Exception
*/
public static function loadFromComposer(string $path): PluginMetadata
public function __construct(string $path)
{
if (!is_dir($path) || !is_readable($path)) {
throw new \Exception(sprintf('Bundle directory "%s" cannot be accessed.', $path));
@@ -59,16 +57,13 @@ class PluginMetadata
throw new \Exception(sprintf('Bundle "%s" defines an invalid Kimai minimum version in extra.kimai.require. Please provide an integer as in Constants::VERSION_ID.', $pluginName));
}
$meta = new self();
$meta->description = $json['description'] ?? '';
$meta->homepage = $json['homepage'] ?? Constants::HOMEPAGE . '/store/';
$meta->name = $json['extra']['kimai']['name'];
$meta->kimaiVersion = $json['extra']['kimai']['require'];
$this->description = $json['description'] ?? '';
$this->homepage = $json['homepage'] ?? Constants::HOMEPAGE . '/store/';
$this->name = $json['extra']['kimai']['name'];
$this->kimaiVersion = $json['extra']['kimai']['require'];
// the version field is required if we use composer to install a plugin via var/packages/
$meta->version = $json['extra']['kimai']['version'] ?? ($json['version'] ?? 'unknown');
return $meta;
$this->version = $json['extra']['kimai']['version'] ?? ($json['version'] ?? 'unknown');
}
public function getDescription(): ?string