TYPO3 v15 dev-main snapshot ()
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Reports\Service;
|
||||
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
|
||||
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
|
||||
use TYPO3\CMS\Core\Pagination\QueryBuilderPaginator;
|
||||
use TYPO3\CMS\Core\Pagination\SimplePagination;
|
||||
use TYPO3\CMS\Core\Schema\Exception\InvalidSchemaTypeException;
|
||||
use TYPO3\CMS\Core\Schema\Exception\UndefinedSchemaException;
|
||||
use TYPO3\CMS\Core\Schema\SchemaLabelResolver;
|
||||
use TYPO3\CMS\Core\Schema\Struct\SelectItem;
|
||||
use TYPO3\CMS\Core\Schema\TcaSchemaFactory;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Service for collecting content element statistics
|
||||
*
|
||||
* @internal This is not part of the public API and may change at any time
|
||||
*/
|
||||
final readonly class ContentStatisticsService
|
||||
{
|
||||
public function __construct(
|
||||
private TcaSchemaFactory $tcaSchemaFactory,
|
||||
private ConnectionPool $connectionPool,
|
||||
private SchemaLabelResolver $schemaLabelResolver,
|
||||
) {}
|
||||
|
||||
public function collectStatistic(): array
|
||||
{
|
||||
$defaultFields = ['colPos', 'CType', 'starttime', 'endtime', 'editlock', 'sys_language_uid', 'l18n_parent', 'fe_group', 'rowDescription', 'hidden'];
|
||||
$countInformation = $this->getCountInformation();
|
||||
|
||||
$schema = $this->tcaSchemaFactory->get('tt_content');
|
||||
try {
|
||||
$typeField = $schema->getSubSchemaTypeInformation()->getFieldName();
|
||||
} catch (InvalidSchemaTypeException) {
|
||||
return ['error' => true];
|
||||
}
|
||||
$fieldConfig = $schema->hasField($typeField) ? $schema->getField($typeField)->getConfiguration() : [];
|
||||
$itemGroups = $fieldConfig['itemGroups'] ?? [];
|
||||
$groupedWizardItems = [];
|
||||
foreach (array_keys($itemGroups) as $groupIdentifier) {
|
||||
$groupedWizardItems['exists'][$groupIdentifier]['header'] = $itemGroups[$groupIdentifier];
|
||||
$groupedWizardItems['unused'][$groupIdentifier]['header'] = $itemGroups[$groupIdentifier];
|
||||
}
|
||||
|
||||
foreach ($fieldConfig['items'] ?? [] as $item) {
|
||||
$selectItem = SelectItem::fromTcaItemArray($item);
|
||||
if ($selectItem->isDivider()) {
|
||||
continue;
|
||||
}
|
||||
$recordType = $selectItem->getValue();
|
||||
$groupIdentifier = $selectItem->getGroup();
|
||||
if (!isset($countInformation[$recordType])
|
||||
|| ($countInformation[$recordType]['visible'] === 0 && $countInformation[$recordType]['hidden'] === 0)
|
||||
) {
|
||||
$usageIdentifier = 'unused';
|
||||
} else {
|
||||
$usageIdentifier = 'exists';
|
||||
}
|
||||
|
||||
$groupedWizardItems[$usageIdentifier][$groupIdentifier]['elements'] ??= [];
|
||||
|
||||
// In case this group is not defined in itemGroups, use the group identifier as label.
|
||||
$groupedWizardItems[$usageIdentifier][$groupIdentifier]['header'] ??= $groupIdentifier;
|
||||
$itemDescription = $selectItem->getDescription();
|
||||
$wizardEntry = [
|
||||
'iconIdentifier' => $selectItem->getIcon(),
|
||||
'iconOverlay' => $selectItem->getIconOverlay(),
|
||||
'title' => $selectItem->getLabel(),
|
||||
'description' => $itemDescription['description'] ?? ($itemDescription ?? ''),
|
||||
];
|
||||
$groupedWizardItems[$usageIdentifier][$groupIdentifier]['elements'][$recordType] = $wizardEntry;
|
||||
$groupedWizardItems[$usageIdentifier][$groupIdentifier]['elements'][$recordType]['count'] = $countInformation[$recordType] ?? [];
|
||||
try {
|
||||
$subschema = $schema->getSubSchema($recordType);
|
||||
$groupedWizardItems[$usageIdentifier][$groupIdentifier]['elements'][$recordType]['fields']
|
||||
= $subschema->getFields(fn($field) => !in_array($field->getName(), $defaultFields, true));
|
||||
} catch (UndefinedSchemaException) {
|
||||
}
|
||||
}
|
||||
return $groupedWizardItems;
|
||||
}
|
||||
|
||||
public function collectStatisticForCtype(string $cType, int $currentPage): array
|
||||
{
|
||||
$paginator = new QueryBuilderPaginator($this->buildQueryBuilderForCtype($cType), $currentPage, 10);
|
||||
$pagination = new SimplePagination($paginator);
|
||||
|
||||
$rows = $paginator->getPaginatedItems();
|
||||
foreach ($rows as &$row) {
|
||||
$row['path'] = BackendUtility::getRecordPath($row['pid'], '', 0);
|
||||
$row['tstamp_formatted'] = BackendUtility::datetime($row['tstamp']);
|
||||
}
|
||||
|
||||
return [
|
||||
'ctype' => $cType,
|
||||
'label' => $this->schemaLabelResolver->getLabelForFieldValue('tt_content', 'CType', $cType),
|
||||
'count' => $this->getCountInformation($cType)[$cType] ?? [],
|
||||
'rows' => $rows,
|
||||
'paginator' => $paginator,
|
||||
'pagination' => $pagination,
|
||||
];
|
||||
}
|
||||
|
||||
public function isValidCtype(string $cType): bool
|
||||
{
|
||||
if ($cType === '') {
|
||||
return false;
|
||||
}
|
||||
return $this->tcaSchemaFactory->get('tt_content')->hasSubSchema($cType);
|
||||
}
|
||||
|
||||
private function buildQueryBuilderForCtype(string $cType): QueryBuilder
|
||||
{
|
||||
$queryBuilder = $this->connectionPool->getQueryBuilderForTable('tt_content');
|
||||
$queryBuilder->getRestrictions()
|
||||
->removeAll()
|
||||
->add(GeneralUtility::makeInstance(DeletedRestriction::class));
|
||||
return $queryBuilder
|
||||
->select('uid', 'pid', 'header', 'tstamp', 'crdate', 'hidden', 'fe_group')
|
||||
->from('tt_content')
|
||||
->where(
|
||||
$queryBuilder->expr()->eq('CType', $queryBuilder->createNamedParameter($cType)),
|
||||
)
|
||||
->orderBy('uid', 'desc');
|
||||
}
|
||||
|
||||
private function getCountInformation(string $cType = ''): array
|
||||
{
|
||||
$queryBuilder = $this->connectionPool->getQueryBuilderForTable('tt_content');
|
||||
$queryBuilder->getRestrictions()->removeAll();
|
||||
$queryBuilder = $queryBuilder
|
||||
->select('CType', 'deleted', 'hidden')
|
||||
->addSelectLiteral('COUNT(*) as count')
|
||||
->from('tt_content')
|
||||
->groupBy('CType')
|
||||
->addGroupBy('hidden')
|
||||
->addGroupBy('deleted');
|
||||
|
||||
if ($cType !== '') {
|
||||
$queryBuilder = $queryBuilder->where($queryBuilder->expr()->eq('CType', $queryBuilder->createNamedParameter($cType)));
|
||||
}
|
||||
|
||||
$counts = [];
|
||||
foreach ($queryBuilder->executeQuery()->fetchAllAssociative() as $row) {
|
||||
$cType = $row['CType'];
|
||||
$deleted = (int)$row['deleted'];
|
||||
$hidden = (int)$row['hidden'];
|
||||
$count = (int)$row['count'];
|
||||
if (!isset($counts[$cType])) {
|
||||
$counts[$cType] = ['deleted' => 0, 'hidden' => 0, 'visible' => 0];
|
||||
}
|
||||
|
||||
$key = match (true) {
|
||||
$deleted === 1 => 'deleted',
|
||||
$hidden === 1 => 'hidden',
|
||||
default => 'visible',
|
||||
};
|
||||
|
||||
$counts[$cType][$key] += $count;
|
||||
}
|
||||
|
||||
return $counts;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Reports\Service;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
|
||||
use TYPO3\CMS\Core\DataHandling\PageDoktypeRegistry;
|
||||
use TYPO3\CMS\Core\Imaging\IconFactory;
|
||||
use TYPO3\CMS\Core\Imaging\IconSize;
|
||||
use TYPO3\CMS\Core\Localization\LanguageService;
|
||||
use TYPO3\CMS\Core\Schema\Capability\TcaSchemaCapability;
|
||||
use TYPO3\CMS\Core\Schema\TcaSchema;
|
||||
use TYPO3\CMS\Core\Schema\TcaSchemaFactory;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Reports\Integrity\DatabaseIntegrityCheck;
|
||||
|
||||
/**
|
||||
* Service for collecting database record statistics
|
||||
*
|
||||
* @internal This is not part of the public API and may change at any time
|
||||
*/
|
||||
#[Autoconfigure(public: true)]
|
||||
final readonly class RecordStatisticsService
|
||||
{
|
||||
public function __construct(
|
||||
private IconFactory $iconFactory,
|
||||
private TcaSchemaFactory $tcaSchemaFactory,
|
||||
private PageDoktypeRegistry $pageDoktypeRegistry,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Collects page statistics including total, translated, hidden, and deleted pages
|
||||
*
|
||||
* @return array<string, array{icon: string, count: int}>
|
||||
*/
|
||||
public function collectPageStatistics(): array
|
||||
{
|
||||
$databaseIntegrityCheck = GeneralUtility::makeInstance(DatabaseIntegrityCheck::class);
|
||||
$databaseIntegrityCheck->genTree(0);
|
||||
|
||||
return [
|
||||
'total_pages' => [
|
||||
'icon' => $this->iconFactory->getIconForRecord('pages', [], IconSize::SMALL)->render(),
|
||||
'count' => count($databaseIntegrityCheck->getPageIdArray()),
|
||||
],
|
||||
'translated_pages' => [
|
||||
'icon' => $this->iconFactory->getIconForRecord('pages', [], IconSize::SMALL)->render(),
|
||||
'count' => count($databaseIntegrityCheck->getPageTranslatedPageIDArray()),
|
||||
],
|
||||
'hidden_pages' => [
|
||||
'icon' => $this->iconFactory->getIconForRecord('pages', ['hidden' => 1], IconSize::SMALL)->render(),
|
||||
'count' => $databaseIntegrityCheck->getRecStats()['hidden'] ?? 0,
|
||||
],
|
||||
'deleted_pages' => [
|
||||
'icon' => $this->iconFactory->getIconForRecord('pages', ['deleted' => 1], IconSize::SMALL)->render(),
|
||||
'count' => isset($databaseIntegrityCheck->getRecStats()['deleted']['pages']) ? count($databaseIntegrityCheck->getRecStats()['deleted']['pages']) : 0,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects statistics for different page doktypes
|
||||
*
|
||||
* @return array<int, array{icon: string, title: string, count: int}>
|
||||
*/
|
||||
public function collectDoktypeStatistics(): array
|
||||
{
|
||||
$languageService = $this->getLanguageService();
|
||||
$databaseIntegrityCheck = GeneralUtility::makeInstance(DatabaseIntegrityCheck::class);
|
||||
$databaseIntegrityCheck->genTree(0);
|
||||
|
||||
$doktypes = [];
|
||||
foreach ($this->pageDoktypeRegistry->getAllDoktypes() as $doktype) {
|
||||
if ($doktype->isDivider()) {
|
||||
continue;
|
||||
}
|
||||
$doktypes[] = [
|
||||
'icon' => $this->iconFactory->getIconForRecord('pages', ['doktype' => $doktype->getValue()], IconSize::SMALL)->render(),
|
||||
'title' => $languageService->sL($doktype->getLabel()) . ' (' . $doktype->getValue() . ')',
|
||||
'count' => (int)($databaseIntegrityCheck->getRecStats()['doktype'][$doktype->getValue()] ?? 0),
|
||||
];
|
||||
}
|
||||
|
||||
return $doktypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects statistics for all TCA tables including lost records
|
||||
*
|
||||
* @return array<string, array{icon: string, title: string, count: int|string, lostRecords: string}>
|
||||
*/
|
||||
public function collectTableStatistics(): array
|
||||
{
|
||||
$languageService = $this->getLanguageService();
|
||||
$databaseIntegrityCheck = GeneralUtility::makeInstance(DatabaseIntegrityCheck::class);
|
||||
$databaseIntegrityCheck->genTree(0);
|
||||
|
||||
// Tables and lost records
|
||||
$pageIds = array_merge([0], array_keys($databaseIntegrityCheck->getPageIdArray()));
|
||||
$lostRecords = $databaseIntegrityCheck->lostRecords($pageIds);
|
||||
|
||||
$tableStatistic = [];
|
||||
$countArr = $databaseIntegrityCheck->countRecords($pageIds);
|
||||
$lostPageIds = isset($lostRecords['pages']) ? array_keys($lostRecords['pages']) : [];
|
||||
|
||||
/** @var TcaSchema $schema */
|
||||
foreach ($this->tcaSchemaFactory->all() as $table => $schema) {
|
||||
if ($schema->hasCapability(TcaSchemaCapability::HideInUi)) {
|
||||
continue;
|
||||
}
|
||||
$lostRecordCount = isset($lostRecords[$table]) ? count($lostRecords[$table]) : 0;
|
||||
$recordCount = 0;
|
||||
if ($countArr['all'][$table] ?? false) {
|
||||
$recordCount = (int)($countArr['non_deleted'][$table] ?? 0) . '/' . $lostRecordCount;
|
||||
}
|
||||
$lostRecordList = [];
|
||||
foreach ($lostRecords[$table] ?? [] as $data) {
|
||||
if (!in_array((int)$data['pid'], $lostPageIds, true)) {
|
||||
$lostRecordList[]
|
||||
= '<div class="record">'
|
||||
. $this->iconFactory->getIcon('status-dialog-error', IconSize::SMALL)->render()
|
||||
. 'uid:' . $data['uid'] . ', pid:' . $data['pid'] . ', ' . htmlspecialchars(GeneralUtility::fixed_lgd_cs(strip_tags($data['title']), 20))
|
||||
. '</div>';
|
||||
} else {
|
||||
$lostRecordList[]
|
||||
= '<div class="record-noicon">'
|
||||
. 'uid:' . $data['uid'] . ', pid:' . $data['pid'] . ', ' . htmlspecialchars(GeneralUtility::fixed_lgd_cs(strip_tags($data['title']), 20))
|
||||
. '</div>';
|
||||
}
|
||||
}
|
||||
$tableStatistic[$table] = [
|
||||
'icon' => $this->iconFactory->getIconForRecord($table, [], IconSize::SMALL)->render(),
|
||||
'title' => $schema->getTitle($languageService->sL(...)),
|
||||
'count' => $recordCount,
|
||||
'lostRecords' => implode(LF, $lostRecordList),
|
||||
];
|
||||
}
|
||||
ksort($tableStatistic);
|
||||
|
||||
return $tableStatistic;
|
||||
}
|
||||
|
||||
private function getLanguageService(): LanguageService
|
||||
{
|
||||
return $GLOBALS['LANG'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Reports\Service;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
|
||||
use TYPO3\CMS\Core\Localization\LanguageService;
|
||||
use TYPO3\CMS\Core\Registry;
|
||||
use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity;
|
||||
use TYPO3\CMS\Reports\ExtendedStatusProviderInterface;
|
||||
use TYPO3\CMS\Reports\Registry\StatusRegistry;
|
||||
use TYPO3\CMS\Reports\RequestAwareStatusProviderInterface;
|
||||
use TYPO3\CMS\Reports\Status;
|
||||
|
||||
/**
|
||||
* Service for collecting and processing system status information
|
||||
*
|
||||
* @internal This is not part of the public API and may change at any time
|
||||
*/
|
||||
#[Autoconfigure(public: true)]
|
||||
final readonly class StatusService
|
||||
{
|
||||
public function __construct(
|
||||
private StatusRegistry $statusRegistry,
|
||||
private Registry $registry,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Runs through all status providers and returns all statuses collected.
|
||||
*
|
||||
* @param ServerRequestInterface|null $request
|
||||
* @return Status[][]
|
||||
*/
|
||||
public function getSystemStatus(?ServerRequestInterface $request = null): array
|
||||
{
|
||||
$status = [];
|
||||
foreach ($this->statusRegistry->getProviders() as $statusProvider) {
|
||||
$statusProviderId = $statusProvider->getLabel();
|
||||
$status[$statusProviderId] ??= [];
|
||||
if ($statusProvider instanceof RequestAwareStatusProviderInterface) {
|
||||
$statuses = $statusProvider->getStatus($request);
|
||||
} else {
|
||||
$statuses = $statusProvider->getStatus();
|
||||
}
|
||||
$status[$statusProviderId] = array_merge($status[$statusProviderId], $statuses);
|
||||
}
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs through all status providers and returns all statuses collected, which are detailed.
|
||||
*
|
||||
* @return Status[][]
|
||||
*/
|
||||
public function getDetailedSystemStatus(): array
|
||||
{
|
||||
$status = [];
|
||||
foreach ($this->statusRegistry->getProviders() as $statusProvider) {
|
||||
$statusProviderId = $statusProvider->getLabel();
|
||||
if ($statusProvider instanceof ExtendedStatusProviderInterface) {
|
||||
$statuses = $statusProvider->getDetailedStatus();
|
||||
$status[$statusProviderId] = array_merge($status[$statusProviderId] ?? [], $statuses);
|
||||
}
|
||||
}
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the highest severity from the given statuses.
|
||||
*
|
||||
* @param array<string, array<string, Status>> $statusCollection An array of Status objects.
|
||||
* @return int The highest severity found from the statuses.
|
||||
*/
|
||||
public function getHighestSeverity(array $statusCollection): int
|
||||
{
|
||||
$highestSeverity = ContextualFeedbackSeverity::NOTICE;
|
||||
foreach ($statusCollection as $providerStatuses) {
|
||||
foreach ($providerStatuses as $status) {
|
||||
if ($status->getSeverity()->value > $highestSeverity->value) {
|
||||
$highestSeverity = $status->getSeverity();
|
||||
}
|
||||
// Reached the highest severity level, no need to go on
|
||||
if ($highestSeverity === ContextualFeedbackSeverity::ERROR) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $highestSeverity->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects system status and stores the highest severity in the registry.
|
||||
* This is useful for displaying warnings at login or in the backend.
|
||||
*
|
||||
* @param ServerRequestInterface|null $request
|
||||
*/
|
||||
public function collectAndStoreSystemStatus(?ServerRequestInterface $request = null): void
|
||||
{
|
||||
$status = $this->getSystemStatus($request);
|
||||
$this->registry->set('tx_reports', 'status.highestSeverity', $this->getHighestSeverity($status));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the status providers (alphabetically and puts primary status providers at the beginning)
|
||||
*
|
||||
* @param array<string, array<string, Status>> $statusCollection A collection of statuses (with providers)
|
||||
* @return array<string, array<string, Status>> The collection of statuses sorted by provider
|
||||
*/
|
||||
public function sortStatusProviders(array $statusCollection): array
|
||||
{
|
||||
$languageService = $this->getLanguageService();
|
||||
|
||||
// Extract the primary status collections, i.e. the status groups
|
||||
// that must appear on top of the status report
|
||||
// Change their keys to localized collection titles
|
||||
$primaryStatuses = [
|
||||
$languageService->sL('LLL:EXT:reports/Resources/Private/Language/locallang_reports.xlf:status_typo3') => $statusCollection['typo3'] ?? [],
|
||||
$languageService->sL('LLL:EXT:reports/Resources/Private/Language/locallang_reports.xlf:status_system') => $statusCollection['system'] ?? [],
|
||||
$languageService->sL('LLL:EXT:reports/Resources/Private/Language/locallang_reports.xlf:status_security') => $statusCollection['security'] ?? [],
|
||||
$languageService->sL('LLL:EXT:reports/Resources/Private/Language/locallang_reports.xlf:status_configuration') => $statusCollection['configuration'] ?? [],
|
||||
];
|
||||
unset($statusCollection['typo3'], $statusCollection['system'], $statusCollection['security'], $statusCollection['configuration']);
|
||||
|
||||
// Assemble list of secondary status collections with left-over collections
|
||||
// Change their keys using localized labels if available
|
||||
$secondaryStatuses = [];
|
||||
foreach ($statusCollection as $statusProviderId => $collection) {
|
||||
if (str_starts_with($statusProviderId, 'LLL:')) {
|
||||
// Label provided by extension
|
||||
$label = $languageService->sL($statusProviderId);
|
||||
} else {
|
||||
// Generic label
|
||||
// @todo phase this out
|
||||
$label = $languageService->sL('LLL:EXT:reports/Resources/Private/Language/locallang_reports.xlf:status_' . $statusProviderId);
|
||||
}
|
||||
$providerLabel = empty($label) ? $statusProviderId : $label;
|
||||
$secondaryStatuses[$providerLabel] = $collection;
|
||||
}
|
||||
// Sort the secondary status collections alphabetically
|
||||
ksort($secondaryStatuses);
|
||||
return array_merge($primaryStatuses, $secondaryStatuses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the statuses by severity
|
||||
*
|
||||
* @param array<string, Status> $statusCollection A collection of statuses per provider
|
||||
* @return list<Status> The collection of statuses sorted by severity
|
||||
*/
|
||||
public function sortStatuses(array $statusCollection): array
|
||||
{
|
||||
$statuses = [];
|
||||
$sortTitle = [];
|
||||
$header = null;
|
||||
foreach ($statusCollection as $status) {
|
||||
if ($status->getTitle() === 'TYPO3') {
|
||||
$header = $status;
|
||||
continue;
|
||||
}
|
||||
$statuses[] = $status;
|
||||
$sortTitle[] = $status->getSeverity();
|
||||
}
|
||||
array_multisort($sortTitle, SORT_DESC, $statuses);
|
||||
// Making sure that the core version information is always on the top
|
||||
if (is_object($header)) {
|
||||
array_unshift($statuses, $header);
|
||||
}
|
||||
return $statuses;
|
||||
}
|
||||
|
||||
private function getLanguageService(): LanguageService
|
||||
{
|
||||
return $GLOBALS['LANG'];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user