Skip to content
Snippets Groups Projects
Commit 87cd9e6e authored by Andreas Nedbal's avatar Andreas Nedbal Committed by Andreas Kienast
Browse files

[FEATURE] Provide backend modules in LiveSearch

Backend users can now search for the backend modules they have
access to in the LiveSearch.

Resolves: #92009
Releases: main
Change-Id: I28d9593655989f064b7a7fac0ab80415eee4c6fe
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/84654


Tested-by: default avatarcore-ci <typo3@b13.com>
Reviewed-by: default avatarOliver Bartsch <bo@cedev.de>
Tested-by: default avatarOliver Bartsch <bo@cedev.de>
Reviewed-by: default avatarAndreas Kienast <a.fernandez@scripting-base.de>
Tested-by: default avatarAndreas Kienast <a.fernandez@scripting-base.de>
parent c1b81725
Branches
Tags
No related merge requests found
import LiveSearchConfigurator from '@typo3/backend/live-search/live-search-configurator';
import { ResultItemInterface } from '@typo3/backend/live-search/element/result/item/item';
export function registerType(type: string) {
LiveSearchConfigurator.addInvokeHandler(type, 'open_module', (resultItem: ResultItemInterface): void => {
TYPO3.ModuleMenu.App.showModule(resultItem.extraData.moduleIdentifier);
});
}
......@@ -18,6 +18,7 @@ declare(strict_types=1);
namespace TYPO3\CMS\Backend\EventListener;
use TYPO3\CMS\Backend\Controller\Event\AfterBackendPageRenderEvent;
use TYPO3\CMS\Backend\Search\LiveSearch\BackendModuleProvider;
use TYPO3\CMS\Backend\Search\LiveSearch\DatabaseRecordProvider;
use TYPO3\CMS\Backend\Search\LiveSearch\PageRecordProvider;
use TYPO3\CMS\Core\Attribute\AsEventListener;
......@@ -43,5 +44,9 @@ final readonly class AfterBackendPageRenderEventListener
JavaScriptModuleInstruction::create('@typo3/backend/live-search/result-types/page-result-type.js', 'registerRenderer')
->invoke(null, PageRecordProvider::class)
);
$javaScriptRenderer->addJavaScriptModuleInstruction(
JavaScriptModuleInstruction::create('@typo3/backend/live-search/result-types/backend-module-result-type.js', 'registerType')
->invoke(null, BackendModuleProvider::class)
);
}
}
<?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\Backend\Search\LiveSearch;
use TYPO3\CMS\Backend\Module\ModuleInterface;
use TYPO3\CMS\Backend\Module\ModuleProvider;
use TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException;
use TYPO3\CMS\Backend\Routing\UriBuilder;
use TYPO3\CMS\Backend\Search\LiveSearch\SearchDemand\SearchDemand;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Imaging\IconFactory;
use TYPO3\CMS\Core\Imaging\IconSize;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Localization\LanguageServiceFactory;
class BackendModuleProvider implements SearchProviderInterface
{
private LanguageService $languageService;
public function __construct(
private readonly LanguageServiceFactory $languageServiceFactory,
private readonly UriBuilder $uriBuilder,
private readonly IconFactory $iconFactory,
private readonly ModuleProvider $moduleProvider
) {
$this->languageService = $this->languageServiceFactory->createFromUserPreferences($this->getBackendUser());
}
public function getFilterLabel(): string
{
return $this->languageService->sL('LLL:EXT:backend/Resources/Private/Language/locallang.xlf:liveSearch.backendModuleProvider.filterLabel');
}
public function find(SearchDemand $searchDemand): array
{
$items = [];
foreach ($this->getFilteredModules($searchDemand) as $module) {
// we can't generate accessible URLs for all modules by their identifier
// if URL generation fails, we don't create an action to open a module
// and if no actions exist, we skip result item creation altogether
try {
$moduleUrl = (string)$this->uriBuilder->buildUriFromRoute($module->getIdentifier());
} catch (RouteNotFoundException) {
continue;
}
$action = (new ResultItemAction('open_module'))
->setLabel($this->languageService->sL('LLL:EXT:backend/Resources/Private/Language/locallang.xlf:resultItem.backendModuleProvider.openModule'))
->setUrl($moduleUrl);
$iconIdentifier = $module->getIconIdentifier();
if ($iconIdentifier === '' && $module->hasParentModule()) {
$iconIdentifier = $module->getParentModule()->getIconIdentifier();
}
$items[] = (new ResultItem(self::class))
->setItemTitle($this->languageService->sL($module->getTitle()))
->setTypeLabel($this->languageService->sL('LLL:EXT:backend/Resources/Private/Language/locallang.xlf:liveSearch.backendModuleProvider.typeLabel'))
->setIcon($this->iconFactory->getIcon($iconIdentifier, IconSize::SMALL))
->setActions($action)
->setExtraData([
'moduleIdentifier' => $module->getIdentifier(),
]);
}
return $items;
}
public function count(SearchDemand $searchDemand): int
{
return count($this->getFilteredModules($searchDemand));
}
/**
* @return list<ModuleInterface>
*/
private function getFilteredModules(SearchDemand $searchDemand): array
{
$filteredModules = array_filter(
$this->moduleProvider->getModules($this->getBackendUser(), true, false),
fn(ModuleInterface $module) => str_contains(mb_strtolower($this->languageService->sL($module->getTitle())), mb_strtolower($searchDemand->getQuery()))
);
$firstResult = $searchDemand->getOffset();
$remainingItems = $searchDemand->getLimit();
return array_slice($filteredModules, $firstResult, $remainingItems, true);
}
private function getBackendUser(): BackendUserAuthentication
{
return $GLOBALS['BE_USER'];
}
}
......@@ -190,6 +190,15 @@ Have a nice day.</source>
<trans-unit id="liveSearch.pageRecordProvider.typeLabel" resname="liveSearch.pageRecordProvider.typeLabel">
<source>Page</source>
</trans-unit>
<trans-unit id="liveSearch.backendModuleProvider.filterLabel" resname="liveSearch.backendModuleProvider.filterLabel">
<source>Backend modules</source>
</trans-unit>
<trans-unit id="liveSearch.backendModuleProvider.typeLabel" resname="liveSearch.backendModuleProvider.typeLabel">
<source>Backend module</source>
</trans-unit>
<trans-unit id="resultItem.backendModuleProvider.openModule" resname="resultItem.backendModuleProvider.openModule">
<source>Open module</source>
</trans-unit>
<trans-unit id="moduleMenu.dropdown.label" resname="moduleMenu.dropdown.label">
<source>Module action</source>
</trans-unit>
......
/*
* 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!
*/
import LiveSearchConfigurator from"@typo3/backend/live-search/live-search-configurator.js";export function registerType(e){LiveSearchConfigurator.addInvokeHandler(e,"open_module",(e=>{TYPO3.ModuleMenu.App.showModule(e.extraData.moduleIdentifier)}))}
\ No newline at end of file
.. include:: /Includes.rst.txt
.. _feature-92009-1718182575:
=======================================================
Feature: #92009 - Provide backend modules in LiveSearch
=======================================================
See :issue:`92009`
Description
===========
The backend LiveSearch is now capable of listing backend modules, a user has
access to, offering the possibility of alternative navigation to different
parts of the backend.
Impact
======
Backend users now have another possibility to quickly access a backend module.
.. index:: Backend, ext:backend
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment