Completed
Push — master ( cb9863...be434f )
by
unknown
40:07 queued 25:06
created

BackendLayoutRenderer   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 222
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 119
dl 0
loc 222
rs 10
c 1
b 0
f 0
wmc 23

8 Methods

Rating   Name   Duplication   Size   Complexity  
A getGridForPageLayoutContext() 0 24 5
A getLanguageService() 0 3 1
A drawContent() 0 45 5
A initializeClipboard() 0 27 5
A __construct() 0 15 1
A getLanguageColumnsForPageLayoutContext() 0 26 4
A drawPasteIcon() 0 12 1
A getBackendUser() 0 3 1
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the TYPO3 CMS project.
7
 *
8
 * It is free software; you can redistribute it and/or modify it under
9
 * the terms of the GNU General Public License, either version 2
10
 * of the License, or any later version.
11
 *
12
 * For the full copyright and license information, please read the
13
 * LICENSE.txt file that was distributed with this source code.
14
 *
15
 * The TYPO3 project - inspiring people to share!
16
 */
17
18
namespace TYPO3\CMS\Backend\View\Drawing;
19
20
use Psr\Log\LoggerAwareTrait;
21
use TYPO3\CMS\Backend\Clipboard\Clipboard;
22
use TYPO3\CMS\Backend\Utility\BackendUtility;
23
use TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher;
24
use TYPO3\CMS\Backend\View\BackendLayout\Grid\Grid;
25
use TYPO3\CMS\Backend\View\BackendLayout\Grid\GridColumn;
26
use TYPO3\CMS\Backend\View\BackendLayout\Grid\GridColumnItem;
27
use TYPO3\CMS\Backend\View\BackendLayout\Grid\GridRow;
28
use TYPO3\CMS\Backend\View\BackendLayout\Grid\LanguageColumn;
29
use TYPO3\CMS\Backend\View\BackendLayout\RecordRememberer;
30
use TYPO3\CMS\Backend\View\PageLayoutContext;
31
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
32
use TYPO3\CMS\Core\Imaging\Icon;
33
use TYPO3\CMS\Core\Imaging\IconFactory;
34
use TYPO3\CMS\Core\Localization\LanguageService;
35
use TYPO3\CMS\Core\Messaging\FlashMessage;
36
use TYPO3\CMS\Core\Messaging\FlashMessageService;
37
use TYPO3\CMS\Core\Page\PageRenderer;
38
use TYPO3\CMS\Core\Utility\GeneralUtility;
39
use TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext;
40
use TYPO3\CMS\Extbase\Mvc\Request;
41
use TYPO3\CMS\Extbase\Object\ObjectManager;
42
use TYPO3\CMS\Fluid\View\TemplateView;
43
44
/**
45
 * Backend Layout Renderer
46
 *
47
 * Draws a page layout - essentially, behaves as a wrapper for a view
48
 * which renders the Resources/Private/PageLayout/PageLayout template
49
 * with necessary assigned template variables.
50
 *
51
 * - Initializes the clipboard used in the page layout
52
 * - Inserts an encoded paste icon as JS which is made visible when clipboard elements are registered
53
 */
54
class BackendLayoutRenderer
55
{
56
    use LoggerAwareTrait;
57
58
    /**
59
     * @var IconFactory
60
     */
61
    protected $iconFactory;
62
63
    /**
64
     * @var PageLayoutContext
65
     */
66
    protected $context;
67
68
    /**
69
     * @var ContentFetcher
70
     */
71
    protected $contentFetcher;
72
73
    /**
74
     * @var Clipboard
75
     */
76
    protected $clipboard;
77
78
    /**
79
     * @var TemplateView
80
     */
81
    protected $view;
82
83
    public function __construct(PageLayoutContext $context)
84
    {
85
        $this->context = $context;
86
        $this->contentFetcher = GeneralUtility::makeInstance(ContentFetcher::class, $context);
87
        $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
88
        $this->initializeClipboard();
89
        $objectManager = GeneralUtility::makeInstance(ObjectManager::class);
90
        $controllerContext = $objectManager->get(ControllerContext::class);
91
        $request = $objectManager->get(Request::class);
92
        $controllerContext->setRequest($request);
93
        $this->view = GeneralUtility::makeInstance(TemplateView::class);
94
        $this->view->getRenderingContext()->setControllerContext($controllerContext);
95
        $this->view->getRenderingContext()->getTemplatePaths()->fillDefaultsByPackageName('backend');
96
        $this->view->getRenderingContext()->setControllerName('PageLayout');
97
        $this->view->assign('context', $context);
98
    }
99
100
    public function getGridForPageLayoutContext(PageLayoutContext $context): Grid
101
    {
102
        $grid = GeneralUtility::makeInstance(Grid::class, $context);
103
        $recordRememberer = GeneralUtility::makeInstance(RecordRememberer::class);
104
        if ($context->getDrawingConfiguration()->getLanguageMode()) {
105
            $languageId = $context->getSiteLanguage()->getLanguageId();
106
        } else {
107
            $languageId = $context->getDrawingConfiguration()->getSelectedLanguageId();
108
        }
109
        foreach ($context->getBackendLayout()->getStructure()['__config']['backend_layout.']['rows.'] ?? [] as $row) {
110
            $rowObject = GeneralUtility::makeInstance(GridRow::class, $context);
111
            foreach ($row['columns.'] as $column) {
112
                $columnObject = GeneralUtility::makeInstance(GridColumn::class, $context, $column);
113
                $rowObject->addColumn($columnObject);
114
                $records = $this->contentFetcher->getContentRecordsPerColumn((int)$column['colPos'], $languageId);
115
                $recordRememberer->rememberRecords($records);
116
                foreach ($records as $contentRecord) {
117
                    $columnItem = GeneralUtility::makeInstance(GridColumnItem::class, $context, $columnObject, $contentRecord);
118
                    $columnObject->addItem($columnItem);
119
                }
120
            }
121
            $grid->addRow($rowObject);
122
        }
123
        return $grid;
124
    }
125
126
    /**
127
     * @return LanguageColumn[]
128
     */
129
    public function getLanguageColumnsForPageLayoutContext(PageLayoutContext $context): iterable
130
    {
131
        $languageColumns = [];
132
        foreach ($context->getLanguagesToShow() as $siteLanguage) {
133
            $localizedLanguageId = $siteLanguage->getLanguageId();
134
            if ($localizedLanguageId > 0) {
135
                $localizedContext = $context->cloneForLanguage($siteLanguage);
136
                if (!$localizedContext->getLocalizedPageRecord()) {
137
                    continue;
138
                }
139
            } else {
140
                $localizedContext = $context;
141
            }
142
            $translationInfo = $this->contentFetcher->getTranslationData(
143
                $this->contentFetcher->getFlatContentRecords($localizedLanguageId),
144
                $localizedContext->getSiteLanguage()->getLanguageId()
145
            );
146
            $languageColumnObject = GeneralUtility::makeInstance(
147
                LanguageColumn::class,
148
                $localizedContext,
149
                $this->getGridForPageLayoutContext($localizedContext),
150
                $translationInfo
151
            );
152
            $languageColumns[] = $languageColumnObject;
153
        }
154
        return $languageColumns;
155
    }
156
157
    /**
158
     * @param bool $renderUnused If true, renders the bottom column with unused records
159
     * @return string
160
     */
161
    public function drawContent(bool $renderUnused = true): string
162
    {
163
        $this->view->assign('hideRestrictedColumns', (bool)(BackendUtility::getPagesTSconfig($this->context->getPageId())['mod.']['web_layout.']['hideRestrictedCols'] ?? false));
164
        $this->view->assign('newContentTitle', $this->getLanguageService()->getLL('newContentElement'));
165
        $this->view->assign('newContentTitleShort', $this->getLanguageService()->getLL('content'));
166
        $this->view->assign('allowEditContent', $this->getBackendUser()->check('tables_modify', 'tt_content'));
167
168
        if ($this->context->getDrawingConfiguration()->getLanguageMode()) {
169
            $this->view->assign('languageColumns', $this->getLanguageColumnsForPageLayoutContext($this->context));
170
        } else {
171
            $this->view->assign('grid', $this->getGridForPageLayoutContext($this->context));
172
        }
173
174
        $rendered = $this->view->render('PageLayout');
175
        if ($renderUnused) {
176
            $unusedRecords = $this->contentFetcher->getUnusedRecords();
177
178
            if (!empty($unusedRecords)) {
179
                $unusedElementsMessage = GeneralUtility::makeInstance(
180
                    FlashMessage::class,
181
                    $this->getLanguageService()->getLL('staleUnusedElementsWarning'),
182
                    $this->getLanguageService()->getLL('staleUnusedElementsWarningTitle'),
183
                    FlashMessage::WARNING
184
                );
185
                $service = GeneralUtility::makeInstance(FlashMessageService::class);
186
                $queue = $service->getMessageQueueByIdentifier();
187
                $queue->addMessage($unusedElementsMessage);
188
189
                $unusedGrid = GeneralUtility::makeInstance(Grid::class, $this->context);
190
                $unusedRow = GeneralUtility::makeInstance(GridRow::class, $this->context);
191
                $unusedColumn = GeneralUtility::makeInstance(GridColumn::class, $this->context, ['colPos' => false, 'name' => 'unused']);
192
193
                $unusedGrid->addRow($unusedRow);
194
                $unusedRow->addColumn($unusedColumn);
195
196
                foreach ($unusedRecords as $unusedRecord) {
197
                    $item = GeneralUtility::makeInstance(GridColumnItem::class, $this->context, $unusedColumn, $unusedRecord);
198
                    $unusedColumn->addItem($item);
199
                }
200
201
                $this->view->assign('grid', $unusedGrid);
202
                $rendered .= $this->view->render('UnusedRecords');
203
            }
204
        }
205
        return $rendered;
206
    }
207
208
    /**
209
     * Initializes the clipboard for generating paste links
210
     *
211
     * @see \TYPO3\CMS\Backend\Controller\ContextMenuController::clipboardAction()
212
     * @see \TYPO3\CMS\Filelist\Controller\FileListController::indexAction()
213
     */
214
    protected function initializeClipboard(): void
215
    {
216
        $this->clipboard = GeneralUtility::makeInstance(Clipboard::class);
217
        $this->clipboard->initializeClipboard();
218
        $this->clipboard->lockToNormal();
219
        $this->clipboard->cleanCurrent();
220
        $this->clipboard->endClipboard();
221
222
        $elFromTable = $this->clipboard->elFromTable('tt_content');
223
        if (!empty($elFromTable) && $this->context->isPageEditable()) {
224
            $pasteItem = (int)substr(key($elFromTable), 11);
225
            $pasteRecord = BackendUtility::getRecord('tt_content', (int)$pasteItem);
226
            $pasteTitle = (string)($pasteRecord['header'] ?: $pasteItem);
227
            $copyMode = $this->clipboard->clipData['normal']['mode'] ? '-' . $this->clipboard->clipData['normal']['mode'] : '';
228
            $addExtOnReadyCode = '
229
                     top.pasteIntoLinkTemplate = '
230
                . $this->drawPasteIcon($pasteItem, $pasteTitle, $copyMode, 't3js-paste-into', 'pasteIntoColumn')
231
                . ';
232
                    top.pasteAfterLinkTemplate = '
233
                . $this->drawPasteIcon($pasteItem, $pasteTitle, $copyMode, 't3js-paste-after', 'pasteAfterRecord')
234
                . ';';
235
        } else {
236
            $addExtOnReadyCode = '
237
                top.pasteIntoLinkTemplate = \'\';
238
                top.pasteAfterLinkTemplate = \'\';';
239
        }
240
        GeneralUtility::makeInstance(PageRenderer::class)->addJsInlineCode('pasteLinkTemplates', $addExtOnReadyCode);
241
    }
242
243
    /**
244
     * Draw a paste icon either for pasting into a column or for pasting after a record
245
     *
246
     * @param int $pasteItem ID of the item in the clipboard
247
     * @param string $pasteTitle Title for the JS modal
248
     * @param string $copyMode copy or cut
249
     * @param string $cssClass CSS class to determine if pasting is done into column or after record
250
     * @param string $title title attribute of the generated link
251
     *
252
     * @return string Generated HTML code with link and icon
253
     */
254
    private function drawPasteIcon(int $pasteItem, string $pasteTitle, string $copyMode, string $cssClass, string $title): string
255
    {
256
        $pasteIcon = json_encode(
257
            ' <a data-content="' . htmlspecialchars((string)$pasteItem) . '"'
258
            . ' data-title="' . htmlspecialchars($pasteTitle) . '"'
259
            . ' data-severity="warning"'
260
            . ' class="t3js-paste t3js-paste' . htmlspecialchars($copyMode) . ' ' . htmlspecialchars($cssClass) . ' btn btn-default btn-sm"'
261
            . ' title="' . htmlspecialchars($this->getLanguageService()->getLL($title)) . '">'
262
            . $this->iconFactory->getIcon('actions-document-paste-into', Icon::SIZE_SMALL)->render()
263
            . '</a>'
264
        );
265
        return $pasteIcon;
266
    }
267
268
    protected function getBackendUser(): BackendUserAuthentication
269
    {
270
        return $GLOBALS['BE_USER'];
271
    }
272
273
    protected function getLanguageService(): LanguageService
274
    {
275
        return $GLOBALS['LANG'];
276
    }
277
}
278