Commit 4fc5aaf9 authored by Tino Goratsch's avatar Tino Goratsch

first part at refactoring the request/response stack and handling

parent 649fee55
......@@ -29,13 +29,13 @@ abstract class AbstractBootstrap implements BootstrapInterface
public function __construct($appMode)
{
$this->appMode = $appMode;
$this->setAppPath($appMode);
$this->initializeApplicationPath($this->appMode);
}
/**
* @param string $appMode
*/
protected function setAppPath($appMode)
protected function initializeApplicationPath($appMode)
{
$this->appPath = new ApplicationPath($appMode);
}
......@@ -66,11 +66,7 @@ abstract class AbstractBootstrap implements BootstrapInterface
protected function databaseConfigExists()
{
$path = $this->appPath->getAppDir() . 'config.yml';
if (is_file($path) === false || filesize($path) === 0) {
echo 'The ACP3 is not correctly installed. Please navigate to the <a href="' . $this->appPath->getWebRoot() . 'installation/">installation wizard</a> and follow its instructions.';
return false;
}
return true;
return is_file($path) === true && filesize($path) !== 0;
}
}
......@@ -12,6 +12,8 @@ use Patchwork\Utf8;
use Symfony\Component\Config\ConfigCache;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/**
* Bootstraps the application
......@@ -27,13 +29,12 @@ class Bootstrap extends AbstractBootstrap
/**
* @inheritdoc
*/
public function run()
public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true)
{
if ($this->startupChecks()) {
$this->setErrorHandler();
$this->initializeClasses();
$this->outputPage();
}
$this->setErrorHandler();
$this->initializeClasses();
return $this->outputPage();
}
/**
......@@ -60,20 +61,27 @@ class Bootstrap extends AbstractBootstrap
$request->getArea() !== AreaEnum::AREA_ADMIN &&
strpos($request->getQuery(), 'users/index/login/') !== 0
) {
header('HTTP/1.0 503 Service Unavailable');
$view = $this->container->get('core.view');
$view->assign('PAGE_TITLE', 'ACP3');
$view->assign('ROOT_DIR', $this->appPath->getWebRoot());
$view->assign('CONTENT', $this->systemSettings['maintenance_message']);
$view->displayTemplate('system/maintenance.tpl');
return true;
}
return false;
}
/**
* @return Response
*/
private function displayMaintenanceMode()
{
header('HTTP/1.0 503 Service Unavailable');
$view = $this->container->get('core.view');
$view->assign('PAGE_TITLE', 'ACP3');
$view->assign('ROOT_DIR', $this->appPath->getWebRoot());
$view->assign('CONTENT', $this->systemSettings['maintenance_message']);
return new Response($view->fetchTemplate('system/maintenance.tpl'));
}
/**
* @inheritdoc
*/
......@@ -103,7 +111,7 @@ class Bootstrap extends AbstractBootstrap
$this->appPath
->setDesignPathWeb($this->appPath->getWebRoot() . $path)
->setDesignPathInternal(ACP3_ROOT_DIR . $path)
->setDesignPathAbsolute($this->container->get('core.http.request')->getDomain() . $this->appPath->getDesignPathWeb());
->setDesignPathAbsolute($this->container->get('core.http.request')->getHttpHost() . $this->appPath->getDesignPathWeb());
}
/**
......@@ -120,26 +128,28 @@ class Bootstrap extends AbstractBootstrap
$request = $this->container->get('core.http.request');
if ($this->maintenanceModeIsEnabled($request)) {
return;
return $this->displayMaintenanceMode();
}
/** @var \ACP3\Core\Http\RedirectResponse $redirect */
$redirect = $this->container->get('core.http.redirect_response');
try {
$this->container->get('core.application.controller_resolver')->dispatch();
$response = $this->container->get('core.application.controller_resolver')->dispatch();
} catch (\ACP3\Core\Controller\Exception\ResultNotExistsException $e) {
$redirect->temporary('errors/index/not_found')->send();
$response = $redirect->temporary('errors/index/not_found');
} catch (\ACP3\Core\Authentication\Exception\UnauthorizedAccessException $e) {
$redirectUri = base64_encode($request->getOriginalQuery());
$redirect->temporary('users/index/login/redirect_' . $redirectUri)->send();
$redirectUri = base64_encode($request->getPathInfo());
$response = $redirect->temporary('users/index/login/redirect_' . $redirectUri);
} catch (\ACP3\Core\ACL\Exception\AccessForbiddenException $e) {
$redirect->temporary('errors/index/access_forbidden');
$response = $redirect->temporary('errors/index/access_forbidden');
} catch (\ACP3\Core\Controller\Exception\ControllerActionNotFoundException $e) {
$this->handleException($e, $redirect, 'errors/index/not_found');
$response = $this->handleException($e, $redirect, 'errors/index/not_found');
} catch (\Exception $e) {
$this->handleException($e, $redirect, 'errors/index/server_error');
$response = $this->handleException($e, $redirect, 'errors/index/server_error');
}
return $response;
}
/**
......@@ -154,6 +164,7 @@ class Bootstrap extends AbstractBootstrap
* Renders an exception
*
* @param \Exception $exception
* @return Response
*/
private function renderApplicationException(\Exception $exception)
{
......@@ -161,21 +172,23 @@ class Bootstrap extends AbstractBootstrap
$view->assign('ROOT_DIR', $this->appPath->getWebRoot());
$view->assign('PAGE_TITLE', 'ACP3');
$view->assign('EXCEPTION', $exception);
$view->displayTemplate('system/exception.tpl');
return new Response($view->fetchTemplate('system/exception.tpl'));
}
/**
* @param \Exception $exception
* @param \Exception $exception
* @param \ACP3\Core\Http\RedirectResponse $redirect
* @param string $path
* @param string $path
* @return Response
*/
protected function handleException(\Exception $exception, RedirectResponse $redirect, $path)
{
if ($this->appMode === ApplicationMode::DEVELOPMENT) {
$this->renderApplicationException($exception);
} else {
$redirect->temporary($path)->send();
return $this->renderApplicationException($exception);
}
return $redirect->temporary($path);
}
/**
......
<?php
namespace ACP3\Core\Application;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\HttpKernelInterface;
/**
* Interface BootstrapInterface
* @package ACP3\Core\Application
*/
interface BootstrapInterface
interface BootstrapInterface extends HttpKernelInterface
{
/**
* Contains the current ACP3 version string
*/
const VERSION = '4.0-dev';
/**
* Executes the application bootstrapping process and outputs the requested page
*/
public function run();
/**
* Performs some startup checks
*/
......@@ -29,6 +27,7 @@ interface BootstrapInterface
/**
* Handle the request and output the page
* @return Response
*/
public function outputPage();
......
......@@ -9,10 +9,10 @@ namespace ACP3\Core\Application;
use ACP3\Core\Application\Event\FrontControllerDispatchEvent;
use ACP3\Core\Controller\ActionInterface;
use ACP3\Core\Controller\Exception\ResultNotExistsException;
use ACP3\Core\Exceptions;
use ACP3\Core\Http\RequestInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Response;
/**
* Class ControllerResolver
......@@ -51,6 +51,7 @@ class ControllerResolver
/**
* @param string $serviceId
* @param array $arguments
* @return Response|string
*
* @throws \ACP3\Core\Controller\Exception\ControllerActionNotFoundException
* @throws \ACP3\Core\Controller\Exception\ResultNotExistsException
......@@ -70,17 +71,19 @@ class ControllerResolver
/** @var \ACP3\Core\Controller\ActionInterface $controller */
$controller = $this->container->get($serviceId);
$controller->preDispatch();
$controller->display($this->executeControllerAction($controller, $arguments));
$response = $controller->display($this->executeControllerAction($controller, $arguments));
$this->eventDispatcher->dispatch(
'core.application.controller_resolver.after_dispatch',
new FrontControllerDispatchEvent($serviceId)
);
return;
return $response;
}
throw new \ACP3\Core\Controller\Exception\ControllerActionNotFoundException('Service-Id ' . $serviceId . ' was not found!');
throw new \ACP3\Core\Controller\Exception\ControllerActionNotFoundException(
'Service-Id ' . $serviceId . ' was not found!'
);
}
/**
......
......@@ -31,15 +31,14 @@ trait DisplayActionTrait
* Outputs the requested module controller action
*
* @param mixed $actionResult
* @return Response
*/
public function display($actionResult)
{
if ($actionResult instanceof Response) {
$actionResult->send();
return;
return $actionResult;
} elseif (is_string($actionResult)) {
echo $actionResult;
return;
return $this->getResponse()->setContent($actionResult);
} elseif (is_array($actionResult)) {
$this->getView()->assign($actionResult);
}
......@@ -59,7 +58,7 @@ trait DisplayActionTrait
$this->getResponse()->setContent($this->getView()->fetchTemplate($this->getTemplate()));
}
$this->getResponse()->send();
return $this->getResponse();
}
/**
......
......@@ -82,14 +82,14 @@ abstract class FrontendAction extends Core\Controller\WidgetAction
'PHP_SELF' => $this->appPath->getPhpSelf(),
'REQUEST_URI' => $this->request->getServer()->get('REQUEST_URI'),
'ROOT_DIR' => $this->appPath->getWebRoot(),
'HOST_NAME' => $this->request->getDomain(),
'ROOT_DIR_ABSOLUTE' => $this->request->getDomain() . $this->appPath->getWebRoot(),
'HOST_NAME' => $this->request->getHttpHost(),
'ROOT_DIR_ABSOLUTE' => $this->request->getHttpHost() . $this->appPath->getWebRoot(),
'DESIGN_PATH' => $this->appPath->getDesignPathWeb(),
'DESIGN_PATH_ABSOLUTE' => $this->appPath->getDesignPathAbsolute(),
'UA_IS_MOBILE' => $this->request->getUserAgent()->isMobileBrowser(),
'IN_ADM' => $this->request->getArea() === AreaEnum::AREA_ADMIN,
'IS_HOMEPAGE' => $this->request->isHomepage(),
'IS_AJAX' => $this->request->isAjax(),
'IS_AJAX' => $this->request->isXmlHttpRequest(),
'LANG_DIRECTION' => $this->translator->getDirection(),
'LANG' => $this->translator->getShortIsoCode(),
]);
......@@ -108,7 +108,7 @@ abstract class FrontendAction extends Core\Controller\WidgetAction
protected function addCustomTemplateVarsBeforeOutput()
{
$this->view->assign('BREADCRUMB', $this->breadcrumb->getBreadcrumb());
$this->view->assign('LAYOUT', $this->request->isAjax() ? 'system/ajax.tpl' : $this->getLayout());
$this->view->assign('LAYOUT', $this->request->isXmlHttpRequest() ? 'system/ajax.tpl' : $this->getLayout());
$this->eventDispatcher->dispatch(
'core.controller.custom_template_variable',
......
......@@ -143,8 +143,10 @@ abstract class WidgetAction implements ActionInterface
public function display($actionResult)
{
if ($this->getNoOutput() === false && $this->getTemplate() !== '') {
$this->view->displayTemplate($this->getTemplate());
return $this->view->fetchTemplate($this->getTemplate());
}
return '';
}
/**
......
......@@ -96,7 +96,7 @@ class Alerts
*/
public function errorBox($errors)
{
$this->view->assign('CONTENT_ONLY', $this->request->isAjax() === true);
$this->view->assign('CONTENT_ONLY', $this->request->isXmlHttpRequest() === true);
return $this->view->fetchTemplate($this->errorBoxContent($errors));
}
......
......@@ -70,7 +70,7 @@ abstract class AbstractRequest implements RequestInterface
*
* @return string
*/
public function getProtocol()
public function getScheme()
{
return $this->protocol;
}
......@@ -80,7 +80,7 @@ abstract class AbstractRequest implements RequestInterface
*
* @return string
*/
public function getHostname()
public function getHost()
{
return $this->hostname;
}
......@@ -90,7 +90,7 @@ abstract class AbstractRequest implements RequestInterface
*
* @return string
*/
public function getDomain()
public function getHttpHost()
{
return $this->protocol . $this->hostname;
}
......@@ -98,7 +98,7 @@ abstract class AbstractRequest implements RequestInterface
/**
* @return bool
*/
public function isAjax()
public function isXmlHttpRequest()
{
if ($this->isAjax === null) {
$this->isAjax = !empty($this->server->get('HTTP_X_REQUESTED_WITH')) && strtolower($this->server->get('HTTP_X_REQUESTED_WITH', '')) == 'xmlhttprequest';
......
......@@ -47,7 +47,7 @@ class RedirectResponse
*/
public function toNewPage($url)
{
if ($this->request->isAjax() === true) {
if ($this->request->isXmlHttpRequest() === true) {
return $this->createAjaxRedirectResponse($url);
}
......@@ -78,7 +78,7 @@ class RedirectResponse
{
$path = $this->router->route($path, true);
if ($this->request->isAjax() === true) {
if ($this->request->isXmlHttpRequest() === true) {
return $this->createAjaxRedirectResponse($path);
}
......
......@@ -71,7 +71,7 @@ class Request extends AbstractRequest
/**
* @inheritdoc
*/
public function getOriginalQuery()
public function getPathInfo()
{
return $this->originalQuery;
}
......
......@@ -12,21 +12,21 @@ interface RequestInterface
*
* @return string
*/
public function getProtocol();
public function getScheme();
/**
* Returns the hostname of the current request
*
* @return string
*/
public function getHostname();
public function getHost();
/**
* Returns the protocol with the hostname
*
* @return string
*/
public function getDomain();
public function getHttpHost();
/**
* @return string
......@@ -38,7 +38,7 @@ interface RequestInterface
*
* @return string
*/
public function getOriginalQuery();
public function getPathInfo();
/**
* @return string
......@@ -110,7 +110,7 @@ interface RequestInterface
/**
* @return bool
*/
public function isAjax();
public function isXmlHttpRequest();
/**
* Returns the parameter bag of the $_COOKIE superglobal
......
......@@ -95,7 +95,7 @@ class Action
private function renderErrorBoxOnFailedFormValidation(\Exception $exception)
{
$errors = $this->alerts->errorBox($exception->getMessage());
if ($this->request->isAjax()) {
if ($this->request->isXmlHttpRequest()) {
return new JsonResponse(['success' => false, 'content' => $errors]);
}
......
......@@ -134,8 +134,8 @@ class Router implements RouterInterface
{
$prefix = '';
if ($isAbsolute === true || $forceSecure === true) {
$prefix .= ($forceSecure === true) ? 'https://' : $this->request->getProtocol();
$prefix .= $this->request->getHostname();
$prefix .= ($forceSecure === true) ? 'https://' : $this->request->getScheme();
$prefix .= $this->request->getHost();
}
$prefix .= $this->useModRewrite($path) ? $this->appPath->getWebRoot() : $this->appPath->getPhpSelf() . '/';
......
......@@ -66,7 +66,7 @@ class RedirectResponseTest extends \PHPUnit_Framework_TestCase
private function setUpRequestMockExpectations($isAjax)
{
$this->requestMock->expects($this->once())
->method('isAjax')
->method('isXmlHttpRequest')
->willReturn($isAjax);
}
......
......@@ -51,7 +51,7 @@ class RouterTest extends \PHPUnit_Framework_TestCase
{
$this->requestMock = $this->getMockBuilder(Request::class)
->disableOriginalConstructor()
->setMethods(['getProtocol', 'getHostname'])
->setMethods(['getScheme', 'getHost'])
->getMock();
$this->appPathMock = $this->getMockBuilder(ApplicationPath::class)
->disableOriginalConstructor()
......@@ -78,10 +78,10 @@ class RouterTest extends \PHPUnit_Framework_TestCase
protected function setUpRequestMockExpectations()
{
$this->requestMock->expects($this->any())
->method('getProtocol')
->method('getScheme')
->willReturn('http://');
$this->requestMock->expects($this->any())
->method('getHostname')
->method('getHost')
->willReturn('example.com');
}
......
......@@ -64,7 +64,7 @@ class MoveToBottom extends AbstractMoveElementFilter
protected function addElementFromMinifier()
{
$minifyJs = '';
if (!$this->request->isAjax()) {
if (!$this->request->isXmlHttpRequest()) {
$minifyJs = '<script type="text/javascript" src="' . $this->minifier->getURI() . '"></script>' . "\n";
}
return $minifyJs;
......
......@@ -22,7 +22,7 @@ class LoadModule extends AbstractFunction
/**
* LoadModule constructor.
*
* @param \ACP3\Core\ACL $acl
* @param \ACP3\Core\ACL $acl
* @param \ACP3\Core\Application\ControllerResolver $frontController
*/
public function __construct(ACL $acl, ControllerResolver $frontController)
......@@ -49,9 +49,10 @@ class LoadModule extends AbstractFunction
$path = $pathArray[0] . '/' . $pathArray[1] . '/' . $pathArray[2] . '/' . $pathArray[3];
if ($this->acl->hasPermission($path) === true) {
$serviceId = strtolower($pathArray[1] . '.controller.' . $pathArray[0] . '.' . $pathArray[2] . '.' . $pathArray[3]);
$this->frontController->dispatch($serviceId, isset($params['args']) ? $params['args'] : []);
return $this->frontController->dispatch($serviceId, isset($params['args']) ? $params['args'] : []);
}
return '';
}
/**
......
......@@ -149,7 +149,7 @@ class Subscribe
$subject = $this->translator->t('newsletter', 'subscribe_mail_subject', ['%title%' => $seoSettings['title']]);
$body = $this->translator->t('newsletter', 'subscribe_mail_body',
['{host}' => $this->request->getHostname()]) . "\n\n";
['{host}' => $this->request->getHost()]) . "\n\n";
$from = [
'email' => $settings['mail'],
......
......@@ -86,6 +86,6 @@ class OnControllerResolverBeforeDispatchListener
private function uriAliasExists()
{
return $this->aliases->uriAliasExists($this->request->getQuery()) === true
&& $this->request->getOriginalQuery() !== $this->aliases->getUriAlias($this->request->getQuery()) . '/';
&& $this->request->getPathInfo() !== $this->aliases->getUriAlias($this->request->getQuery()) . '/';
}
}
......@@ -148,7 +148,7 @@ class ForgotPwd extends Core\Controller\FrontendAction
*/
protected function sendPasswordChangeEmail(array $user, $newPassword)
{
$host = $this->request->getHostname();
$host = $this->request->getHost();
$seoSettings = $this->config->getSettings('seo');
$subject = $this->translator->t(
......
......@@ -129,7 +129,7 @@ class Register extends Core\Controller\FrontendAction
'register_mail_subject',
[
'{title}' => $seoSettings['title'],
'{host}' => $this->request->getHostname(),
'{host}' => $this->request->getHost(),
]);
$body = $this->translator->t(
'users',
......@@ -139,7 +139,7 @@ class Register extends Core\Controller\FrontendAction
'{mail}' => $formData['mail'],
'{password}' => $formData['pwd'],
'{title}' => $seoSettings['title'],
'{host}' => $this->request->getHostname()
'{host}' => $this->request->getHost()
]
);
$mailIsSent = $this->sendEmail->execute('', $formData['mail'], $settings['mail'], $subject, $body);
......
......@@ -4,8 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "bcc94b4543cc8bbcd2a38bf23d84eb81",
"content-hash": "a5c2dbc374dfc972d898243aa9aeb669",
"hash": "be56b0d37befbc3b80c3a3f85c7899ab",
"content-hash": "46b1bbaca2c3d3cb51f75d5635a9727e",
"packages": [
{
"name": "ckeditor/ckeditor",
......@@ -1084,6 +1084,63 @@
"homepage": "https://symfony.com",
"time": "2016-03-04 07:56:56"
},
{
"name": "symfony/debug",
"version": "v3.1.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/debug.git",
"reference": "c9ed5a44dbc783a352f06b20440863c7463de907"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/debug/zipball/c9ed5a44dbc783a352f06b20440863c7463de907",
"reference": "c9ed5a44dbc783a352f06b20440863c7463de907",
"shasum": ""
},
"require": {
"php": ">=5.5.9",
"psr/log": "~1.0"
},
"conflict": {
"symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2"
},
"require-dev": {
"symfony/class-loader": "~2.8|~3.0",
"symfony/http-kernel": "~2.8|~3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.1-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Debug\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Debug Component",
"homepage": "https://symfony.com",
"time": "2016-05-24 10:06:56"
},
{
"name": "symfony/dependency-injection",
"version": "v3.1.0",
......@@ -1306,6 +1363,88 @@
"homepage": "https://symfony.com",
"time": "2016-05-13 18:06:41"
},
{
"name": "symfony/http-kernel",
"version": "v3.1.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-kernel.git",
"reference": "01586fd019f01c98ea4d34d26b493de06e619d20"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/01586fd019f01c98ea4d34d26b493de06e619d20",
"reference": "01586fd019f01c98ea4d34d26b493de06e619d20",
"shasum": ""
},
"require": {
"php": ">=5.5.9",
"psr/log": "~1.0",
"symfony/debug": "~2.8|~3.0",
"symfony/event-dispatcher": "~2.8|~3.0",
"symfony/http-foundation": "~2.8|~3.0"
},
"conflict": {
"symfony/config": "<2.8"
},
"require-dev": {
"symfony/browser-kit": "~2.8|~3.0",
"symfony/class-loader": "~2.8|~3.0",
"symfony/config": "~2.8|~3.0",
"symfony/console": "~2.8|~3.0",
"symfony/css-selector": "~2.8|~3.0",
"symfony/dependency-injection": "~2.8|~3.0",
"symfony/dom-crawler": "~2.8|~3.0",
"symfony/expression-language": "~2.8|~3.0",
"symfony/finder": "~2.8|~3.0",
"symfony/process": "~2.8|~3.0",
"symfony/routing": "~2.8|~3.0",
"symfony/stopwatch": "~2.8|~3.0",
"symfony/templating": "~2.8|~3.0",
"symfony/translation": "~2.8|~3.0",
"symfony/var-dumper": "~2.8|~3.0"
},
"suggest": {
"symfony/browser-kit": "",
"symfony/class-loader": "",
"symfony/config": "",
"symfony/console": "",
"symfony/dependency-injection": "",
"symfony/finder": "",
"symfony/var-dumper": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.1-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\HttpKernel\\": ""
},
"exclude-from-classmap": [
"/Tests/"