From d288e5f53ca9fd9517bee9d8f791ecfdee445c5b Mon Sep 17 00:00:00 2001 From: Wilhelm Behncke Date: Mon, 22 Jan 2024 13:06:12 +0100 Subject: [PATCH] !!!FEATURE: Introduce `ApplicationView` to replace Fusion for bootstrapping the UI The `ApplicationView` builds the foundational HTML document (via string concatenation) for bootstrapping the Neos UI. Formerly, Fusion was used to retrieve and render the initial data for the UI. This task has been delegated to a set of dedicated classes, namely: - `ConfigurationProvider` - retrieves the `nodeTree` and `structureTree` segments from `Neos.Neos.userInterface.navigateComponent` settings, `allowedTargetWorkspaces` from the ContentRepository's `WorkspaceService` as well as the `nodeTypeSchema` and `translations` endpoints. - `RoutesProviderInterface` - retrieves all other routes/endpoints required for communication with the Neos server application - `FrontendConfigurationProvider` - reads and preprocesses the `Neos.Neos.Ui.frontendConfiguration` settings that are mostly used by third-party plugins to share data between server and client - `NodeTypesProvider` - retrieves information about node type roles and node type groups. Roles are a dedicated UI-concept that is meant to distinguish between document, content and collection nodes. Groups refer the grouping of node types in the creation dialog. - `MenuProvider` - retrieves all data needed to render the main burger menu located in the top left corner of the UI. - `InitialStateProvider` - reads and preprocesses the `Neos.Neos.Ui.frontendConfiguration` settings that are used to hydrate the UI's redux store The responsibilities of these classes is entirely derived from their former Fusion counterparts. All of them have been marked `@internal` to allow for later removal. Nonetheless, it remains possible to implement their accompanying interfaces and replace their implementations via `Objects.yaml`. This enables rare edge cases and unplanned extensibility. The splash screen, formerly a Fluid template, is now hard-coded into the `ApplicationView` class, which renders the `Neos.Neos.Ui.splashScreen` setting obsolete. It has therefore been removed. --- Classes/Controller/BackendController.php | 99 ++++++---- ...eConfigurationVersionProviderInterface.php | 23 +++ .../Domain/ConfigurationProviderInterface.php | 29 +++ ...FrontendConfigurationProviderInterface.php | 27 +++ .../Domain/InitialStateProviderInterface.php | 34 ++++ Classes/Domain/MenuProviderInterface.php | 25 +++ Classes/Domain/NodeTypesProviderInterface.php | 23 +++ Classes/Domain/RoutesProviderInterface.php | 25 +++ .../CacheConfigurationVersionProvider.php | 68 +++++++ .../Configuration/ConfigurationProvider.php | 93 +++++++++ .../FrontendConfigurationProvider.php | 44 +++++ .../Configuration/InitialStateProvider.php | 59 ++++++ .../ContentRepository/NodeTypesProvider.php | 38 ++++ Classes/Infrastructure/MVC/RoutesProvider.php | 177 ++++++++++++++++++ .../MVC/RoutesProviderHelper.php | 65 +++++++ Classes/Infrastructure/Neos/MenuProvider.php | 88 +++++++++ Classes/Presentation/ApplicationView.php | 171 +++++++++++++++++ Configuration/Objects.yaml | 24 +++ Configuration/Settings.yaml | 3 - Configuration/Views.yaml | 5 +- 20 files changed, 1074 insertions(+), 46 deletions(-) create mode 100644 Classes/Domain/CacheConfigurationVersionProviderInterface.php create mode 100644 Classes/Domain/ConfigurationProviderInterface.php create mode 100644 Classes/Domain/FrontendConfigurationProviderInterface.php create mode 100644 Classes/Domain/InitialStateProviderInterface.php create mode 100644 Classes/Domain/MenuProviderInterface.php create mode 100644 Classes/Domain/NodeTypesProviderInterface.php create mode 100644 Classes/Domain/RoutesProviderInterface.php create mode 100644 Classes/Infrastructure/Cache/CacheConfigurationVersionProvider.php create mode 100644 Classes/Infrastructure/Configuration/ConfigurationProvider.php create mode 100644 Classes/Infrastructure/Configuration/FrontendConfigurationProvider.php create mode 100644 Classes/Infrastructure/Configuration/InitialStateProvider.php create mode 100644 Classes/Infrastructure/ContentRepository/NodeTypesProvider.php create mode 100644 Classes/Infrastructure/MVC/RoutesProvider.php create mode 100644 Classes/Infrastructure/MVC/RoutesProviderHelper.php create mode 100644 Classes/Infrastructure/Neos/MenuProvider.php create mode 100644 Classes/Presentation/ApplicationView.php create mode 100644 Configuration/Objects.yaml diff --git a/Classes/Controller/BackendController.php b/Classes/Controller/BackendController.php index bcbc11e625..33becc51a7 100644 --- a/Classes/Controller/BackendController.php +++ b/Classes/Controller/BackendController.php @@ -12,18 +12,13 @@ * source code. */ -use Neos\ContentRepository\Core\NodeType\NodeTypeName; use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints; use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry; use Neos\Flow\Annotations as Flow; use Neos\Flow\Mvc\Controller\ActionController; -use Neos\Flow\Mvc\View\ViewInterface; use Neos\Flow\Persistence\PersistenceManagerInterface; -use Neos\Flow\ResourceManagement\ResourceManager; use Neos\Flow\Security\Context; use Neos\Flow\Session\SessionInterface; -use Neos\Fusion\View\FusionView; -use Neos\Neos\Controller\Backend\MenuHelper; use Neos\Neos\Domain\Repository\DomainRepository; use Neos\Neos\Domain\Repository\SiteRepository; use Neos\Neos\Domain\Service\NodeTypeNameFactory; @@ -32,13 +27,18 @@ use Neos\Neos\FrontendRouting\SiteDetection\SiteDetectionResult; use Neos\Neos\Service\BackendRedirectionService; use Neos\Neos\Service\UserService; -use Neos\Neos\Ui\Domain\Service\StyleAndJavascriptInclusionService; -use Neos\Neos\Ui\Service\NodeClipboard; +use Neos\Neos\Ui\Domain\ConfigurationProviderInterface; +use Neos\Neos\Ui\Domain\FrontendConfigurationProviderInterface; +use Neos\Neos\Ui\Domain\InitialStateProviderInterface; +use Neos\Neos\Ui\Domain\MenuProviderInterface; +use Neos\Neos\Ui\Domain\NodeTypesProviderInterface; +use Neos\Neos\Ui\Domain\RoutesProviderInterface; +use Neos\Neos\Ui\Presentation\ApplicationView; class BackendController extends ActionController { /** - * @var FusionView + * @var ApplicationView */ protected $view; @@ -72,53 +72,59 @@ class BackendController extends ActionController */ protected $session; + /** + * @Flow\Inject(lazy=false) + * @var BackendRedirectionService + */ + protected $backendRedirectionService; + /** * @Flow\Inject - * @var ResourceManager + * @var ContentRepositoryRegistry */ - protected $resourceManager; + protected $contentRepositoryRegistry; /** * @Flow\Inject - * @var MenuHelper + * @var Context */ - protected $menuHelper; + protected $securityContext; /** - * @Flow\Inject(lazy=false) - * @var BackendRedirectionService + * @Flow\Inject + * @var ConfigurationProviderInterface */ - protected $backendRedirectionService; + protected $configurationProvider; /** * @Flow\Inject - * @var ContentRepositoryRegistry + * @var RoutesProviderInterface */ - protected $contentRepositoryRegistry; + protected $routesProvider; /** * @Flow\Inject - * @var Context + * @var FrontendConfigurationProviderInterface */ - protected $securityContext; + protected $frontendConfigurationProvider; /** * @Flow\Inject - * @var StyleAndJavascriptInclusionService + * @var NodeTypesProviderInterface */ - protected $styleAndJavascriptInclusionService; + protected $nodeTypesProvider; /** * @Flow\Inject - * @var NodeClipboard + * @var MenuProviderInterface */ - protected $clipboard; + protected $menuProvider; /** - * @Flow\InjectConfiguration(package="Neos.Neos.Ui", path="splashScreen.partial") - * @var string + * @Flow\Inject + * @var InitialStateProviderInterface */ - protected $splashScreenPartial; + protected $initialStateProvider; /** * Displays the backend interface @@ -181,22 +187,35 @@ public function indexAction(string $node = null) $node = $subgraph->findNodeById($nodeAddress->nodeAggregateId); } - $this->view->assign('user', $user); - $this->view->assign('documentNode', $node); - $this->view->assign('site', $siteNode); - $this->view->assign('clipboardNodes', $this->clipboard->getSerializedNodeAddresses()); - $this->view->assign('clipboardMode', $this->clipboard->getMode()); - $this->view->assign('headScripts', $this->styleAndJavascriptInclusionService->getHeadScripts()); - $this->view->assign('headStylesheets', $this->styleAndJavascriptInclusionService->getHeadStylesheets()); - $this->view->assign('sitesForMenu', $this->menuHelper->buildSiteList($this->getControllerContext())); - $this->view->assign('modulesForMenu', $this->menuHelper->buildModuleList($this->getControllerContext())); - $this->view->assign('contentRepositoryId', $siteDetectionResult->contentRepositoryId); - $this->view->assignMultiple([ - 'subgraph' => $subgraph + 'configuration' => + $this->configurationProvider->getConfiguration( + contentRepository: $contentRepository, + uriBuilder: $this->controllerContext->getUriBuilder(), + ), + 'routes' => + $this->routesProvider->getRoutes( + uriBuilder: $this->controllerContext->getUriBuilder() + ), + 'frontendConfiguration' => + $this->frontendConfigurationProvider->getFrontendConfiguration( + controllerContext: $this->controllerContext, + ), + 'nodeTypes' => + $this->nodeTypesProvider->getNodeTypes(), + 'menu' => + $this->menuProvider->getMenu( + controllerContext: $this->controllerContext, + ), + 'initialState' => + $this->initialStateProvider->getInitialState( + controllerContext: $this->controllerContext, + contentRepositoryId: $siteDetectionResult->contentRepositoryId, + documentNode: $node, + site: $siteNode, + user: $user, + ), ]); - - $this->view->assign('interfaceLanguage', $this->userService->getInterfaceLanguage()); } /** diff --git a/Classes/Domain/CacheConfigurationVersionProviderInterface.php b/Classes/Domain/CacheConfigurationVersionProviderInterface.php new file mode 100644 index 0000000000..253a672a56 --- /dev/null +++ b/Classes/Domain/CacheConfigurationVersionProviderInterface.php @@ -0,0 +1,23 @@ +computedCacheConfigurationVersion ??= + $this->computeCacheConfigurationVersion(); + } + + private function computeCacheConfigurationVersion(): string + { + /** @var ?Account $account */ + $account = $this->securityContext->getAccount(); + + // Get all roles and sort them by identifier + $roles = $account ? array_map(static fn ($role) => $role->getIdentifier(), $account->getRoles()) : []; + sort($roles); + + // Use the roles combination as cache key to allow multiple users sharing the same configuration version + $configurationIdentifier = md5(implode('_', $roles)); + $cacheKey = 'ConfigurationVersion_' . $configurationIdentifier; + /** @var string|false $version */ + $version = $this->configurationCache->get($cacheKey); + + if ($version === false) { + $version = (string)time(); + $this->configurationCache->set($cacheKey, $version); + } + return $configurationIdentifier . '_' . $version; + } +} diff --git a/Classes/Infrastructure/Configuration/ConfigurationProvider.php b/Classes/Infrastructure/Configuration/ConfigurationProvider.php new file mode 100644 index 0000000000..87d130525a --- /dev/null +++ b/Classes/Infrastructure/Configuration/ConfigurationProvider.php @@ -0,0 +1,93 @@ + $this->configurationManager->getConfiguration( + ConfigurationManager::CONFIGURATION_TYPE_SETTINGS, + 'Neos.Neos.userInterface.navigateComponent.nodeTree', + ), + 'structureTree' => $this->configurationManager->getConfiguration( + ConfigurationManager::CONFIGURATION_TYPE_SETTINGS, + 'Neos.Neos.userInterface.navigateComponent.structureTree', + ), + 'allowedTargetWorkspaces' => + $this->workspaceService->getAllowedTargetWorkspaces( + $contentRepository + ), + 'endpoints' => [ + 'nodeTypeSchema' => $uriBuilder->reset() + ->setCreateAbsoluteUri(true) + ->uriFor( + actionName: 'nodeTypeSchema', + controllerArguments: [ + 'version' => + $this->cacheConfigurationVersionProvider + ->getCacheConfigurationVersion(), + ], + controllerName: 'Backend\\Schema', + packageKey: 'Neos.Neos', + ), + 'translations' => $uriBuilder->reset() + ->setCreateAbsoluteUri(true) + ->uriFor( + actionName: 'xliffAsJson', + controllerArguments: [ + 'locale' => + $this->userService + ->getInterfaceLanguage(), + 'version' => + $this->cacheConfigurationVersionProvider + ->getCacheConfigurationVersion(), + ], + controllerName: 'Backend\\Backend', + packageKey: 'Neos.Neos', + ), + ] + ]; + } +} diff --git a/Classes/Infrastructure/Configuration/FrontendConfigurationProvider.php b/Classes/Infrastructure/Configuration/FrontendConfigurationProvider.php new file mode 100644 index 0000000000..b4f007f366 --- /dev/null +++ b/Classes/Infrastructure/Configuration/FrontendConfigurationProvider.php @@ -0,0 +1,44 @@ + */ + #[Flow\InjectConfiguration('frontendConfiguration')] + protected array $frontendConfigurationBeforeProcessing; + + /** @return array */ + public function getFrontendConfiguration( + ControllerContext $controllerContext + ): array { + return $this->configurationRenderingService->computeConfiguration( + $this->frontendConfigurationBeforeProcessing, + ['controllerContext' => $controllerContext] + ); + } +} diff --git a/Classes/Infrastructure/Configuration/InitialStateProvider.php b/Classes/Infrastructure/Configuration/InitialStateProvider.php new file mode 100644 index 0000000000..9851377f3c --- /dev/null +++ b/Classes/Infrastructure/Configuration/InitialStateProvider.php @@ -0,0 +1,59 @@ + */ + #[Flow\InjectConfiguration('initialState')] + protected array $initialStateBeforeProcessing; + + public function getInitialState( + ControllerContext $controllerContext, + ContentRepositoryId $contentRepositoryId, + ?Node $documentNode, + ?Node $site, + User $user, + ): array { + return $this->configurationRenderingService->computeConfiguration( + $this->initialStateBeforeProcessing, + [ + 'controllerContext' => $controllerContext, + 'contentRepositoryId' => $contentRepositoryId, + 'documentNode' => $documentNode, + 'site' => $site, + 'user' => $user, + 'clipboardNodes' => $this->clipboard->getSerializedNodeAddresses(), + 'clipboardMode' => $this->clipboard->getMode(), + ] + ); + } +} diff --git a/Classes/Infrastructure/ContentRepository/NodeTypesProvider.php b/Classes/Infrastructure/ContentRepository/NodeTypesProvider.php new file mode 100644 index 0000000000..12771f8b54 --- /dev/null +++ b/Classes/Infrastructure/ContentRepository/NodeTypesProvider.php @@ -0,0 +1,38 @@ + */ + #[Flow\InjectConfiguration(path: 'nodeTypeRoles')] + protected array $roles; + + /** @var array */ + #[Flow\InjectConfiguration(path: 'nodeTypes.groups', package: 'Neos.Neos')] + protected array $groups; + + public function getNodeTypes(): array + { + return [ + 'roles' => $this->roles, + 'groups' => $this->groups, + ]; + } +} diff --git a/Classes/Infrastructure/MVC/RoutesProvider.php b/Classes/Infrastructure/MVC/RoutesProvider.php new file mode 100644 index 0000000000..4303b5ae3b --- /dev/null +++ b/Classes/Infrastructure/MVC/RoutesProvider.php @@ -0,0 +1,177 @@ + + $helper->buildUiServiceRoute('change'), + 'publish' => + $helper->buildUiServiceRoute('publish'), + 'discard' => + $helper->buildUiServiceRoute('discard'), + 'changeBaseWorkspace' => + $helper->buildUiServiceRoute('changeBaseWorkspace'), + 'rebaseWorkspace' => + $helper->buildUiServiceRoute('rebaseWorkspace'), + 'copyNodes' => + $helper->buildUiServiceRoute('copyNodes'), + 'cutNodes' => + $helper->buildUiServiceRoute('cutNodes'), + 'clearClipboard' => + $helper->buildUiServiceRoute('clearClipboard'), + 'flowQuery' => + $helper->buildUiServiceRoute('flowQuery'), + 'generateUriPathSegment' => + $helper->buildUiServiceRoute('generateUriPathSegment'), + 'getWorkspaceInfo' => + $helper->buildUiServiceRoute('getWorkspaceInfo'), + 'getAdditionalNodeMetadata' => + $helper->buildUiServiceRoute('getAdditionalNodeMetadata'), + ]; + + $routes['core']['content'] = [ + 'imageWithMetadata' => + $helper->buildCoreRoute( + controllerName: 'Backend\\Content', + actionName: 'imageWithMetaData' + ), + 'createImageVariant' => + $helper->buildCoreRoute( + controllerName: 'Backend\\Content', + actionName: 'createImageVariant' + ), + 'loadMasterPlugins' => + $helper->buildCoreRoute( + controllerName: 'Backend\\Content', + actionName: 'masterPlugins' + ), + 'loadPluginViews' => + $helper->buildCoreRoute( + controllerName: 'Backend\\Content', + actionName: 'pluginViews' + ), + 'uploadAsset' => + $helper->buildCoreRoute( + controllerName: 'Backend\\Content', + actionName: 'uploadAsset' + ), + ]; + + $routes['core']['service'] = [ + 'assetProxies' => + $helper->buildCoreRoute( + controllerName: 'Service\\AssetProxies', + actionName: 'index' + ), + 'assets' => + $helper->buildCoreRoute( + controllerName: 'Service\\Assets', + actionName: 'index' + ), + 'nodes' => + $helper->buildCoreRoute( + controllerName: 'Service\\Nodes', + actionName: 'index' + ), + 'userPreferences' => + $helper->buildCoreRoute( + subPackageKey: 'Service', + controllerName: 'UserPreference', + actionName: 'index', + format: 'json', + ), + 'dataSource' => + $helper->buildCoreRoute( + subPackageKey: 'Service', + controllerName: 'DataSource', + actionName: 'index', + format: 'json', + ), + 'contentDimensions' => + $helper->buildCoreRoute( + controllerName: 'Service\\ContentDimensions', + actionName: 'index', + ), + 'impersonateStatus' => + $helper->buildCoreRoute( + controllerName: 'Backend\\Impersonate', + actionName: 'status', + format: 'json', + ), + 'impersonateRestore' => + $helper->buildCoreRoute( + controllerName: 'Backend\\Impersonate', + actionName: 'restoreWithResponse', + format: 'json', + ), + ]; + + $routes['core']['modules'] = [ + 'workspaces' => + $helper->buildCoreRoute( + controllerName: 'Backend\\Module', + actionName: 'index', + arguments: [ + 'module' => 'management/workspaces' + ] + ), + 'userSettings' => + $helper->buildCoreRoute( + controllerName: 'Backend\\Module', + actionName: 'index', + arguments: [ + 'module' => 'user/usersettings' + ] + ), + 'mediaBrowser' => + $helper->buildCoreRoute( + controllerName: 'Backend\\Module', + actionName: 'index', + arguments: [ + 'module' => 'media/browser' + ] + ), + ]; + + $routes['core']['login'] = + $helper->buildCoreRoute( + controllerName: 'Login', + actionName: 'index', + format: 'json', + ); + + $routes['core']['logout'] = + $helper->buildCoreRoute( + controllerName: 'Login', + actionName: 'logout', + ); + + return $routes; + } +} diff --git a/Classes/Infrastructure/MVC/RoutesProviderHelper.php b/Classes/Infrastructure/MVC/RoutesProviderHelper.php new file mode 100644 index 0000000000..6e91883336 --- /dev/null +++ b/Classes/Infrastructure/MVC/RoutesProviderHelper.php @@ -0,0 +1,65 @@ +uriBuilder->reset() + ->setCreateAbsoluteUri(true) + ->uriFor( + actionName: $actionName, + controllerArguments: [], + controllerName: 'BackendService', + packageKey: 'Neos.Neos.Ui', + ); + } + + public function buildCoreRoute( + string $controllerName, + string $actionName, + ?string $subPackageKey = null, + ?string $format = null, + array $arguments = [], + ): string { + $this->uriBuilder->reset() + ->setCreateAbsoluteUri(true); + + if ($format !== null) { + $this->uriBuilder->setFormat($format); + } + + return $this->uriBuilder->uriFor( + actionName: $actionName, + controllerArguments: $arguments, + controllerName: $controllerName, + packageKey: 'Neos.Neos', + subPackageKey: $subPackageKey, + ); + } +} diff --git a/Classes/Infrastructure/Neos/MenuProvider.php b/Classes/Infrastructure/Neos/MenuProvider.php new file mode 100644 index 0000000000..bd6787ca6a --- /dev/null +++ b/Classes/Infrastructure/Neos/MenuProvider.php @@ -0,0 +1,88 @@ +menuHelper->buildModuleList($controllerContext); + + $result = []; + foreach ($modulesForMenu as $moduleName => $module) { + $result[$moduleName]['label'] = $module['label']; + $result[$moduleName]['icon'] = $module['icon']; + $result[$moduleName]['uri'] = $module['uri']; + $result[$moduleName]['target'] = 'Window'; + + $result[$moduleName]['children'] = match ($module['module']) { + 'content' => $this->buildChildrenForSites($controllerContext), + default => $this->buildChildrenForBackendModule($module), + }; + } + + return array_values($result); + } + + private function buildChildrenForSites(ControllerContext $controllerContext): array + { + $sitesForMenu = $this->menuHelper->buildSiteList($controllerContext); + + $result = []; + foreach ($sitesForMenu as $index => $site) { + $result[$index]['icon'] = 'globe'; + $result[$index]['label'] = $site['name']; + $result[$index]['uri'] = $site['uri']; + $result[$index]['target'] = 'Window'; + $result[$index]['isActive'] = $site['active']; + $result[$index]['skipI18n'] = true; + } + + return array_values($result); + } + + private function buildChildrenForBackendModule(array $module): array + { + $result = []; + foreach ($module['submodules'] as $submoduleName => $submodule) { + if ($submodule['hideInMenu'] === true) { + continue; + } + + $result[$submoduleName]['icon'] = $submodule['icon']; + $result[$submoduleName]['label'] = $submodule['label']; + $result[$submoduleName]['uri'] = $submodule['uri']; + $result[$submoduleName]['position'] = $submodule['position']; + $result[$submoduleName]['isActive'] = true; + $result[$submoduleName]['target'] = 'Window'; + $result[$submoduleName]['skipI18n'] = false; + } + + $positionalArraySorter = new PositionalArraySorter($result); + $result = $positionalArraySorter->toArray(); + + return array_values($result); + } +} diff --git a/Classes/Presentation/ApplicationView.php b/Classes/Presentation/ApplicationView.php new file mode 100644 index 0000000000..f09b91eaa6 --- /dev/null +++ b/Classes/Presentation/ApplicationView.php @@ -0,0 +1,171 @@ + [null, 'The application title which will be used as the HTML .', 'string'], + ]; + + public function render(): string + { + $result = '<!DOCTYPE html>'; + $result .= '<html lang="' . $this->renderLang() . '">'; + $result .= '<head>'; + $result .= $this->renderHead(); + $result .= '</head>'; + $result .= '<body>'; + $result .= $this->renderBody(); + $result .= '</body>'; + $result .= '</html>'; + + return $result; + } + + private function renderLang(): string + { + return $this->userService->getInterfaceLanguage(); + } + + private function renderHead(): string + { + $result = '<meta charset="UTF-8">'; + $result .= '<meta name="viewport" content="width=device-width, initial-scale=1.0">'; + + $result .= '<title>' . $this->options['title'] . ''; + + $result .= $this->styleAndJavascriptInclusionService->getHeadStylesheets(); + $result .= $this->styleAndJavascriptInclusionService->getHeadScripts(); + + $result .= sprintf( + '', + $this->resourceManager->getPublicPackageResourceUriByPath( + 'resource://Neos.Neos.Ui/Public/Images/apple-touch-icon.png' + ) + ); + $result .= sprintf( + '', + $this->resourceManager->getPublicPackageResourceUriByPath( + 'resource://Neos.Neos.Ui/Public/Images/favicon-16x16.png' + ) + ); + $result .= sprintf( + '', + $this->resourceManager->getPublicPackageResourceUriByPath( + 'resource://Neos.Neos.Ui/Public/Images/favicon-32x32.png' + ) + ); + $result .= sprintf( + '', + $this->resourceManager->getPublicPackageResourceUriByPath( + 'resource://Neos.Neos.Ui/Public/Images/safari-pinned-tab.svg' + ) + ); + + $result .= sprintf( + '', + json_encode($this->variables), + ); + + return $result; + } + + private function renderBody(): string + { + $result = sprintf( + '
', + $this->securityContext->getCsrfProtectionToken(), + (string) $this->bootstrap->getContext(), + ); + $result .= $this->renderSplashScreen(); + $result .= '
'; + + return $result; + } + + private function renderSplashScreen(): string + { + return << + @keyframes color_change { + 0% { + filter: drop-shadow(0 0 0 #00adee) opacity(25%); + } + 100% { + filter: drop-shadow(0 0 5px #00adee) opacity(100%); + } + } + + .loadingIcon { + color: #00adee; + animation-name: color_change; + animation-duration: 1.2s; + animation-iteration-count: infinite; + animation-direction: alternate; + animation-timing-function: ease-in-out; + } + .splash { + width: 100vw; + height: 100vh; + background-color: #222222; + display: flex; + align-items: center; + justify-content: center; + font-size: 30px; + } + +
+ +
+ HTML; + } +} diff --git a/Configuration/Objects.yaml b/Configuration/Objects.yaml new file mode 100644 index 0000000000..83e90f7560 --- /dev/null +++ b/Configuration/Objects.yaml @@ -0,0 +1,24 @@ +Neos\Neos\Ui\Controller\BackendController: + properties: + configurationProvider: + object: Neos\Neos\Ui\Infrastructure\Configuration\ConfigurationProvider + routesProvider: + object: Neos\Neos\Ui\Infrastructure\MVC\RoutesProvider + frontendConfigurationProvider: + object: Neos\Neos\Ui\Infrastructure\Configuration\FrontendConfigurationProvider + nodeTypesProvider: + object: Neos\Neos\Ui\Infrastructure\ContentRepository\NodeTypesProvider + menuProvider: + object: Neos\Neos\Ui\Infrastructure\Neos\MenuProvider + initialStateProvider: + object: Neos\Neos\Ui\Infrastructure\Configuration\InitialStateProvider + +Neos\Neos\Ui\Infrastructure\Cache\CacheConfigurationVersionProvider: + properties: + configurationCache: + object: + factoryObjectName: Neos\Flow\Cache\CacheManager + factoryMethodName: getCache + arguments: + 1: + value: Neos_Neos_Configuration_Version diff --git a/Configuration/Settings.yaml b/Configuration/Settings.yaml index a5aa25d1cf..d545779a1d 100644 --- a/Configuration/Settings.yaml +++ b/Configuration/Settings.yaml @@ -31,9 +31,6 @@ Neos: Ui: - splashScreen: - partial: 'SplashScreen' - # API: "start 100" and smaller numbers; "no numbers", ... resources: diff --git a/Configuration/Views.yaml b/Configuration/Views.yaml index 1e29893dac..434c5d0fc7 100644 --- a/Configuration/Views.yaml +++ b/Configuration/Views.yaml @@ -1,7 +1,6 @@ - requestFilter: 'isPackage("Neos.Neos.Ui") && isController("Backend")' - viewObjectName: 'Neos\Fusion\View\FusionView' + viewObjectName: 'Neos\Neos\Ui\Presentation\ApplicationView' options: - fusionPathPatterns: - - 'resource://Neos.Neos.Ui/Private/Fusion/Backend' + title: 'Neos CMS'