maintenance-1.0.0-beta1/maintenance.module
maintenance.module
<?php
/**
* @file
* Module file for maintenance.
*/
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Url;
use Drupal\node\NodeInterface;
/**
* Implements hook_help().
*/
function maintenance_help($route_name, RouteMatchInterface $route_match) {
$output = '';
switch ($route_name) {
case 'help.page.maintenance':
// Title and introduction.
$output .= '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('The Maintenance module enhances Drupal’s core Maintenance Mode with advanced features, providing site administrators with full control over how maintenance mode is displayed, scheduled, and accessed.') . '</p>';
// Main configuration.
$output .= '<h4>' . t('Main Configuration') . '</h4>';
$output .= '<p>' . t('All module settings are located at <a href=":maintenance">/admin/config/development/maintenance</a> and allow for extensive customization:', [
':maintenance' => Url::fromRoute('system.site_maintenance_mode')->toString(),
]) . '</p>';
// Maintenance content display.
$output .= '<h5>' . t('Maintenance Content Display') . '</h5>';
$output .= '<p>' . t('Select the type of content to display on the maintenance page:') . '</p>';
$output .= '<ul>';
$output .= '<li>' . t('<strong>Plain text message:</strong> Enter a simple text message.') . '</li>';
$output .= '<li>' . t('<strong>HTML formatted message:</strong> Use the WYSIWYG editor to create rich content.') . '</li>';
$output .= '<li>' . t('<strong>Display a node:</strong> Show an existing node as the maintenance page.') . '</li>';
$output .= '</ul>';
// Scheduling system.
$output .= '<h5>' . t('Scheduling System') . '</h5>';
$output .= '<p>' . t('Enable the <strong>Enable scheduling</strong> option to automatically start and stop maintenance mode at specified times. Additionally, you can:') . '</p>';
$output .= '<ul>';
$output .= '<li>' . t('Display a warning message before maintenance begins (e.g., 30 minutes prior).') . '</li>';
$output .= '<li>' . t('Automatically disable maintenance mode after the specified end time.') . '</li>';
$output .= '</ul>';
// User redirection.
$output .= '<h5>' . t('User Redirection') . '</h5>';
$output .= '<p>' . t('Enable <strong>Enable URL redirect</strong> to redirect visitors to a custom URL (internal or external). You can also:') . '</p>';
$output .= '<ul>';
$output .= '<li>' . t('Set a delay in seconds before redirection (0 for immediate redirect).') . '</li>';
$output .= '</ul>';
// Reload and refresh options.
$output .= '<h5>' . t('Reload and Refresh Options') . '</h5>';
$output .= '<p>' . t('With <strong>Enable reload feature</strong>, you can:') . '</p>';
$output .= '<ul>';
$output .= '<li>' . t('Add a "Reload" button for manual page reload.') . '</li>';
$output .= '<li>' . t('Auto-reload the page every 15 seconds.') . '</li>';
$output .= '<li>' . t('Or choose not to display any reload option.') . '</li>';
$output .= '</ul>';
// Custom HTTP status code.
$output .= '<h5>' . t('Custom HTTP Status Code') . '</h5>';
$output .= '<p>' . t('Enable <strong>Override status code</strong> to change the default HTTP response status code (e.g., 200 for OK, 403 for Forbidden, or 503 for Service Unavailable).') . '</p>';
// Visibility conditions.
$output .= '<h5>' . t('Visibility Conditions') . '</h5>';
$output .= '<p>' . t('Control who sees the maintenance page:') . '</p>';
$output .= '<ul>';
$output .= '<li>' . t('<strong>IPs:</strong> Restrict access to specific IPs or target only those IPs (CIDR notation supported).') . '</li>';
$output .= '<li>' . t('<strong>Routes:</strong> Limit maintenance mode to specific routes (e.g., <code>/user/*</code> or <code><front></code>).') . '</li>';
$output .= '<li>' . t('<strong>Query strings:</strong> Allow visitors to bypass maintenance mode by adding a specific query parameter (e.g., <code>?access=preview</code>).') . '</li>';
$output .= '</ul>';
// Custom maintenance themes.
$output .= '<h5>' . t('Custom Maintenance Themes') . '</h5>';
$output .= '<p>' . t('Choose from various templates (e.g., <code>clean</code> or <code>particles</code>) and customize the display of the site logo, name, and slogan.') . '</p>';
// Logging and status report.
$output .= '<h5>' . t('Logging and Status Report') . '</h5>';
$output .= '<p>' . t('Activate the <strong>Enable log entry</strong> option to record each time maintenance mode is enabled, including an optional note to explain the reason. The timestamp of the last maintenance mode activation is also recorded and displayed.') . '</p>';
// Tips and recommendations.
$output .= '<h6>' . t('Tips and Recommendations') . '</h6>';
$output .= '<ul>';
$output .= '<li>' . t('After making changes, save the configuration and clear the site cache to apply updates.') . '</li>';
$output .= '<li>' . t('Use wildcards (e.g., <code>*</code>) for complex route patterns.') . '</li>';
$output .= '<li>' . t('Ensure the end time is after the start time when scheduling maintenance.') . '</li>';
$output .= '</ul>';
// Frequently asked questions.
$output .= '<h6>' . t('Frequently Asked Questions') . '</h6>';
$output .= '<p>' . t('How can I maintain admin access? Users with the "Access site in maintenance mode" permission are exempt by default.') . '</p>';
$output .= '<p>' . t('For more information, visit the module’s documentation on the <a href=":project">project page</a>.', [
':project' => 'https://www.drupal.org/project/maintenance',
]) . '</p>';
break;
}
return $output;
}
/**
* Implements hook_theme().
*/
function maintenance_theme() {
return [
'maintenance_form' => [
'render element' => 'form',
],
];
}
/**
* Implements hook_preprocess_HOOK() for HTML.
*/
function maintenance_preprocess_html(&$variables) {
// First check Gin theme is installed.
if (_maintenance_check_route() && _maintenance_admin_theme()) {
$variables['attributes']['class'][] = 'maintenance-form';
}
}
/**
* Implements hook_theme_registry_alter().
*/
function maintenance_theme_registry_alter(&$theme_registry) {
// Abort if the maintenance_page hook is not defined in current theme.
if (!isset($theme_registry['maintenance_page'])) {
return;
}
// Prevent override during system update route (/update.php).
$route_name = \Drupal::routeMatch()->getRouteName();
if ($route_name === 'system.db_update') {
return;
}
// Load maintenance module configuration.
$config = \Drupal::config('maintenance.settings');
$template = $config->get('theme.template');
// Only override if the selected template is supported.
if (!in_array($template, ['clean', 'particles'], TRUE)) {
return;
}
// Dynamically determine the module path.
$module_path = \Drupal::service('extension.list.module')->getPath('maintenance');
// Override theme registry paths and template name.
$theme_registry['maintenance_page']['theme path'] = $module_path;
$theme_registry['maintenance_page']['path'] = $module_path . '/templates';
$theme_registry['maintenance_page']['template'] = 'maintenance-page--' . $template;
// Initialize page variables to be injected into the Twig template.
$site_config = \Drupal::config('system.site');
$site_name = $site_config->get('name') ?: 'Drupal';
$title = t('Site under maintenance');
$message = \Drupal::config('system.maintenance')->get('message') ?: '';
// Handle HTML-based custom content.
if ($config->get('content.base') === 'html') {
$title = $config->get('content.title') ?: $title;
$message = $config->get('content.message') ?: $message;
}
// Handle node-based content if configured.
elseif ($config->get('content.base') === 'node') {
$nid = $config->get('content.node');
if (is_numeric($nid)) {
$node = \Drupal::entityTypeManager()->getStorage('node')->load($nid);
// Verify loaded node and extract fields safely.
if ($node instanceof NodeInterface && $node->hasField('body')) {
$title = $node->label();
$body = $node->get('body')->value ?? '';
if (!empty($body)) {
$message = $body;
}
}
}
}
// Format message with site tokens.
$formatted_message = new FormattableMarkup($message, ['@site' => $site_name]);
// Inject custom variables into the template context.
$theme_registry['maintenance_page']['variables']['page']['#title'] = $title;
$theme_registry['maintenance_page']['variables']['site_name'] = $site_name;
$theme_registry['maintenance_page']['variables']['maintenance_text'] = $formatted_message;
}
/**
* Determines whether the current route is the maintenance config form.
*
* Used for applying custom logic (e.g. classes) only on the
* maintenance configuration page.
*
* @return bool
* TRUE if current route is 'system.site_maintenance_mode'; otherwise FALSE.
*/
function _maintenance_check_route() {
// Get the current route match object.
$route_match = \Drupal::routeMatch();
// Check if the current route is the core maintenance mode form.
return $route_match->getRouteName() === 'system.site_maintenance_mode';
}
/**
* Checks if the current admin theme is Gin.
*
* Useful for conditionally loading Gin-specific styles, templates, or logic
* in configuration forms where UI adjustments or theme tweaks are required.
*
* @return bool
* TRUE if the current admin theme is 'Gin'; otherwise FALSE.
*/
function _maintenance_admin_theme() {
// Get the configured admin theme machine name.
$admin_theme = \Drupal::config('system.theme')->get('admin');
// Ensure theme handler service returns a valid name.
if (!$admin_theme || !\Drupal::service('theme_handler')->themeExists($admin_theme)) {
return FALSE;
}
// Get the human-readable name of the admin theme.
$admin_theme_name = \Drupal::service('theme_handler')->getName($admin_theme);
// Check if the theme name is exactly 'Gin'.
return $admin_theme_name === 'Gin';
}
