TYPO3 v15 dev-main snapshot ()

This commit is contained in:
2026-08-10 22:31:00 +02:00
commit f9941541b7
1178 changed files with 135377 additions and 0 deletions
+116
View File
@@ -0,0 +1,116 @@
<?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\LinkHandler;
use TYPO3\CMS\Backend\Controller\AbstractLinkBrowserController;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Imaging\IconFactory;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Page\PageRenderer;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\View\ViewInterface;
/**
* Base class for core link handlers.
*
* @internal This class should only be used internally. Extensions must implement the LinkHandlerInterface.
*/
abstract class AbstractLinkHandler
{
/**
* Available additional link attributes
*
* @var string[]
*/
protected $linkAttributes = ['target', 'title', 'class', 'params', 'rel'];
/**
* @var bool
*/
protected $updateSupported = true;
/**
* @var AbstractLinkBrowserController
*/
protected $linkBrowser;
/**
* @var IconFactory
*/
protected $iconFactory;
protected ViewInterface $view;
/**
* @var PageRenderer
*/
protected $pageRenderer;
public function __construct() {}
public function initialize(AbstractLinkBrowserController $linkBrowser, $identifier, array $configuration)
{
$this->linkBrowser = $linkBrowser;
$this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
$this->pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
}
/**
* @return array
*/
public function getLinkAttributes()
{
return $this->linkAttributes;
}
/**
* @param string[] $fieldDefinitions Array of link attribute field definitions
* @return string[]
*/
public function modifyLinkAttributes(array $fieldDefinitions)
{
return $fieldDefinitions;
}
/**
* Return TRUE if the handler supports to update a link.
*
* This is useful for e.g. file or page links, when only attributes are changed.
*
* @return bool
*/
public function isUpdateSupported()
{
return $this->updateSupported;
}
protected function getBackendUser(): BackendUserAuthentication
{
return $GLOBALS['BE_USER'];
}
protected function getLanguageService(): LanguageService
{
return $GLOBALS['LANG'];
}
public function setView(ViewInterface $view): void
{
$this->view = $view;
}
}
@@ -0,0 +1,95 @@
<?php
/*
* 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\LinkHandler;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Controller\AbstractLinkBrowserController;
/**
* Interface for link handlers displayed in the LinkBrowser.
*
* Link handlers are used when the global "link" modal is rendered: When linking
* an RTE text snipped to something, and for fields like "header_link" in table "tt_content".
*
* A link handler is a tab in the link modal.
*
* Link handlers are configured with page TSconfig TCEMAIN.linkHandler - each tab is a sub-key in this area.
* The core configures a couple of default link handlers like linking to a page, a mail, telephone and similar.
*
* Link handlers create a TYPO3 specific URI prefixed with 't3://' managed by ext:core LinkHandling classes.
* The frontend translates this to appropriate HTML using the ext:frontend Typolink classes.
*/
interface LinkHandlerInterface
{
/**
* @return array
*/
public function getLinkAttributes();
/**
* @param string[] $fieldDefinitions Array of link attribute field definitions
* @return string[]
*/
public function modifyLinkAttributes(array $fieldDefinitions);
/**
* Initialize the handler
*
* @param string $identifier
* @param array $configuration Page TSconfig of this link handler: TCEMAIN.linkHandler.<identifier>.configuration
*/
public function initialize(AbstractLinkBrowserController $linkBrowser, $identifier, array $configuration);
/**
* Checks if this is the handler for the given link
*
* The handler may store this information locally for later usage.
*
* @param array $linkParts Link parts as returned from TypoLinkCodecService
*
* @return bool
*/
public function canHandleLink(array $linkParts);
/**
* Format the current link for HTML output
*
* @return string
*/
public function formatCurrentUrl();
/**
* Render the link handler. Ideally this modifies the view, but it can also render content directly.
*
*
* @return string
*/
public function render(ServerRequestInterface $request);
/**
* Return TRUE if the handler supports to update a link.
*
* This is useful for file or page links, when only attributes are changed.
*
* @return bool
*/
public function isUpdateSupported();
/**
* @return string[] Array of body-tag attributes
*/
public function getBodyTagAttributes();
}
@@ -0,0 +1,23 @@
<?php
/*
* 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\LinkHandler;
use Psr\Http\Message\ServerRequestInterface;
interface LinkHandlerVariableProviderInterface
{
public function initializeVariables(ServerRequestInterface $request): void;
}
@@ -0,0 +1,27 @@
<?php
/*
* 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\LinkHandler;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\View\BackendViewFactory;
use TYPO3\CMS\Core\View\ViewInterface;
interface LinkHandlerViewProviderInterface
{
public function createView(BackendViewFactory $backendViewFactory, ServerRequestInterface $request): ViewInterface;
public function setView(ViewInterface $view): self;
public function getView(): ViewInterface;
}
+99
View File
@@ -0,0 +1,99 @@
<?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\LinkHandler;
use Psr\Http\Message\ServerRequestInterface;
/**
* Link to an email address.
*
* @internal This class is a specific LinkHandler implementation and is not part of the TYPO3's Core API.
*/
class MailLinkHandler extends AbstractLinkHandler implements LinkHandlerInterface
{
/**
* Parts of the current link
*
* @var array
*/
protected $linkParts = [];
/**
* "target" and "rel" are not allowed
*
* @var string[]
*/
protected $linkAttributes = ['title', 'class', 'subject', 'body', 'cc', 'bcc'];
/**
* We don't support updates since there is no difference to simply set the link again.
*
* @var bool
*/
protected $updateSupported = false;
/**
* Checks if this is the handler for the given link
*
* The handler may store this information locally for later usage.
*
* @param array $linkParts Link parts as returned from TypoLinkCodecService
*
* @return bool
*/
public function canHandleLink(array $linkParts)
{
if (isset($linkParts['url']['email'])) {
$this->linkParts = $linkParts;
return true;
}
return false;
}
/**
* Format the current link for HTML output
*
* @return string
*/
public function formatCurrentUrl()
{
return $this->linkParts['url']['email'];
}
/**
* Render the link handler
*/
public function render(ServerRequestInterface $request): string
{
$this->pageRenderer->loadJavaScriptModule('@typo3/backend/mail-link-handler.js');
if (is_array($this->linkParts['url'] ?? null)) {
foreach ($this->linkParts['url'] as $name => $value) {
$this->view->assign($name, rawurldecode($value));
}
}
return $this->view->render('LinkBrowser/Mail');
}
/**
* @return string[] Array of body-tag attributes
*/
public function getBodyTagAttributes()
{
return [];
}
}
+283
View File
@@ -0,0 +1,283 @@
<?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\LinkHandler;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Tree\View\LinkParameterProviderInterface;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
use TYPO3\CMS\Core\Database\Query\Restriction\WorkspaceRestriction;
use TYPO3\CMS\Core\DataHandling\PageDoktypeRegistry;
use TYPO3\CMS\Core\Imaging\IconSize;
use TYPO3\CMS\Core\LinkHandling\LinkService;
use TYPO3\CMS\Core\Type\Bitmask\Permission;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\MathUtility;
/**
* Link to a page record.
*
* @internal This class is a specific LinkHandler implementation and is not part of the TYPO3's Core API.
*/
class PageLinkHandler extends AbstractLinkHandler implements LinkHandlerInterface, LinkParameterProviderInterface
{
/**
* @var int
*/
protected $expandPage = 0;
/**
* Parts of the current link
*
* @var array
*/
protected $linkParts = [];
protected PageDoktypeRegistry $pageDoktypeRegistry;
public function __construct()
{
parent::__construct();
$this->pageDoktypeRegistry = GeneralUtility::makeInstance(PageDoktypeRegistry::class);
}
/**
* Checks if this is the handler for the given link
*
* The handler may store this information locally for later usage.
*
* @param array $linkParts Link parts as returned from TypoLinkCodecService
*
* @return bool
*/
public function canHandleLink(array $linkParts)
{
if (empty($linkParts['url'] ?? '')) {
return false;
}
$data = $linkParts['url'];
// Resolve "current" to the actual page ID from the link browser context
if (($data['pageuid'] ?? '') === 'current') {
$currentPageId = (int)($this->linkBrowser->getParameters()['pid'] ?? 0);
if ($currentPageId > 0) {
$linkParts['url']['pageuid'] = $currentPageId;
$data = $linkParts['url'];
}
}
// Check if the page still exists
if ((int)($data['pageuid'] ?? 0) > 0) {
$pageRow = BackendUtility::getRecordWSOL('pages', $data['pageuid']);
if (!$pageRow) {
return false;
}
} else {
return false;
}
$this->linkParts = $linkParts;
return true;
}
/**
* Format the current link for HTML output
*
* @return string
*/
public function formatCurrentUrl()
{
$lang = $this->getLanguageService();
$id = (int)$this->linkParts['url']['pageuid'];
$idInfo = 'ID: ' . $id . (!empty($this->linkParts['url']['fragment']) ? ', #' . $this->linkParts['url']['fragment'] : '');
$permsClause = $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW);
$pageRecord = BackendUtility::readPageAccess($id, $permsClause);
if ($pageRecord === false) {
return $lang->sL('LLL:EXT:backend/Resources/Private/Language/locallang_browse_links.xlf:page') . ' ' . $idInfo;
}
$pageTitle = $pageRecord['title'] ?? '';
return $lang->sL('LLL:EXT:backend/Resources/Private/Language/locallang_browse_links.xlf:page')
. ($pageTitle ? ' \'' . BackendUtility::cropToTitleLength($pageTitle) . '\'' : '')
. ' (' . $idInfo . ')';
}
/**
* Render the link handler
*/
public function render(ServerRequestInterface $request): string
{
$this->pageRenderer->loadJavaScriptModule('@typo3/backend/page-link-handler.js');
$this->pageRenderer->loadJavaScriptModule('@typo3/backend/tree/page-browser.js');
$this->getBackendUser()->initializeWebmountsForElementBrowser();
$this->expandPage = isset($request->getQueryParams()['expandPage']) ? (int)$request->getQueryParams()['expandPage'] : 0;
$this->view->assign('initialNavigationWidth', $this->getBackendUser()->uc['selector']['navigation']['width'] ?? 250);
$this->view->assign('treeActions', ['link']);
$this->getRecordsOnExpandedPage($this->expandPage);
return $this->view->render('LinkBrowser/Page');
}
/**
* This adds all content elements on a page to the view and lets you create a link to the element.
*
* @param int $pageId Page uid to expand
*/
protected function getRecordsOnExpandedPage($pageId)
{
// If there is an anchor value (content element reference) in the element reference, then force an ID to expand:
if (!$pageId && isset($this->linkParts['url']['fragment'])) {
// Set to the current link page id.
$pageId = $this->linkParts['url']['pageuid'];
}
$linkService = GeneralUtility::makeInstance(LinkService::class);
$this->view->assign('expandedPage', $pageId ?: $this->linkParts['url']['pageuid'] ?? 0);
// Draw the record list IF there is a page id to expand:
if ($pageId && MathUtility::canBeInterpretedAsInteger($pageId) && $this->getBackendUser()->isInWebMount($pageId)) {
$pageId = (int)$pageId;
$activePageRecord = BackendUtility::getRecordWSOL('pages', $pageId);
$this->view->assign('expandActivePage', true);
// Create header for listing, showing the page title/icon
$this->view->assign('activePage', $activePageRecord);
$this->view->assign('activePageTitle', BackendUtility::getRecordTitle('pages', $activePageRecord, true));
$this->view->assign('activePageIcon', $this->iconFactory->getIconForRecord('pages', $activePageRecord, IconSize::SMALL)->render());
if ($this->isPageLinkable($activePageRecord)) {
$this->view->assign('activePageLink', $linkService->asString(['type' => LinkService::TYPE_PAGE, 'pageuid' => $pageId]));
}
// Look up tt_content elements from the expanded page
// @todo: this should be grouped by colPos and use the layout from the page module
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable('tt_content');
$queryBuilder->getRestrictions()
->removeAll()
->add(GeneralUtility::makeInstance(DeletedRestriction::class))
->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, $this->getBackendUser()->workspace));
$contentElements = $queryBuilder
->select('*')
->from('tt_content')
->where(
$queryBuilder->expr()->and(
$queryBuilder->expr()->eq(
'pid',
$queryBuilder->createNamedParameter($pageId, Connection::PARAM_INT)
),
$queryBuilder->expr()->in(
'sys_language_uid',
$queryBuilder->createNamedParameter([$activePageRecord['language_tag'], -1], Connection::PARAM_INT_ARRAY)
)
)
)
->orderBy('colPos')
->addOrderBy('sorting')
->executeQuery()
->fetchAllAssociative();
// Enrich list of records
$items = [];
foreach ($contentElements as $contentElement) {
BackendUtility::workspaceOL('tt_content', $contentElement, $this->getBackendUser()->workspace, true);
if (is_array($contentElement)) {
// Ensure to always link to the live version of the record
if ((int)$contentElement['t3ver_oid'] > 0) {
$contentElementId = (int)$contentElement['t3ver_oid'];
} else {
$contentElementId = (int)$contentElement['uid'];
}
$contentElement['url'] = $linkService->asString(['type' => LinkService::TYPE_PAGE, 'pageuid' => $pageId, 'fragment' => $contentElementId]);
$contentElement['isSelected'] = (int)($this->linkParts['url']['fragment'] ?? 0) === $contentElementId;
$contentElement['icon'] = $this->iconFactory->getIconForRecord('tt_content', $contentElement, IconSize::SMALL)->render();
$contentElement['title'] = BackendUtility::getRecordTitle('tt_content', $contentElement, true);
$items[] = $contentElement;
}
}
$this->view->assign('contentElements', $items);
}
}
/**
* @return string[] Array of body-tag attributes
*/
public function getBodyTagAttributes()
{
if (count($this->linkParts) === 0 || empty($this->linkParts['url']['pageuid'])) {
return [];
}
return [
'data-linkbrowser-current-link' => GeneralUtility::makeInstance(LinkService::class)->asString([
'type' => LinkService::TYPE_PAGE,
'pageuid' => (int)$this->linkParts['url']['pageuid'],
'fragment' => $this->linkParts['url']['fragment'] ?? '',
]),
];
}
/**
* @param array $values Array of values to include into the parameters or which might influence the parameters
* @return array Array of parameters which have to be added to URLs
*/
public function getUrlParameters(array $values): array
{
$parameters = [
'expandPage' => isset($values['pid']) ? (int)$values['pid'] : $this->expandPage,
];
return array_merge($this->linkBrowser->getUrlParameters($values), $parameters);
}
/**
* @param string[] $fieldDefinitions Array of link attribute field definitions
* @return string[]
*/
public function modifyLinkAttributes(array $fieldDefinitions)
{
$configuration = $this->linkBrowser->getConfiguration();
// Depending on where the configuration is set it can be 'pageIdSelector' (CKEditor yaml) or 'pageIdSelector.' (TSconfig)
if (!empty($configuration['pageIdSelector']['enabled']) || !empty($configuration['pageIdSelector.']['enabled'])) {
$this->linkAttributes[] = 'pageIdSelector';
$fieldDefinitions['pageIdSelector'] = '
<form><div class="row mt-3">
<label class="col-3 col-form-label">
' . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_browse_links.xlf:page_id')) . '
</label>
<div class="col-2">
<input type="number" size="6" name="luid" id="luid" class="form-control" />
</div>
<div class="col-7">
<input class="btn btn-default t3js-pageLink" type="submit" value="' . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_browse_links.xlf:setLink')) . '" />
</div>
</div></form>';
}
return $fieldDefinitions;
}
protected function isPageLinkable(array $page): bool
{
return $this->pageDoktypeRegistry->isPageViewable(
(int)$page['doktype'],
(int)$page['uid']
);
}
}
+274
View File
@@ -0,0 +1,274 @@
<?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\LinkHandler;
use Psr\Http\Message\ServerRequestInterface;
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
use TYPO3\CMS\Backend\Controller\AbstractLinkBrowserController;
use TYPO3\CMS\Backend\Module\ModuleData;
use TYPO3\CMS\Backend\RecordList\ElementBrowserRecordList;
use TYPO3\CMS\Backend\Tree\View\LinkParameterProviderInterface;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Backend\View\RecordSearchBoxComponent;
use TYPO3\CMS\Core\Imaging\IconSize;
use TYPO3\CMS\Core\LinkHandling\LinkService;
use TYPO3\CMS\Core\Schema\TcaSchemaFactory;
use TYPO3\CMS\Core\Type\Bitmask\Permission;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\MathUtility;
/**
* This link handler allows linking to arbitrary database records.
* They can be configured in addition to default core link handlers and are rendered
* as additional tab in the link browser.
*
* A typical use case is linking a single new record.
*
* Additional page TSconfig TCEMAIN.linkHandler setup is necessary to use this.
*
* A typical configuration looks like the below snippet. It configures a tab that allows linking to
* ext:news news records ("table" is mandatory), labels them as "Book reports" (LLL: is possible),
* forces a specific page-uid (optional), and hides page-tree selection (optional).
*
* TCEMAIN.linkHandler.bookreports {
* handler = TYPO3\CMS\Backend\LinkHandler\RecordLinkHandler
* label = Book Reports
* configuration {
* table = tx_news_domain_model_news
* storagePid = 42
* hidePageTree = 1
* }
* }
*
* @internal This class is a specific LinkHandler implementation and is not part of the TYPO3's Core API.
*/
#[Autoconfigure(public: true, shared: false)]
final class RecordLinkHandler extends AbstractLinkHandler implements LinkHandlerInterface, LinkParameterProviderInterface
{
/**
* Configuration key in TSconfig TCEMAIN.linkHandler.<identifier>
*/
private string $identifier;
/**
* Specific TSconfig for the current instance (corresponds to TCEMAIN.linkHandler.record.<identifier>.configuration)
*/
private array $configuration = [];
/**
* Parts of the current link
*/
private array $linkParts = [];
private int $expandPage = 0;
public function __construct(
private readonly ElementBrowserRecordList $elementBrowserRecordList,
private readonly RecordSearchBoxComponent $recordSearchBoxComponent,
private readonly LinkService $linkService,
private readonly TcaSchemaFactory $tcaSchemaFactory,
) {
parent::__construct();
}
public function initialize(AbstractLinkBrowserController $linkBrowser, $identifier, array $configuration)
{
parent::initialize($linkBrowser, $identifier, $configuration);
$this->identifier = $identifier;
if (empty($configuration['table'])) {
throw new \LogicException(
'Page TSconfig TCEMAIN.linkHandler.' . $identifier . '.configuration.table is mandatory and must be set to a table name.',
1657960610
);
}
$this->configuration = $configuration;
}
/**
* Checks if this is the right handler for the given link.
* Also stores information locally about currently linked record.
*
* @param array $linkParts Link parts as returned from TypoLinkCodecService
*/
public function canHandleLink(array $linkParts): bool
{
if (!$linkParts['url'] || !isset($linkParts['url']['identifier']) || $linkParts['url']['identifier'] !== $this->identifier) {
return false;
}
$data = $linkParts['url'];
// Get the related record
$table = $this->configuration['table'];
$record = BackendUtility::getRecord($table, $data['uid']);
if ($record === null) {
$linkParts['title'] = $this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_browse_links.xlf:recordNotFound');
} else {
$linkParts['pid'] = (int)$record['pid'];
$linkParts['title'] = !empty($linkParts['title']) ? $linkParts['title'] : BackendUtility::getRecordTitle($table, $record);
}
$linkParts['tableName'] = $this->tcaSchemaFactory->get($table)->getTitle($this->getLanguageService()->sL(...));
$linkParts['url']['type'] = $linkParts['type'];
$this->linkParts = $linkParts;
return true;
}
/**
* Formats information for the current record for HTML output.
*/
public function formatCurrentUrl(): string
{
return sprintf(
'%s: %s [uid: %d]',
$this->linkParts['tableName'],
$this->linkParts['title'],
$this->linkParts['url']['uid']
);
}
/**
* Renders the link handler.
*/
public function render(ServerRequestInterface $request): string
{
$this->pageRenderer->loadJavaScriptModule('@typo3/backend/record-link-handler.js');
$this->pageRenderer->loadJavaScriptModule('@typo3/backend/recordlist.js');
$this->pageRenderer->loadJavaScriptModule('@typo3/backend/record-search.js');
$this->pageRenderer->loadJavaScriptModule('@typo3/backend/column-selector-button.js');
$this->pageRenderer->loadJavaScriptModule('@typo3/backend/tree/page-browser.js');
$this->getBackendUser()->initializeWebmountsForElementBrowser();
// Define the current page
if (isset($request->getQueryParams()['expandPage'])) {
$this->expandPage = (int)$request->getQueryParams()['expandPage'];
} elseif (isset($this->configuration['storagePid'])) {
$this->expandPage = (int)$this->configuration['storagePid'];
} elseif (isset($this->linkParts['pid'])) {
$this->expandPage = (int)$this->linkParts['pid'];
}
$pageTreeMountPoints = (string)($this->configuration['pageTreeMountPoints'] ?? '');
$this->view->assignMultiple([
'treeEnabled' => (bool)($this->configuration['hidePageTree'] ?? false) === false,
'pageTreeMountPoints' => GeneralUtility::intExplode(',', $pageTreeMountPoints, true),
'recordList' => $this->renderTableRecords($request),
'initialNavigationWidth' => $this->getBackendUser()->uc['selector']['navigation']['width'] ?? 250,
'treeActions' => ['link'],
]);
return $this->view->render('LinkBrowser/Record');
}
/**
* Returns attributes for the body tag.
*
* @return string[] Array of body-tag attributes
*/
public function getBodyTagAttributes(): array
{
$attributes = [
'data-linkbrowser-identifier' => 't3://record?identifier=' . $this->identifier . '&uid=',
];
if (!empty($this->linkParts)) {
$attributes['data-linkbrowser-current-link'] = $this->linkService->asString($this->linkParts['url']);
}
return $attributes;
}
/**
* Returns all parameters needed to build a URL with all the necessary information.
*
* @param array $values Array of values to include into the parameters or which might influence the parameters
* @return array Array of parameters which have to be added to URLs
*/
public function getUrlParameters(array $values): array
{
$pid = isset($values['pid']) ? (int)$values['pid'] : $this->expandPage;
$parameters = [
'expandPage' => $pid,
];
return array_merge(
$this->linkBrowser->getUrlParameters($values),
['P' => $this->linkBrowser->getParameters()],
$parameters
);
}
/**
* Render elements of configured table
*/
private function renderTableRecords(ServerRequestInterface $request): string
{
$html = [];
$backendUser = $this->getBackendUser();
$selectedPage = $this->expandPage;
if ($selectedPage < 0 || !$backendUser->isInWebMount($selectedPage)) {
return '';
}
$table = $this->configuration['table'];
$modTSconfig = BackendUtility::getPagesTSconfig($selectedPage)['mod.']['web_list.'] ?? [];
$permsClause = $backendUser->getPagePermsClause(Permission::PAGE_SHOW);
$pageInfo = BackendUtility::readPageAccess($selectedPage, $permsClause);
$selectedTable = (string)($request->getParsedBody()['table'] ?? $request->getQueryParams()['table'] ?? '');
$searchWord = (string)($request->getParsedBody()['searchTerm'] ?? $request->getQueryParams()['searchTerm'] ?? '');
$pointer = (int)($request->getParsedBody()['pointer'] ?? $request->getQueryParams()['pointer'] ?? 0);
$searchLevels = (int)($request->getParsedBody()['search_levels'] ?? $request->getQueryParams()['search_levels'] ?? $modTSconfig['searchLevel.']['default'] ?? 0);
$existingModuleData = $backendUser->getModuleData('records');
$moduleData = new ModuleData('records', is_array($existingModuleData) ? $existingModuleData : []);
// If table is 'pages', add a pre-entry to make selected page selectable directly.
$mainPageRecord = BackendUtility::getRecordWSOL('pages', $selectedPage);
if (is_array($mainPageRecord)) {
$pText = htmlspecialchars(BackendUtility::cropToTitleLength($mainPageRecord['title']));
$html[] = '<p>' . $this->iconFactory->getIconForRecord('pages', $mainPageRecord, IconSize::SMALL)->render() . '&nbsp;';
if ($table === 'pages') {
$html[] = '<span data-uid="' . htmlspecialchars((string)$mainPageRecord['uid']) . '" data-table="pages" data-title="' . htmlspecialchars($mainPageRecord['title']) . '">';
$html[] = '<a href="#" data-close="0">' . $this->iconFactory->getIcon('actions-plus', IconSize::SMALL)->render() . '</a>';
$html[] = '<a href="#" data-close="1">' . $pText . '</a>';
$html[] = '</span>';
} else {
$html[] = $pText;
}
$html[] = '</p>';
}
$dbList = $this->elementBrowserRecordList;
$dbList->setRequest($request);
$dbList->setModuleData($moduleData);
$dbList->setOverrideUrlParameters(array_merge($this->getUrlParameters([]), ['mode' => 'db', 'expandPage' => $selectedPage]), $request);
$dbList->setIsEditable(false);
$dbList->calcPerms = new Permission($backendUser->calcPerms($pageInfo));
$dbList->noControlPanels = true;
$dbList->clickMenuEnabled = false;
$dbList->displayRecordDownload = false;
$dbList->tableList = $table;
$dbList->start($selectedPage, $selectedTable, MathUtility::forceIntegerInRange($pointer, 0, 100000), $searchWord, $searchLevels);
$html[] = $this->recordSearchBoxComponent
->setAllowedSearchLevels((array)($modTSconfig['searchLevel.']['items.'] ?? []))
->setSearchLevel($searchLevels)
->setSearchWord($searchWord)
->render($request, $dbList->listURL('', null, 'pointer,searchTerm'));
$html[] = $dbList->generateList();
return implode("\n", $html);
}
}
@@ -0,0 +1,99 @@
<?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\LinkHandler;
use Psr\Http\Message\ServerRequestInterface;
/**
* Link to a telephone number.
*
* @internal This class is a specific LinkHandler implementation and is not part of the TYPO3's Core API.
*/
class TelephoneLinkHandler extends AbstractLinkHandler implements LinkHandlerInterface
{
/**
* Parts of the current link
*
* @var array
*/
protected $linkParts = [];
/**
* We don't support updates since there is no difference to simply set the link again.
*
* @var bool
*/
protected $updateSupported = false;
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
// remove unsupported link attributes
foreach (['target', 'rel'] as $attribute) {
$position = array_search($attribute, $this->linkAttributes, true);
if ($position !== false) {
unset($this->linkAttributes[$position]);
}
}
}
/**
* Checks if this is the handler for the given link
*
* The handler may store this information locally for later usage.
*
* @param array $linkParts Link parts as returned from TypoLinkCodecService
*/
public function canHandleLink(array $linkParts): bool
{
if (isset($linkParts['url']['telephone'])) {
$this->linkParts = $linkParts;
return true;
}
return false;
}
/**
* Format the current link for HTML output
*/
public function formatCurrentUrl(): string
{
return $this->linkParts['url']['telephone'];
}
/**
* Render the link handler
*/
public function render(ServerRequestInterface $request): string
{
$this->pageRenderer->loadJavaScriptModule('@typo3/backend/telephone-link-handler.js');
$this->view->assign('telephone', !empty($this->linkParts) ? $this->linkParts['url']['telephone'] : '');
return $this->view->render('LinkBrowser/Telephone');
}
/**
* @return string[] Array of body-tag attributes
*/
public function getBodyTagAttributes(): array
{
return [];
}
}
+100
View File
@@ -0,0 +1,100 @@
<?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\LinkHandler;
use Psr\Http\Message\ServerRequestInterface;
/**
* Link to an arbitrary external URL.
*
* @internal This class is a specific LinkHandler implementation and is not part of the TYPO3's Core API.
*/
class UrlLinkHandler extends AbstractLinkHandler implements LinkHandlerInterface
{
/**
* Parts of the current link
*
* @var array
*/
protected $linkParts = [];
/**
* We don't support updates since there is no difference to simply set the link again.
*
* @var bool
*/
protected $updateSupported = false;
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
// remove unsupported link attribute
unset($this->linkAttributes[array_search('params', $this->linkAttributes, true)]);
}
/**
* Checks if this is the handler for the given link
*
* The handler may store this information locally for later usage.
*
* @param array $linkParts Link parts as returned from TypoLinkCodecService
*
* @return bool
*/
public function canHandleLink(array $linkParts)
{
if (!isset($linkParts['url']['url'])) {
return false;
}
$linkParts['url'] = $linkParts['url']['url'];
$this->linkParts = $linkParts;
return true;
}
/**
* Format the current link for HTML output
*
* @return string
*/
public function formatCurrentUrl()
{
return $this->linkParts['url'];
}
/**
* Render the link handler
*/
public function render(ServerRequestInterface $request)
{
$this->pageRenderer->loadJavaScriptModule('@typo3/backend/url-link-handler.js');
$this->view->assign('url', !empty($this->linkParts) ? $this->linkParts['url'] : '');
return $this->view->render('LinkBrowser/Url');
}
/**
* @return string[] Array of body-tag attributes
*/
public function getBodyTagAttributes()
{
return [];
}
}