From f68dbeb8efb2f093a228f9d9941dc077ac2c5a5d Mon Sep 17 00:00:00 2001
From: Benni Mack <benni@typo3.org>
Date: Fri, 27 May 2016 13:19:47 +0200
Subject: [PATCH] [TASK] Use Fluid for rendering LinkBrowsers

In order to keep output and compiling data separate,
the LinkBrowser / LinkPickers are moved to Fluid.

Resolves: #76332
Releases: master
Change-Id: Ib8a41a591c26af7cc965d7a098643a5c285b47db
Reviewed-on: https://review.typo3.org/48323
Reviewed-by: Wouter Wolters <typo3@wouterwolters.nl>
Tested-by: Wouter Wolters <typo3@wouterwolters.nl>
Reviewed-by: Georg Ringer <georg.ringer@gmail.com>
Tested-by: Georg Ringer <georg.ringer@gmail.com>
---
 .../Classes/Browser/FileBrowser.php           |   2 +-
 .../LinkHandler/AbstractLinkHandler.php       |  10 +
 .../Classes/LinkHandler/FileLinkHandler.php   | 189 +++++++-----------
 .../Classes/LinkHandler/FolderLinkHandler.php |  25 +--
 .../Classes/LinkHandler/MailLinkHandler.php   |  24 +--
 .../Classes/LinkHandler/PageLinkHandler.php   | 120 ++++-------
 .../Classes/LinkHandler/UrlLinkHandler.php    |  24 +--
 .../Private/Templates/LinkBrowser/File.html   |  40 ++++
 .../Private/Templates/LinkBrowser/Folder.html |  33 +++
 .../Private/Templates/LinkBrowser/Mail.html   |  13 ++
 .../Private/Templates/LinkBrowser/Page.html   |  40 ++++
 .../Private/Templates/LinkBrowser/Url.html    |  14 ++
 12 files changed, 279 insertions(+), 255 deletions(-)
 create mode 100644 typo3/sysext/recordlist/Resources/Private/Templates/LinkBrowser/File.html
 create mode 100644 typo3/sysext/recordlist/Resources/Private/Templates/LinkBrowser/Folder.html
 create mode 100644 typo3/sysext/recordlist/Resources/Private/Templates/LinkBrowser/Mail.html
 create mode 100644 typo3/sysext/recordlist/Resources/Private/Templates/LinkBrowser/Page.html
 create mode 100644 typo3/sysext/recordlist/Resources/Private/Templates/LinkBrowser/Url.html

diff --git a/typo3/sysext/recordlist/Classes/Browser/FileBrowser.php b/typo3/sysext/recordlist/Classes/Browser/FileBrowser.php
index 656a15fbfbe6..be3cb4e0b0d5 100644
--- a/typo3/sysext/recordlist/Classes/Browser/FileBrowser.php
+++ b/typo3/sysext/recordlist/Classes/Browser/FileBrowser.php
@@ -291,7 +291,7 @@ class FileBrowser extends AbstractElementBrowser implements ElementBrowserInterf
                 $pDim = '';
             }
             // Create file icon:
-            $size = ' (' . GeneralUtility::formatSize($fileObject->getSize()) . 'bytes' . ($pDim ? ', ' . $pDim : '') . ')';
+            $size = ' (' . GeneralUtility::formatSize($fileObject->getSize(), $this->getLanguageService()->sL('LLL:EXT:lang/locallang_common.xlf:byteSizeUnits')) . ($pDim ? ', ' . $pDim : '') . ')';
             $icon = '<span title="' . htmlspecialchars($fileObject->getName() . $size) . '">' . $this->iconFactory->getIconForResource($fileObject, Icon::SIZE_SMALL) . '</span>';
             // Create links for adding the file:
             $filesIndex = count($this->elements);
diff --git a/typo3/sysext/recordlist/Classes/LinkHandler/AbstractLinkHandler.php b/typo3/sysext/recordlist/Classes/LinkHandler/AbstractLinkHandler.php
index 3b80c6cc319e..af52262f2cc3 100644
--- a/typo3/sysext/recordlist/Classes/LinkHandler/AbstractLinkHandler.php
+++ b/typo3/sysext/recordlist/Classes/LinkHandler/AbstractLinkHandler.php
@@ -51,6 +51,11 @@ abstract class AbstractLinkHandler
      */
     protected $iconFactory;
 
+    /**
+     * @var \TYPO3\CMS\Fluid\View\StandaloneView
+     */
+    protected $view;
+
     /**
      * Constructor
      */
@@ -71,6 +76,11 @@ abstract class AbstractLinkHandler
     {
         $this->linkBrowser = $linkBrowser;
         $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
+        $this->view = GeneralUtility::makeInstance(\TYPO3\CMS\Fluid\View\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')]);
     }
 
     /**
diff --git a/typo3/sysext/recordlist/Classes/LinkHandler/FileLinkHandler.php b/typo3/sysext/recordlist/Classes/LinkHandler/FileLinkHandler.php
index beddb2d7b365..085a63852b82 100644
--- a/typo3/sysext/recordlist/Classes/LinkHandler/FileLinkHandler.php
+++ b/typo3/sysext/recordlist/Classes/LinkHandler/FileLinkHandler.php
@@ -57,11 +57,6 @@ class FileLinkHandler extends AbstractLinkHandler implements LinkHandlerInterfac
      */
     protected $expandFolder;
 
-    /**
-     * @var string
-     */
-    protected $additionalFolderClass = '';
-
     /**
      * Checks if this is the handler for the given link
      *
@@ -124,79 +119,33 @@ class FileLinkHandler extends AbstractLinkHandler implements LinkHandlerInterfac
         /** @var ElementBrowserFolderTreeView $folderTree */
         $folderTree = GeneralUtility::makeInstance(ElementBrowserFolderTreeView::class);
         $folderTree->setLinkParameterProvider($this);
-        $tree = $folderTree->getBrowsableTree();
+        $this->view->assign('tree', $folderTree->getBrowsableTree());
 
         // Create upload/create folder forms, if a path is given
-        $selectedFolder = false;
-        if ($this->expandFolder) {
-            $fileOrFolderObject = null;
-            try {
-                $fileOrFolderObject = ResourceFactory::getInstance()->retrieveFileOrFolderObject($this->expandFolder);
-            } catch (\Exception $e) {
-                // No path is selected
-            }
+        $selectedFolder = $this->getSelectedFolder($this->expandFolder);
 
-            if ($fileOrFolderObject instanceof Folder) {
-                // It's a folder
-                $selectedFolder = $fileOrFolderObject;
-            } elseif ($fileOrFolderObject instanceof FileInterface) {
-                // It's a file
-                try {
-                    $selectedFolder = $fileOrFolderObject->getParentFolder();
-                } catch (\Exception $e) {
-                    // Accessing the parent folder failed for some reason. e.g. permissions
-                }
-            }
-        }
-
-        $backendUser = $this->getBackendUser();
-        // If no folder is selected, get the user's default upload folder
-        if (!$selectedFolder) {
-            try {
-                $selectedFolder = $backendUser->getDefaultUploadFolder();
-            } catch (\Exception $e) {
-                // The configured default user folder does not exist
-            }
-        }
         // Build the file upload and folder creation form
-        $uploadForm = '';
-        $createFolder = '';
-        $content = '';
         if ($selectedFolder) {
             $folderUtilityRenderer = GeneralUtility::makeInstance(FolderUtilityRenderer::class, $this);
             $uploadForm = $this->mode === 'file' ? $folderUtilityRenderer->uploadForm($selectedFolder, []) : '';
             $createFolder = $folderUtilityRenderer->createFolder($selectedFolder);
-        }
-        // Insert the upload form on top, if so configured
-        if ($backendUser->getTSConfigVal('options.uploadFieldsInTopOfEB')) {
-            $content .= $uploadForm;
-        }
 
-        // Render the filelist if there is a folder selected
-        $files = '';
-        if ($selectedFolder) {
-            $parameters = $this->linkBrowser->getUrlParameters();
-            $allowedExtensions = isset($parameters['allowedExtensions']) ? $parameters['allowedExtensions'] : '';
-            $files = $this->expandFolder($selectedFolder, $allowedExtensions);
-        }
-        // Create folder tree:
-        $content .= '
-            <div class="row link-browser-section link-browser-filetree">
-                <div class="col-xs-6">
-                    <h3>' . $this->getLanguageService()->getLL('folderTree') . ':</h3>' .
-                    $tree . '
-                </div>
-                <div class="col-xs-6">
-                    ' . $files . '
-                </div>
-            </div>';
+            // Insert the upload form on top, if so configured
+            $positionOfUploadFieldsOnTop = $this->getBackendUser()->getTSConfigVal('options.uploadFieldsInTopOfEB');
+            $this->view->assign('positionOfUploadFields', $positionOfUploadFieldsOnTop ? 'top' : 'bottom');
+            $this->view->assign('uploadFileForm', $uploadForm);
+            $this->view->assign('createFolderForm', $createFolder);
 
-        // Adding create folder + upload form if applicable
-        if (!$backendUser->getTSConfigVal('options.uploadFieldsInTopOfEB')) {
-            $content .= $uploadForm;
+            // Render the file or folderlist
+            if ($selectedFolder->checkActionPermission('read')) {
+                $this->view->assign('selectedFolder', $selectedFolder);
+                $parameters = $this->linkBrowser->getUrlParameters();
+                $allowedExtensions = isset($parameters['allowedExtensions']) ? $parameters['allowedExtensions'] : '';
+                $this->expandFolder($selectedFolder, $allowedExtensions);
+            }
         }
-        $content .= $createFolder;
-        return $content;
+
+        return $this->view->render(ucfirst($this->mode));
     }
 
     /**
@@ -208,52 +157,23 @@ class FileLinkHandler extends AbstractLinkHandler implements LinkHandlerInterfac
      */
     public function expandFolder(Folder $folder, $extensionList = '')
     {
-        if (!$folder->checkActionPermission('read')) {
-            return '';
-        }
-        $out = '<h3>' . htmlspecialchars($this->getTitle()) . ':</h3>';
-
         // Create header element; The folder from which files are listed.
-        $titleLen = (int)$this->getBackendUser()->uc['titleLen'];
-        $folderIcon = $this->iconFactory->getIconForResource($folder, Icon::SIZE_SMALL);
-        $folderIcon .= htmlspecialchars(GeneralUtility::fixed_lgd_cs($folder->getIdentifier(), $titleLen));
+        $folderIcon = $this->iconFactory->getIconForResource($folder, Icon::SIZE_SMALL)->render();
+        $this->view->assign('selectedFolderIcon', $folderIcon);
+        $this->view->assign('selectedFolderTitle', GeneralUtility::fixed_lgd_cs($folder->getIdentifier(), (int)$this->getBackendUser()->uc['titleLen']));
+        $this->view->assign('currentIdentifier', !empty($this->linkParts) ? $this->linkParts['url'] : '');
 
-        $currentIdentifier = !empty($this->linkParts) ? $this->linkParts['url'] : '';
-        $selected = $currentIdentifier === $folder->getCombinedIdentifier() ? $this->additionalFolderClass : '';
-        $out .= '
-			<span class="' . $selected . '" title="' . htmlspecialchars($folder->getIdentifier()) . '">
-				' . $folderIcon . '
-			</span>
-			';
         // Get files from the folder:
-        $folderContent = $this->getFolderContent($folder, $extensionList);
-        if (!empty($folderContent)) {
-            $out .= '<ul class="list-tree">';
-            foreach ($folderContent as $fileOrFolderObject) {
-                list($fileIdentifier, $icon) = $this->renderItem($fileOrFolderObject);
-                $selected = $currentIdentifier === $fileIdentifier ? ' class="active"' : '';
-                $out .=
-                    '<li' . $selected . '>
-                        <span class="list-tree-group">
-                            <a href="#" class="t3js-fileLink list-tree-group" title="' . htmlspecialchars($fileOrFolderObject->getName()) . '" data-file="file:' . htmlspecialchars($fileIdentifier) . '">
-                                <span class="list-tree-icon">' . $icon . '</span>
-                                <span class="list-tree-title">' . htmlspecialchars(GeneralUtility::fixed_lgd_cs($fileOrFolderObject->getName(), $titleLen)) . '</span>
-                            </a>
-                        </span>
-                    </li>';
+        $fileObjects = $this->getFolderContent($folder, $extensionList);
+        $itemsInSelectedFolder = [];
+        if (!empty($fileObjects)) {
+            foreach ($fileObjects as $fileOrFolderObject) {
+                $itemsInSelectedFolder[] = $this->renderItem($fileOrFolderObject);
             }
-            $out .= '</ul>';
         }
-        return $out;
+        $this->view->assign('itemsInSelectedFolder', $itemsInSelectedFolder);
     }
 
-    /**
-     * @return string
-     */
-    protected function getTitle()
-    {
-        return $this->getLanguageService()->getLL('files');
-    }
 
     /**
      * @param Folder $folder
@@ -285,13 +205,19 @@ class FileLinkHandler extends AbstractLinkHandler implements LinkHandlerInterfac
         if (!$fileOrFolderObject instanceof File) {
             throw new \InvalidArgumentException('Expected File object, got "' . get_class($fileOrFolderObject) . '" object.', 1443651368);
         }
-        $fileIdentifier = $fileOrFolderObject->getUid();
         // Get size and icon:
-        $size = ' (' . GeneralUtility::formatSize($fileOrFolderObject->getSize()) . 'bytes)';
-        $icon = '<span title="' . htmlspecialchars($fileOrFolderObject->getName() . $size) . '">'
-            . $this->iconFactory->getIconForResource($fileOrFolderObject, Icon::SIZE_SMALL)
-            . '</span>';
-        return [$fileIdentifier, $icon];
+        $size = GeneralUtility::formatSize(
+            $fileOrFolderObject->getSize(),
+            $this->getLanguageService()->sL('LLL:EXT:lang/locallang_common.xlf:byteSizeUnits')
+        );
+
+        return [
+            'icon' => $this->iconFactory->getIconForResource($fileOrFolderObject, Icon::SIZE_SMALL)->render(),
+            'uid'  => $fileOrFolderObject->getUid(),
+            'size' => $size,
+            'name' => $fileOrFolderObject->getName(),
+            'title' => GeneralUtility::fixed_lgd_cs($fileOrFolderObject->getName(), (int)$this->getBackendUser()->uc['titleLen'])
+        ];
     }
 
     /**
@@ -336,4 +262,43 @@ class FileLinkHandler extends AbstractLinkHandler implements LinkHandlerInterfac
     {
         return $this->linkBrowser->getScriptUrl();
     }
+
+    /**
+     * Returns the currently selected folder, or th default upload folder
+     *
+     * @param string $folderIdentifier
+     * @return mixed the folder object or false if nothing was found
+     */
+    protected function getSelectedFolder($folderIdentifier = '') {
+        $selectedFolder = false;
+        if ($folderIdentifier) {
+            try {
+                $fileOrFolderObject = ResourceFactory::getInstance()->retrieveFileOrFolderObject($folderIdentifier);
+                if ($fileOrFolderObject instanceof Folder) {
+                    // It's a folder
+                    $selectedFolder = $fileOrFolderObject;
+                } elseif ($fileOrFolderObject instanceof FileInterface) {
+                    // It's a file
+                    try {
+                        $selectedFolder = $fileOrFolderObject->getParentFolder();
+                    } catch (\Exception $e) {
+                        // Accessing the parent folder failed for some reason. e.g. permissions
+                    }
+                }
+            } catch (\Exception $e) {
+                // No path is selected
+            }
+
+        }
+
+        // If no folder is selected, get the user's default upload folder
+        if (!$selectedFolder) {
+            try {
+                $selectedFolder = $this->getBackendUser()->getDefaultUploadFolder();
+            } catch (\Exception $e) {
+                // The configured default user folder does not exist
+            }
+        }
+        return $selectedFolder;
+    }
 }
diff --git a/typo3/sysext/recordlist/Classes/LinkHandler/FolderLinkHandler.php b/typo3/sysext/recordlist/Classes/LinkHandler/FolderLinkHandler.php
index 58e38ad0fd6c..10f65d6e0e94 100644
--- a/typo3/sysext/recordlist/Classes/LinkHandler/FolderLinkHandler.php
+++ b/typo3/sysext/recordlist/Classes/LinkHandler/FolderLinkHandler.php
@@ -19,6 +19,7 @@ use TYPO3\CMS\Core\Resource\FileInterface;
 use TYPO3\CMS\Core\Resource\Folder;
 use TYPO3\CMS\Core\Resource\InaccessibleFolder;
 use TYPO3\CMS\Core\Resource\ResourceInterface;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
  * Link handler for folder links
@@ -30,24 +31,11 @@ class FolderLinkHandler extends FileLinkHandler
      */
     protected $mode = 'folder';
 
-    /**
-     * @var string
-     */
-    protected $additionalFolderClass = 'bg-success';
-
     /**
      * @var string
      */
     protected $expectedClass = Folder::class;
 
-    /**
-     * @return string
-     */
-    protected function getTitle()
-    {
-        return $this->getLanguageService()->getLL('folders');
-    }
-
     /**
      * @param Folder $folder
      * @param string $extensionList
@@ -71,14 +59,15 @@ class FolderLinkHandler extends FileLinkHandler
         if (!$fileOrFolderObject instanceof Folder) {
             throw new \InvalidArgumentException('Expected Folder object, got "' . get_class($fileOrFolderObject) . '" object.', 1443651369);
         }
-        $fileIdentifier = $fileOrFolderObject->getCombinedIdentifier();
         $overlay = null;
         if ($fileOrFolderObject instanceof InaccessibleFolder) {
             $overlay = array('status-overlay-locked' => array());
         }
-        $icon = '<span title="' . htmlspecialchars($fileOrFolderObject->getName()) . '">'
-            . $this->iconFactory->getIcon('apps-filetree-folder-default', Icon::SIZE_SMALL, $overlay)->render()
-            . '</span>';
-        return [$fileIdentifier, $icon];
+        return [
+            'icon' => $this->iconFactory->getIcon('apps-filetree-folder-default', Icon::SIZE_SMALL, $overlay)->render(),
+            'identifier' => $fileOrFolderObject->getCombinedIdentifier(),
+            'name' => $fileOrFolderObject->getName(),
+            'title' => GeneralUtility::fixed_lgd_cs($fileOrFolderObject->getName(), (int)$this->getBackendUser()->uc['titleLen'])
+        ];
     }
 }
diff --git a/typo3/sysext/recordlist/Classes/LinkHandler/MailLinkHandler.php b/typo3/sysext/recordlist/Classes/LinkHandler/MailLinkHandler.php
index 149095ae39cf..6ad7704ccda6 100644
--- a/typo3/sysext/recordlist/Classes/LinkHandler/MailLinkHandler.php
+++ b/typo3/sysext/recordlist/Classes/LinkHandler/MailLinkHandler.php
@@ -91,28 +91,8 @@ class MailLinkHandler extends AbstractLinkHandler implements LinkHandlerInterfac
     {
         GeneralUtility::makeInstance(PageRenderer::class)->loadRequireJsModule('TYPO3/CMS/Recordlist/MailLinkHandler');
 
-        $lang = $this->getLanguageService();
-        $mailAddress = '
-            <!--
-                Enter mail address:
-            -->
-            <div class="link-browser-section link-browser-tab-content-mail">
-                <form action="" id="lmailform" class="form-horizontal">
-                        <div class="form-group form-group-sm">
-                            <label class="col-xs-4 control-label">' . htmlspecialchars($lang->getLL('emailAddress')) . ':</label>
-                            <div class="col-xs-6">
-                                <input type="text" name="lemail" size="20" class="form-control" value="'
-                                    . htmlspecialchars(!empty($this->linkParts) ? $this->linkParts['url'] : '')
-                                    . '" />
-                            </div>
-                            <div class="col-xs-2">
-                                <input class="btn btn-default" type="submit" value="' . htmlspecialchars($lang->getLL('setLink')) . '" />
-                            </div>
-                        </div>
-                </form>
-            </div>';
-
-        return $mailAddress;
+        $this->view->assign('email', !empty($this->linkParts) ? $this->linkParts['url'] : '');
+        return $this->view->render('Mail');
     }
 
     /**
diff --git a/typo3/sysext/recordlist/Classes/LinkHandler/PageLinkHandler.php b/typo3/sysext/recordlist/Classes/LinkHandler/PageLinkHandler.php
index f981c091c084..e60a8f0adf89 100644
--- a/typo3/sysext/recordlist/Classes/LinkHandler/PageLinkHandler.php
+++ b/typo3/sysext/recordlist/Classes/LinkHandler/PageLinkHandler.php
@@ -124,111 +124,71 @@ class PageLinkHandler extends AbstractLinkHandler implements LinkHandlerInterfac
         $pageTree->ext_showPageId = (bool)$backendUser->getTSConfigVal('options.pageTree.showPageIdWithTitle');
         $pageTree->ext_showNavTitle = (bool)$backendUser->getTSConfigVal('options.pageTree.showNavTitle');
         $pageTree->addField('nav_title');
-        $tree = $pageTree->getBrowsableTree();
 
-        return '
-            <div class="link-browser-section link-browser-pagetree">
-                <div class="col-xs-6">
-                    <h3>' . $this->getLanguageService()->getLL('pageTree') . ':</h3>'
-                            . $this->getTemporaryTreeMountCancelNotice() . $tree . '
-                </div>
-                <div class="col-xs-6">' .
-                    $this->expandPage($this->expandPage) . '
-                </div>
-            </div>';
+        $this->view->assign('temporaryTreeMountCancelLink', $this->getTemporaryTreeMountCancelNotice());
+        $this->view->assign('tree', $pageTree->getBrowsableTree());
+        $this->getRecordsOnExpandedPage($this->expandPage);
+        return $this->view->render('Page');
     }
 
     /**
-     * This displays all content elements on a page and lets you create a link to the element.
+     * This adds all content elements on a page to the view and lets you create a link to the element.
      *
-     * @param int $expPageId Page uid to expand
+     * @param int $pageId Page uid to expand
      *
-     * @return string HTML output. Returns content only if the ->expandPage value is set (pointing to a page uid to show tt_content records from ...)
+     * @return void
      */
-    public function expandPage($expPageId)
+    protected function getRecordsOnExpandedPage($pageId)
     {
         // If there is an anchor value (content element reference) in the element reference, then force an ID to expand:
-        if (!$expPageId && isset($this->linkParts['anchor'])) {
+        if (!$pageId && isset($this->linkParts['anchor'])) {
             // Set to the current link page id.
-            $expPageId = $this->linkParts['pageid'];
+            $pageId = $this->linkParts['pageid'];
         }
         // Draw the record list IF there is a page id to expand:
-        if (!$expPageId || !MathUtility::canBeInterpretedAsInteger($expPageId) || !$this->getBackendUser()->isInWebMount($expPageId)) {
-            return '';
-        }
+        if ($pageId && MathUtility::canBeInterpretedAsInteger($pageId) && $this->getBackendUser()->isInWebMount($pageId)) {
+            $pageId = (int)$pageId;
+
+            $activePageRecord = BackendUtility::getRecordWSOL('pages', $pageId);
+            $this->view->assign('expandActivePage', true);
 
-        // Set header:
-        $out = '<h3>' . $this->getLanguageService()->getLL('contentElements') . ':</h3>';
-        // Create header for listing, showing the page title/icon:
-        $mainPageRec = BackendUtility::getRecordWSOL('pages', $expPageId);
-        $db = $this->getDatabaseConnection();
-        $out .= '
-			<ul class="list-tree list-tree-root list-tree-root-clean">
-				<li class="list-tree-control-open">
-					<span class="list-tree-group">
-						<span class="list-tree-icon">' . $this->iconFactory->getIconForRecord('pages', $mainPageRec, Icon::SIZE_SMALL)->render() . '</span>
-						<span class="list-tree-title">' . htmlspecialchars(BackendUtility::getRecordTitle('pages', $mainPageRec, true)) . '</span>
-					</span>
-					<ul>
-			';
+            // 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, Icon::SIZE_SMALL)->render());
 
-        // Look up tt_content elements from the expanded page:
-        $res = $db->exec_SELECTquery(
-            'uid,header,hidden,starttime,endtime,fe_group,CType,colPos,bodytext',
-            'tt_content',
-            'pid=' . (int)$expPageId . BackendUtility::deleteClause('tt_content')
-            . BackendUtility::versioningPlaceholderClause('tt_content'),
-            '',
-            'colPos,sorting'
-        );
-        // Traverse list of records:
-        $c = 0;
-        while ($row = $db->sql_fetch_assoc($res)) {
-            $c++;
-            $icon = $this->iconFactory->getIconForRecord('tt_content', $row, Icon::SIZE_SMALL)->render();
-            $selected = '';
-            if (!empty($this->linkParts) && (int)$this->linkParts['anchor'] === (int)$row['uid']) {
-                $selected = ' class="active"';
+            // Look up tt_content elements from the expanded page
+            $contentElements = $this->getDatabaseConnection()->exec_SELECTgetRows(
+                'uid,header,hidden,starttime,endtime,fe_group,CType,colPos,bodytext',
+                'tt_content',
+                'pid=' . (int)$pageId . BackendUtility::deleteClause('tt_content')
+                . BackendUtility::versioningPlaceholderClause('tt_content'),
+                '',
+                'colPos,sorting'
+            );
+
+            // Enrich list of records
+            foreach ($contentElements as &$contentElement) {
+                $contentElement['isSelected'] = !empty($this->linkParts) && (int)$this->linkParts['anchor'] === (int)$contentElement['uid'];
+                $contentElement['icon'] = $this->iconFactory->getIconForRecord('tt_content', $contentElement, Icon::SIZE_SMALL)->render();
+                $contentElement['title'] = BackendUtility::getRecordTitle('tt_content', $contentElement, true);
             }
-            // Putting list element HTML together:
-            // Output of BackendUtility::getRecordTitle() is already hsc'ed
-            $out .= '
-				<li' . $selected . '>
-					<span class="list-tree-group">
-						<span class="list-tree-icon">
-							' . $icon . '
-						</span>
-						<span class="list-tree-title">
-							<a href="#" class="t3js-pageLink" data-id="' . (int)$expPageId . '" data-anchor="#' . (int)$row['uid'] . '">
-								' . BackendUtility::getRecordTitle('tt_content', $row, true) . '
-							</a>
-						</span>
-					</span>
-				</li>
-				';
+            $this->view->assign('contentElements', $contentElements);
         }
-        $out .= '
-					</ul>
-				</li>
-			</ul>
-			';
-
-        return $out;
     }
 
     /**
-     * Check if a temporary tree mount is set and return a cancel button
+     * Check if a temporary tree mount is set and return a cancel button link
      *
-     * @return string HTML code
+     * @return string the link to cancel the temporary tree mount
      */
     protected function getTemporaryTreeMountCancelNotice()
     {
-        if ((int)$this->getBackendUser()->getSessionData('pageTree_temporaryMountPoint') === 0) {
+        if ((int)$this->getBackendUser()->getSessionData('pageTree_temporaryMountPoint') > 0) {
+            return GeneralUtility::linkThisScript(['setTempDBmount' => 0]);
+        } else {
             return '';
         }
-        $link = '<p><a href="' . htmlspecialchars(GeneralUtility::linkThisScript(array('setTempDBmount' => 0))) . '" class="btn btn-primary">'
-            . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.temporaryDBmount')) . '</a></p>';
-        return $link;
     }
 
     /**
diff --git a/typo3/sysext/recordlist/Classes/LinkHandler/UrlLinkHandler.php b/typo3/sysext/recordlist/Classes/LinkHandler/UrlLinkHandler.php
index 242a54e51a1a..c1eabb9e9d2f 100644
--- a/typo3/sysext/recordlist/Classes/LinkHandler/UrlLinkHandler.php
+++ b/typo3/sysext/recordlist/Classes/LinkHandler/UrlLinkHandler.php
@@ -90,28 +90,8 @@ class UrlLinkHandler extends AbstractLinkHandler implements LinkHandlerInterface
     {
         GeneralUtility::makeInstance(PageRenderer::class)->loadRequireJsModule('TYPO3/CMS/Recordlist/UrlLinkHandler');
 
-        $extUrl = '
-                <!--
-                    Enter External URL:
-                -->
-                <div class="link-browser-section link-browser-tab-content-url">
-                    <form action="" id="lurlform" class="form-horizontal">
-                        <div class="form-group form-group-sm" id="typo3-linkURL">
-                            <label class="col-xs-4 control-label">URL</label>
-                            <div class="col-xs-6">
-                                <input type="text" name="lurl" size="30" class="form-control" value="'
-                                    . htmlspecialchars(!empty($this->linkParts) ? $this->linkParts['url'] : 'http://')
-                                    . '" />
-                            </div>
-                            <div class="col-xs-2">
-                                <input class="btn btn-default" type="submit"
-                                    value="' . htmlspecialchars($this->getLanguageService()->getLL('setLink')) . '" />
-                            </div>
-                        </div>
-                    </form>
-                </div>';
-
-        return $extUrl;
+        $this->view->assign('url', !empty($this->linkParts) ? $this->linkParts['url'] : 'http://');
+        return $this->view->render('Url');
     }
 
     /**
diff --git a/typo3/sysext/recordlist/Resources/Private/Templates/LinkBrowser/File.html b/typo3/sysext/recordlist/Resources/Private/Templates/LinkBrowser/File.html
new file mode 100644
index 000000000000..0a998d1d82a4
--- /dev/null
+++ b/typo3/sysext/recordlist/Resources/Private/Templates/LinkBrowser/File.html
@@ -0,0 +1,40 @@
+<f:if condition="{positionOfUploadFields} === 'top'">
+	{createFolderForm -> f:format.raw()}
+</f:if>
+
+<div class="row link-browser-section link-browser-filetree">
+	<div class="col-xs-6">
+		<h3><f:translate key="LLL:EXT:lang/locallang_browse_links.xlf:folderTree" /></h3>
+		{tree -> f:format.raw()}
+	</div>
+	<div class="col-xs-6">
+		<f:if condition="{selectedFolder}">
+			<h3><f:translate key="LLL:EXT:lang/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="#" class="t3js-fileLink list-tree-group" title="{file.name}" data-file="file:{file.uid}">
+										<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>
+		</f:if>
+	</div>
+</div>
+
+<f:if condition="{positionOfUploadFields} === 'bottom'">
+	{uploadFileForm -> f:format.raw()}
+</f:if>
+{createFolderForm -> f:format.raw()}
diff --git a/typo3/sysext/recordlist/Resources/Private/Templates/LinkBrowser/Folder.html b/typo3/sysext/recordlist/Resources/Private/Templates/LinkBrowser/Folder.html
new file mode 100644
index 000000000000..4e6314b6f959
--- /dev/null
+++ b/typo3/sysext/recordlist/Resources/Private/Templates/LinkBrowser/Folder.html
@@ -0,0 +1,33 @@
+<div class="row link-browser-section link-browser-filetree">
+	<div class="col-xs-6">
+		<h3><f:translate key="LLL:EXT:lang/locallang_browse_links.xlf:folderTree" /></h3>
+		{tree -> f:format.raw()}
+	</div>
+	<div class="col-xs-6">
+		<f:if condition="{selectedFolder}">
+			<h3><f:translate key="LLL:EXT:lang/locallang_browse_links.xlf:folders" /></h3>
+			<span class="{f:if(condition: '{selectedFolder.combinedIdentifier} == {currentIdentifier}', then: 'bg-success')}" title="{selectedFolder.identifier}">
+				{selectedFolderIcon -> f:format.raw()} {selectedFolderTitle}
+			</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="#" class="t3js-fileLink list-tree-group" title="{folder.name}" data-file="file:{folder.identifier}">
+										<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>
+	</div>
+</div>
+
+{createFolderForm -> f:format.raw()}
diff --git a/typo3/sysext/recordlist/Resources/Private/Templates/LinkBrowser/Mail.html b/typo3/sysext/recordlist/Resources/Private/Templates/LinkBrowser/Mail.html
new file mode 100644
index 000000000000..9e7488183905
--- /dev/null
+++ b/typo3/sysext/recordlist/Resources/Private/Templates/LinkBrowser/Mail.html
@@ -0,0 +1,13 @@
+<div class="link-browser-section link-browser-tab-content-mail">
+	<form action="" id="lmailform" class="form-horizontal">
+		<div class="form-group form-group-sm">
+			<label class="col-xs-4 control-label"><f:translate key="LLL:EXT:lang/locallang_browse_links.xlf:emailAddress" /></label>
+			<div class="col-xs-6">
+				<input type="text" name="lemail" size="20" class="form-control" value="{email}" />
+			</div>
+			<div class="col-xs-2">
+				<input class="btn btn-default" type="submit" value="{f:translate(key: 'LLL:EXT:lang/locallang_browse_links.xlf:setLink')}" />
+			</div>
+		</div>
+	</form>
+</div>
diff --git a/typo3/sysext/recordlist/Resources/Private/Templates/LinkBrowser/Page.html b/typo3/sysext/recordlist/Resources/Private/Templates/LinkBrowser/Page.html
new file mode 100644
index 000000000000..ec769fcdbd73
--- /dev/null
+++ b/typo3/sysext/recordlist/Resources/Private/Templates/LinkBrowser/Page.html
@@ -0,0 +1,40 @@
+<div class="link-browser-section link-browser-pagetree">
+	<div class="col-xs-6">
+		<h3><f:translate key="LLL:EXT:lang/locallang_browse_links.xlf:pageTree" /></h3>
+		<f:if condition="{temporaryTreeMountCancelLink}">
+			<p>
+				<a href="{temporaryTreeMountCancelLink}" class="btn btn-primary">
+					<f:translate key="LLL:EXT:lang/locallang_core.xlf:labels.temporaryDBmount" />
+				</a>
+			</p>
+		</f:if>
+		{tree -> f:format.raw()}
+	</div>
+	<div class="col-xs-6">
+		<f:if condition="{expandActivePage}">
+			<h3><f:translate key="LLL:EXT:lang/locallang_browse_links.xlf:contentElements" /></h3>
+			<ul class="list-tree list-tree-root list-tree-root-clean">
+				<li class="list-tree-control-open">
+					<span class="list-tree-group">
+						<span class="list-tree-icon">{activePageIcon -> f:format.raw()}</span>
+						<span class="list-tree-title">{activePageTitle}</span>
+					</span>
+					<ul>
+						<f:for each="{contentElements}" as="content">
+							<li{f:if(condition: content.isSelected, then: ' class="active"')}>
+								<span class="list-tree-group">
+									<span class="list-tree-icon">{content.icon -> f:format.raw()}</span>
+									<span class="list-tree-title">
+										<a href="#" class="t3js-pageLink" data-id="{pageId}" data-anchor="#{content.uid}">
+											{content.title}
+										</a>
+									</span>
+								</span>
+							</li>
+						</f:for>
+					</ul>
+				</li>
+			</ul>
+		</f:if>
+	</div>
+</div>
diff --git a/typo3/sysext/recordlist/Resources/Private/Templates/LinkBrowser/Url.html b/typo3/sysext/recordlist/Resources/Private/Templates/LinkBrowser/Url.html
new file mode 100644
index 000000000000..9aa6fc048068
--- /dev/null
+++ b/typo3/sysext/recordlist/Resources/Private/Templates/LinkBrowser/Url.html
@@ -0,0 +1,14 @@
+<div class="link-browser-section link-browser-tab-content-url">
+	<form action="" id="lurlform" class="form-horizontal">
+		<div class="form-group form-group-sm" id="typo3-linkURL">
+			<label class="col-xs-4 control-label">URL</label>
+			<div class="col-xs-6">
+				<input type="text" name="lurl" size="30" class="form-control" value="{url}" />
+			</div>
+			<div class="col-xs-2">
+				<input class="btn btn-default" type="submit"
+							 value="{f:translate(key: 'LLL:EXT:lang/locallang_browse_links.xlf:setLink')}" />
+			</div>
+		</div>
+	</form>
+</div>
-- 
GitLab