1000, ]; /** @var AdminController */ protected $controller; /** * @return bool */ public static function checkRequirements(): bool { return version_compare(GRAV_VERSION, static::MIN_GRAV_VERSION, '>='); } /** * @return array * * The getSubscribedEvents() gives the core a list of events * that the plugin wants to listen to. The key of each * array section is the event that the plugin listens to * and the value (in the form of an array) contains the * callable (or function) as well as the priority. The * higher the number the higher the priority. */ public static function getSubscribedEvents(): array { if (!static::checkRequirements()) { return []; } return [ PluginsLoadedEvent::class => [ ['initializeFlex', 10] ], PermissionsRegisterEvent::class => [ ['onRegisterPermissions', 100] ], FlexRegisterEvent::class => [ ['onRegisterFlex', 100] ], 'onCliInitialize' => [ ['autoload', 100000], ['initializeFlex', 10] ], 'onPluginsInitialized' => [ ['onPluginsInitialized', 0], ], 'onFormRegisterTypes' => [ ['onFormRegisterTypes', 0] ] ]; } /** * [PluginsLoadedEvent:100000] Composer autoload. * * @return ClassLoader */ public function autoload(): ClassLoader { return require __DIR__ . '/vendor/autoload.php'; } /** * [PluginsLoadedEvent:10]: Initialize Flex * * @return void */ public function initializeFlex(): void { $config = $this->config->get('plugins.flex-objects.directories') ?? []; // Add to DI container $this->grav['flex_objects'] = static function (Grav $grav) use ($config) { /** @var FlexInterface $flex */ $flex = $grav['flex']; $flexObjects = new Flex($flex, $config); // This event is for backwards compatibility only, do not use it! $grav->fireEvent('onFlexInit', new Event(['flex' => $flexObjects])); return $flexObjects; }; } /** * Initialize the plugin * * @return void */ public function onPluginsInitialized(): void { if ($this->isAdmin()) { /** @var UserInterface|null $user */ $user = $this->grav['user'] ?? null; if (null === $user || !$user->authorize('login', 'admin')) { return; } $this->enable([ 'onAdminTwigTemplatePaths' => [ ['onAdminTwigTemplatePaths', 10] ], 'onAdminMenu' => [ ['onAdminMenu', 0] ], 'onAdminPage' => [ ['onAdminPage', 0] ], 'onAdminCompilePresetSCSS' => [ ['onAdminCompilePresetSCSS', 0] ], 'onDataTypeExcludeFromDataManagerPluginHook' => [ ['onDataTypeExcludeFromDataManagerPluginHook', 0] ], 'onAdminControllerInit' => [ ['onAdminControllerInit', 0] ], 'onThemeInitialized' => [ ['onThemeInitialized', 0] ], 'onPageInitialized' => [ ['onAdminPageInitialized', 0] ], 'onTwigSiteVariables' => [ ['onTwigAdminVariables', 0] ], 'onGetPageTemplates' => ['onGetPageTemplates', 0] ]); } else { $this->enable([ 'onTwigTemplatePaths' => [ ['onTwigTemplatePaths', 0] ], ]); } } /** * @param FlexRegisterEvent $event * @return void */ public function onRegisterFlex(FlexRegisterEvent $event): void { /** @var \Grav\Framework\Flex\Flex $flex */ $flex = $event->flex; $types = (array)$this->config->get('plugins.flex-objects.directories', []); $this->registerDirectories($flex, $types); } /** * @return void */ public function onThemeInitialized(): void { // Register directories defined in the theme. /** @var \Grav\Framework\Flex\Flex $flex */ $flex = $this->grav['flex']; $types = (array)$this->config->get('plugins.flex-objects.directories', []); $this->registerDirectories($flex, $types, true); /** @var AdminController controller */ $this->controller = new AdminController(); /** @var Debugger $debugger */ $debugger = Grav::instance()['debugger']; $names = implode(', ', array_keys($flex->getDirectories())); $debugger->addMessage(sprintf('Registered flex types: %s', $names), 'debug'); } /** * @param \Grav\Framework\Flex\Flex $flex * @param array $types * @param bool $report */ protected function registerDirectories(\Grav\Framework\Flex\Flex $flex, array $types, bool $report = false): void { $map = Flex::getLegacyBlueprintMap(false); foreach ($types as $blueprint) { // Backwards compatibility to v1.0.0-rc.3 $blueprint = $map[$blueprint] ?? $blueprint; $type = basename((string)$blueprint, '.yaml'); if (!$type) { continue; } if (!file_exists($blueprint)) { if ($report) { /** @var Debugger $debugger */ $debugger = Grav::instance()['debugger']; $debugger->addMessage(sprintf('Flex: blueprint for flex type %s is missing', $type), 'error'); } continue; } $directory = $flex->getDirectory($type); if (!$directory || !$directory->isEnabled()) { $flex->addDirectoryType($type, $blueprint); } } } /** * Initial stab at registering permissions (WIP) * * @param PermissionsRegisterEvent $event * @return void */ public function onRegisterPermissions(PermissionsRegisterEvent $event): void { /** @var Flex $flex */ $flex = $this->grav['flex_objects']; $directories = $flex->getDirectories(); $permissions = $event->permissions; $actions = []; foreach ($directories as $directory) { $data = $directory->getConfig('admin.permissions', []); $actions[] = PermissionsReader::fromArray($data, $permissions->getTypes()); } $actions[] = PermissionsReader::fromYaml("plugin://{$this->name}/permissions.yaml"); $permissions->addActions(array_replace(...$actions)); } /** * @param Event $event * @return void */ public function onFormRegisterTypes(Event $event): void { /** @var Forms $forms */ $forms = $event['forms']; $forms->registerType('flex', new FlexFormFactory()); } /** * @param Event $event * @return void */ public function onAdminPage(Event $event): void { if ($this->controller->isActive()) { $event->stopPropagation(); /** @var PageInterface $page */ $page = $event['page']; $page->init(new \SplFileInfo(__DIR__ . '/admin/pages/flex-objects.md')); $page->slug($this->controller->getLocation()); $header = $page->header(); $header->access = ['admin.login']; $header->controller = $this->controller->getInfo(); } } /** * [onPageInitialized:0]: Run controller * * @return void */ public function onAdminPageInitialized(): void { if ($this->controller->isActive()) { $this->controller->execute(); $this->controller->redirect(); } } /** * @param Event $event * @return void */ public function onAdminControllerInit(Event $event): void { $eventController = $event['controller']; // Blacklist all admin routes, including aliases and redirects. $eventController->blacklist_views[] = 'flex-objects'; foreach ($this->controller->getAdminRoutes() as $route => $info) { $eventController->blacklist_views[] = trim($route, '/'); } } /** * Add Flex-Object's preset.scss to the Admin Preset SCSS compile process * * @param Event $event * @return void */ public function onAdminCompilePresetSCSS(Event $event): void { $event['scss']->add($this->grav['locator']->findResource('plugins://flex-objects/scss/_preset.scss')); } /** * @param Event $event * @return void */ public function onGetPageTemplates(Event $event): void { /** @var Types $types */ $types = $event->types; $types->register('flex-objects', 'plugins://flex-objects/blueprints/pages/flex-objects.yaml'); } /** * Form select options listing all enabled directories. * * @return array */ public static function directoryOptions(): array { /** @var Flex $flex */ $flex = Grav::instance()['flex_objects']; $directories = $flex->getDirectories(); $list = []; /** * @var string $type * @var FlexDirectory $directory */ foreach ($directories as $type => $directory) { if (!$directory->getConfig('site.hidden')) { $list[$type] = $directory->getTitle(); } } return $list; } /** * @return array */ public function getAdminMenu(): array { /** @var Flex $flex */ $flex = $this->grav['flex_objects']; $list = []; foreach ($flex->getAdminMenuItems() as $name => $item) { $route = trim($item['route'] ?? $name, '/'); $list[$route] = $item; } return $list; } /** * Add Flex Directory to admin menu * * @return void */ public function onAdminMenu(): void { /** @var Flex $flex */ $flex = $this->grav['flex_objects']; /** @var Admin $admin */ $admin = $this->grav['admin']; foreach ($this->getAdminMenu() as $route => $item) { $directory = null; if (isset($item['directory'])) { $directory = $flex->getDirectory($item['directory']); if (!$directory || !$directory->isEnabled()) { continue; } } $title = $item['title'] ?? 'PLUGIN_FLEX_OBJECTS.TITLE'; $index = $item['index'] ?? 0; if (($this->grav['twig']->plugins_hooked_nav[$title]['index'] ?? 1000) <= $index) { continue; } $location = $item['location'] ?? $route; $hidden = $item['hidden'] ?? false; $icon = $item['icon'] ?? 'fa-list'; $authorize = $item['authorize'] ?? ($directory ? null : ['admin.flex-objects', 'admin.super']); if ($hidden || (null === $authorize && $directory->isAuthorized('list', 'admin', $admin->user))) { continue; } $cache = $directory ? $directory->getCache('index') : null; $count = $cache ? $cache->get('admin-count-' . md5($admin->user->username)) : false; if (null === $count) { try { $collection = $directory->getCollection(); if (is_callable([$collection, 'isAuthorized'])) { $count = $collection->isAuthorized('list', 'admin', $admin->user)->count(); } else { $count = $collection->count(); } $cache->set('admin-count-' . md5($admin->user->username), $count); } catch (\InvalidArgumentException $e) { continue; } } $badge = $directory ? ['badge' => ['count' => $count]] : []; $priority = $item['priority'] ?? 0; $this->grav['twig']->plugins_hooked_nav[$title] = [ 'location' => $location, 'route' => $route, 'index' => $index, 'icon' => $icon, 'authorize' => $authorize, 'priority' => $priority ] + $badge; } } /** * Exclude Flex Directory data from the Data Manager plugin * * @return void */ public function onDataTypeExcludeFromDataManagerPluginHook(): void { $this->grav['admin']->dataTypesExcludedFromDataManagerPlugin[] = 'flex-objects'; } /** * Add current directory to twig lookup paths. * * @return void */ public function onTwigTemplatePaths(): void { $extra_site_twig_path = $this->config->get('plugins.flex-objects.extra_site_twig_path'); $extra_path = $extra_site_twig_path ? $this->grav['locator']->findResource($extra_site_twig_path) : null; if ($extra_path) { $this->grav['twig']->twig_paths[] = $extra_path; } $this->grav['twig']->twig_paths[] = __DIR__ . '/templates'; } /** * Add plugin templates path * * @param Event $event * @return void */ public function onAdminTwigTemplatePaths(Event $event): void { $extra_admin_twig_path = $this->config->get('plugins.flex-objects.extra_admin_twig_path'); $extra_path = $extra_admin_twig_path ? $this->grav['locator']->findResource($extra_admin_twig_path) : null; $paths = $event['paths']; if ($extra_path) { $paths[] = $extra_path; } $paths[] = __DIR__ . '/admin/templates'; $event['paths'] = $paths; } /** * Set needed variables to display directory. * * @return void */ public function onTwigAdminVariables(): void { if ($this->controller->isActive()) { // Twig shortcuts $this->grav['twig']->twig_vars['controller'] = $this->controller; $this->grav['twig']->twig_vars['action'] = $this->controller->getAction(); $this->grav['twig']->twig_vars['task'] = $this->controller->getTask(); $this->grav['twig']->twig_vars['target'] = $this->controller->getTarget(); $this->grav['twig']->twig_vars['key'] = $this->controller->getId(); $this->grav['twig']->twig_vars['flex'] = $this->grav['flex_objects']; $this->grav['twig']->twig_vars['directory'] = $this->controller->getDirectory(); $this->grav['twig']->twig_vars['collection'] = $this->controller->getCollection(); $this->grav['twig']->twig_vars['object'] = $this->controller->getObject(); // CSS / JS Assets $this->grav['assets']->addCss('plugin://flex-objects/css/admin.css'); $this->grav['assets']->addCss('plugin://admin/themes/grav/css/codemirror/codemirror.css'); } } }