diff --git a/core/core.services.yml b/core/core.services.yml
index f30cee7aaeb96f13d60ec118ceff7aa3df49b422..9b07193fee160ba9d437655ea5438f0147329b12 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -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:
diff --git a/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php
index 1b665f333432322bb68576a54c5a2d6afac04622..8556f9b7923401a704e104761bdb4f2333f9e94d 100644
--- a/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php
@@ -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. Go online.', 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. Go online.', 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;
}
diff --git a/core/lib/Drupal/Core/Site/MaintenanceMode.php b/core/lib/Drupal/Core/Site/MaintenanceMode.php
new file mode 100644
index 0000000000000000000000000000000000000000..cac817c773f925e4b384d468348bc9e03eec13f7
--- /dev/null
+++ b/core/lib/Drupal/Core/Site/MaintenanceMode.php
@@ -0,0 +1,60 @@
+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');
+ }
+
+}
diff --git a/core/lib/Drupal/Core/Site/MaintenanceModeInterface.php b/core/lib/Drupal/Core/Site/MaintenanceModeInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..45c4457403cd6161fa066e3a7887578b0daa107c
--- /dev/null
+++ b/core/lib/Drupal/Core/Site/MaintenanceModeInterface.php
@@ -0,0 +1,40 @@
+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;
- }
-
-}
diff --git a/core/modules/user/src/EventSubscriber/MaintenanceModeSubscriber.php b/core/modules/user/src/EventSubscriber/MaintenanceModeSubscriber.php
index 4d80474babe07a79c448d0cec3fd301822219ffb..f6aee6925652bfb1de87bfc15af2a4ef10a2f530 100644
--- a/core/modules/user/src/EventSubscriber/MaintenanceModeSubscriber.php
+++ b/core/modules/user/src/EventSubscriber/MaintenanceModeSubscriber.php
@@ -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('', 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;
}
}
diff --git a/core/modules/user/user.routing.yml b/core/modules/user/user.routing.yml
index 55c57d1ffb34f3b4a3601fc873f301dd9097bfdd..0a7b8420f69d924a679817a6c492fc384e27a084 100644
--- a/core/modules/user/user.routing.yml
+++ b/core/modules/user/user.routing.yml
@@ -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
diff --git a/core/modules/user/user.services.yml b/core/modules/user/user.services.yml
index 1e8f9b3e2049a190c657cb0141383bb01571dc7d..a408fcf977827735b3c09519dc43d55a3b13ed16 100644
--- a/core/modules/user/user.services.yml
+++ b/core/modules/user/user.services.yml
@@ -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: