445 lines
13 KiB
PHP
445 lines
13 KiB
PHP
|
<?php
|
||
|
|
||
|
declare(strict_types=1);
|
||
|
|
||
|
namespace Grav\Plugin\FlexObjects;
|
||
|
|
||
|
use Grav\Common\Config\Config;
|
||
|
use Grav\Common\Filesystem\Folder;
|
||
|
use Grav\Common\Grav;
|
||
|
use Grav\Common\Page\Interfaces\PageInterface;
|
||
|
use Grav\Common\Utils;
|
||
|
use Grav\Framework\Flex\FlexDirectory;
|
||
|
use Grav\Framework\Flex\FlexObject;
|
||
|
use Grav\Framework\Flex\Interfaces\FlexCollectionInterface;
|
||
|
use Grav\Framework\Flex\Interfaces\FlexCommonInterface;
|
||
|
use Grav\Framework\Flex\Interfaces\FlexDirectoryInterface;
|
||
|
use Grav\Framework\Flex\Interfaces\FlexInterface;
|
||
|
use Grav\Framework\Flex\Interfaces\FlexObjectInterface;
|
||
|
use Grav\Plugin\FlexObjects\Admin\AdminController;
|
||
|
use Grav\Plugin\FlexObjects\Table\DataTable;
|
||
|
|
||
|
/**
|
||
|
* Class Flex
|
||
|
* @package Grav\Plugin\FlexObjects
|
||
|
*/
|
||
|
class Flex implements FlexInterface
|
||
|
{
|
||
|
/** @var FlexInterface */
|
||
|
protected $flex;
|
||
|
/** @var array */
|
||
|
protected $adminRoutes;
|
||
|
/** @var array */
|
||
|
protected $adminMenu;
|
||
|
/** @var array */
|
||
|
protected $managed;
|
||
|
|
||
|
/**
|
||
|
* @param bool $newToOld
|
||
|
* @return array
|
||
|
* @internal
|
||
|
*/
|
||
|
public static function getLegacyBlueprintMap(bool $newToOld = true): array
|
||
|
{
|
||
|
$map = [
|
||
|
'blueprints://flex-objects/pages.yaml' => 'blueprints://flex-objects/grav-pages.yaml',
|
||
|
'blueprints://flex-objects/user-accounts.yaml' => 'blueprints://flex-objects/grav-accounts.yaml',
|
||
|
'blueprints://flex-objects/user-groups.yaml' => 'blueprints://flex-objects/grav-user-groups.yaml'
|
||
|
];
|
||
|
|
||
|
return $newToOld ? $map : array_flip($map);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Flex constructor.
|
||
|
* @param FlexInterface $flex
|
||
|
* @param array $types
|
||
|
*/
|
||
|
public function __construct(FlexInterface $flex, array $types)
|
||
|
{
|
||
|
$this->flex = $flex;
|
||
|
$this->managed = [];
|
||
|
|
||
|
$legacy = static::getLegacyBlueprintMap(false);
|
||
|
foreach ($types as $blueprint) {
|
||
|
// Backwards compatibility to v1.0.0-rc.3
|
||
|
$blueprint = $legacy[$blueprint] ?? $blueprint;
|
||
|
|
||
|
$type = basename((string)$blueprint, '.yaml');
|
||
|
if ($type) {
|
||
|
$this->managed[] = $type;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param string $type
|
||
|
* @param string $blueprint
|
||
|
* @param array $config
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function addDirectoryType(string $type, string $blueprint, array $config = [])
|
||
|
{
|
||
|
$this->flex->addDirectoryType($type, $blueprint, $config);
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param FlexDirectory $directory
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function addDirectory(FlexDirectory $directory)
|
||
|
{
|
||
|
$this->flex->addDirectory($directory);
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param string $type
|
||
|
* @return bool
|
||
|
*/
|
||
|
public function hasDirectory(string $type): bool
|
||
|
{
|
||
|
return $this->flex->hasDirectory($type);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param string[]|null $types
|
||
|
* @param bool $keepMissing
|
||
|
* @return array<FlexDirectoryInterface|null>
|
||
|
*/
|
||
|
public function getDirectories(array $types = null, bool $keepMissing = false): array
|
||
|
{
|
||
|
return $this->flex->getDirectories($types, $keepMissing);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get directories which are not hidden in the site.
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
public function getDefaultDirectories(): array
|
||
|
{
|
||
|
$list = $this->getDirectories();
|
||
|
foreach ($list as $type => $directory) {
|
||
|
if ($directory->getConfig('site.hidden', false)) {
|
||
|
unset($list[$type]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $list;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param string $type
|
||
|
* @return FlexDirectory|null
|
||
|
*/
|
||
|
public function getDirectory(string $type): ?FlexDirectory
|
||
|
{
|
||
|
return $this->flex->getDirectory($type);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param string $type
|
||
|
* @param array|null $keys
|
||
|
* @param string|null $keyField
|
||
|
* @return FlexCollectionInterface|null
|
||
|
*/
|
||
|
public function getCollection(string $type, array $keys = null, string $keyField = null): ?FlexCollectionInterface
|
||
|
{
|
||
|
return $this->flex->getCollection($type, $keys, $keyField);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param array $keys
|
||
|
* @param array $options In addition to the options in getObjects(), following options can be passed:
|
||
|
* collection_class: Class to be used to create the collection. Defaults to ObjectCollection.
|
||
|
* @return FlexCollectionInterface
|
||
|
* @throws \RuntimeException
|
||
|
*/
|
||
|
public function getMixedCollection(array $keys, array $options = []): FlexCollectionInterface
|
||
|
{
|
||
|
return $this->flex->getMixedCollection($keys, $options);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param array $keys
|
||
|
* @param array $options Following optional options can be passed:
|
||
|
* types: List of allowed types.
|
||
|
* type: Allowed type if types isn't defined, otherwise acts as default_type.
|
||
|
* default_type: Set default type for objects given without type (only used if key_field isn't set).
|
||
|
* keep_missing: Set to true if you want to return missing objects as null.
|
||
|
* key_field: Key field which is used to match the objects.
|
||
|
* @return array
|
||
|
*/
|
||
|
public function getObjects(array $keys, array $options = []): array
|
||
|
{
|
||
|
return $this->flex->getObjects($keys, $options);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param string $key
|
||
|
* @param string|null $type
|
||
|
* @param string|null $keyField
|
||
|
* @return FlexObjectInterface|null
|
||
|
*/
|
||
|
public function getObject(string $key, string $type = null, string $keyField = null): ?FlexObjectInterface
|
||
|
{
|
||
|
return $this->flex->getObject($key, $type, $keyField);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return int
|
||
|
*/
|
||
|
public function count(): int
|
||
|
{
|
||
|
return $this->flex->count();
|
||
|
}
|
||
|
|
||
|
public function isManaged(string $type): bool
|
||
|
{
|
||
|
return \in_array($type, $this->managed, true);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return array
|
||
|
*/
|
||
|
public function getAll(): array
|
||
|
{
|
||
|
$directories = $this->getDirectories($this->managed);
|
||
|
$all = $this->getBlueprints();
|
||
|
|
||
|
/** @var FlexDirectory $directory */
|
||
|
foreach ($all as $type => $directory) {
|
||
|
if (!isset($directories[$type])) {
|
||
|
$directories[$type] = $directory;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ksort($directories);
|
||
|
|
||
|
return $directories;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return array
|
||
|
*/
|
||
|
public function getBlueprints(): array
|
||
|
{
|
||
|
$params = [
|
||
|
'pattern' => '|\.yaml|',
|
||
|
'value' => 'Url',
|
||
|
'recursive' => false,
|
||
|
'folders' => false
|
||
|
];
|
||
|
|
||
|
$directories = [];
|
||
|
$all = Folder::all('blueprints://flex-objects', $params);
|
||
|
foreach ($all as $url) {
|
||
|
$type = basename($url, '.yaml');
|
||
|
$directory = new FlexDirectory($type, $url);
|
||
|
if ($directory->getConfig('hidden') !== true) {
|
||
|
$directories[$type] = $directory;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Order blueprints by title.
|
||
|
usort($directories, static function (FlexDirectory $a, FlexDirectory $b) {
|
||
|
return $a->getTitle() <=> $b->getTitle();
|
||
|
});
|
||
|
|
||
|
return $directories;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param string|FlexDirectory $type
|
||
|
* @param array $options
|
||
|
* @return DataTable
|
||
|
*/
|
||
|
public function getDataTable($type, array $options = []): DataTable
|
||
|
{
|
||
|
$directory = $type instanceof FlexDirectory ? $type : $this->getDirectory($type);
|
||
|
if (!$directory) {
|
||
|
throw new \RuntimeException('Not Found', 404);
|
||
|
}
|
||
|
|
||
|
$collection = $options['collection'] ?? $directory->getCollection();
|
||
|
if (isset($options['filters']) && is_array($options['filters'])) {
|
||
|
$collection = $collection->filterBy($options['filters']);
|
||
|
}
|
||
|
$table = new DataTable($options);
|
||
|
$table->setCollection($collection);
|
||
|
|
||
|
return $table;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param string|object|null $type
|
||
|
* @param array $params
|
||
|
* @param string $extension
|
||
|
* @return string
|
||
|
*/
|
||
|
public function adminRoute($type = null, array $params = [], string $extension = ''): string
|
||
|
{
|
||
|
if (\is_object($type)) {
|
||
|
$object = $type;
|
||
|
if ($object instanceof FlexCommonInterface || $object instanceof FlexDirectory) {
|
||
|
$type = $type->getFlexType();
|
||
|
} else {
|
||
|
return '';
|
||
|
}
|
||
|
} else {
|
||
|
$object = null;
|
||
|
}
|
||
|
|
||
|
$routes = $this->getAdminRoutes();
|
||
|
|
||
|
$grav = Grav::instance();
|
||
|
|
||
|
/** @var Config $config */
|
||
|
$config = $grav['config'];
|
||
|
if (!Utils::isAdminPlugin()) {
|
||
|
$parts = [
|
||
|
trim($grav['base_url'], '/'),
|
||
|
trim($config->get('plugins.admin.route'), '/')
|
||
|
];
|
||
|
}
|
||
|
|
||
|
if ($type && isset($routes[$type])) {
|
||
|
if (!$routes[$type]) {
|
||
|
// Directory has empty route.
|
||
|
return '';
|
||
|
}
|
||
|
|
||
|
// Directory has it's own menu item.
|
||
|
$parts[] = trim($routes[$type], '/');
|
||
|
} else {
|
||
|
if (empty($routes[''])) {
|
||
|
// Default route has been disabled.
|
||
|
return '';
|
||
|
}
|
||
|
|
||
|
// Use default route.
|
||
|
$parts[] = trim($routes[''], '/');
|
||
|
if ($type) {
|
||
|
$parts[] = $type;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Append object key if available.
|
||
|
if ($object instanceof FlexObject) {
|
||
|
if ($object->exists()) {
|
||
|
$parts[] = trim($object->getKey(), '/');
|
||
|
} else {
|
||
|
if ($object->hasKey()) {
|
||
|
$parts[] = trim($object->getKey(), '/');
|
||
|
}
|
||
|
$params = ['' => 'add'] + $params;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$p = [];
|
||
|
$separator = $config->get('system.param_sep');
|
||
|
foreach ($params as $key => $val) {
|
||
|
$p[] = $key . $separator . $val;
|
||
|
}
|
||
|
|
||
|
$parts = array_filter($parts, static function ($val) { return $val !== ''; });
|
||
|
$route = '/' . implode('/', $parts);
|
||
|
$extension = $extension ? '.' . $extension : '';
|
||
|
|
||
|
return $route . $extension . ($p ? '/' . implode('/', $p) : '');
|
||
|
}
|
||
|
|
||
|
public function getAdminController(): ?AdminController
|
||
|
{
|
||
|
$grav = Grav::instance();
|
||
|
if (!isset($grav['admin'])) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
/** @var PageInterface $page */
|
||
|
$page = $grav['page'];
|
||
|
$header = $page->header();
|
||
|
$callable = $header->controller['controller']['instance'] ?? null;
|
||
|
if (null !== $callable && \is_callable($callable)) {
|
||
|
return $callable();
|
||
|
}
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return array
|
||
|
*/
|
||
|
public function getAdminRoutes(): array
|
||
|
{
|
||
|
if (null === $this->adminRoutes) {
|
||
|
$routes = [];
|
||
|
/** @var FlexDirectory $directory */
|
||
|
foreach ($this->getDirectories() as $directory) {
|
||
|
$config = $directory->getConfig('admin');
|
||
|
if (!$directory->isEnabled() || !empty($config['disabled'])) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Resolve route.
|
||
|
$route = $config['router']['path']
|
||
|
?? $config['menu']['list']['route']
|
||
|
?? "/flex-objects/{$directory->getFlexType()}";
|
||
|
|
||
|
$routes[$directory->getFlexType()] = $route;
|
||
|
}
|
||
|
|
||
|
$this->adminRoutes = $routes;
|
||
|
}
|
||
|
|
||
|
return $this->adminRoutes;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return array
|
||
|
*/
|
||
|
public function getAdminMenuItems(): array
|
||
|
{
|
||
|
if (null === $this->adminMenu) {
|
||
|
$routes = [];
|
||
|
$count = 0;
|
||
|
|
||
|
$directories = $this->getDirectories();
|
||
|
/** @var FlexDirectory $directory */
|
||
|
foreach ($directories as $directory) {
|
||
|
$config = $directory->getConfig('admin');
|
||
|
if (!$directory->isEnabled() || !empty($config['disabled'])) {
|
||
|
continue;
|
||
|
}
|
||
|
$type = $directory->getFlexType();
|
||
|
$items = $directory->getConfig('admin.menu') ?? [];
|
||
|
if ($items) {
|
||
|
foreach ($items as $view => $item) {
|
||
|
$item += [
|
||
|
'route' => '/' . $type,
|
||
|
'title' => $directory->getTitle(),
|
||
|
'icon' => 'fa fa-file',
|
||
|
'directory' => $type
|
||
|
];
|
||
|
$routes[$type] = $item;
|
||
|
}
|
||
|
} else {
|
||
|
$count++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ($count && !isset($routes[''])) {
|
||
|
$routes[''] = ['route' => '/flex-objects'];
|
||
|
}
|
||
|
|
||
|
$this->adminMenu = $routes;
|
||
|
}
|
||
|
|
||
|
return $this->adminMenu;
|
||
|
}
|
||
|
}
|