Commit 19e103f2 authored by Tino Goratsch's avatar Tino Goratsch

Merge branch 'release/v4.28.0'

parents 31efe947 fcb4a754
Pipeline #28214588 passed with stages
in 11 minutes and 43 seconds
......@@ -2,7 +2,8 @@
"env": {
"browser": true,
"jquery": true,
"node": true
"node": true,
"es6": true
},
"extends": "eslint:recommended",
"globals": {
......@@ -33,6 +34,7 @@
"vars": "local"
}
],
"no-var": "error",
"no-console": [
"error",
{
......
......@@ -4,24 +4,26 @@ variables:
COMPOSER_HOME: .composerhome
COMPOSER_ALLOW_SUPERUSER: 1
cache:
key: $CI_COMMIT_REF_NAME
paths:
- ${COMPOSER_HOME}
- vendor
- .php_cs.cache
stages:
- test
- static-analysis
- deploy
- post-deploy
test:php71-unit: &php-unit-base
stage: test
.php-base: &php-base
cache:
key: $CI_COMMIT_REF_NAME
paths:
- ${COMPOSER_HOME}
- vendor
- .php_cs.cache
before_script:
- bash ./build/gitlab/before_script.sh > /dev/null
- bash ./build/gitlab/before_script_php.sh > /dev/null
test:php71-unit: &php-unit-base
<<: *php-base
stage: test
script:
- php composer.phar install -n -o
- phpdbg -qrr ./vendor/bin/phpunit -c ./tests/phpunit.dist.xml --coverage-text --colors=never
......@@ -33,10 +35,8 @@ test:php72-unit:
coverage: ~
static-analysis:phpcs:
<<: *php-base
stage: static-analysis
before_script:
- bash ./build/gitlab/before_script.sh > /dev/null
- bash ./build/gitlab/before_script_php.sh > /dev/null
script:
- php composer.phar install -n -o
- php composer.phar run-script lint
......@@ -47,11 +47,11 @@ static-analysis:eslint:
cache:
key: node-$CI_COMMIT_REF_NAME
paths:
- node_modules
- node_modules
before_script:
- bash ./build/gitlab/before_script.sh > /dev/null
script:
- npm install
- npm ci
- npm run-script eslint
deploy:subtree-split:
......@@ -77,10 +77,15 @@ deploy:subtree-split:
- gitsplit --ref "${CI_COMMIT_REF_NAME}"
deploy:generate-artifact:
<<: *php-base
stage: deploy
before_script:
- bash ./build/gitlab/before_script.sh > /dev/null
- bash ./build/gitlab/before_script_php.sh > /dev/null
cache:
key: $CI_COMMIT_REF_NAME
paths:
- ${COMPOSER_HOME}
- vendor
- .php_cs.cache
policy: pull
script:
- php composer.phar install --no-dev --prefer-dist -o -n --ignore-platform-reqs
artifacts:
......
......@@ -8,6 +8,7 @@
namespace ACP3\Core;
use ACP3\Core\ACL\Model\Repository\UserRoleRepositoryInterface;
use ACP3\Core\Controller\Helper\ControllerActionExists;
use ACP3\Modules\ACP3\Permissions;
use ACP3\Modules\ACP3\Users\Model\UserModel;
......@@ -47,18 +48,24 @@ class ACL
* @var array
*/
protected $resources = [];
/**
* @var \ACP3\Core\Controller\Helper\ControllerActionExists
*/
private $controllerActionExists;
/**
* ACL constructor.
*
* @param \ACP3\Modules\ACP3\Users\Model\UserModel $user
* @param \ACP3\Core\Modules $modules
* @param \ACP3\Core\Controller\Helper\ControllerActionExists $controllerActionExists
* @param \ACP3\Core\ACL\Model\Repository\UserRoleRepositoryInterface $userRoleRepository
* @param \ACP3\Modules\ACP3\Permissions\Cache $permissionsCache
*/
public function __construct(
UserModel $user,
Modules $modules,
ControllerActionExists $controllerActionExists,
UserRoleRepositoryInterface $userRoleRepository,
Permissions\Cache $permissionsCache
) {
......@@ -66,6 +73,7 @@ class ACL
$this->modules = $modules;
$this->userRoleRepository = $userRoleRepository;
$this->permissionsCache = $permissionsCache;
$this->controllerActionExists = $controllerActionExists;
}
/**
......@@ -75,7 +83,7 @@ class ACL
*
* @return array
*/
public function getUserRoleIds($userId)
public function getUserRoleIds(int $userId)
{
if (isset($this->userRoles[$userId]) === false) {
// Special case for guest user
......@@ -98,7 +106,7 @@ class ACL
*
* @return array
*/
public function getUserRoleNames($userId)
public function getUserRoleNames(int $userId)
{
$roles = [];
foreach ($this->userRoleRepository->getRolesByUserId($userId) as $userRole) {
......@@ -121,7 +129,7 @@ class ACL
*
* @return bool
*/
public function userHasRole($roleId)
public function userHasRole(int $roleId)
{
return \in_array($roleId, $this->getUserRoleIds($this->user->getUserId()));
}
......@@ -157,9 +165,9 @@ class ACL
*
* @return bool
*/
public function hasPermission($resource)
public function hasPermission(string $resource)
{
if (!empty($resource) && $this->modules->controllerActionExists($resource) === true) {
if (!empty($resource) && $this->controllerActionExists->controllerActionExists($resource) === true) {
$resourceParts = \explode('/', $resource);
if ($this->modules->isActive($resourceParts[1]) === true) {
......@@ -175,7 +183,7 @@ class ACL
*
* @return bool
*/
protected function canAccessResource($resource)
protected function canAccessResource(string $resource)
{
$resourceParts = $this->convertResourcePathToArray($resource);
......@@ -198,7 +206,7 @@ class ACL
*
* @return array
*/
protected function convertResourcePathToArray($resource)
protected function convertResourcePathToArray(string $resource)
{
$resourceArray = \explode('/', $resource);
......@@ -234,7 +242,7 @@ class ACL
*
* @return bool
*/
protected function userHasPrivilege($module, $privilegeKey)
protected function userHasPrivilege(string $module, string $privilegeKey)
{
$privilegeKey = \strtolower($privilegeKey);
if (isset($this->getPrivileges()[$module][$privilegeKey])) {
......
......@@ -7,10 +7,12 @@
namespace ACP3\Core\Application;
use ACP3\Core\Environment\ApplicationMode;
use ACP3\Core\Environment\ApplicationPath;
use ACP3\Core\ErrorHandler;
use ACP3\Core\Logger\LoggerFactory;
use Psr\Log\LoggerInterface;
use Symfony\Component\Debug\ErrorHandler;
use Symfony\Component\Debug\ExceptionHandler;
abstract class AbstractBootstrap implements BootstrapInterface
{
......@@ -53,7 +55,13 @@ abstract class AbstractBootstrap implements BootstrapInterface
*/
public function setErrorHandler()
{
ErrorHandler::register($this->logger);
$debug = $this->appMode === ApplicationMode::DEVELOPMENT;
ExceptionHandler::register($debug);
$errorHandler = new ErrorHandler();
$errorHandler->setDefaultLogger($this->logger);
ErrorHandler::register($errorHandler);
}
/**
......
......@@ -7,11 +7,9 @@
namespace ACP3\Core\Application;
use ACP3\Core\Controller\AreaEnum;
use ACP3\Core\Application\Exception\MaintenanceModeActiveException;
use ACP3\Core\DependencyInjection\ServiceContainerBuilder;
use ACP3\Core\Environment\ApplicationMode;
use ACP3\Core\View;
use ACP3\Modules\ACP3\System\Installer\Schema;
use Patchwork\Utf8;
use Symfony\Component\Config\ConfigCache;
use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
......@@ -23,11 +21,6 @@ use Symfony\Component\HttpFoundation\Response;
*/
class Bootstrap extends AbstractBootstrap
{
/**
* @var array
*/
private $systemSettings = [];
/**
* {@inheritdoc}
*/
......@@ -82,6 +75,8 @@ class Bootstrap extends AbstractBootstrap
/**
* {@inheritdoc}
*
* @throws \Exception
*/
public function outputPage()
{
......@@ -89,14 +84,8 @@ class Bootstrap extends AbstractBootstrap
$redirect = $this->container->get('core.http.redirect_response');
try {
$this->systemSettings = $this->container->get('core.config')->getSettings(Schema::MODULE_NAME);
$this->setThemePaths();
$this->container->get('core.authentication')->authenticate();
if ($this->isMaintenanceModeEnabled()) {
return $this->handleMaintenanceMode();
}
$response = $this->container->get('core.application.controller_action_dispatcher')->dispatch();
} catch (\ACP3\Core\Controller\Exception\ResultNotExistsException $e) {
$response = $redirect->temporary('errors/index/not_found');
......@@ -109,104 +98,17 @@ class Bootstrap extends AbstractBootstrap
$response = $redirect->temporary('errors/index/access_forbidden');
} catch (\ACP3\Core\Controller\Exception\ControllerActionNotFoundException $e) {
$response = $redirect->temporary('errors/index/not_found');
} catch (MaintenanceModeActiveException $e) {
$response = new Response($e->getMessage(), $e->getCode());
} catch (\Exception $e) {
$this->logger->critical($e);
$response = $this->handleException($e, 'errors/index/server_error');
throw $e;
}
return $response;
}
/**
* Sets the theme paths.
*/
private function setThemePaths()
{
$path = 'designs/' . $this->systemSettings['design'] . '/';
$this->appPath
->setDesignPathWeb($this->appPath->getWebRoot() . $path)
->setDesignPathInternal($this->systemSettings['design'] . '/');
}
/**
* Checks, whether the maintenance mode is active.
*
* @return bool
*/
private function isMaintenanceModeEnabled()
{
/** @var \ACP3\Core\Http\Request $request */
$request = $this->container->get('core.http.request');
return (bool) $this->systemSettings['maintenance_mode'] === true &&
$request->getArea() !== AreaEnum::AREA_ADMIN &&
\strpos($request->getQuery(), 'users/index/login/') !== 0;
}
/**
* @return Response
*/
private function handleMaintenanceMode()
{
/** @var View $view */
$view = $this->container->get('core.view');
$view->assign([
'PAGE_TITLE' => 'ACP3',
'ROOT_DIR' => $this->appPath->getWebRoot(),
'CONTENT' => $this->systemSettings['maintenance_message'],
]);
$response = new Response($view->fetchTemplate('System/layout.maintenance.tpl'));
$response->setStatusCode(Response::HTTP_SERVICE_UNAVAILABLE);
return $response;
}
/**
* @param \Exception $exception
* @param string $route
*
* @return Response
*/
private function handleException(\Exception $exception, $route)
{
if ($this->appMode === ApplicationMode::DEVELOPMENT) {
return $this->renderApplicationException($exception);
}
/** @var \ACP3\Core\Http\RedirectResponse $redirect */
$redirect = $this->container->get('core.http.redirect_response');
return $redirect->temporary($route);
}
/**
* Renders an exception.
*
* @param \Exception $exception
*
* @return Response
*/
private function renderApplicationException(\Exception $exception)
{
/** @var View $view */
$view = $this->container->get('core.view');
$view->assign([
'PAGE_TITLE' => 'ACP3',
'ROOT_DIR' => $this->appPath->getWebRoot(),
'EXCEPTION' => $exception,
]);
$response = new Response($view->fetchTemplate('System/layout.exception.tpl'));
$response->setStatusCode(Response::HTTP_INTERNAL_SERVER_ERROR);
return $response;
}
/**
* {@inheritdoc}
*/
......
......@@ -16,7 +16,7 @@ interface BootstrapInterface extends HttpKernelInterface
/**
* Contains the current ACP3 version string.
*/
const VERSION = '4.27.3';
const VERSION = '4.28.0';
/**
* Performs some startup checks.
......
......@@ -12,7 +12,7 @@ use ACP3\Core\Application\Event\ControllerActionBeforeDispatchEvent;
use ACP3\Core\Controller\ActionInterface;
use ACP3\Core\Controller\Exception\ControllerActionNotFoundException;
use ACP3\Core\Http\RequestInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Psr\Container\ContainerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface;
......@@ -28,7 +28,7 @@ class ControllerActionDispatcher
*/
protected $request;
/**
* @var \Symfony\Component\DependencyInjection\ContainerInterface
* @var \Psr\Container\ContainerInterface
*/
protected $container;
/**
......@@ -39,7 +39,7 @@ class ControllerActionDispatcher
/**
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $eventDispatcher
* @param \ACP3\Core\Http\RequestInterface $request
* @param \Symfony\Component\DependencyInjection\ContainerInterface $container
* @param \Psr\Container\ContainerInterface $container
* @param ArgumentResolverInterface $argumentResolver
*/
public function __construct(
......@@ -112,7 +112,6 @@ class ControllerActionDispatcher
*
* @return mixed
*
* @throws \ACP3\Core\Controller\Exception\ResultNotExistsException
* @throws \ReflectionException
*/
private function executeControllerAction(ActionInterface $controller, array $arguments)
......
......@@ -22,7 +22,7 @@ class ControllerActionAfterDispatchEvent extends ControllerActionBeforeDispatchE
* @param string $serviceId
* @param Response $response
*/
public function __construct($serviceId, Response $response)
public function __construct(string $serviceId, Response $response)
{
parent::__construct($serviceId);
......
......@@ -16,7 +16,7 @@ class ControllerActionBeforeDispatchEvent extends Event
*/
private $controllerServiceId;
/**
* @var array
* @var string[]
*/
private $serviceIdParts = [];
......@@ -25,7 +25,7 @@ class ControllerActionBeforeDispatchEvent extends Event
*
* @param string $controllerServiceId
*/
public function __construct($controllerServiceId)
public function __construct(string $controllerServiceId)
{
$this->controllerServiceId = $controllerServiceId;
......@@ -46,7 +46,9 @@ class ControllerActionBeforeDispatchEvent extends Event
}
/**
* @return mixed|string
* @return string
*
* @deprecated since 4.28.0, to be removed with version 5.0.0. Use ::getArea instead
*/
public function getControllerArea()
{
......@@ -54,15 +56,41 @@ class ControllerActionBeforeDispatchEvent extends Event
}
/**
* @return mixed|string
* @return string
*/
public function getArea()
{
return isset($this->serviceIdParts[2]) ? $this->serviceIdParts[2] : '';
}
/**
* @return string
*
* @deprecated since 4.28.0, to be removed with version 5.0.0. Use ::getModule instead
*/
public function getControllerModule()
{
return isset($this->serviceIdParts[0]) ? $this->serviceIdParts[0] : '';
}
/**
* @return string
*/
public function getModule()
{
return isset($this->serviceIdParts[0]) ? $this->serviceIdParts[0] : '';
}
/**
* @return string
*/
public function getController()
{
return isset($this->serviceIdParts[3]) ? $this->serviceIdParts[3] : '';
}
/**
* @return mixed|string
* @return string
*/
public function getControllerAction()
{
......
<?php
/**
* Copyright (c) by the ACP3 Developers.
* See the LICENSE file at the top-level module directory for licensing details.
*/
namespace ACP3\Core\Application\Event\Listener;
use ACP3\Core\Application\Event\ControllerActionBeforeDispatchEvent;
use ACP3\Core\Environment\ApplicationPath;
use ACP3\Core\Environment\ThemePathInterface;
use ACP3\Core\Http\RequestInterface;
use ACP3\Core\I18n\Translator;
use ACP3\Core\View;
class AddTemplateVariablesListener
{
/**
* @var \ACP3\Core\Environment\ApplicationPath
*/
private $appPath;
/**
* @var \ACP3\Core\Http\RequestInterface
*/
private $request;
/**
* @var \ACP3\Core\View
*/
private $view;
/**
* @var \ACP3\Core\I18n\Translator
*/
private $translator;
/**
* @var \ACP3\Core\Environment\ThemePathInterface
*/
private $theme;
public function __construct(
ApplicationPath $appPath,
RequestInterface $request,
ThemePathInterface $theme,
View $view,
Translator $translator
) {
$this->appPath = $appPath;
$this->request = $request;
$this->view = $view;
$this->translator = $translator;
$this->theme = $theme;
}
public function __invoke(ControllerActionBeforeDispatchEvent $event)
{
$this->view->assign([
'PHP_SELF' => $this->appPath->getPhpSelf(),
'ROOT_DIR' => $this->appPath->getWebRoot(),
'HOST_NAME' => $this->request->getHttpHost(),
'ROOT_DIR_ABSOLUTE' => $this->request->getScheme() . '://' . $this->request->getHttpHost() . $this->appPath->getWebRoot(),
'DESIGN_PATH' => $this->theme->getDesignPathWeb(),
'DESIGN_PATH_ABSOLUTE' => $this->request->getScheme() . '://' . $this->request->getHttpHost() . $this->theme->getDesignPathWeb(),
'LANG_DIRECTION' => $this->translator->getDirection(),
'LANG' => $this->translator->getShortIsoCode(),