Commit 0e418614 authored by Tino Goratsch's avatar Tino Goratsch

add a component registry

parent a1ea00fa
Pipeline #92086140 passed with stages
in 4 minutes and 3 seconds
......@@ -32,7 +32,7 @@ abstract class AbstractBootstrap implements BootstrapInterface
*
* @throws \Exception
*/
public function __construct($appMode)
public function __construct(string $appMode)
{
$this->appMode = $appMode;
$this->initializeApplicationPath();
......
......@@ -59,14 +59,14 @@ class CheckMaintenanceModeListener
*
* @return bool
*/
private function canShowMaintenanceMessage()
private function canShowMaintenanceMessage(): bool
{
return (bool) $this->settings->getSettings('system')['maintenance_mode'] === true &&
\in_array($this->request->getArea(), [AreaEnum::AREA_ADMIN, AreaEnum::AREA_WIDGET]) === false &&
\in_array($this->request->getArea(), [AreaEnum::AREA_ADMIN, AreaEnum::AREA_WIDGET], true) === false &&
\strpos($this->request->getQuery(), 'users/index/login/') !== 0;
}
private function renderMaintenanceMessage()
private function renderMaintenanceMessage(): void
{
$this->view->assign([
'PAGE_TITLE' => 'ACP3',
......
......@@ -9,6 +9,7 @@ namespace ACP3\Core\Assets;
use ACP3\Core;
use ACP3\Core\Assets\FileResolver\FileCheckerStrategyInterface;
use ACP3\Core\Component\ComponentRegistry;
class FileResolver
{
......@@ -21,11 +22,7 @@ class FileResolver
*/
private $resourcesCache;
/**
* @var \ACP3\Core\Modules
*/
private $modules;
/**
* @var \ACP3\Core\Environment\Theme
* @var \ACP3\Core\Environment\ThemePathInterface
*/
private $theme;
/**
......@@ -50,22 +47,14 @@ class FileResolver
*/
private $currentTheme;
/**
* @param \ACP3\Core\Assets\Cache $resourcesCache
* @param \ACP3\Core\Environment\ApplicationPath $appPath
* @param \ACP3\Core\Environment\Theme $theme
* @param \ACP3\Core\Modules $modules
*/
public function __construct(
Core\Assets\Cache $resourcesCache,
Core\Environment\ApplicationPath $appPath,
Core\Environment\Theme $theme,
Core\Modules $modules
Core\Environment\ThemePathInterface $theme
) {
$this->resourcesCache = $resourcesCache;
$this->appPath = $appPath;
$this->cachedPaths = $resourcesCache->getCache();
$this->modules = $modules;
$this->theme = $theme;
$this->addStrategy(new Core\Assets\FileResolver\MinifiedAwareFileCheckerStrategy());
......@@ -96,48 +85,41 @@ class FileResolver
{
// A path without any slash was given -> has to be a layout file of the current design
if (\strpos($templatePath, '/') === false) {
return $this->getStaticAssetPath('', '', '', $templatePath);
return $this->getStaticAssetPath('', '', $templatePath);
}
// Split the template path in its components
$fragments = \explode('/', \ucfirst($templatePath));
if (isset($fragments[2])) {
$fragments[1] = \ucfirst($fragments[1]);
}
$modulesPath = $fragments[0] . '/Resources/';
$designPath = $fragments[0];
$moduleName = $fragments[0];
$templatePath = \implode('/', \array_slice($fragments, 1));
return $this->getStaticAssetPath($modulesPath, $designPath, 'View', $templatePath);
return $this->getStaticAssetPath($moduleName, 'View', $templatePath);
}
/**
* @param string $modulePath
* @param string $designPath
* @param string $dir
* @param string $moduleName
* @param string $resourceDirectory
* @param string $file
*
* @return string
*/
public function getStaticAssetPath(
string $modulePath,
string $designPath,
string $dir = '',
string $moduleName,
string $resourceDirectory = '',
string $file = ''
): string {
if ($this->needsTrailingSlash($modulePath)) {
$modulePath .= '/';
}
if ($this->needsTrailingSlash($designPath)) {
$designPath .= '/';
}
if (!empty($dir) && !\preg_match('=/$=', $dir)) {
$dir .= '/';
if (!empty($resourceDirectory) && !\preg_match('=/$=', $resourceDirectory)) {
$resourceDirectory .= '/';
}
$systemAssetPath = $this->appPath->getModulesDir() . $modulePath . $dir . $file;
$systemAssetPath = $moduleName . '-' . $resourceDirectory . '-' . $file;
if (!isset($this->cachedPaths[$systemAssetPath])) {
$this->cachedPaths[$systemAssetPath] = $this->resolveAssetPath($modulePath, $designPath, $dir, $file);
$this->cachedPaths[$systemAssetPath] = $this->resolveAssetPath($moduleName, $resourceDirectory, $file);
$this->newAssetPathsAdded = true;
}
......@@ -146,45 +128,41 @@ class FileResolver
}
/**
* @param string $path
*
* @return bool
*/
protected function needsTrailingSlash(string $path): bool
{
return $path !== '' && \strpos($path, '.') === false && !\preg_match('=/$=', $path);
}
/**
* @param string $modulePath
* @param string $designPath
* @param string $dir
* @param string $moduleName
* @param string $resourceDirectory
* @param string $file
*
* @return string|null
*/
private function resolveAssetPath(string $modulePath, string $designPath, string $dir, string $file): ?string
private function resolveAssetPath(string $moduleName, string $resourceDirectory, string $file): ?string
{
if ($this->designAssetsPath === null) {
$this->resetTheme();
}
$assetPath = $this->findAssetInInheritedThemes($modulePath, $designPath, $dir, $file);
return $assetPath ?: $this->findAssetInModules($modulePath, $dir, $file);
$assetPath = $this->findAssetInInheritedThemes(
\ucfirst($moduleName),
!empty($resourceDirectory) ? '/' . $resourceDirectory : '',
$file
);
return $assetPath ?: $this->findAssetInModules(
$moduleName,
'/Resources/' . $resourceDirectory,
$file
);
}
/**
* @param string $modulePath
* @param string $designPath
* @param string $dir
* @param string $moduleName
* @param string $resourceDirectory
* @param string $file
*
* @return string|null
*/
private function findAssetInInheritedThemes(string $modulePath, string $designPath, string $dir, string $file): ?string
private function findAssetInInheritedThemes(string $moduleName, string $resourceDirectory, string $file): ?string
{
$designAssetPath = $this->designAssetsPath . $designPath . $dir . $file;
$designAssetPath = $this->designAssetsPath . $moduleName . $resourceDirectory . $file;
if (null !== ($resourcePath = $this->findAssetInStrategies($designAssetPath))) {
return $resourcePath;
......@@ -196,7 +174,7 @@ class FileResolver
// Recursively iterate over the nested themes
if ($parentTheme !== false) {
$this->changeTheme($parentTheme);
$assetPath = $this->getStaticAssetPath($modulePath, $designPath, $dir, $file);
$assetPath = $this->getStaticAssetPath($moduleName, $resourceDirectory, $file);
$this->resetTheme();
return $assetPath;
......@@ -227,17 +205,16 @@ class FileResolver
$this->changeTheme($this->theme->getCurrentTheme());
}
private function findAssetInModules(string $modulePath, string $dir, string $file): ?string
private function findAssetInModules(string $moduleName, string $resourceDirectory, string $file): ?string
{
$moduleName = \substr($modulePath, 0, \strpos($modulePath, '/'));
$moduleInfo = $this->modules->getModuleInfo($moduleName);
if (!empty($moduleInfo)) {
$moduleAssetPath = $this->appPath->getModulesDir() . $moduleInfo['vendor'] . '/' . $modulePath . $dir . $file;
try {
$moduleAssetPath = ComponentRegistry::getPathByComponentName($moduleName) . $resourceDirectory . $file;
if (null !== ($resourcePath = $this->findAssetInStrategies($moduleAssetPath))) {
return $resourcePath;
}
} catch (Core\Component\Exception\ComponentNotFoundException $e) {
// Intentionally omitted
}
return null;
......
......@@ -44,64 +44,61 @@ class IncludeJs
}
/**
* @param string $module
* @param string $moduleName
* @param string $filePath
* @param string[] $dependencies
*
* @return string
*/
public function add(string $module, string $filePath, array $dependencies = []): string
public function add(string $moduleName, string $filePath, array $dependencies = []): string
{
if (!empty($dependencies)) {
$this->assets->enableLibraries($dependencies);
}
if ($this->hasValidParams($module, $filePath)) {
$key = $module . '/' . $filePath;
// Do not include the same file multiple times
if (isset($this->alreadyIncluded[$key]) === false) {
$this->alreadyIncluded[$key] = true;
if (!$this->hasValidParams($moduleName, $filePath)) {
throw new \InvalidArgumentException(
'Not all necessary arguments for the function ' . __FUNCTION__ . ' were passed!'
);
}
return \sprintf(
'<script defer src="%s"></script>',
$this->resolvePath($module, $filePath) . '?v=' . Core\Application\BootstrapInterface::VERSION
);
}
$key = $moduleName . '/' . $filePath;
// Do not include the same file multiple times
if (isset($this->alreadyIncluded[$key])) {
return '';
}
throw new \InvalidArgumentException(
'Not all necessary arguments for the function ' . __FUNCTION__ . ' were passed!'
$this->alreadyIncluded[$key] = true;
return \sprintf(
'<script defer src="%s"></script>',
$this->resolvePath($moduleName, $filePath) . '?v=' . Core\Application\BootstrapInterface::VERSION
);
}
/**
* @param string $module
* @param string $moduleName
* @param string $filePath
*
* @return bool
*/
private function hasValidParams(string $module, string $filePath): bool
private function hasValidParams(string $moduleName, string $filePath): bool
{
return \preg_match('=/=', $module) === 0
return \preg_match('=/=', $moduleName) === 0
&& \preg_match('=\./=', $filePath) === 0;
}
/**
* @param string $module
* @param string $moduleName
* @param string $filePath
*
* @return string
*/
protected function resolvePath(string $module, string $filePath): string
protected function resolvePath(string $moduleName, string $filePath): string
{
$module = \ucfirst($module);
$path = $this->fileResolver->getStaticAssetPath(
$module . '/Resources/',
$module . '/',
$moduleName,
'Assets/js',
$filePath . '.js'
);
......
......@@ -10,6 +10,7 @@ namespace ACP3\Core\Assets\Minifier;
use ACP3\Core\Assets;
use ACP3\Core\Assets\FileResolver;
use ACP3\Core\Cache;
use ACP3\Core\Environment\ApplicationMode;
use ACP3\Core\Environment\ApplicationPath;
use ACP3\Core\Modules;
use ACP3\Core\Settings\SettingsInterface;
......@@ -19,8 +20,9 @@ use Psr\Log\LoggerInterface;
abstract class AbstractMinifier implements MinifierInterface
{
const ASSETS_PATH_CSS = 'Assets/css';
const ASSETS_PATH_JS = 'Assets/js';
protected const ASSETS_PATH_CSS = 'Assets/css';
protected const ASSETS_PATH_JS = 'Assets/js';
protected const SYSTEM_MODULE_NAME = 'system';
/**
* @var \ACP3\Core\Assets
......@@ -50,30 +52,11 @@ abstract class AbstractMinifier implements MinifierInterface
* @var string
*/
protected $environment;
/**
* @var string
*/
protected $systemAssetsModulePath = 'System/Resources/';
/**
* @var string
*/
protected $systemAssetsDesignPath = 'System/';
/**
* @var LoggerInterface
*/
private $logger;
/**
* @param LoggerInterface $logger
* @param \ACP3\Core\Assets $assets
* @param \ACP3\Core\Environment\ApplicationPath $appPath
* @param \ACP3\Core\Cache $systemCache
* @param SettingsInterface $config
* @param \ACP3\Core\Modules $modules
* @param \ACP3\Core\Assets\FileResolver $fileResolver
* @param string $environment
*/
public function __construct(
LoggerInterface $logger,
Assets $assets,
......@@ -82,7 +65,7 @@ abstract class AbstractMinifier implements MinifierInterface
SettingsInterface $config,
Modules $modules,
FileResolver $fileResolver,
$environment
string $environment
) {
$this->assets = $assets;
$this->appPath = $appPath;
......@@ -144,7 +127,7 @@ abstract class AbstractMinifier implements MinifierInterface
*/
public function getURI(string $layout = 'layout'): string
{
$debug = $this->environment === 'dev';
$debug = $this->environment === ApplicationMode::DEVELOPMENT;
$filenameHash = $this->generateFilenameHash($this->getAssetGroup(), $layout);
$cacheId = 'assets-last-generated-' . $filenameHash;
......@@ -155,11 +138,11 @@ abstract class AbstractMinifier implements MinifierInterface
$path = $this->buildAssetPath($debug, $this->getAssetGroup(), $filenameHash, $lastGenerated);
// If the requested minified StyleSheet and/or the JavaScript file doesn't exist, generate it
if (\is_file($this->appPath->getUploadsDir() . $path) === false || $debug === true) {
if ($debug === true || \is_file($this->appPath->getUploadsDir() . $path) === false) {
// Get the enabled libraries and filter out empty entries
$files = \array_filter(
$this->processLibraries($layout),
function ($var) {
static function ($var) {
return !empty($var);
}
);
......
......@@ -21,6 +21,9 @@ class CSS extends AbstractMinifier
/**
* {@inheritdoc}
*
* @throws \MJS\TopSort\CircularDependencyException
* @throws \MJS\TopSort\ElementNotFoundException
*/
protected function processLibraries(string $layout): array
{
......@@ -54,8 +57,7 @@ class CSS extends AbstractMinifier
foreach ($stylesheets as $stylesheet) {
$this->stylesheets[] = $this->fileResolver->getStaticAssetPath(
!empty($library['module']) ? $library['module'] . '/Resources' : $this->systemAssetsModulePath,
$library['module'] ?? $this->systemAssetsDesignPath,
!empty($library['module']) ? $library['module'] : static::SYSTEM_MODULE_NAME,
static::ASSETS_PATH_CSS,
$stylesheet
);
......@@ -72,7 +74,6 @@ class CSS extends AbstractMinifier
{
foreach ($this->assets->fetchAdditionalThemeCssFiles() as $file) {
$this->stylesheets[] = $this->fileResolver->getStaticAssetPath(
'',
'',
static::ASSETS_PATH_CSS,
\trim($file)
......@@ -81,13 +82,11 @@ class CSS extends AbstractMinifier
// Include general system styles and the stylesheet of the current theme
$this->stylesheets[] = $this->fileResolver->getStaticAssetPath(
$this->systemAssetsModulePath,
$this->systemAssetsDesignPath,
static::SYSTEM_MODULE_NAME,
static::ASSETS_PATH_CSS,
'style.css'
);
$this->stylesheets[] = $this->fileResolver->getStaticAssetPath(
'',
'',
static::ASSETS_PATH_CSS,
$layout . '.css'
......@@ -99,25 +98,19 @@ class CSS extends AbstractMinifier
*/
protected function fetchModuleStylesheets(): void
{
$modules = $this->modules->getActiveModules();
foreach ($modules as $module) {
$modulePath = $module['dir'] . '/Resources/';
$designPath = $module['dir'] . '/';
foreach ($this->modules->getActiveModules() as $module) {
$stylesheet = $this->fileResolver->getStaticAssetPath(
$modulePath,
$designPath,
$module['name'],
static::ASSETS_PATH_CSS,
'style.css'
);
if ('' !== $stylesheet && $module['dir'] !== 'System') {
if ('' !== $stylesheet && $module['name'] !== self::SYSTEM_MODULE_NAME) {
$this->stylesheets[] = $stylesheet;
}
// Append custom styles to the default module styling
$appendStylesheet = $this->fileResolver->getStaticAssetPath(
$modulePath,
$designPath,
$module['name'],
static::ASSETS_PATH_CSS,
'append.css'
);
......
......@@ -21,6 +21,9 @@ class JavaScript extends AbstractMinifier
/**
* {@inheritdoc}
*
* @throws \MJS\TopSort\CircularDependencyException
* @throws \MJS\TopSort\ElementNotFoundException
*/
protected function processLibraries(string $layout): array
{
......@@ -44,8 +47,7 @@ class JavaScript extends AbstractMinifier
foreach ($this->assets->getLibraries() as $library) {
if ($library['enabled'] === true && isset($library[$this->getAssetGroup()]) === true) {
$this->javascript[] = $this->fileResolver->getStaticAssetPath(
!empty($library['module']) ? $library['module'] . '/Resources' : $this->systemAssetsModulePath,
!empty($library['module']) ? $library['module'] : $this->systemAssetsDesignPath,
!empty($library['module']) ? $library['module'] : static::SYSTEM_MODULE_NAME,
static::ASSETS_PATH_JS,
$library[$this->getAssetGroup()]
);
......@@ -61,10 +63,10 @@ class JavaScript extends AbstractMinifier
protected function fetchThemeJavaScript(string $layout): void
{
foreach ($this->assets->fetchAdditionalThemeJsFiles() as $file) {
$this->javascript[] = $this->fileResolver->getStaticAssetPath('', '', static::ASSETS_PATH_JS, $file);
$this->javascript[] = $this->fileResolver->getStaticAssetPath('', static::ASSETS_PATH_JS, $file);
}
// Include general js file of the layout
$this->javascript[] = $this->fileResolver->getStaticAssetPath('', '', static::ASSETS_PATH_JS, $layout . '.js');
$this->javascript[] = $this->fileResolver->getStaticAssetPath('', static::ASSETS_PATH_JS, $layout . '.js');
}
}
......@@ -10,6 +10,7 @@ namespace ACP3\Core\Cache;
use ACP3\Core\Environment\ApplicationMode;
use ACP3\Core\Environment\ApplicationPath;
use Doctrine\Common\Cache\ArrayCache;
use Doctrine\Common\Cache\CacheProvider;
use Doctrine\Common\Cache\PhpFileCache;
class CacheDriverFactory
......@@ -34,7 +35,7 @@ class CacheDriverFactory
* @param string $cacheDriver
* @param string $environment
*/
public function __construct(ApplicationPath $appPath, $cacheDriver, $environment)
public function __construct(ApplicationPath $appPath, string $cacheDriver, string $environment)
{
$this->appPath = $appPath;
$this->cacheDriver = $cacheDriver;
......@@ -46,7 +47,7 @@ class CacheDriverFactory
*
* @return \Doctrine\Common\Cache\CacheProvider
*/
public function create($namespace)
public function create(string $namespace): CacheProvider
{
$driver = $this->initializeCacheDriver($this->getCacheDriverName());
$driver->setNamespace($namespace);
......@@ -57,7 +58,7 @@ class CacheDriverFactory
/**
* @return string
*/
protected function getCacheDriverName()
protected function getCacheDriverName(): string
{
return $this->environment !== ApplicationMode::DEVELOPMENT ? $this->cacheDriver : 'Array';
}
......@@ -69,7 +70,7 @@ class CacheDriverFactory
*
* @throws \InvalidArgumentException
*/
protected function initializeCacheDriver($driverName)
protected function initializeCacheDriver(string $driverName): CacheProvider
{
/* @var \Doctrine\Common\Cache\CacheProvider $driver */
switch (\strtolower($driverName)) {
......
<?php
/**
* Copyright (c) by the ACP3 Developers.
* See the LICENSE file at the top-level module directory for licensing details.
*/
namespace ACP3\Core\Component;
use ACP3\Core\Component\Dto\ComponentDataDto;
use ACP3\Core\Component\Exception\ComponentNotFoundException;
use MJS\TopSort\Implementations\StringSort;
class ComponentRegistry
{
/**
* @var ComponentDataDto[]
*/
private static $components = [];
/**
* @var ComponentDataDto[]
*/
private static $componentsTopSorted;
/**
* Adds a new component with its name and its filesystem path to the component registry.
*
* @param \ACP3\Core\Component\Dto\ComponentDataDto $component
*/
public static function add(ComponentDataDto $component): void
{
self::$components[$component->getPath()] = $component;
}
/**
* Return all currently registered components.
*
* @return ComponentDataDto[]
*/
public static function getAllComponents(): array
{