g2-8.x-1.x-dev/src/Requirements.php
src/Requirements.php
<?php
declare(strict_types=1);
namespace Drupal\g2;
use Drupal\Component\Render\MarkupInterface;
use Drupal\Core\Config\ImmutableConfig;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Routing\RouteProviderInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Url;
use Drupal\node\NodeInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Routing\Exception\RouteNotFoundException;
/**
* Class Requirements contains the hook_requirements() checks.
*
* It is NOT a Symfony service.
*
* @phpstan-consistent-constructor
*/
class Requirements implements ContainerInjectionInterface {
use StringTranslationTrait;
/**
* The core entity_type.manager service.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected EntityTypeManagerInterface $etm;
/**
* The g2.settings configuration.
*
* @var \Drupal\Core\Config\ImmutableConfig
*/
protected ImmutableConfig $g2Config;
/**
* The module_handler service.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected ModuleHandlerInterface $moduleHandler;
/**
* The accumulated results of requirements checks.
*
* @var array<string,array<string,mixed>>
*/
protected array $result = [];
/**
* The router.route_provider service.
*
* @var \Drupal\Core\Routing\RouteProviderInterface
*/
protected RouteProviderInterface $routeProvider;
/**
* The statistics.settings configuration.
*
* @var \Drupal\Core\Config\ImmutableConfig
*/
protected $statisticsConfig;
/**
* Requirements constructor.
*
* @param \Drupal\Core\Config\ImmutableConfig $g2_config
* The g2.settings configuration.
* @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
* The router.route_provider service.
* @param \Drupal\Core\Config\ImmutableConfig $statistics_config
* The statistics.settings configuration.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module_handler service.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $etm
* The entity_type.manager service.
*/
public function __construct(
ImmutableConfig $g2_config,
RouteProviderInterface $route_provider,
ImmutableConfig $statistics_config,
ModuleHandlerInterface $module_handler,
EntityTypeManagerInterface $etm,
) {
$this->etm = $etm;
$this->g2Config = $g2_config;
$this->routeProvider = $route_provider;
$this->statisticsConfig = $statistics_config;
$this->moduleHandler = $module_handler;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container): static {
/** @var \Drupal\Core\Extension\ModuleHandlerInterface $module_handler */
$module_handler = $container->get('module_handler');
/** @var \Drupal\Core\Routing\RouteProvider $route_provider */
$route_provider = $container->get('router.route_provider');
/** @var \Drupal\Core\Entity\EntityTypeManagerInterface $etm */
$etm = $container->get(G2::SVC_ETM);
/** @var \Drupal\Core\Config\ConfigFactoryInterface $config_factory */
$config_factory = $container->get(G2::SVC_CONF);
$g2_config = $config_factory->get(G2::CONFIG_NAME);
$statistics_settings = $config_factory->get('statistics.settings');
return new static($g2_config, $route_provider, $statistics_settings,
$module_handler, $etm);
}
/**
* Perform controller requirements checks.
*/
public function checkControllers(): void {
$this->result['main.nid'] = $this->checkNid(G2::VARMAINNID,
$this->t('G2 main page node'));
$this->result['main.route'] = $this->checkRoute(G2::VARMAINROUTE,
$this->t('G2 main page route'));
$this->result['homonyms.nid'] = $this->checkNid(G2::VARHOMONYMSNID,
$this->t('G2 homonyms page node'));
$this->result['homonyms.route'] = $this->checkRoute(G2::VARHOMONYMSROUTE,
$this->t('G2 homonyms page route'));
}
/**
* Check whether a node is valid.
*
* @param string $key
* The config key for the route to validate.
* @param \Drupal\Component\Render\MarkupInterface $title
* The requirement check title.
*
* @return array<string,\Drupal\Component\Render\MarkupInterface|int>
* A hook_requirements value.
*/
protected function checkNid(string $key, MarkupInterface $title): array {
$result = ['title' => $title];
$main = $this->g2Config->get($key);
// Should be guaranteed by config schema.
assert(is_numeric($main));
if ($main === 0) {
$result += [
'value' => $this->t('Node set to empty.'),
'severity' => REQUIREMENT_OK,
];
return $result;
}
$node = $this->etm
->getStorage(G2::TYPE)
->load($main);
if (!($node instanceof NodeInterface)) {
$result += [
'value' => $this->t(
'The node id must be 0 or match an existing node, but "@nid" cannot be found.',
['@nid' => $main]),
'severity' => REQUIREMENT_ERROR,
];
return $result;
}
if ($node->isPublished()) {
$result += [
'value' => $this->t(
'The chosen node must not be published, but node "@nid" is.',
['@nid' => $main]),
'severity' => REQUIREMENT_ERROR,
];
return $result;
}
$url = Url::fromRoute(
G2::ROUTE_NODE_CANONICAL,
[G2::TYPE => $main])
->toString();
$result += [
'value' => $this->t(
'Existing unpublished node @nid: <a href=":url">@title</a>. Remember that <a href=":deprecated">such node use is deprecated.</a>', [
'@nid' => $node->id(),
'@title' => $node->label(),
':url' => $url,
':deprecated' => 'https://www.drupal.org/project/g2/issues/3369887',
]
),
'severity' => REQUIREMENT_WARNING,
];
return $result;
}
/**
* Check whether a route is valid.
*
* @param string $key
* The config key for the route to validate.
* @param \Drupal\Component\Render\MarkupInterface $title
* The requirement check title.
*
* @return array<string,\Drupal\Component\Render\MarkupInterface|int>
* A hook_requirements() value.
*/
protected function checkRoute($key, MarkupInterface $title): array {
$result = ['title' => $title];
$mixedName = $this->g2Config->get($key);
assert(is_string($mixedName));
$name = (string) $mixedName;
$arguments = ['%route' => $name];
try {
$route = $this->routeProvider->getRouteByName($name);
if ($route->hasOption('parameters')) {
$value = $this->t('Valid parametric route %route', $arguments);
}
else {
$value = $this->t('Valid static route <a href=":url">%route</a>',
$arguments + [
':url' => Url::fromRoute($name)->toString(),
]);
}
$result += [
'value' => $value,
'severity' => REQUIREMENT_OK,
];
}
catch (RouteNotFoundException $e) {
$result += [
'value' => $this->t('The chosen route is not available: %route',
$arguments),
'severity' => REQUIREMENT_ERROR,
];
}
return $result;
}
/**
* Perform statistics-related requirements checks.
*/
public function checkStatistics(): void {
[$stats, $count, $severity, $value] = $this->prepareStatisticCheck();
if ($severity == REQUIREMENT_ERROR) {
$description = [];
}
else {
$items = [];
$modules_url = [
':link' => Url::fromRoute('system.modules_list', [], [
'fragment' => 'module-statistics',
])->toString(),
];
$items[] = $stats
? $this->t('<a href=":link">Statistics module</a> installed: OK.',
$modules_url)
: $this->t('<a href=":link">Statistics module</a> not installed.',
$modules_url);
$link_text = $count ? $this->t('ON') : $this->t('OFF');
if ($stats) {
$stats_url = [
':stats_url' => Url::fromRoute('statistics.settings', [], [
'query' => [
'destination' => Url::fromRoute('system.status')->toString(),
],
'fragment' => 'edit-content',
])->toString(),
];
$items[] = $this->t('Count content views" setting is <a href=":stats_url">@on_off</a>',
$stats_url + ['@on_off' => $link_text]
);
}
else {
$items[] = $this->t('G2 relies on statistics.module to provide data for the G2 "Top" block and API.
If you do not use either block, you can leave statistics.module disabled.');
}
$description = [
'#theme' => 'item_list',
'#items' => $items,
];
}
$this->result['statistics'] = [
'title' => $this->t('G2 statistics'),
'value' => $value,
'description' => $description,
'severity' => $severity,
];
}
/**
* Helper for checkStatistics(): build the check data.
*
* @return array{boolean,int,int,\Drupal\Core\StringTranslation\TranslatableMarkup}
* - stats
* - count
* - severity
* - value
*/
protected function prepareStatisticCheck(): array {
$stats = $this->moduleHandler->moduleExists('statistics');
$mixedCount = $this->statisticsConfig->get('count_content_views');
// See statistics.schema.
assert(is_int($mixedCount) || is_null($mixedCount));
$count = (int) $mixedCount;
if ($stats && is_null($mixedCount)) {
// Should not happen, see https://www.drupal.org/project/g2/issues/3438261
$severity = REQUIREMENT_ERROR;
$value = $this->t('Statistics schema missing. Please uninstall G2 and Statistics, and reinstall G2.');
}
elseif (!$stats && !$count) {
// This one is a (questionable) choice.
$severity = REQUIREMENT_INFO;
$value = $this->t('G2 statistics disabled.');
}
elseif ($stats xor $count) {
// This one is inconsistent.
$severity = REQUIREMENT_WARNING;
$value = $this->t('G2 statistics incorrectly configured.');
}
else {
// Both on: optimal.
$severity = REQUIREMENT_OK;
$value = $this->t('G2 statistics configured correctly.');
}
return [$stats, $count, $severity, $value];
}
/**
* Return the check results.
*
* @return array<string,array<mixed>>
* In the hook_requirements() format.
*/
public function getResult() {
return $this->result;
}
}
