Commit 7aad9f6d authored by Benni Mack's avatar Benni Mack

[TASK] Centralize LinkBrowser / ElementBrowser HTML

This change migrates the ElementBrowser / LinkBrowser HTML
Layouts to use the ElementBrowser Layout.

In addition, the TypeScript files now work without jQuery.

Resolves: #93880
Releases: master
Change-Id: Ia9de506ce7a8534fb3ba4757ee6c1980c71c1428
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/68725Tested-by: default avatarTYPO3com <noreply@typo3.com>
Tested-by: core-ci's avatarcore-ci <typo3@b13.com>
Tested-by: Richard Haeser's avatarRichard Haeser <richard@richardhaeser.com>
Tested-by: default avatarDaniel Sattler <sattler@b13.de>
Tested-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
Reviewed-by: Richard Haeser's avatarRichard Haeser <richard@richardhaeser.com>
Reviewed-by: default avatarDaniel Sattler <sattler@b13.de>
Reviewed-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
parent 571a5e8a
......@@ -11,10 +11,10 @@
* The TYPO3 project - inspiring people to share!
*/
import $ from 'jquery';
import LinkBrowser = require('./LinkBrowser');
// Yes we really need this import, because Tree... is used in inline markup...
import Tree = require('TYPO3/CMS/Backend/LegacyTree');
import RegularEvent from 'TYPO3/CMS/Core/Event/RegularEvent';
/**
* Module: TYPO3/CMS/Recordlist/FileLinkHandler
......@@ -22,27 +22,22 @@ import Tree = require('TYPO3/CMS/Backend/LegacyTree');
* @exports TYPO3/CMS/Recordlist/FileLinkHandler
*/
class FileLinkHandler {
currentLink: string = '';
constructor() {
// until we use onclick attributes, we need the Tree component
Tree.noop();
$(() => {
this.currentLink = $('body').data('currentLink');
$('a.t3js-fileLink').on('click', this.linkFile);
$('input.t3js-linkCurrent').on('click', this.linkCurrent);
});
}
new RegularEvent('click', (evt: MouseEvent, targetEl: HTMLElement): void => {
evt.preventDefault();
LinkBrowser.finalizeFunction(targetEl.getAttribute('href'));
}).delegateTo(document, 'a.t3js-fileLink');
public linkFile = (event: JQueryEventObject): void => {
event.preventDefault();
LinkBrowser.finalizeFunction($(event.currentTarget).attr('href'));
}
// Link to current page
new RegularEvent('click', (evt: MouseEvent, targetEl: HTMLElement): void => {
evt.preventDefault();
LinkBrowser.finalizeFunction(document.body.dataset.currentLink);
}).delegateTo(document, 'input.t3js-linkCurrent');
public linkCurrent = (event: JQueryEventObject): void => {
event.preventDefault();
LinkBrowser.finalizeFunction(this.currentLink);
}
}
export = new FileLinkHandler();
......@@ -11,8 +11,8 @@
* The TYPO3 project - inspiring people to share!
*/
import $ from 'jquery';
import LinkBrowser = require('./LinkBrowser');
import RegularEvent from 'TYPO3/CMS/Core/Event/RegularEvent';
/**
* Module: TYPO3/CMS/Recordlist/MailLinkHandler
......@@ -21,22 +21,20 @@ import LinkBrowser = require('./LinkBrowser');
*/
class MailLinkHandler {
constructor() {
$((): void => {
$('#lmailform').on('submit', (event: JQueryEventObject): void => {
event.preventDefault();
new RegularEvent('submit', (evt: MouseEvent, targetEl: HTMLElement): void => {
evt.preventDefault();
const inputField = targetEl.querySelector('[name="lemail"]') as HTMLInputElement;
let value = inputField.value;
if (value === 'mailto:') {
return;
}
let value = $(event.currentTarget).find('[name="lemail"]').val();
if (value === 'mailto:') {
return;
}
while (value.substr(0, 7) === 'mailto:') {
value = value.substr(7);
}
while (value.substr(0, 7) === 'mailto:') {
value = value.substr(7);
}
LinkBrowser.finalizeFunction('mailto:' + value);
});
});
LinkBrowser.finalizeFunction('mailto:' + value);
}).delegateTo(document, '#lmailform');
}
}
......
......@@ -11,8 +11,8 @@
* The TYPO3 project - inspiring people to share!
*/
import $ from 'jquery';
import LinkBrowser = require('./LinkBrowser');
import RegularEvent from 'TYPO3/CMS/Core/Event/RegularEvent';
/**
* Module: TYPO3/CMS/Recordlist/PageLinkHandler
......@@ -20,52 +20,38 @@ import LinkBrowser = require('./LinkBrowser');
* Page link interaction
*/
class PageLinkHandler {
private currentLink: string = '';
constructor() {
$((): void => {
this.currentLink = $('body').data('currentLink');
$('a.t3js-pageLink').on('click', this.linkPage);
$('input.t3js-linkCurrent').on('click', this.linkCurrent);
$('input.t3js-pageLink').on('click', this.linkPageByTextfield);
});
}
/**
* @param {JQueryEventObject} event
*/
public linkPage = (event: JQueryEventObject): void => {
event.preventDefault();
LinkBrowser.finalizeFunction($(event.currentTarget).attr('href'));
new RegularEvent('click', (evt: MouseEvent, targetEl: HTMLElement): void => {
evt.preventDefault();
LinkBrowser.finalizeFunction(targetEl.getAttribute('href'));
}).delegateTo(document, 'a.t3js-pageLink');
// Link to current page
new RegularEvent('click', (evt: MouseEvent, targetEl: HTMLElement): void => {
evt.preventDefault();
LinkBrowser.finalizeFunction(document.body.dataset.currentLink);
}).delegateTo(document, 'input.t3js-linkCurrent');
// Input field
new RegularEvent('click', (evt: MouseEvent, targetEl: HTMLElement): void => {
evt.preventDefault();
this.linkPageByTextfield();
}).delegateTo(document, 'input.t3js-pageLink');
}
/**
* @param {JQueryEventObject} event
*/
public linkPageByTextfield = (event: JQueryEventObject): void => {
event.preventDefault();
let value = $('#luid').val();
private linkPageByTextfield = (): void => {
const textField = document.getElementById('luid') as HTMLInputElement;
let value = textField.value;
if (!value) {
return;
}
// make sure we use proper link syntax if this is an integer only
const valueAsNumber = parseInt(value, 10);
if (!isNaN(valueAsNumber)) {
value = 't3://page?uid=' + valueAsNumber;
}
LinkBrowser.finalizeFunction(value);
}
/**
* @param {JQueryEventObject} event
*/
public linkCurrent = (event: JQueryEventObject): void => {
event.preventDefault();
LinkBrowser.finalizeFunction(this.currentLink);
}
}
export = new PageLinkHandler();
......@@ -19,21 +19,15 @@ import RegularEvent from 'TYPO3/CMS/Core/Event/RegularEvent';
* record link interaction
*/
class RecordLinkHandler {
private currentLink: string = '';
private identifier: string = '';
constructor() {
this.currentLink = document.body.dataset.currentLink;
this.identifier = document.body.dataset.identifier;
new RegularEvent('click', (evt: MouseEvent, targetEl: HTMLElement): void => {
evt.preventDefault();
const data = targetEl.closest('span').dataset;
LinkBrowser.finalizeFunction(this.identifier + data.uid);
LinkBrowser.finalizeFunction(document.body.dataset.identifier + data.uid);
}).delegateTo(document, '[data-close]');
new RegularEvent('click', (evt: MouseEvent, targetEl: HTMLElement): void => {
evt.preventDefault();
LinkBrowser.finalizeFunction(this.currentLink);
LinkBrowser.finalizeFunction(document.body.dataset.currentLink);
}).delegateTo(document, 'input.t3js-linkCurrent');
}
}
......
......@@ -11,8 +11,8 @@
* The TYPO3 project - inspiring people to share!
*/
import $ from 'jquery';
import LinkBrowser = require('./LinkBrowser');
import RegularEvent from 'TYPO3/CMS/Core/Event/RegularEvent';
/**
* Module: TYPO3/CMS/Recordlist/TelephoneLinkHandler
......@@ -21,21 +21,19 @@ import LinkBrowser = require('./LinkBrowser');
*/
class TelephoneLinkHandler {
constructor() {
$((): void => {
$('#ltelephoneform').on('submit', (event: JQueryEventObject): void => {
event.preventDefault();
new RegularEvent('submit', (evt: MouseEvent, targetEl: HTMLElement): void => {
evt.preventDefault();
const inputField = targetEl.querySelector('[name="ltelephone"]') as HTMLInputElement;
let value = inputField.value;
if (value === 'tel:') {
return;
}
if (value.startsWith('tel:')) {
value = value.substr(4);
}
let value = $(event.currentTarget).find('[name="ltelephone"]').val();
if (value === 'tel:') {
return;
}
if (value.startsWith('tel:')) {
value = value.substr(4);
}
LinkBrowser.finalizeFunction('tel:' + value);
});
});
LinkBrowser.finalizeFunction('tel:' + value);
}).delegateTo(document, '#ltelephoneform');
}
}
......
......@@ -11,8 +11,8 @@
* The TYPO3 project - inspiring people to share!
*/
import $ from 'jquery';
import LinkBrowser = require('./LinkBrowser');
import RegularEvent from 'TYPO3/CMS/Core/Event/RegularEvent';
/**
* Module: TYPO3/CMS/Recordlist/UrlLinkHandler
......@@ -21,19 +21,15 @@ import LinkBrowser = require('./LinkBrowser');
*/
class UrlLinkHandler {
constructor() {
$((): void => {
$('#lurlform').on('submit', this.link);
});
}
public link = (event: JQueryEventObject): void => {
event.preventDefault();
const value = $(event.currentTarget).find('[name="lurl"]').val();
if (value === '') {
return;
}
LinkBrowser.finalizeFunction(value);
new RegularEvent('submit', (evt: MouseEvent, targetEl: HTMLElement): void => {
evt.preventDefault();
const inputField = targetEl.querySelector('[name="lurl"]') as HTMLInputElement;
let value = inputField.value;
if (value === '') {
return;
}
LinkBrowser.finalizeFunction(value);
}).delegateTo(document, '#lurlform');
}
}
......
<div class="element-browser {f:if(condition: treeEnabled, then: 'scaffold-content-navigation-available scaffold-content-navigation-expanded')}">
<div class="element-browser-panel element-browser-main">
<f:if condition="{treeEnabled}">
<div class="element-browser-main-sidebar" style="width: {initialNavigationWidth}px">
<div class="element-browser-body">
<f:render section="Tree"/>
</div>
</div>
<typo3-backend-navigation-switcher
parent=".element-browser"
navigation=".element-browser-main-sidebar"
minimum-width="250"
initial-width="{initialNavigationWidth}"
persistence-identifier="selector.navigation.width"
></typo3-backend-navigation-switcher>
</f:if>
<div class="element-browser-main-content">
<div class="element-browser-body">
<f:flashMessages queueIdentifier="{flashMessageQueueIdentifier}"/>
<f:render section="Main"/>
</div>
<div class="element-browser-main">
<div class="element-browser-main-content">
<div class="element-browser-body">
<f:render section="Content" />
</div>
</div>
</div>
<div class="element-browser-main scaffold-content-navigation-available scaffold-content-navigation-expanded">
<div class="element-browser-main-sidebar" style="width: {initialNavigationWidth}px">
<div class="element-browser-body">
<f:render section="Navigation" />
</div>
</div>
<typo3-backend-navigation-switcher
parent=".element-browser-main"
navigation=".element-browser-main-sidebar"
minimum-width="250"
initial-width="{initialNavigationWidth}"
persistence-identifier="selector.navigation.width"
></typo3-backend-navigation-switcher>
<div class="element-browser-main-content">
<div class="element-browser-body">
<f:render section="Content" />
</div>
</div>
</div>
<f:layout name="ElementBrowser" />
<f:section name="Tree">
<f:layout name="{f:if(condition: treeEnabled, then: 'ElementBrowserWithNavigation', else: 'ElementBrowser')}" />
<f:section name="Navigation">
<f:if condition="{temporaryTreeMountCancelUrl}">
<p><a href="{temporaryTreeMountCancelUrl}" class="btn btn-primary"><f:translate key="LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.temporaryDBmount" /></a></p>
</f:if>
<f:format.raw>{tree}</f:format.raw>
</f:section>
<f:section name="Main">
<f:section name="Content">
<f:flashMessages queueIdentifier="{flashMessageQueueIdentifier}"/>
<f:format.raw>{content}</f:format.raw>
</f:section>
......@@ -78,9 +78,9 @@ abstract class AbstractLinkHandler
$this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
$this->view = GeneralUtility::makeInstance(StandaloneView::class);
$this->view->getRequest()->setControllerExtensionName('recordlist');
$this->view->setTemplateRootPaths([GeneralUtility::getFileAbsFileName('EXT:recordlist/Resources/Private/Templates/LinkBrowser')]);
$this->view->setPartialRootPaths([GeneralUtility::getFileAbsFileName('EXT:recordlist/Resources/Private/Partials/LinkBrowser')]);
$this->view->setLayoutRootPaths([GeneralUtility::getFileAbsFileName('EXT:recordlist/Resources/Private/Layouts/LinkBrowser')]);
$this->view->setTemplateRootPaths(['EXT:recordlist/Resources/Private/Templates/LinkBrowser/']);
$this->view->setPartialRootPaths(['EXT:recordlist/Resources/Private/Partials/LinkBrowser/']);
$this->view->setLayoutRootPaths(['EXT:backend/Resources/Private/Layouts/', 'EXT:recordlist/Resources/Private/Layouts/']);
}
/**
......
......@@ -151,11 +151,13 @@ class RecordLinkHandler extends AbstractLinkHandler implements LinkHandlerInterf
$this->getUrlParameters([])
);
$treeEnabled = ($this->configuration['hidePageTree'] ?? false) === false;
$path = GeneralUtility::getFileAbsFileName('EXT:recordlist/Resources/Private/Templates/LinkBrowser/Record.html');
$view = GeneralUtility::makeInstance(StandaloneView::class);
$view->setTemplatePathAndFilename($path);
$view->assignMultiple([
'tree' => $this->configuration['hidePageTree'] ? '' : $this->renderPageTree(),
'treeEnabled' => $treeEnabled,
'tree' => $treeEnabled ? $this->renderPageTree() : '',
'recordList' => $recordList,
'initialNavigationWidth' => $this->getBackendUser()->uc['selector']['navigation']['width'] ?? 250
]);
......
<div class="element-browser-panel element-browser-main scaffold-content-navigation-available scaffold-content-navigation-expanded">
<div class="element-browser-main-sidebar" style="width: {initialNavigationWidth}px">
<div class="element-browser-body">
{tree -> f:format.raw()}
</div>
</div>
<typo3-backend-navigation-switcher
parent=".element-browser-main"
navigation=".element-browser-main-sidebar"
minimum-width="250"
initial-width="{initialNavigationWidth}"
persistence-identifier="selector.navigation.width"
></typo3-backend-navigation-switcher>
<div class="element-browser-main-content">
<div class="element-browser-body">
<f:if condition="{selectedFolder}">
<div class="row pb-3">
<h3><f:translate key="LLL:EXT:recordlist/Resources/Private/Language/locallang_browse_links.xlf:files" /></h3>
<span title="{selectedFolder.identifier}">
{selectedFolderIcon -> f:format.raw()} {selectedFolderTitle}
</span>
<f:if condition="{itemsInSelectedFolder}">
<ul class="list-tree">
<f:for each="{itemsInSelectedFolder}" as="file">
<li{f:if(condition: '{file.uid} == {currentIdentifier}', then: ' class="active"')}>
<span class="list-tree-group">
<a href="{file.url}" class="t3js-fileLink list-tree-group" title="{file.name}">
<span class="list-tree-icon">
<span title="{file.name} ({file.size})">{file.icon -> f:format.raw()}</span>
</span>
<span class="list-tree-title">{file.title}</span>
</a>
</span>
</li>
</f:for>
</ul>
</f:if>
</div>
<f:layout name="ElementBrowserWithNavigation" />
<f:section name="Navigation">
{tree -> f:format.raw()}
</f:section>
<f:section name="Content">
<f:if condition="{selectedFolder}">
<div class="row pb-3">
<h3><f:translate key="LLL:EXT:recordlist/Resources/Private/Language/locallang_browse_links.xlf:files" /></h3>
<span title="{selectedFolder.identifier}">
{selectedFolderIcon -> f:format.raw()} {selectedFolderTitle}
</span>
<f:if condition="{itemsInSelectedFolder}">
<ul class="list-tree">
<f:for each="{itemsInSelectedFolder}" as="file">
<li{f:if(condition: '{file.uid} == {currentIdentifier}', then: ' class="active"')}>
<span class="list-tree-group">
<a href="{file.url}" class="t3js-fileLink list-tree-group" title="{file.name}">
<span class="list-tree-icon">
<span title="{file.name} ({file.size})">{file.icon -> f:format.raw()}</span>
</span>
<span class="list-tree-title">{file.title}</span>
</a>
</span>
</li>
</f:for>
</ul>
</f:if>
{uploadFileForm -> f:format.raw()}
{createFolderForm -> f:format.raw()}
</div>
</div>
</div>
</f:if>
{uploadFileForm -> f:format.raw()}
{createFolderForm -> f:format.raw()}
</f:section>
<div class="element-browser-panel element-browser-main scaffold-content-navigation-available scaffold-content-navigation-expanded">
<div class="element-browser-main-sidebar" style="width: {initialNavigationWidth}px">
<div class="element-browser-body">
{tree -> f:format.raw()}
</div>
</div>
<typo3-backend-navigation-switcher
parent=".element-browser-main"
navigation=".element-browser-main-sidebar"
minimum-width="250"
initial-width="{initialNavigationWidth}"
persistence-identifier="selector.navigation.width"
></typo3-backend-navigation-switcher>
<div class="element-browser-main-content">
<div class="element-browser-body">
<f:if condition="{selectedFolder}">
<h3><f:translate key="LLL:EXT:recordlist/Resources/Private/Language/locallang_browse_links.xlf:folders" /></h3>
<span class="{f:if(condition: '{selectedFolder.combinedIdentifier} == {currentIdentifier}', then: 'bg-success')}">
<f:if condition="{selectedFolderUrl}">
<f:then>
<a href="{selectedFolderUrl}" class="t3js-fileLink list-tree-group" title="{selectedFolder.identifier}">{selectedFolderIcon -> f:format.raw()} {selectedFolderTitle}</a>
</f:then>
<f:else>
{selectedFolderIcon -> f:format.raw()} {selectedFolderTitle}
</f:else>
</f:if>
</span>
<f:if condition="{itemsInSelectedFolder}">
<ul class="list-tree">
<f:for each="{itemsInSelectedFolder}" as="folder">
<li{f:if(condition: '{folder.identifier} == {currentIdentifier}', then: ' class="active"')}>
<span class="list-tree-group">
<a href="{folder.url}" class="t3js-fileLink list-tree-group" title="{folder.name}">
<span class="list-tree-icon">
<span title="{folder.name}">{folder.icon -> f:format.raw()}</span>
</span>
<span class="list-tree-title">{folder.title}</span>
</a>
</span>
</li>
</f:for>
</ul>
</f:if>
<f:layout name="ElementBrowserWithNavigation" />
<f:section name="Navigation">
{tree -> f:format.raw()}
</f:section>
<f:section name="Content">
<f:if condition="{selectedFolder}">
<h3><f:translate key="LLL:EXT:recordlist/Resources/Private/Language/locallang_browse_links.xlf:folders" /></h3>
<span class="{f:if(condition: '{selectedFolder.combinedIdentifier} == {currentIdentifier}', then: 'bg-success')}">
<f:if condition="{selectedFolderUrl}">
<f:then>
<a href="{selectedFolderUrl}" class="t3js-fileLink list-tree-group" title="{selectedFolder.identifier}">{selectedFolderIcon -> f:format.raw()} {selectedFolderTitle}</a>
</f:then>
<f:else>
{selectedFolderIcon -> f:format.raw()} {selectedFolderTitle}
</f:else>
</f:if>
{createFolderForm -> f:format.raw()}
</div>
</div>
</div>
</span>
<f:if condition="{itemsInSelectedFolder}">
<ul class="list-tree">
<f:for each="{itemsInSelectedFolder}" as="folder">
<li{f:if(condition: '{folder.identifier} == {currentIdentifier}', then: ' class="active"')}>
<span class="list-tree-group">
<a href="{folder.url}" class="t3js-fileLink list-tree-group" title="{folder.name}">
<span class="list-tree-icon">
<span title="{folder.name}">{folder.icon -> f:format.raw()}</span>
</span>
<span class="list-tree-title">{folder.title}</span>
</a>
</span>
</li>
</f:for>
</ul>
</f:if>
</f:if>
{createFolderForm -> f:format.raw()}
</f:section>
<div class="element-browser-panel element-browser-main">
<div class="element-browser-main-content">
<div class="element-browser-body">
<form action="" id="lmailform" class="form-horizontal">
<div class="form-group">
<label class="col-4 control-label"><f:translate key="LLL:EXT:recordlist/Resources/Private/Language/locallang_browse_links.xlf:emailAddress" /></label>
<div class="col-8">
<div class="input-group">
<input type="text" name="lemail" size="20" class="form-control" value="{email}" />
<div class="input-group-btn">
<input class="btn btn-default" type="submit" value="{f:translate(key: 'LLL:EXT:recordlist/Resources/Private/Language/locallang_browse_links.xlf:setLink')}" />
</div>
</div>
<f:layout name="ElementBrowser" />
<f:section name="Content">