From 426f59e41aff9f5d63e5ddfc532911e97a209c78 Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Wed, 22 Jan 2020 10:26:34 +0200 Subject: [PATCH] Added support for Flex Directory specific configuration --- CHANGELOG.md | 1 + system/blueprints/flex/accounts.yaml | 9 ++ system/blueprints/flex/user-groups.yaml | 9 ++ system/src/Grav/Common/Debugger.php | 14 ++- system/src/Grav/Framework/Flex/Flex.php | 2 +- .../src/Grav/Framework/Flex/FlexDirectory.php | 89 +++++++++++++-- .../Grav/Framework/Flex/FlexDirectoryForm.php | 108 +++++++++--------- system/src/Grav/Framework/Flex/FlexForm.php | 42 ++++++- .../src/Grav/Framework/Flex/FlexFormFlash.php | 7 +- .../Interfaces/FlexDirectoryFormInterface.php | 27 +++++ .../Flex/Interfaces/FlexFormInterface.php | 8 -- .../Interfaces/FlexObjectFormInterface.php | 27 +++++ 12 files changed, 264 insertions(+), 79 deletions(-) create mode 100644 system/src/Grav/Framework/Flex/Interfaces/FlexDirectoryFormInterface.php create mode 100644 system/src/Grav/Framework/Flex/Interfaces/FlexObjectFormInterface.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 981a6e551..67de5995c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/system/blueprints/flex/accounts.yaml b/system/blueprints/flex/accounts.yaml index 86cf958dd..896dd6c66 100644 --- a/system/blueprints/flex/accounts.yaml +++ b/system/blueprints/flex/accounts.yaml @@ -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 diff --git a/system/blueprints/flex/user-groups.yaml b/system/blueprints/flex/user-groups.yaml index eab61dbc3..dd27621ab 100644 --- a/system/blueprints/flex/user-groups.yaml +++ b/system/blueprints/flex/user-groups.yaml @@ -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 diff --git a/system/src/Grav/Common/Debugger.php b/system/src/Grav/Common/Debugger.php index 5434ede32..08614d03a 100644 --- a/system/src/Grav/Common/Debugger.php +++ b/system/src/Grav/Common/Debugger.php @@ -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]); } } diff --git a/system/src/Grav/Framework/Flex/Flex.php b/system/src/Grav/Framework/Flex/Flex.php index da0abf8a3..5ddb513ee 100644 --- a/system/src/Grav/Framework/Flex/Flex.php +++ b/system/src/Grav/Framework/Flex/Flex.php @@ -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); diff --git a/system/src/Grav/Framework/Flex/FlexDirectory.php b/system/src/Grav/Framework/Flex/FlexDirectory.php index bad910b58..5a7d7761a 100644 --- a/system/src/Grav/Framework/Flex/FlexDirectory.php +++ b/system/src/Grav/Framework/Flex/FlexDirectory.php @@ -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 */ diff --git a/system/src/Grav/Framework/Flex/FlexDirectoryForm.php b/system/src/Grav/Framework/Flex/FlexDirectoryForm.php index 64d6f27a3..00c5db582 100644 --- a/system/src/Grav/Framework/Flex/FlexDirectoryForm.php +++ b/system/src/Grav/Framework/Flex/FlexDirectoryForm.php @@ -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); } } } diff --git a/system/src/Grav/Framework/Flex/FlexForm.php b/system/src/Grav/Framework/Flex/FlexForm.php index eaa94e96f..2efc78819 100644 --- a/system/src/Grav/Framework/Flex/FlexForm.php +++ b/system/src/Grav/Framework/Flex/FlexForm.php @@ -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); diff --git a/system/src/Grav/Framework/Flex/FlexFormFlash.php b/system/src/Grav/Framework/Flex/FlexFormFlash.php index f79cf5ca4..9943a285c 100644 --- a/system/src/Grav/Framework/Flex/FlexFormFlash.php +++ b/system/src/Grav/Framework/Flex/FlexFormFlash.php @@ -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'])) { diff --git a/system/src/Grav/Framework/Flex/Interfaces/FlexDirectoryFormInterface.php b/system/src/Grav/Framework/Flex/Interfaces/FlexDirectoryFormInterface.php new file mode 100644 index 000000000..0e666bbf5 --- /dev/null +++ b/system/src/Grav/Framework/Flex/Interfaces/FlexDirectoryFormInterface.php @@ -0,0 +1,27 @@ +