Commit baf2bbaa authored by alexpott's avatar alexpott

Issue #2288665 by znerol: Remove _maintenance request attribute and replace it...

Issue #2288665 by znerol: Remove _maintenance request attribute and replace it with a maintenance mode service.
parent 013a6e8c
......@@ -604,9 +604,12 @@ services:
tags:
- { name: access_check, applies_to: _csrf_token }
arguments: ['@csrf_token']
maintenance_mode:
class: Drupal\Core\Site\MaintenanceMode
arguments: ['@state', '@current_user']
maintenance_mode_subscriber:
class: Drupal\Core\EventSubscriber\MaintenanceModeSubscriber
arguments: ['@state', '@config.factory', '@string_translation', '@url_generator', '@current_user']
arguments: ['@maintenance_mode', '@config.factory', '@string_translation', '@url_generator', '@current_user']
tags:
- { name: event_subscriber }
path_subscriber:
......
......@@ -11,14 +11,13 @@
use Drupal\Component\Utility\Xss;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Page\DefaultHtmlPageRenderer;
use Drupal\Core\Routing\RouteMatch;
use Drupal\Core\Routing\UrlGeneratorInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\State\StateInterface;
use Drupal\Core\Site\MaintenanceModeInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
......@@ -31,18 +30,18 @@ class MaintenanceModeSubscriber implements EventSubscriberInterface {
use StringTranslationTrait;
/**
* The current account.
* The maintenance mode.
*
* @var \Drupal\Core\Session\AccountInterface
* @var \Drupal\Core\Site\MaintenanceModeInterface
*/
protected $account;
protected $maintenanceMode;
/**
* The state.
* The current account.
*
* @var \Drupal\Core\State\StateInterface
* @var \Drupal\Core\Session\AccountInterface
*/
protected $state;
protected $account;
/**
* The config factory.
......@@ -58,31 +57,11 @@ class MaintenanceModeSubscriber implements EventSubscriberInterface {
*/
protected $urlGenerator;
/**
* @defgroup menu_status_codes Menu status codes
* @{
* Status codes to be used to check the maintenance code.
*/
/**
* Internal menu status code -- Menu item inaccessible because site is offline.
*/
const SITE_OFFLINE = 4;
/**
* Internal menu status code -- Everything is working fine.
*/
const SITE_ONLINE = 5;
/**
* @} End of "defgroup menu_status_codes".
*/
/**
* Constructs a new MaintenanceModeSubscriber.
*
* @param \Drupal\Core\State\StateInterface $state
* The state.
* @param \Drupal\Core\Site\MaintenanceModeInterface $maintenance_mode
* The maintenance mode.
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The config factory.
* @param \Drupal\Core\StringTranslation\TranslationInterface $translation
......@@ -92,27 +71,14 @@ class MaintenanceModeSubscriber implements EventSubscriberInterface {
* @param \Drupal\Core\Session\AccountInterface $account
* The current user.
*/
public function __construct(StateInterface $state, ConfigFactoryInterface $config_factory, TranslationInterface $translation, UrlGeneratorInterface $url_generator, AccountInterface $account) {
$this->state = $state;
public function __construct(MaintenanceModeInterface $maintenance_mode, ConfigFactoryInterface $config_factory, TranslationInterface $translation, UrlGeneratorInterface $url_generator, AccountInterface $account) {
$this->maintenanceMode = $maintenance_mode;
$this->config = $config_factory;
$this->stringTranslation = $translation;
$this->urlGenerator = $url_generator;
$this->account = $account;
}
/**
* Determine whether the page is configured to be offline.
*
* @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
* The Event to process.
*/
public function onKernelRequestDetermineSiteStatus(GetResponseEvent $event) {
// Check if the site is offline.
$request = $event->getRequest();
$is_offline = $this->isSiteInMaintenance($request) ? static::SITE_OFFLINE : static::SITE_ONLINE;
$request->attributes->set('_maintenance', $is_offline);
}
/**
* Returns the site maintenance page if the site is offline.
*
......@@ -120,51 +86,36 @@ public function onKernelRequestDetermineSiteStatus(GetResponseEvent $event) {
* The event to process.
*/
public function onKernelRequestMaintenance(GetResponseEvent $event) {
$request = $event->getRequest();
$response = $event->getResponse();
// Continue if the site is online and the response is not a redirection.
if ($request->attributes->get('_maintenance') != static::SITE_ONLINE && !($response instanceof RedirectResponse)) {
// Deliver the 503 page.
drupal_maintenance_theme();
$content = Xss::filterAdmin(String::format($this->config->get('system.maintenance')->get('message'), array(
'@site' => $this->config->get('system.site')->get('name'),
)));
$content = DefaultHtmlPageRenderer::renderPage($content, t('Site under maintenance'));
$response = new Response('Service unavailable', 503);
$response->setContent($content);
$event->setResponse($response);
}
$can_access_maintenance = $this->account->hasPermission('access site in maintenance mode');
$is_maintenance = $this->state->get('system.maintenance_mode');
// Ensure that the maintenance mode message is displayed only once
// (allowing for page redirects) and specifically suppress its display on
// the maintenance mode settings page.
$is_maintenance_route = $request->attributes->get(RouteObjectInterface::ROUTE_NAME) == 'system.site_maintenance_mode';
if ($is_maintenance && $can_access_maintenance && !$is_maintenance_route) {
if ($this->account->hasPermission('administer site configuration')) {
$this->drupalSetMessage($this->t('Operating in maintenance mode. <a href="@url">Go online.</a>', array('@url' => $this->urlGenerator->generate('system.site_maintenance_mode'))), 'status', FALSE);
$route_match = RouteMatch::createFromRequest($event->getRequest());
if ($this->maintenanceMode->applies($route_match)) {
if (!$this->maintenanceMode->exempt($this->account)) {
// Deliver the 503 page if the site is in maintenance mode and the
// logged in user is not allowed to bypass it.
drupal_maintenance_theme();
$content = Xss::filterAdmin(String::format($this->config->get('system.maintenance')->get('message'), array(
'@site' => $this->config->get('system.site')->get('name'),
)));
// @todo Break the dependency on DefaultHtmlPageRenderer, see:
// https://www.drupal.org/node/2295609
$content = DefaultHtmlPageRenderer::renderPage($content, $this->t('Site under maintenance'));
$response = new Response('Service unavailable', 503);
$response->setContent($content);
$event->setResponse($response);
}
else {
$this->drupalSetMessage($this->t('Operating in maintenance mode.'), 'status', FALSE);
}
}
}
/**
* Checks whether the site is in maintenance mode.
*
* @return bool
* FALSE if the site is not in maintenance mode
*/
protected function isSiteInMaintenance() {
// Check if site is in maintenance mode.
if ($this->state->get('system.maintenance_mode')) {
if (!$this->account->hasPermission('access site in maintenance mode')) {
return TRUE;
// Display a message if the logged in user has access to the site in
// maintenance mode. However, suppress it on the maintenance mode
// settings page.
if ($route_match->getRouteName() != 'system.site_maintenance_mode') {
if ($this->account->hasPermission('administer site configuration')) {
$this->drupalSetMessage($this->t('Operating in maintenance mode. <a href="@url">Go online.</a>', array('@url' => $this->urlGenerator->generate('system.site_maintenance_mode'))), 'status', FALSE);
}
else {
$this->drupalSetMessage($this->t('Operating in maintenance mode.'), 'status', FALSE);
}
}
}
}
return FALSE;
}
/**
......@@ -177,10 +128,7 @@ protected function drupalSetMessage($message = NULL, $type = 'status', $repeat =
/**
* {@inheritdoc}
*/
static function getSubscribedEvents() {
// In order to change the maintenance status an event subscriber with a
// priority between 30 and 40 should be added.
$events[KernelEvents::REQUEST][] = array('onKernelRequestDetermineSiteStatus', 40);
public static function getSubscribedEvents() {
$events[KernelEvents::REQUEST][] = array('onKernelRequestMaintenance', 30);
return $events;
}
......
<?php
/**
* @file
* Contains \Drupal\Core\Site\MaintenanceMode.
*/
namespace Drupal\Core\Site;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\State\StateInterface;
/**
* Provides the default implementation of the maintenance mode service.
*/
class MaintenanceMode implements MaintenanceModeInterface {
/**
* The state.
*
* @var \Drupal\Core\State\StateInterface
*/
protected $state;
/**
* Constructs a new maintenance mode service.
*
* @param \Drupal\Core\State\StateInterface $state
* The state.
*/
public function __construct(StateInterface $state) {
$this->state = $state;
}
/**
* {@inheritdoc}
*/
public function applies(RouteMatchInterface $route_match) {
if (!$this->state->get('system.maintenance_mode')) {
return FALSE;
}
if ($route = $route_match->getRouteObject()) {
if ($route->getOption('_maintenance_access')) {
return FALSE;
}
}
return TRUE;
}
/**
* {@inheritdoc}
*/
public function exempt(AccountInterface $account) {
return $account->hasPermission('access site in maintenance mode');
}
}
<?php
/**
* @file
* Contains \Drupal\Core\Site\MaintenanceModeInterface.
*/
namespace Drupal\Core\Site;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Session\AccountInterface;
/**
* Defines the interface for the maintenance mode service.
*/
interface MaintenanceModeInterface {
/**
* Returns whether the site is in maintenance mode.
*
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The current route match.
*
* @return bool
* TRUE if the site is in maintenance mode.
*/
public function applies(RouteMatchInterface $route_match);
/**
* Determines whether a user has access to the site in maintenance mode.
*
* @param \Drupal\Core\Session\AccountInterface $account
* The logged in user.
*
* @return bool
* TRUE if the user should be exempted from maintenance mode.
*/
public function exempt(AccountInterface $account);
}
......@@ -20,6 +20,8 @@ menu_test.login_callback:
_content: '\Drupal\menu_test\TestControllers::testLogin'
requirements:
_access: 'TRUE'
options:
_maintenance_access: TRUE
menu_test.callback_description:
path: '/menu_callback_description'
......
services:
menu_test_maintenance_mode_subscriber:
class: Drupal\menu_test\EventSubscriber\MaintenanceModeSubscriber
tags:
- { name: event_subscriber }
theme.negotiator.test_theme:
class: Drupal\menu_test\Theme\TestThemeNegotiator
tags:
......
<?php
/**
* @file
* Contains \Drupal\menu_test\EventSubscriber\MaintenanceModeSubscriber.
*/
namespace Drupal\menu_test\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Drupal\Core\EventSubscriber\MaintenanceModeSubscriber as CoreMaintenanceModeSubscriber;
/**
* Maintenance mode subscriber to set site online on a test.
*/
class MaintenanceModeSubscriber implements EventSubscriberInterface {
/**
* Set the page online if called from a certain path.
*
* @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
* The event to process.
*/
public function onKernelRequestMaintenance(GetResponseEvent $event) {
$request = $event->getRequest();
// Allow access to menu_login_callback even if in maintenance mode.
if ($request->attributes->get('_maintenance') == CoreMaintenanceModeSubscriber::SITE_OFFLINE && $request->attributes->get('_system_path') == 'menu_login_callback') {
$request->attributes->set('_maintenance', CoreMaintenanceModeSubscriber::SITE_ONLINE);
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
$events[KernelEvents::REQUEST][] = array('onKernelRequestMaintenance', 35);
return $events;
}
}
......@@ -7,17 +7,46 @@
namespace Drupal\user\EventSubscriber;
use Drupal\Core\Routing\RouteMatch;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Site\MaintenanceModeInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Drupal\Core\EventSubscriber\MaintenanceModeSubscriber as CoreMaintenanceModeSubscriber;
/**
* Maintenance mode subscriber to logout users.
*/
class MaintenanceModeSubscriber implements EventSubscriberInterface {
/**
* The maintenance mode.
*
* @var \Drupal\Core\Site\MaintenanceMode
*/
protected $maintenanceMode;
/**
* The current account.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $account;
/**
* Constructs a new MaintenanceModeSubscriber.
*
* @param \Drupal\Core\Site\MaintenanceModeInterface $maintenance_mode
* The maintenance mode.
* @param \Drupal\Core\Session\AccountInterface $account
* The current user.
*/
public function __construct(MaintenanceModeInterface $maintenance_mode, AccountInterface $account) {
$this->maintenanceMode = $maintenance_mode;
$this->account = $account;
}
/**
* Determine whether the page is configured to be offline.
*
......@@ -25,42 +54,25 @@ class MaintenanceModeSubscriber implements EventSubscriberInterface {
* The event to process.
*/
public function onKernelRequestMaintenance(GetResponseEvent $event) {
$user = \Drupal::currentUser();
$request = $event->getRequest();
$site_status = $request->attributes->get('_maintenance');
// @todo Remove dependency on the internal _system_path attribute:
// https://www.drupal.org/node/2288911.
$route_match = RouteMatch::createFromRequest($request);
$path = $request->attributes->get('_system_path');
if ($site_status == CoreMaintenanceModeSubscriber::SITE_OFFLINE) {
if ($this->maintenanceMode->applies($route_match)) {
// If the site is offline, log out unprivileged users.
if ($user->isAuthenticated() && !$user->hasPermission('access site in maintenance mode')) {
if ($this->account->isAuthenticated() && !$this->maintenanceMode->exempt($this->account)) {
user_logout();
// Redirect to homepage.
$event->setResponse(new RedirectResponse(url('<front>', array('absolute' => TRUE))));
return;
}
if ($user->isAnonymous()) {
switch ($path) {
case 'user':
// Forward anonymous user to login page.
$event->setResponse(new RedirectResponse(url('user/login', array('absolute' => TRUE))));
return;
case 'user/login':
case 'user/password':
// Disable offline mode.
$request->attributes->set('_maintenance', CoreMaintenanceModeSubscriber::SITE_ONLINE);
break;
default:
if (strpos($path, 'user/reset/') === 0) {
// Disable offline mode.
$request->attributes->set('_maintenance', CoreMaintenanceModeSubscriber::SITE_ONLINE);
}
break;
}
if ($this->account->isAnonymous() && $path == 'user') {
// Forward anonymous user to login page.
$event->setResponse(new RedirectResponse(url('user/login', array('absolute' => TRUE))));
return;
}
}
if ($user->isAuthenticated()) {
if ($this->account->isAuthenticated()) {
if ($path == 'user/login') {
// If user is logged in, redirect to 'user' instead of giving 403.
$event->setResponse(new RedirectResponse(url('user', array('absolute' => TRUE))));
......@@ -68,7 +80,7 @@ public function onKernelRequestMaintenance(GetResponseEvent $event) {
}
if ($path == 'user/register') {
// Authenticated user should be redirected to user edit page.
$event->setResponse(new RedirectResponse(url('user/' . $user->id() . '/edit', array('absolute' => TRUE))));
$event->setResponse(new RedirectResponse(url('user/' . $this->account->id() . '/edit', array('absolute' => TRUE))));
return;
}
}
......
......@@ -122,6 +122,8 @@ user.pass:
_title: 'Request new password'
requirements:
_access: 'TRUE'
options:
_maintenance_access: TRUE
user.page:
path: '/user'
......@@ -146,6 +148,8 @@ user.login:
_title: 'Log in'
requirements:
_access: 'TRUE'
options:
_maintenance_access: TRUE
user.edit:
path: '/user/{user}/edit'
......@@ -185,3 +189,5 @@ user.reset:
operation: NULL
requirements:
_access: 'TRUE'
options:
_maintenance_access: TRUE
......@@ -38,6 +38,7 @@ services:
arguments: ['@database', '@config.factory', '@entity.manager', '@entity.query']
user_maintenance_mode_subscriber:
class: Drupal\user\EventSubscriber\MaintenanceModeSubscriber
arguments: ['@maintenance_mode', '@current_user']
tags:
- { name: event_subscriber }
theme.negotiator.admin_theme:
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment