Update Composer packages
This commit is contained in:
parent
0320235f6c
commit
a8b68fb61b
378 changed files with 28466 additions and 28852 deletions
90
kirby/src/Blueprint/Collection.php
Normal file
90
kirby/src/Blueprint/Collection.php
Normal file
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
namespace Kirby\Blueprint;
|
||||
|
||||
use Kirby\Cms\Collection as BaseCollection;
|
||||
use Kirby\Cms\ModelWithContent;
|
||||
use Kirby\Toolkit\A;
|
||||
use TypeError;
|
||||
|
||||
/**
|
||||
* Typed collection
|
||||
*
|
||||
* @package Kirby Blueprint
|
||||
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||
* @link https://getkirby.com
|
||||
* @copyright Bastian Allgeier
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*
|
||||
* // TODO: include in test coverage in 3.9
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class Collection extends BaseCollection
|
||||
{
|
||||
/**
|
||||
* The expected object type
|
||||
*/
|
||||
public const TYPE = Node::class;
|
||||
|
||||
public function __construct(array $objects = [])
|
||||
{
|
||||
foreach ($objects as $object) {
|
||||
$this->__set($object->id, $object);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The Kirby Collection class only shows the key to
|
||||
* avoid huge tress with dump, but for the blueprint
|
||||
* collections this is really not useful
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return A::map($this->data, fn ($item) => (array)$item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the type of every item that is being
|
||||
* added to the collection. They cneed to have
|
||||
* the class defined by static::TYPE.
|
||||
*/
|
||||
public function __set(string $key, $value): void
|
||||
{
|
||||
if (
|
||||
is_a($value, static::TYPE) === false
|
||||
) {
|
||||
throw new TypeError('Each value in the collection must be an instance of ' . static::TYPE);
|
||||
}
|
||||
|
||||
parent::__set($key, $value);
|
||||
}
|
||||
|
||||
public static function factory(array $items)
|
||||
{
|
||||
$collection = new static();
|
||||
$className = static::TYPE;
|
||||
|
||||
foreach ($items as $id => $item) {
|
||||
if (is_array($item) === true) {
|
||||
$item['id'] ??= $id;
|
||||
$item = $className::factory($item);
|
||||
$collection->__set($item->id, $item);
|
||||
} else {
|
||||
$collection->__set($id, $className::factory($item));
|
||||
}
|
||||
}
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
public function render(ModelWithContent $model)
|
||||
{
|
||||
$props = [];
|
||||
|
||||
foreach ($this->data as $key => $item) {
|
||||
$props[$key] = $item->render($model);
|
||||
}
|
||||
|
||||
return $props;
|
||||
}
|
||||
}
|
75
kirby/src/Blueprint/Config.php
Normal file
75
kirby/src/Blueprint/Config.php
Normal file
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
namespace Kirby\Blueprint;
|
||||
|
||||
use Closure;
|
||||
use Kirby\Cms\App;
|
||||
use Kirby\Data\Yaml;
|
||||
use Kirby\Exception\NotFoundException;
|
||||
use Kirby\Filesystem\F;
|
||||
|
||||
/**
|
||||
* Config
|
||||
*
|
||||
* @package Kirby Blueprint
|
||||
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||
* @link https://getkirby.com
|
||||
* @copyright Bastian Allgeier
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*
|
||||
* // TODO: include in test coverage in 3.9
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class Config
|
||||
{
|
||||
public string $file;
|
||||
public string $id;
|
||||
public string|array|Closure|null $plugin;
|
||||
public string $root;
|
||||
|
||||
public function __construct(
|
||||
public string $path
|
||||
) {
|
||||
$kirby = App::instance();
|
||||
|
||||
$this->id = basename($this->path);
|
||||
$this->root = $kirby->root('blueprints');
|
||||
$this->file = $this->root . '/' . $this->path . '.yml';
|
||||
$this->plugin = $kirby->extension('blueprints', $this->path);
|
||||
}
|
||||
|
||||
public function read(): array
|
||||
{
|
||||
if (F::exists($this->file, $this->root) === true) {
|
||||
return $this->unpack($this->file);
|
||||
}
|
||||
|
||||
return $this->unpack($this->plugin);
|
||||
}
|
||||
|
||||
public function write(array $props): bool
|
||||
{
|
||||
return Yaml::write($this->file, $props);
|
||||
}
|
||||
|
||||
public function unpack(string|array|Closure|null $extension): array
|
||||
{
|
||||
return match (true) {
|
||||
// extension does not exist
|
||||
is_null($extension)
|
||||
=> throw new NotFoundException('"' . $this->path . '" could not be found'),
|
||||
|
||||
// extension is stored as a file path
|
||||
is_string($extension)
|
||||
=> Yaml::read($extension),
|
||||
|
||||
// extension is a callback to be resolved
|
||||
is_callable($extension)
|
||||
=> $extension(App::instance()),
|
||||
|
||||
// extension is already defined as array
|
||||
default
|
||||
=> $extension
|
||||
};
|
||||
}
|
||||
}
|
65
kirby/src/Blueprint/Extension.php
Normal file
65
kirby/src/Blueprint/Extension.php
Normal file
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
namespace Kirby\Blueprint;
|
||||
|
||||
/**
|
||||
* Extension
|
||||
*
|
||||
* @package Kirby Blueprint
|
||||
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||
* @link https://getkirby.com
|
||||
* @copyright Bastian Allgeier
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*
|
||||
* // TODO: include in test coverage in 3.9
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class Extension
|
||||
{
|
||||
public function __construct(
|
||||
public string $path
|
||||
) {
|
||||
}
|
||||
|
||||
public static function apply(array $props): array
|
||||
{
|
||||
if (isset($props['extends']) === false) {
|
||||
return $props;
|
||||
}
|
||||
|
||||
// already extended
|
||||
if (is_a($props['extends'], Extension::class) === true) {
|
||||
return $props;
|
||||
}
|
||||
|
||||
$extension = new static($props['extends']);
|
||||
return $extension->extend($props);
|
||||
}
|
||||
|
||||
public function extend(array $props): array
|
||||
{
|
||||
$props = array_replace_recursive(
|
||||
$this->read(),
|
||||
$props
|
||||
);
|
||||
|
||||
$props['extends'] = $this;
|
||||
|
||||
return $props;
|
||||
}
|
||||
|
||||
public static function factory(string|array $path): static
|
||||
{
|
||||
if (is_string($path) === true) {
|
||||
return new static(path: $path);
|
||||
}
|
||||
|
||||
return new static(...$path);
|
||||
}
|
||||
|
||||
public function read(): array
|
||||
{
|
||||
$config = new Config($this->path);
|
||||
return $config->read();
|
||||
}
|
||||
}
|
119
kirby/src/Blueprint/Factory.php
Normal file
119
kirby/src/Blueprint/Factory.php
Normal file
|
@ -0,0 +1,119 @@
|
|||
<?php
|
||||
|
||||
namespace Kirby\Blueprint;
|
||||
|
||||
use ReflectionException;
|
||||
use ReflectionNamedType;
|
||||
use ReflectionProperty;
|
||||
use ReflectionUnionType;
|
||||
|
||||
/**
|
||||
* Factory
|
||||
*
|
||||
* @package Kirby Blueprint
|
||||
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||
* @link https://getkirby.com
|
||||
* @copyright Bastian Allgeier
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*
|
||||
* // TODO: include in test coverage in 3.9
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class Factory
|
||||
{
|
||||
/**
|
||||
* Resolves the properties by
|
||||
* applying a map of factories (propName => class)
|
||||
*/
|
||||
public static function apply(array $properties, array $factories): array
|
||||
{
|
||||
foreach ($factories as $property => $class) {
|
||||
// skip non-existing properties, empty properties
|
||||
// or properties that are matching objects
|
||||
if (
|
||||
isset($properties[$property]) === false ||
|
||||
$properties[$property] === null ||
|
||||
is_a($properties[$property], $class) === true
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$properties[$property] = $class::factory($properties[$property]);
|
||||
}
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
public static function forNamedType(ReflectionNamedType|null $type, $value)
|
||||
{
|
||||
// get the class name for the single type
|
||||
$className = $type->getName();
|
||||
|
||||
// check if there's a factory for the value
|
||||
if (method_exists($className, 'factory') === true) {
|
||||
return $className::factory($value);
|
||||
}
|
||||
|
||||
// try to assign the value directly and trust
|
||||
// in PHP's type system.
|
||||
return $value;
|
||||
}
|
||||
|
||||
public static function forProperties(string $class, array $properties): array
|
||||
{
|
||||
foreach ($properties as $property => $value) {
|
||||
try {
|
||||
$properties[$property] = static::forProperty($class, $property, $value);
|
||||
} catch (ReflectionException $e) {
|
||||
// the property does not exist
|
||||
unset($properties[$property]);
|
||||
}
|
||||
}
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
public static function forProperty(string $class, string $property, $value)
|
||||
{
|
||||
if (is_null($value) === true) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
// instantly assign objects
|
||||
// PHP's type system will find issues automatically
|
||||
if (is_object($value) === true) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
// get the type for the property
|
||||
$reflection = new ReflectionProperty($class, $property);
|
||||
$propType = $reflection->getType();
|
||||
|
||||
// no type given
|
||||
if ($propType === null) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
// union types
|
||||
if (is_a($propType, ReflectionUnionType::class) === true) {
|
||||
return static::forUnionType($propType, $value);
|
||||
}
|
||||
|
||||
return static::forNamedType($propType, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* For properties with union types,
|
||||
* the first named type is used to create
|
||||
* the factory or pass a built-in value
|
||||
*/
|
||||
public static function forUnionType(ReflectionUnionType $type, $value)
|
||||
{
|
||||
return static::forNamedType($type->getTypes()[0], $value);
|
||||
}
|
||||
|
||||
public static function make(string $class, array $properties): object
|
||||
{
|
||||
return new $class(...static::forProperties($class, $properties));
|
||||
}
|
||||
}
|
118
kirby/src/Blueprint/Node.php
Normal file
118
kirby/src/Blueprint/Node.php
Normal file
|
@ -0,0 +1,118 @@
|
|||
<?php
|
||||
|
||||
namespace Kirby\Blueprint;
|
||||
|
||||
use Kirby\Cms\ModelWithContent;
|
||||
|
||||
/**
|
||||
* A node of the blueprint
|
||||
*
|
||||
* @package Kirby Blueprint
|
||||
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||
* @link https://getkirby.com
|
||||
* @copyright Bastian Allgeier
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*
|
||||
* // TODO: include in test coverage in 3.9
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class Node
|
||||
{
|
||||
public const TYPE = 'node';
|
||||
|
||||
public function __construct(
|
||||
public string $id,
|
||||
public Extension|null $extends = null,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamic getter for properties
|
||||
*/
|
||||
public function __call(string $name, array $args)
|
||||
{
|
||||
$this->defaults();
|
||||
return $this->$name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply default values
|
||||
*/
|
||||
public function defaults(): static
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance by a set of array properties.
|
||||
*/
|
||||
public static function factory(array $props): static
|
||||
{
|
||||
$props = Extension::apply($props);
|
||||
$props = static::polyfill($props);
|
||||
return Factory::make(static::class, $props);
|
||||
}
|
||||
|
||||
|
||||
public static function load(string|array $props): static
|
||||
{
|
||||
// load by path
|
||||
if (is_string($props) === true) {
|
||||
$props = static::loadProps($props);
|
||||
}
|
||||
|
||||
return static::factory($props);
|
||||
}
|
||||
|
||||
public static function loadProps(string $path): array
|
||||
{
|
||||
$config = new Config($path);
|
||||
$props = $config->read();
|
||||
|
||||
// add the id if it's not set yet
|
||||
$props['id'] ??= basename($path);
|
||||
|
||||
return $props;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional method that runs before static::factory sends
|
||||
* its properties to the instance. This is perfect to clean
|
||||
* up props or keep deprecated props compatible.
|
||||
*/
|
||||
public static function polyfill(array $props): array
|
||||
{
|
||||
return $props;
|
||||
}
|
||||
|
||||
public function render(ModelWithContent $model)
|
||||
{
|
||||
// apply default values
|
||||
$this->defaults();
|
||||
|
||||
$array = [];
|
||||
|
||||
// go through all public properties
|
||||
foreach (get_object_vars($this) as $key => $value) {
|
||||
if (is_object($value) === false && is_resource($value) === false) {
|
||||
$array[$key] = $value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (method_exists($value, 'render') === true) {
|
||||
$array[$key] = $value->render($model);
|
||||
}
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Universal setter for properties
|
||||
*/
|
||||
public function set(string $property, $value): static
|
||||
{
|
||||
$this->$property = Factory::forProperty(static::class, $property, $value);
|
||||
return $this;
|
||||
}
|
||||
}
|
44
kirby/src/Blueprint/NodeI18n.php
Normal file
44
kirby/src/Blueprint/NodeI18n.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace Kirby\Blueprint;
|
||||
|
||||
use Kirby\Cms\ModelWithContent;
|
||||
use Kirby\Toolkit\I18n;
|
||||
|
||||
/**
|
||||
* Translatable node property
|
||||
*
|
||||
* @package Kirby Blueprint
|
||||
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||
* @link https://getkirby.com
|
||||
* @copyright Bastian Allgeier
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*
|
||||
* // TODO: include in test coverage in 3.9
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class NodeI18n extends NodeProperty
|
||||
{
|
||||
public function __construct(
|
||||
public array $translations,
|
||||
) {
|
||||
}
|
||||
|
||||
public static function factory($value = null): static|null
|
||||
{
|
||||
if ($value === false || $value === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (is_array($value) === false) {
|
||||
$value = ['en' => $value];
|
||||
}
|
||||
|
||||
return new static($value);
|
||||
}
|
||||
|
||||
public function render(ModelWithContent $model): string|null
|
||||
{
|
||||
return I18n::translate($this->translations, $this->translations);
|
||||
}
|
||||
}
|
27
kirby/src/Blueprint/NodeIcon.php
Normal file
27
kirby/src/Blueprint/NodeIcon.php
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace Kirby\Blueprint;
|
||||
|
||||
/**
|
||||
* Custom emoji or icon from the Kirby iconset
|
||||
*
|
||||
* @package Kirby Blueprint
|
||||
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||
* @link https://getkirby.com
|
||||
* @copyright Bastian Allgeier
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*
|
||||
* // TODO: include in test coverage in 3.9
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class NodeIcon extends NodeString
|
||||
{
|
||||
public static function field()
|
||||
{
|
||||
$field = parent::field();
|
||||
$field->id = 'icon';
|
||||
$field->label->translations = ['en' => 'Icon'];
|
||||
|
||||
return $field;
|
||||
}
|
||||
}
|
27
kirby/src/Blueprint/NodeProperty.php
Normal file
27
kirby/src/Blueprint/NodeProperty.php
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace Kirby\Blueprint;
|
||||
|
||||
use Kirby\Cms\ModelWithContent;
|
||||
|
||||
/**
|
||||
* Represents a property for a node
|
||||
*
|
||||
* @package Kirby Blueprint
|
||||
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||
* @link https://getkirby.com
|
||||
* @copyright Bastian Allgeier
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*
|
||||
* // TODO: include in test coverage in 3.9
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
abstract class NodeProperty
|
||||
{
|
||||
abstract public static function factory($value = null): static|null;
|
||||
|
||||
public function render(ModelWithContent $model)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
39
kirby/src/Blueprint/NodeString.php
Normal file
39
kirby/src/Blueprint/NodeString.php
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace Kirby\Blueprint;
|
||||
|
||||
use Kirby\Cms\ModelWithContent;
|
||||
|
||||
/**
|
||||
* Simple string blueprint node
|
||||
*
|
||||
* @package Kirby Blueprint
|
||||
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||
* @link https://getkirby.com
|
||||
* @copyright Bastian Allgeier
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*
|
||||
* // TODO: include in test coverage in 3.9
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class NodeString extends NodeProperty
|
||||
{
|
||||
public function __construct(
|
||||
public string $value,
|
||||
) {
|
||||
}
|
||||
|
||||
public static function factory($value = null): static|null
|
||||
{
|
||||
if ($value === null) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return new static($value);
|
||||
}
|
||||
|
||||
public function render(ModelWithContent $model): string|null
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
}
|
30
kirby/src/Blueprint/NodeText.php
Normal file
30
kirby/src/Blueprint/NodeText.php
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Kirby\Blueprint;
|
||||
|
||||
use Kirby\Cms\ModelWithContent;
|
||||
|
||||
/**
|
||||
* The text node is translatable
|
||||
* and will parse query template strings
|
||||
*
|
||||
* @package Kirby Blueprint
|
||||
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||
* @link https://getkirby.com
|
||||
* @copyright Bastian Allgeier
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*
|
||||
* // TODO: include in test coverage in 3.9
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class NodeText extends NodeI18n
|
||||
{
|
||||
public function render(ModelWithContent $model): ?string
|
||||
{
|
||||
if ($text = parent::render($model)) {
|
||||
return $model->toSafeString($text);
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue