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; | ||
|  |     } | ||
|  | } |