Commit 38b3dd10 authored by webchick's avatar webchick

Issue #665790 by chrisrockwell, Sumit kumar, lauriii, joelpittet, sun,...

Issue #665790 by chrisrockwell, Sumit kumar, lauriii, joelpittet, sun, vulcanr, vaplas, yoroy, leahtard, tompagabor, aspilicious, ckrina, Gábor Hojtsy, David_Rothstein, Manuel Garcia, rootwork, nod_, kostyashupenko, pguillard, Bojhan, droplet, typhonius, wturrell, mgifford, webkenny, cosmicdreams, tkoleary, YesCT, marcvangend, markabur, LewisNyman, mrfelton, oriol_e9g, prabhu9484, catch, hedrickbt, tsvenson, longwave, dww, kika, borisson_, rdellis87, realityloop, klonos: Redesign the status report page
parent 90ba2d40
......@@ -2158,7 +2158,7 @@ function install_display_requirements($install_state, $requirements) {
// and indicating a desire to continue anyway. See drupal_requirements_url().
if ($severity == REQUIREMENT_ERROR || ($severity == REQUIREMENT_WARNING && empty($install_state['parameters']['continue']))) {
if ($install_state['interactive']) {
$build['report']['#theme'] = 'status_report';
$build['report']['#type'] = 'status_report';
$build['report']['#requirements'] = $requirements;
if ($severity == REQUIREMENT_WARNING) {
$build['#title'] = t('Requirements review');
......
<?php
namespace Drupal\Core\Render\Element;
/**
* Creates status report page element.
*
* @RenderElement("status_report")
*/
class StatusReport extends RenderElement {
/**
* {@inheritdoc}
*/
public function getInfo() {
$class = get_class($this);
return [
'#theme' => 'status_report_grouped',
'#priorities' => [
'error',
'warning',
'checked',
'ok',
],
'#pre_render' => [
[$class, 'preRenderGroupRequirements'],
],
];
}
/**
* #pre_render callback to group requirements.
*/
public static function preRenderGroupRequirements($element) {
$severities = static::getSeverities();
$grouped_requirements = [];
foreach ($element['#requirements'] as $key => $requirement) {
$severity = $severities[REQUIREMENT_INFO];
if (isset($requirement['severity'])) {
$requirement_severity = (int) $requirement['severity'] === REQUIREMENT_OK ? REQUIREMENT_INFO : (int) $requirement['severity'];
$severity = $severities[$requirement_severity];
}
elseif (defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'install') {
$severity = $severities[REQUIREMENT_OK];
}
$grouped_requirements[$severity['status']]['title'] = $severity['title'];
$grouped_requirements[$severity['status']]['type'] = $severity['status'];
$grouped_requirements[$severity['status']]['items'][$key] = $requirement;
}
// Order the grouped requirements by a set order.
$order = array_flip($element['#priorities']);
uksort($grouped_requirements, function ($a, $b) use ($order) {
return $order[$a] > $order[$b];
});
$element['#grouped_requirements'] = $grouped_requirements;
return $element;
}
/**
* Gets the severities.
*
* @return array
*/
public static function getSeverities() {
return [
REQUIREMENT_INFO => [
'title' => t('Checked'),
'status' => 'checked',
],
REQUIREMENT_OK => [
'title' => t('OK'),
'status' => 'ok',
],
REQUIREMENT_WARNING => [
'title' => t('Warnings found'),
'status' => 'warning',
],
REQUIREMENT_ERROR => [
'title' => t('Errors found'),
'status' => 'error',
],
];
}
}
......@@ -35,7 +35,7 @@ public function testEnable() {
// No pending updates should be available.
$this->drupalGet('admin/reports/status');
$requirement_value = $this->cssSelect("tr.system-status-report__entry th:contains('Entity/field definitions') + td");
$requirement_value = $this->cssSelect("details.system-status-report__entry summary:contains('Entity/field definitions') + div");
$this->assertEqual(t('Up to date'), trim((string) $requirement_value[0]));
$this->drupalGet('admin/config/regional/content-language');
......@@ -53,7 +53,7 @@ public function testEnable() {
// No pending updates should be available.
$this->drupalGet('admin/reports/status');
$requirement_value = $this->cssSelect("tr.system-status-report__entry th:contains('Entity/field definitions') + td");
$requirement_value = $this->cssSelect("details.system-status-report__entry summary:contains('Entity/field definitions') + div");
$this->assertEqual(t('Up to date'), trim((string) $requirement_value[0]));
// Create a node type and check the content translation settings are now
......
/**
* @file
* Styles for the system status counter component.
*/
.system-status-counter__status-icon {
display: inline-block;
height: 25px;
width: 25px;
vertical-align: middle;
}
.system-status-counter__status-icon:before {
content: "";
background-size: 16px;
background-position: center 2px;
background-repeat: no-repeat;
width: 100%;
height: 100%;
display: block;
}
.system-status-counter__status-icon--error:before {
background-image: url(../../../../misc/icons/e32700/error.svg);
}
.system-status-counter__status-icon--warning:before {
background-image: url(../../../../misc/icons/e29700/warning.svg);
}
.system-status-counter__status-icon--checked:before {
background-image: url(../../../../misc/icons/73b355/check.svg);
}
/**
* @file
* Styles for the system status report counters.
*/
.system-status-report-counters__item {
width: 100%;
padding: .5em 0;
text-align: center;
white-space: nowrap;
background-color: rgba(0, 0, 0, 0.063);
margin-bottom: .5em;
}
@media screen and (min-width: 60em) {
.system-status-report-counters {
flex-wrap: wrap;
display: flex;
justify-content: space-between;
}
.system-status-report-counters__item--half-width {
width: 49%;
}
.system-status-report-counters__item--third-width {
width: 33%;
}
}
/**
* @file
* Default styles for the System Status general info.
*/
.system-status-general-info__item {
border: 1px solid #ccc;
margin-top: 1em;
padding: 0 1em 1em;
}
.system-status-general-info__item-title {
border-bottom: 1px solid #ccc;
}
......@@ -204,10 +204,11 @@ small .admin-link:after {
.system-status-report__status-title {
position: relative;
vertical-align: top;
width: 25%;
width: 100%;
padding: 10px 6px 10px 40px; /* LTR */
box-sizing: border-box;
font-weight: normal;
background-color: transparent;
}
[dir="rtl"] .system-status-report__status-title {
padding: 10px 40px 10px 6px;
......@@ -232,6 +233,9 @@ small .admin-link:after {
.system-status-report__status-icon--warning:before {
background-image: url(../../../misc/icons/e29700/warning.svg);
}
.system-status-report__entry__value {
padding: 1em .5em;
}
/**
* Appearance page.
......@@ -387,3 +391,6 @@ small .admin-link:after {
[dir="rtl"] .system-themes-admin-form {
clear: right;
}
.cron-description__run-cron {
display: block;
}
......@@ -522,7 +522,7 @@ public function requirements($severity, array $requirements, Request $request) {
$try_again_url = Url::fromUri($request->getUriForPath(''))->setOptions(['query' => $options])->toString(TRUE)->getGeneratedUrl();
$build['status_report'] = array(
'#theme' => 'status_report',
'#type' => 'status_report',
'#requirements' => $requirements,
'#suffix' => $this->t('Check the messages and <a href=":url">try again</a>.', array(':url' => $try_again_url))
);
......
......@@ -47,7 +47,7 @@ public function __construct(SystemManager $systemManager) {
*/
public function status() {
$requirements = $this->systemManager->listRequirements();
return array('#theme' => 'status_report', '#requirements' => $requirements);
return ['#type' => 'status_report_page', '#requirements' => $requirements];
}
/**
......
<?php
namespace Drupal\system\Element;
use Drupal\Core\Render\Element\RenderElement;
use Drupal\Core\Render\Element\StatusReport;
use Drupal\Core\StringTranslation\PluralTranslatableMarkup;
/**
* Creates status report page element.
*
* @RenderElement("status_report_page")
*/
class StatusReportPage extends RenderElement {
/**
* {@inheritdoc}
*/
public function getInfo() {
$class = get_class($this);
return [
'#theme' => 'status_report_page',
'#pre_render' => [
[$class, 'preRenderCounters'],
[$class, 'preRenderGeneralInfo'],
[$class, 'preRenderRequirements'],
],
];
}
/**
* #pre_render callback to get general info out of requirements.
*/
public static function preRenderGeneralInfo($element) {
$element['#general_info'] = [
'#theme' => 'status_report_general_info',
];
// Loop through requirements and pull out items.
foreach ($element['#requirements'] as $key => $requirement) {
switch ($key) {
case 'cron':
foreach ($requirement['description'] as &$description_elements) {
foreach ($description_elements as &$description_element) {
if (isset($description_element['#url']) && $description_element['#url']->getRouteName() == 'system.run_cron') {
$description_element['#attributes']['class'][] = 'button';
$description_element['#attributes']['class'][] = 'button--small';
$description_element['#attributes']['class'][] = 'button--primary';
$description_element['#attributes']['class'][] = 'system-status-general-info__run-cron';
}
}
}
$element['#general_info']['#' . $key] = $requirement;
unset($element['#requirements'][$key]);
break;
case 'drupal':
case 'webserver':
case 'database_system':
case 'database_system_version':
case 'php':
case 'php_memory_limit':
$element['#general_info']['#' . $key] = $requirement;
unset($element['#requirements'][$key]);
break;
}
}
return $element;
}
/**
* #pre_render callback to create counter elements.
*/
public static function preRenderCounters($element) {
// Count number of items with different severity for summary.
$counters = [
'error' => [
'amount' => 0,
'text' => t('Error'),
'text_plural' => t('Errors'),
],
'warning' => [
'amount' => 0,
'text' => t('Warning'),
'text_plural' => t('Warnings'),
],
'checked' => [
'amount' => 0,
'text' => t('Checked'),
'text_plural' => t('Checked'),
],
];
$severities = StatusReport::getSeverities();
foreach ($element['#requirements'] as $key => &$requirement) {
$severity = $severities[REQUIREMENT_INFO];
if (isset($requirement['severity'])) {
$severity = $severities[(int) $requirement['severity']];
}
elseif (defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'install') {
$severity = $severities[REQUIREMENT_OK];
}
if (isset($counters[$severity['status']])) {
$counters[$severity['status']]['amount']++;
}
}
foreach ($counters as $key => $counter) {
if ($counter['amount'] === 0) {
continue;
}
$text = new PluralTranslatableMarkup($counter['amount'], $counter['text'], $counter['text_plural']);
$element['#counters'][$key] = [
'#theme' => 'status_report_counter',
'#amount' => $counter['amount'],
'#text' => $text,
'#severity' => $key,
];
}
return $element;
}
/**
* #pre_render callback to create status report requirements.
*/
public static function preRenderRequirements($element) {
$element['#requirements'] = [
'#type' => 'status_report',
'#requirements' => $element['#requirements'],
];
return $element;
}
}
......@@ -110,7 +110,7 @@ public function listRequirements() {
// Check run-time requirements and status information.
$requirements = $this->moduleHandler->invokeAll('requirements', array('runtime'));
usort($requirements, function($a, $b) {
uasort($requirements, function($a, $b) {
if (!isset($a['weight'])) {
if (!isset($b['weight'])) {
return strcasecmp($a['title'], $b['title']);
......
......@@ -121,7 +121,7 @@ public function testManualCron() {
$this->assertResponse(403);
$this->drupalGet('admin/reports/status');
$this->clickLink(t('run cron manually'));
$this->clickLink(t('Run cron'));
$this->assertResponse(200);
$this->assertText(t('Cron ran successfully.'));
}
......
......@@ -105,66 +105,6 @@ function template_preprocess_system_admin_index(&$variables) {
}
}
/**
* Prepares variables for status report template.
*
* Default template: status-report.html.twig.
*
* This theme function is dependent on install.inc being loaded, because
* that's where the constants are defined.
*
* @param $variables
* An associative array containing:
* - requirements: An array of requirements/status items. Each requirement
* is an associative array containing the following elements:
* - title: The name of the requirement.
* - value: (optional) The current value (version, time, level, etc).
* - description: (optional) The description of the requirement.
* - severity: (optional) The requirement's result/severity level, one of:
* - REQUIREMENT_INFO: Status information.
* - REQUIREMENT_OK: The requirement is satisfied.
* - REQUIREMENT_WARNING: The requirement failed with a warning.
* - REQUIREMENT_ERROR: The requirement failed with an error.
*/
function template_preprocess_status_report(&$variables) {
$severities = array(
REQUIREMENT_INFO => array(
'title' => t('Info'),
'status' => 'info',
),
REQUIREMENT_OK => array(
'title' => t('OK'),
'status' => 'ok',
),
REQUIREMENT_WARNING => array(
'title' => t('Warning'),
'status' => 'warning',
),
REQUIREMENT_ERROR => array(
'title' => t('Error'),
'status' => 'error',
),
);
foreach ($variables['requirements'] as $i => $requirement) {
// Always use the explicit requirement severity, if defined. Otherwise,
// default to REQUIREMENT_OK in the installer to visually confirm that
// installation requirements are met. And default to REQUIREMENT_INFO to
// denote neutral information without special visualization.
if (isset($requirement['severity'])) {
$severity = $severities[(int) $requirement['severity']];
}
elseif (defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'install') {
$severity = $severities[REQUIREMENT_OK];
}
else {
$severity = $severities[REQUIREMENT_INFO];
}
$variables['requirements'][$i]['severity_title'] = $severity['title'];
$variables['requirements'][$i]['severity_status'] = $severity['status'];
}
}
/**
* Prepares variables for the module details templates.
*
......
......@@ -535,14 +535,20 @@ function system_requirements($phase) {
],
];
}
$cron_url = \Drupal::url('system.cron', ['key' => \Drupal::state()->get('system.cron_key'), ['absolute' => TRUE]]);
$requirements['cron']['description'][] = [
[
'#markup' => t('You can <a href=":cron">run cron manually</a>.', [':cron' => \Drupal::url('system.run_cron')]),
'#type' => 'link',
'#prefix' => '(',
'#title' => t('more information'),
'#suffix' => ')',
'#url' => Url::fromRoute('system.cron_settings'),
],
[
'#prefix' => '<br />',
'#markup' => t('To run cron from outside the site, go to <a href=":url">@cron</a>', [':url' => $cron_url, '@cron' => $cron_url]),
'#prefix' => '<span class="cron-description__run-cron">',
'#suffix' => '</span>',
'#type' => 'link',
'#title' => t('Run cron'),
'#url' => Url::fromRoute('system.run_cron'),
],
];
}
......
......@@ -19,6 +19,9 @@ base:
css/components/reset-appearance.module.css: { weight: -10 }
css/components/resize.module.css: { weight: -10 }
css/components/sticky-header.module.css: { weight: -10 }
css/components/system-status-counter.css: { weight: -10 }
css/components/system-status-report-counters.css: { weight: -10 }
css/components/system-status-report-general-info.css: { weight: -10 }
css/components/tabledrag.module.css: { weight: -10 }
css/components/tablesort.module.css: { weight: -10 }
css/components/tree-child.module.css: { weight: -10 }
......
......@@ -197,9 +197,38 @@ function system_theme() {
'render element' => 'form',
'file' => 'system.admin.inc',
),
'status_report_page' => array(
'variables' => array(
'counters' => array(),
'general_info' => array(),
'requirements' => NULL,
),
),
'status_report' => array(
'variables' => array('requirements' => NULL),
'file' => 'system.admin.inc',
'variables' => array(
'grouped_requirements' => NULL,
'requirements' => NULL,
),
),
'status_report_grouped' => array(
'variables' => array(
'grouped_requirements' => NULL,
'requirements' => NULL,
),
),
'status_report_counter' => array(
'variables' => array('amount' => NULL, 'text' => NULL, 'severity' => NULL),
),
'status_report_general_info' => array(
'variables' => array(
'drupal' => array(),
'cron' => array(),
'database_system' => array(),
'database_system_version' => array(),
'php' => array(),
'php_memory_limit' => array(),
'webserver' => array(),
),
),
'admin_page' => array(
'variables' => array('blocks' => NULL),
......
{#
/**
* @file
* Default theme implementation for the status report counter.
*
* Available variables:
* - amount: The number shown on counter.
* - text: The text shown on counter.
* - severity: The severity of the counter.
*
* @ingroup themable
*/
#}
<span class="system-status-counter__status-icon system-status-counter__status-icon--{{ severity }}"></span>
<span>{{ amount }} {{ text }}</span>
<a href="#{{ severity }}"><span class="visually-hidden">{{ text }} </span>Details</a>
{#
/**
* @file
* Default theme implementation for the status report general info.
*
* Available variables:
* - drupal: The status of Drupal installation:
* - value: The current status of Drupal installation.
* - description: The description for current status of Drupal installation.
* - cron: The status of cron:
* - value: The current status of cron.
* - description: The description for current status of cron.
* - cron.run_cron: An array to render a button for running cron.
* - database_system: The status of database system:
* - value: The current status of database sytem.
* - description: The description for current status of cron.
* - database_system_version: The info about current database version:
* - value: The current version of database.
* - description: The description for current version of database.
* - php: The current version of PHP:
* - value: The status of currently installed PHP version.
* - description: The description for current installed PHP version.
* - php_memory_limit: The info about current PHP memory limit:
* - value: The status of currently set PHP memory limit.
* - description: The description for currently set PHP memory limit.
* - webserver: The info about currently installed web server:
* - value: The status of currently installed web server.
* - description: The description for the status of currently installed web
* server.
*/
#}
<h2>{{ 'General System Information'|t }}</h2>
<div class="system-status-general-info__item">
<h3 class="system-status-general-info__item-title">{{ 'Drupal Version'|t }}</h3>
{{ drupal.value }}
{% if drupal.description %}
{{ drupal.description }}
{% endif %}
</div>
<div class="system-status-general-info__item">
<h3 class="system-status-general-info__item-title">{{ 'Last Cron Run'|t }}</h3>
{{ cron.value }}
{% if cron.run_cron %}
{{ cron.run_cron }}
{% endif %}
{% if cron.description %}
{{ cron.description }}
{% endif %}
</div>
<div class="system-status-general-info__item">
<h3 class="system-status-general-info__item-title">{{ 'Web Server'|t }}</h3>
{{ webserver.value }}
{% if webserver.description %}
{{ webserver.description }}
{% endif %}
</div>
<div class="system-status-general-info__item">
<h3 class="system-status-general-info__item-title">{{ 'PHP'|t }}</h3>
<h4>{{ 'Version'|t }}</h4> {{ php.value }}
{% if php.description %}
{{ php.description }}
{% endif %}
<h4>{{ 'Memory limit'|t }}</h4>{{ php_memory_limit.value }}
{% if php_memory_limit.description %}
{{ php_memory_limit.description }}
{% endif %}
</div>
<div class="system-status-general-info__item">
<h3 class="system-status-general-info__item-title">{{ 'Database'|t }}</h3>
<h4>{{ 'Version'|t }}</h4>{{ database_system_version.value }}
{% if database_system_version.description %}
{{ database_system_version.description }}
{% endif %}
<h4>{{ 'System'|t }}</h4>{{ database_system.value }}
{% if database_system.description %}
{{ database_system.description }}
{% endif %}
</div>
{#
/**
* @file
* Default theme implementation of grouped status report requirements.
*
* - grouped_requirements: Contains grouped requirements.
* Each group contains:
* - title: The title of the group.
* - type: The severity of the group.
* - items: The requirement instances.
* Each requirement item contains:
* - title: The title of the requirement.
* - value: (optional) The requirement's status.
* - description: (optional) The requirement's description.
* - severity_title: The title of the severity.
* - severity_status: Indicates the severity status.
*/
#}
{{ attach_library('core/drupal.collapse') }}
<div>
{% for group in grouped_requirements %}
<div>
<h3 id="{{ group.type }}">{{ group.title }}</h3>
{% for requirement in group.items %}
<details class="system-status-report__entry">
{%
set summary_classes = [
'system-status-report__status-title',
group.type in ['warning', 'error'] ? 'system-status-report__status-icon system-status-report__status-icon--' ~ group.type
]
%}
<summary{{ create_attribute({'class': summary_classes}) }} role="button">
{% if requirement.severity_title %}
<span class="visually-hidden">{{ requirement.severity_title }}</span>
{% endif %}
{{ requirement.title }}
</summary>
<div class="system-status-report__entry__value">
{{ requirement.value }}
{% if requirement.description %}
<div class="description">{{ requirement.description }}</div>
{% endif %}
</div>
</details>