Added support for Flex Directory specific configuration

This commit is contained in:
Matias Griese
2020-01-22 10:26:34 +02:00
parent fff9fa0ca5
commit 426f59e41a
12 changed files with 264 additions and 79 deletions

View File

@@ -9,6 +9,7 @@
* Added `PluginsLoadedEvent` which triggers after plugins have been loaded but not yet initialized
* Added `SessionStartEvent` which triggers when session is started
* Added `PermissionsRegisterEvent` which triggers when `$grav['permissions']` is being accessed the first time
* Added support for Flex Directory specific configuration
1. [](#improved)
* Blueprint validation: Added `validate: value_type: bool|int|float|string|trim` to `array` to filter all the values inside the array
* Twig `url()` takes now third parameter (`true`) to return URL on non-existing file instead of returning false

View File

@@ -13,6 +13,8 @@ config:
admin:
# Admin route
route: '/accounts/users'
aliases:
configure: '/accounts/configure'
redirect_from:
- '/user'
- '/accounts'
@@ -60,6 +62,13 @@ config:
title:
template: '{{ object.fullname ?? object.username }} <{{ object.email }}>'
# Configure view
configure:
hidden: true
form: 'accounts'
title:
template: "{{ 'PLUGIN_ADMIN.ACCOUNTS'|tu }} {{ 'PLUGIN_ADMIN.CONFIGURATION'|tu }}"
# Site Configuration
site:
# Hide from flex types

View File

@@ -13,6 +13,8 @@ config:
admin:
# Admin route
route: '/accounts/groups'
aliases:
configure: '/accounts/configure'
# Admin menu
menu:
@@ -57,6 +59,13 @@ config:
title:
template: '{{ object.readableName ?? object.groupname }}'
# Configure view
configure:
hidden: true
form: 'accounts'
title:
template: "{{ 'PLUGIN_ADMIN.ACCOUNTS'|tu }} {{ 'PLUGIN_ADMIN.CONFIGURATION'|tu }}"
# Site Configuration
site:
# Hide from flex types

View File

@@ -713,11 +713,13 @@ class Debugger
if (!is_scalar($message)) {
$isString = $message;
$message = '';
} elseif (is_bool($isString)) {
}
if (is_bool($isString)) {
$isString = [];
}
if (!is_array($isString)) {
$isString = [gettype($isString) => $isString];
$type = gettype($isString);
$isString = [$type => $isString];
}
$this->clockwork->log($label, $message, $isString);
}
@@ -736,11 +738,17 @@ class Debugger
{
if ($this->enabled) {
if ($this->clockwork) {
$data = null;
if ($event && method_exists($event, '__debugInfo')) {
$data = $event;
}
$listeners = [];
foreach ($dispatcher->getListeners($name) as $listener) {
$listeners[] = $this->resolveCallable($listener);
}
$this->clockwork->addEvent($name, null, microtime(true), ['listeners' => $listeners]);
$this->clockwork->addEvent($name, $data, microtime(true), ['listeners' => $listeners]);
}
}

View File

@@ -60,7 +60,7 @@ class Flex implements FlexInterface
*/
public function addDirectoryType(string $type, string $blueprint, array $config = [])
{
$config = array_merge_recursive(['enabled' => true], $this->config['object'] ?? [], $config);
$config = array_replace_recursive(['enabled' => true], $this->config ?? [], $config);
$this->types[$type] = new FlexDirectory($type, $blueprint, $config);

View File

@@ -22,12 +22,14 @@ use Grav\Framework\Cache\Adapter\MemoryCache;
use Grav\Framework\Cache\CacheInterface;
use Grav\Framework\Flex\Interfaces\FlexAuthorizeInterface;
use Grav\Framework\Flex\Interfaces\FlexCollectionInterface;
use Grav\Framework\Flex\Interfaces\FlexFormInterface;
use Grav\Framework\Flex\Interfaces\FlexIndexInterface;
use Grav\Framework\Flex\Interfaces\FlexObjectInterface;
use Grav\Framework\Flex\Interfaces\FlexStorageInterface;
use Grav\Framework\Flex\Storage\SimpleStorage;
use Grav\Framework\Flex\Traits\FlexAuthorizeTrait;
use Psr\SimpleCache\InvalidArgumentException;
use RocketTheme\Toolbox\File\YamlFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use RuntimeException;
@@ -147,7 +149,7 @@ class FlexDirectory implements FlexAuthorizeInterface
{
if (null === $this->config) {
$config = $this->getBlueprintInternal()->get('config', []);
$config = is_array($config) ? array_replace_recursive($config, $this->defaults) : null;
$config = is_array($config) ? array_replace_recursive($config, $this->defaults, $this->getDirectoryConfig($config['admin']['configure']['form'] ?? null)) : null;
if (!is_array($config)) {
throw new \RuntimeException('Bad configuration');
}
@@ -160,16 +162,87 @@ class FlexDirectory implements FlexAuthorizeInterface
/**
* @param string|null $name
* @return Blueprint|null
* @param array $options
* @return FlexFormInterface
* @internal
*/
public function getConfigureBlueprint(string $name = null)
public function getDirectoryForm(string $name = null, array $options = [])
{
$name = $name ?? 'configure';
$name = $name ?: $this->getConfig('admin.configure.form', '');
return new FlexDirectoryForm($name ?? '', $this, $options);
}
/**
* @return Blueprint
* @internal
*/
public function getDirectoryBlueprint()
{
$name = 'configure';
$path = "blueprints://flex/shared/{$name}.yaml";
$blueprint = new Blueprint($path);
$blueprint->load()->init();
return $blueprint->form() ? $blueprint : null;
return $blueprint;
}
/**
* @param string $name
* @param array $data
* @throws \Exception
* @internal
*/
public function saveDirectoryConfig(string $name, array $data)
{
$grav = Grav::instance();
/** @var UniformResourceLocator $locator */
$locator = $grav['locator'];
$filename = $locator->findResource($this->getDirectoryConfigUri($name), true, true);
$file = YamlFile::instance($filename);
if (!empty($data)) {
$file->save($data);
} else {
$file->delete();
}
}
/**
* @param string $name
* @return array
* @internal
*/
public function loadDirectoryConfig(string $name): array
{
$grav = Grav::instance();
/** @var UniformResourceLocator $locator */
$locator = $grav['locator'];
$filename = $locator->findResource($this->getDirectoryConfigUri($name), true);
$file = YamlFile::instance($filename);
return $file->content();
}
public function getDirectoryConfigUri(string $name = null): string
{
$name = $name ?: $this->getFlexType();
return "config://flex/{$name}.yaml";
}
protected function getDirectoryConfig(string $name = null): array
{
$grav = Grav::instance();
/** @var Config $config */
$config = $grav['config'];
$name = $name ?: $this->getFlexType();
return $config->get("flex.{$name}", []);
}
/**
@@ -354,17 +427,17 @@ class FlexDirectory implements FlexAuthorizeInterface
/** @var Cache $gravCache */
$gravCache = $grav['cache'];
$config = $this->getConfig('cache.' . $namespace);
$config = $this->getConfig('object.cache.' . $namespace);
if (empty($config['enabled'])) {
$cache = new MemoryCache('flex-objects-' . $this->getFlexType());
} else {
$timeout = $config['timeout'] ?? 60;
$lifetime = $config['lifetime'] ?? 60;
$key = $gravCache->getKey();
if (Utils::isAdminPlugin()) {
$key = substr($key, 0, -1);
}
$cache = new DoctrineCache($gravCache->getCacheDriver(), 'flex-objects-' . $this->getFlexType() . $key, $timeout);
$cache = new DoctrineCache($gravCache->getCacheDriver(), 'flex-objects-' . $this->getFlexType() . $key, $lifetime);
}
} catch (\Exception $e) {
/** @var Debugger $debugger */

View File

@@ -58,7 +58,7 @@ class FlexDirectoryForm implements FlexDirectoryFormInterface, \JsonSerializable
*
* @return FlexFormInterface
*/
public static function instance(array $options = [])
public static function instance(array $options = []): FlexFormInterface
{
if (isset($options['directory'])) {
$directory = $options['directory'];
@@ -72,13 +72,13 @@ class FlexDirectoryForm implements FlexDirectoryFormInterface, \JsonSerializable
$name = $options['name'] ?? '';
return $directory->getConfigureForm($name, $options);
return $directory->getDirectoryForm($name, $options);
}
/**
* FlexForm constructor.
* @param string $name
* @param FlexDirectory $object
* @param FlexDirectory $directory
* @param array $options
*/
public function __construct(string $name, FlexDirectory $directory, array $options = null)
@@ -90,10 +90,10 @@ class FlexDirectoryForm implements FlexDirectoryFormInterface, \JsonSerializable
$uniqueId = $options['unique_id'] ?? null;
if (!$uniqueId) {
$uniqueId = md5($directory->getFlexType());
$uniqueId = md5($directory->getFlexType() . '-directory-' . $this->name);
}
$this->setUniqueId($uniqueId);
$this->setFlashLookupFolder($directory->getConfigureBlueprint($name)->get('form/flash_folder') ?? 'tmp://forms/[SESSIONID]');
$this->setFlashLookupFolder($directory->getDirectoryBlueprint()->get('form/flash_folder') ?? 'tmp://forms/[SESSIONID]');
$this->form = $options['form'] ?? null;
$this->initialize();
@@ -106,7 +106,7 @@ class FlexDirectoryForm implements FlexDirectoryFormInterface, \JsonSerializable
{
$this->messages = [];
$this->submitted = false;
$this->data = null;
$this->data = new Data($this->directory->loadDirectoryConfig($this->name), $this->getBlueprint());
$this->files = [];
$this->unsetFlash();
@@ -156,7 +156,7 @@ class FlexDirectoryForm implements FlexDirectoryFormInterface, \JsonSerializable
* @param string $name
* @param mixed $value
* @param string|null $separator
* @return FlexForm
* @return $this
*/
public function set($name, $value, $separator = null)
{
@@ -183,16 +183,15 @@ class FlexDirectoryForm implements FlexDirectoryFormInterface, \JsonSerializable
// Make sure that both type and name do not have dash (convert dashes to underscores).
$type = str_replace('-', '_', $type);
$name = str_replace('-', '_', $name);
// FIXME:
$this->flexName = $name ? "flex-{$type}-{$name}" : "flex-{$type}";
$this->flexName = $name ? "flex_conf-{$type}-{$name}" : "flex_conf-{$type}";
}
/**
* @return Data|FlexObjectInterface|object
* @return Data|object
*/
public function getData()
{
return $this->data ?? $this->getObject();
return $this->data;
}
/**
@@ -209,12 +208,16 @@ class FlexDirectoryForm implements FlexDirectoryFormInterface, \JsonSerializable
$value = $this->data ? $this->data[$name] : null;
// Return the form data or fall back to the object property.
return $value ?? $this->getObject()->getFormValue($name);
return $value ?? null;
}
/**
* @param string $name
* @return array|mixed|null
*/
public function getDefaultValue(string $name)
{
return $this->object->getDefaultValue($name);
return $this->getBlueprint()->getDefaultValue($name);
}
/**
@@ -222,7 +225,7 @@ class FlexDirectoryForm implements FlexDirectoryFormInterface, \JsonSerializable
*/
public function getDefaultValues(): array
{
return $this->object->getDefaultValues();
return $this->getBlueprint()->getDefaults();
}
/**
* @return string
@@ -246,7 +249,7 @@ class FlexDirectoryForm implements FlexDirectoryFormInterface, \JsonSerializable
'unique_id' => $this->getUniqueId(),
'form_name' => $this->getName(),
'folder' => $this->getFlashFolder(),
'object' => $this->getObject()
'directory' => $this->getDirectory()
];
$this->flash = new FlexFormFlash($config);
@@ -266,14 +269,6 @@ class FlexDirectoryForm implements FlexDirectoryFormInterface, \JsonSerializable
return $this->directory;
}
public function updateObject(): FlexObjectInterface
{
$data = $this->data instanceof Data ? $this->data->toArray() : [];
$files = $this->files;
return $this->getObject()->update($data, $files);
}
/**
* @return Blueprint
*/
@@ -281,7 +276,10 @@ class FlexDirectoryForm implements FlexDirectoryFormInterface, \JsonSerializable
{
if (null === $this->blueprint) {
try {
$blueprint = $this->getObject()->getBlueprint($this->name);
$blueprint = $this->getDirectory()->getDirectoryBlueprint();
if (!$blueprint) {
throw new \RuntimeException('Blueprint not found');
}
if ($this->form) {
// We have field overrides available.
$blueprint->extend(['form' => $this->form], true);
@@ -295,7 +293,7 @@ class FlexDirectoryForm implements FlexDirectoryFormInterface, \JsonSerializable
// Blueprint is not defined, but we have custom form fields available.
$blueprint = new Blueprint(null, ['form' => $this->form]);
$blueprint->load();
$blueprint->setScope('object');
$blueprint->setScope('directory');
$blueprint->init();
}
@@ -310,12 +308,7 @@ class FlexDirectoryForm implements FlexDirectoryFormInterface, \JsonSerializable
*/
public function getFileUploadAjaxRoute(): ?Route
{
$object = $this->getObject();
if (!method_exists($object, 'route')) {
return null;
}
return $object->route('/edit.json/task:media.upload');
return null;
}
/**
@@ -325,24 +318,16 @@ class FlexDirectoryForm implements FlexDirectoryFormInterface, \JsonSerializable
*/
public function getFileDeleteAjaxRoute($field, $filename): ?Route
{
$object = $this->getObject();
if (!method_exists($object, 'route')) {
return null;
}
return $object->route('/edit.json/task:media.delete');
return null;
}
public function getMediaTaskRoute(array $params = [], $extension = null): string
/**
* @param array $params
* @param string|null $extension
* @return string
*/
public function getMediaTaskRoute(array $params = [], string $extension = null): string
{
$grav = Grav::instance();
/** @var Flex $flex */
$flex = $grav['flex'];
if (method_exists($flex, 'adminRoute')) {
return $flex->adminRoute($this->getObject(), $params, $extension ?? 'json');
}
return '';
}
@@ -358,6 +343,10 @@ class FlexDirectoryForm implements FlexDirectoryFormInterface, \JsonSerializable
$this->doUnserialize($data);
}
/**
* @param string $name
* @return mixed|null
*/
public function __get($name)
{
$method = "get{$name}";
@@ -370,6 +359,10 @@ class FlexDirectoryForm implements FlexDirectoryFormInterface, \JsonSerializable
return $form[$name] ?? null;
}
/**
* @param string $name
* @param mixed $value
*/
public function __set($name, $value)
{
$method = "set{$name}";
@@ -378,6 +371,10 @@ class FlexDirectoryForm implements FlexDirectoryFormInterface, \JsonSerializable
}
}
/**
* @param string $name
* @return bool
*/
public function __isset($name)
{
$method = "get{$name}";
@@ -390,6 +387,9 @@ class FlexDirectoryForm implements FlexDirectoryFormInterface, \JsonSerializable
return isset($form[$name]);
}
/**
* @param string $name
*/
public function __unset($name)
{
}
@@ -437,17 +437,14 @@ class FlexDirectoryForm implements FlexDirectoryFormInterface, \JsonSerializable
*/
protected function doSubmit(array $data, array $files)
{
// FIXME:
/** @var FlexObject $object */
$object = clone $this->getObject();
$this->directory->saveDirectoryConfig($this->name, $data);
$object->update($data, $files);
$object->save();
$this->setObject($object);
$this->reset();
}
/**
* @return array
*/
protected function doSerialize(): array
{
return $this->doTraitSerialize() + [
@@ -455,6 +452,9 @@ class FlexDirectoryForm implements FlexDirectoryFormInterface, \JsonSerializable
];
}
/**
* @param array $data
*/
protected function doUnserialize(array $data): void
{
$this->doTraitUnserialize($data);
@@ -470,7 +470,7 @@ class FlexDirectoryForm implements FlexDirectoryFormInterface, \JsonSerializable
protected function filterData($data = null): void
{
if ($data instanceof Data) {
$data->filter(true, true);
$data->filter(true, false);
}
}
}

View File

@@ -14,6 +14,7 @@ use Grav\Common\Data\Data;
use Grav\Common\Grav;
use Grav\Common\Twig\Twig;
use Grav\Framework\Flex\Interfaces\FlexFormInterface;
use Grav\Framework\Flex\Interfaces\FlexObjectFormInterface;
use Grav\Framework\Flex\Interfaces\FlexObjectInterface;
use Grav\Framework\Form\Interfaces\FormFlashInterface;
use Grav\Framework\Form\Traits\FormTrait;
@@ -28,7 +29,7 @@ use Twig\TemplateWrapper;
* Class FlexForm
* @package Grav\Framework\Flex
*/
class FlexForm implements FlexFormInterface, \JsonSerializable
class FlexForm implements FlexObjectFormInterface, \JsonSerializable
{
use NestedArrayAccessWithGetters {
NestedArrayAccessWithGetters::get as private traitGet;
@@ -196,6 +197,10 @@ class FlexForm implements FlexFormInterface, \JsonSerializable
return $this->flexName;
}
/**
* @param string $type
* @param string $name
*/
protected function setName(string $type, string $name): void
{
// Make sure that both type and name do not have dash (convert dashes to underscores).
@@ -229,6 +234,10 @@ class FlexForm implements FlexFormInterface, \JsonSerializable
return $value ?? $this->getObject()->getFormValue($name);
}
/**
* @param string $name
* @return array|mixed|null
*/
public function getDefaultValue(string $name)
{
return $this->object->getDefaultValue($name);
@@ -283,6 +292,9 @@ class FlexForm implements FlexFormInterface, \JsonSerializable
return $this->object;
}
/**
* @return FlexObjectInterface
*/
public function updateObject(): FlexObjectInterface
{
$data = $this->data instanceof Data ? $this->data->toArray() : [];
@@ -350,7 +362,12 @@ class FlexForm implements FlexFormInterface, \JsonSerializable
return $object->route('/edit.json/task:media.delete');
}
public function getMediaTaskRoute(array $params = [], $extension = null): string
/**
* @param array $params
* @param string|null $extension
* @return string
*/
public function getMediaTaskRoute(array $params = [], string $extension = null): string
{
$grav = Grav::instance();
/** @var Flex $flex */
@@ -375,6 +392,10 @@ class FlexForm implements FlexFormInterface, \JsonSerializable
$this->doUnserialize($data);
}
/**
* @param string $name
* @return mixed|null
*/
public function __get($name)
{
$method = "get{$name}";
@@ -387,6 +408,10 @@ class FlexForm implements FlexFormInterface, \JsonSerializable
return $form[$name] ?? null;
}
/**
* @param string $name
* @param mixed $value
*/
public function __set($name, $value)
{
$method = "set{$name}";
@@ -395,6 +420,10 @@ class FlexForm implements FlexFormInterface, \JsonSerializable
}
}
/**
* @param string $name
* @return bool
*/
public function __isset($name)
{
$method = "get{$name}";
@@ -407,6 +436,9 @@ class FlexForm implements FlexFormInterface, \JsonSerializable
return isset($form[$name]);
}
/**
* @param string $name
*/
public function __unset($name)
{
}
@@ -464,6 +496,9 @@ class FlexForm implements FlexFormInterface, \JsonSerializable
$this->reset();
}
/**
* @return array
*/
protected function doSerialize(): array
{
return $this->doTraitSerialize() + [
@@ -471,6 +506,9 @@ class FlexForm implements FlexFormInterface, \JsonSerializable
];
}
/**
* @param array $data
*/
protected function doUnserialize(array $data): void
{
$this->doTraitUnserialize($data);

View File

@@ -34,6 +34,7 @@ class FlexFormFlash extends FormFlash
public function setObject(FlexObjectInterface $object): void
{
$this->object = $object;
$this->directory = $object->getFlexDirectory();
}
/**
@@ -98,14 +99,14 @@ class FlexFormFlash extends FormFlash
/** @var FlexObjectInterface|null $object */
$object = $config['object'] ?? null;
$create = true;
if ($object) {
$directory = $object->getFlexDirectory();
$create = !$object->exists();
} else {
} elseif (null === ($directory = $config['directory'] ?? null)) {
$flex = $config['flex'] ?? static::$flex;
$type = $data['object']['type'] ?? $data['directory']['type'] ?? null;
$directory = $flex ? $flex->getDirectory($type) : null;
$create = true;
$directory = $flex && $type ? $flex->getDirectory($type) : null;
}
if ($directory && $create && isset($data['object']['serialized'])) {

View File

@@ -0,0 +1,27 @@
<?php
/**
* @package Grav\Framework\Flex
*
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Framework\Flex\Interfaces;
/**
* Defines Forms for Flex Objects.
*
* @used-by \Grav\Framework\Flex\FlexForm
* @since 1.7
*/
interface FlexDirectoryFormInterface extends FlexFormInterface
{
/**
* Get object associated to the form.
*
* @return FlexObjectInterface Returns Flex Object associated to the form.
* @api
*/
public function getDirectory();
}

View File

@@ -20,14 +20,6 @@ use Grav\Framework\Route\Route;
*/
interface FlexFormInterface extends \Serializable, FormInterface
{
/**
* Get object associated to the form.
*
* @return FlexObjectInterface Returns Flex Object associated to the form.
* @api
*/
public function getObject();
/**
* Get media task route.
*

View File

@@ -0,0 +1,27 @@
<?php
/**
* @package Grav\Framework\Flex
*
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Framework\Flex\Interfaces;
/**
* Defines Forms for Flex Objects.
*
* @used-by \Grav\Framework\Flex\FlexForm
* @since 1.7
*/
interface FlexObjectFormInterface extends FlexFormInterface
{
/**
* Get object associated to the form.
*
* @return FlexObjectInterface Returns Flex Object associated to the form.
* @api
*/
public function getObject();
}