RecordListController.php 23.6 KB
Newer Older
1
<?php
2

3
/*
4
 * This file is part of the TYPO3 CMS project.
5
 *
6
7
8
 * 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.
9
 *
10
11
 * For the full copyright and license information, please read the
 * LICENSE.txt file that was distributed with this source code.
12
 *
13
14
 * The TYPO3 project - inspiring people to share!
 */
15

16
17
namespace TYPO3\CMS\Recordlist\Controller;

18
use Psr\EventDispatcher\EventDispatcherInterface;
19
20
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
Markus Klein's avatar
Markus Klein committed
21
use TYPO3\CMS\Backend\Clipboard\Clipboard;
22
use TYPO3\CMS\Backend\Routing\UriBuilder;
23
use TYPO3\CMS\Backend\Template\Components\ButtonBar;
24
use TYPO3\CMS\Backend\Template\ModuleTemplate;
25
use TYPO3\CMS\Backend\Utility\BackendUtility;
Markus Klein's avatar
Markus Klein committed
26
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
27
use TYPO3\CMS\Core\Database\ConnectionPool;
28
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
29
use TYPO3\CMS\Core\Database\Query\Restriction\WorkspaceRestriction;
Markus Klein's avatar
Markus Klein committed
30
use TYPO3\CMS\Core\DataHandling\DataHandler;
31
use TYPO3\CMS\Core\Http\HtmlResponse;
32
33
use TYPO3\CMS\Core\Imaging\Icon;
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\Site\Entity\SiteLanguage;
38
use TYPO3\CMS\Core\Type\Bitmask\Permission;
39
use TYPO3\CMS\Core\TypoScript\TypoScriptService;
40
use TYPO3\CMS\Core\Utility\GeneralUtility;
41
use TYPO3\CMS\Recordlist\Event\RenderAdditionalContentToRecordListEvent;
42
use TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList;
43

44
45
/**
 * Script Class for the Web > List module; rendering the listing of records on a page
46
 * @internal This class is a specific Backend controller implementation and is not part of the TYPO3's Core API.
47
 */
48
class RecordListController
49
50
51
52
53
54
{
    /**
     * @var IconFactory
     */
    protected $iconFactory;

55
56
57
58
59
60
61
    /**
     * ModuleTemplate object
     *
     * @var ModuleTemplate
     */
    protected $moduleTemplate;

62
63
64
65
66
    /**
     * @var SiteLanguage[]
     */
    protected $siteLanguages = [];

67
68
69
70
71
    /**
     * @var EventDispatcherInterface
     */
    protected $eventDispatcher;

72
73
74
75
76
    /**
     * @var UriBuilder
     */
    protected $uriBuilder;

77
78
79
    /**
     * Constructor
     */
80
    public function __construct(IconFactory $iconFactory, ModuleTemplate $moduleTemplate, EventDispatcherInterface $eventDispatcher, UriBuilder $uriBuilder)
81
    {
82
        $this->moduleTemplate = $moduleTemplate;
83
        $this->getLanguageService()->includeLLFile('EXT:core/Resources/Private/Language/locallang_mod_web_list.xlf');
84
85
        $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Recordlist/FieldSelectBox');
        $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Recordlist/Recordlist');
86
        $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Recordlist/ClearCache');
87
88
89
90
        $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/AjaxDataHandler');
        $this->moduleTemplate->getPageRenderer()->addInlineLanguageLabelFile('EXT:core/Resources/Private/Language/locallang_mod_web_list.xlf');
        $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Recordlist/Tooltip');

91
92
        $this->iconFactory = $iconFactory;
        $this->eventDispatcher = $eventDispatcher;
93
        $this->uriBuilder = $uriBuilder;
94
95
96
    }

    /**
97
     * Injects the request object for the current request or subrequest
98
     *
99
100
     * @param ServerRequestInterface $request the current request
     * @return ResponseInterface the response with the content
101
     */
102
    public function mainAction(ServerRequestInterface $request): ResponseInterface
103
    {
104
105
        // init
        BackendUtility::lockRecords();
106
107
        $parsedBody = $request->getParsedBody();
        $queryParams = $request->getQueryParams();
108
        $backendUser = $this->getBackendUserAuthentication();
109
        $perms_clause = $backendUser->getPagePermsClause(Permission::PAGE_SHOW);
110
        // GPvars:
111
112
113
114
115
116
117
118
119
120
121
122
        $id = (int)($parsedBody['id'] ?? $queryParams['id'] ?? 0);
        $pointer =  max(0, (int)($parsedBody['pointer'] ?? $queryParams['pointer'] ?? 0));
        $table = (string)($parsedBody['table'] ?? $queryParams['table'] ?? '');
        $search_field = (string)($parsedBody['search_field'] ?? $queryParams['search_field'] ?? '');
        $search_levels = (int)($parsedBody['search_levels'] ?? $queryParams['search_levels'] ?? 0);
        $showLimit = (int)($parsedBody['showLimit'] ?? $queryParams['showLimit'] ?? 0);
        $returnUrl = GeneralUtility::sanitizeLocalUrl((string)($parsedBody['returnUrl'] ?? $queryParams['returnUrl'] ?? ''));
        $cmd = (string)($parsedBody['cmd'] ?? $queryParams['cmd'] ?? '');
        $cmd_table = (string)($parsedBody['cmd_table'] ?? $queryParams['cmd_table'] ?? '');
        // Set site languages
        $site = $request->getAttribute('site');
        $this->siteLanguages = $site->getAvailableLanguages($this->getBackendUserAuthentication(), false, $id);
123
        // Loading module configuration:
124
        $modTSconfig['properties'] = BackendUtility::getPagesTSconfig($id)['mod.']['web_list.'] ?? [];
125
        // Clean up settings:
126
127
        $MOD_SETTINGS = BackendUtility::getModuleData(['bigControlPanel' => '', 'clipBoard' => ''], (array)($parsedBody['SET'] ?? $queryParams['SET'] ?? []), 'web_list');
        // main
128
129
130
        $backendUser = $this->getBackendUserAuthentication();
        $lang = $this->getLanguageService();
        // Loading current page record and checking access:
131
132
        $pageinfo = BackendUtility::readPageAccess($id, $perms_clause);
        $access = is_array($pageinfo);
133

134
135
        $calcPerms = new Permission($backendUser->calcPerms($pageinfo));
        $userCanEditPage = $calcPerms->editPagePermissionIsGranted() && !empty($id) && ($backendUser->isAdmin() || (int)$pageinfo['editlock'] === 0);
136
        $pageActionsCallback = null;
137
        if ($userCanEditPage) {
138
            $pageActionsCallback = 'function(PageActions) {
139
                PageActions.setPageId(' . (int)$id . ');
140
            }';
141
        }
142
        $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/PageActions', $pageActionsCallback);
143
144
        // Apply predefined values for hidden checkboxes
        // Set predefined value for DisplayBigControlPanel:
145
146
147
148
        if ($modTSconfig['properties']['enableDisplayBigControlPanel'] === 'activated') {
            $MOD_SETTINGS['bigControlPanel'] = true;
        } elseif ($modTSconfig['properties']['enableDisplayBigControlPanel'] === 'deactivated') {
            $MOD_SETTINGS['bigControlPanel'] = false;
149
150
        }
        // Set predefined value for Clipboard:
151
152
153
154
        if ($modTSconfig['properties']['enableClipBoard'] === 'activated') {
            $MOD_SETTINGS['clipBoard'] = true;
        } elseif ($modTSconfig['properties']['enableClipBoard'] === 'deactivated') {
            $MOD_SETTINGS['clipBoard'] = false;
155
        } else {
156
157
            if ($MOD_SETTINGS['clipBoard'] === null) {
                $MOD_SETTINGS['clipBoard'] = true;
158
159
160
161
            }
        }

        // Initialize the dblist object:
162
        $dblist = GeneralUtility::makeInstance(DatabaseRecordList::class);
163
        $dblist->setModuleData($MOD_SETTINGS ?? []);
164
        $dblist->calcPerms = $calcPerms;
165
166
167
168
169
170
171
172
173
174
175
        $dblist->returnUrl = $returnUrl;
        $dblist->allFields = $MOD_SETTINGS['bigControlPanel'] || $table ? 1 : 0;
        $dblist->showClipboard = true;
        $dblist->disableSingleTableView = $modTSconfig['properties']['disableSingleTableView'];
        $dblist->listOnlyInSingleTableMode = $modTSconfig['properties']['listOnlyInSingleTableView'];
        $dblist->hideTables = $modTSconfig['properties']['hideTables'];
        $dblist->hideTranslations = $modTSconfig['properties']['hideTranslations'];
        $dblist->tableTSconfigOverTCA = $modTSconfig['properties']['table.'];
        $dblist->allowedNewTables = GeneralUtility::trimExplode(',', $modTSconfig['properties']['allowedNewTables'], true);
        $dblist->deniedNewTables = GeneralUtility::trimExplode(',', $modTSconfig['properties']['deniedNewTables'], true);
        $dblist->pageRow = $pageinfo;
176
        $dblist->MOD_MENU = ['bigControlPanel' => '', 'clipBoard' => ''];
177
        $dblist->modTSconfig = $modTSconfig;
178
        $dblist->setLanguagesAllowedForUser($this->siteLanguages);
179
        $clickTitleMode = trim($modTSconfig['properties']['clickTitleMode']);
180
        $dblist->clickTitleMode = $clickTitleMode === '' ? 'edit' : $clickTitleMode;
181
        if (isset($modTSconfig['properties']['tableDisplayOrder.'])) {
182
            $typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class);
183
            $dblist->setTableDisplayOrder($typoScriptService->convertTypoScriptArrayToPlainArray($modTSconfig['properties']['tableDisplayOrder.']));
184
185
186
187
188
189
190
191
        }
        // Clipboard is initialized:
        // Start clipboard
        $dblist->clipObj = GeneralUtility::makeInstance(Clipboard::class);
        // Initialize - reads the clipboard content from the user session
        $dblist->clipObj->initializeClipboard();
        // Clipboard actions are handled:
        // CB is the clipboard command array
192
193
        $CB = $queryParams['CB'] ?? [];
        if ($cmd === 'setCB') {
194
195
196
            // CBH is all the fields selected for the clipboard, CBC is the checkbox fields which were checked.
            // By merging we get a full array of checked/unchecked elements
            // This is set to the 'el' array of the CB after being parsed so only the table in question is registered.
197
            $CB['el'] = $dblist->clipObj->cleanUpCBC(array_merge($parsedBody['CBH'] ?? [], (array)($parsedBody['CBC'] ?? [])), $cmd_table);
198
        }
199
        if (!$MOD_SETTINGS['clipBoard']) {
200
201
202
203
204
205
206
207
208
209
210
            // If the clipboard is NOT shown, set the pad to 'normal'.
            $CB['setP'] = 'normal';
        }
        // Execute commands.
        $dblist->clipObj->setCmd($CB);
        // Clean up pad
        $dblist->clipObj->cleanCurrent();
        // Save the clipboard content
        $dblist->clipObj->endClipboard();
        // This flag will prevent the clipboard panel in being shown.
        // It is set, if the clickmenu-layer is active AND the extended view is not enabled.
211
        $dblist->dontShowClipControlPanels = ($dblist->clipObj->current === 'normal' && !$modTSconfig['properties']['showClipControlPanelsDespiteOfCMlayers']);
212
        // If there is access to the page or root page is used for searching, then render the list contents and set up the document template object:
213
        $tableOutput = '';
214
        if ($access || ($id === 0 && $search_levels !== 0 && $search_field !== '')) {
215
216
            // Deleting records...:
            // Has not to do with the clipboard but is simply the delete action. The clipboard object is used to clean up the submitted entries to only the selected table.
217
218
            if ($cmd === 'delete') {
                $items = $dblist->clipObj->cleanUpCBC($parsedBody['CBC'] ?? [], $cmd_table, 1);
219
                if (!empty($items)) {
220
                    $cmd = [];
221
                    foreach ($items as $iK => $value) {
222
                        $iKParts = explode('|', (string)$iK);
223
224
225
                        $cmd[$iKParts[0]][$iKParts[1]]['delete'] = 1;
                    }
                    $tce = GeneralUtility::makeInstance(DataHandler::class);
226
                    $tce->start([], $cmd);
227
228
229
230
                    $tce->process_cmdmap();
                    if (isset($cmd['pages'])) {
                        BackendUtility::setUpdateSignal('updatePageTree');
                    }
231
                    $tce->printLogErrorMessages();
232
233
234
                }
            }
            // Initialize the listing object, dblist, for rendering the list:
235
            $dblist->start($id, $table, $pointer, $search_field, $search_levels, $showLimit);
236
237
            $dblist->setDispFields();
            // Render the list of tables:
238
            $tableOutput = $dblist->generateList();
239

240
            // Add JavaScript functions to the page:
241
            $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ClipboardComponent');
242
243
244
245

            $this->moduleTemplate->addJavaScriptCode(
                'RecordListInlineJS',
                '
246
247
248
				function setHighlight(id) {
					top.fsMod.recentIds["web"] = id;
					top.fsMod.navFrameHighlightedID["web"] = top.fsMod.currentBank + "_" + id; // For highlighting
249

250
251
					if (top.nav_frame && top.nav_frame.refresh_nav) {
						top.nav_frame.refresh_nav();
252
253
					}
				}
254
				function editRecords(table,idList,addParams,CBflag) {
255
					window.location.href="' . (string)$this->uriBuilder->buildUriFromRoute('record_edit', ['returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')]) . '&edit["+table+"]["+idList+"]=edit"+addParams;
256
257
				}

258
				if (top.fsMod) top.fsMod.recentIds["web"] = ' . (int)$id . ';
259
260
261
			'
            );

262
            // Setting up the context sensitive menu:
263
            $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu');
264
265
266
        }
        // access
        // Begin to compile the whole page, starting out with page header:
267
        if (!$id) {
268
            $title = $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'];
269
        } else {
270
            $title = $pageinfo['title'];
271
        }
272
        $body = $this->moduleTemplate->header($title);
273
274

        // Additional header content
275
276
        /** @var RenderAdditionalContentToRecordListEvent $additionalRecordListEvent */
        $additionalRecordListEvent = $this->eventDispatcher->dispatch(new RenderAdditionalContentToRecordListEvent($request));
277
        $body .= $additionalRecordListEvent->getAdditionalContentAbove();
278
        $this->moduleTemplate->setTitle($title);
279

280
        $output = '';
281
        // Show the selector to add page translations and the list of translations of the current page
282
        // but only when in "default" mode
283
284
        if ($id && !$dblist->csvOutput && !$search_field && !$cmd && !$table) {
            $output .= $this->languageSelector($id);
285
286
287
288
289
            $pageTranslationsDatabaseRecordList = clone $dblist;
            $pageTranslationsDatabaseRecordList->listOnlyInSingleTableMode = false;
            $pageTranslationsDatabaseRecordList->disableSingleTableView = true;
            $pageTranslationsDatabaseRecordList->deniedNewTables = ['pages'];
            $pageTranslationsDatabaseRecordList->hideTranslations = '';
290
            $pageTranslationsDatabaseRecordList->setLanguagesAllowedForUser($this->siteLanguages);
291
            $pageTranslationsDatabaseRecordList->showOnlyTranslatedRecords(true);
292
            $output .= $pageTranslationsDatabaseRecordList->getTable('pages', $id);
293
294
        }

295
296
        if (!empty($tableOutput)) {
            $output .= $tableOutput;
297
        } else {
298
299
300
            if (isset($GLOBALS['TCA'][$table]['ctrl']['title'])) {
                if (strpos($GLOBALS['TCA'][$table]['ctrl']['title'], 'LLL:') === 0) {
                    $ll = sprintf($lang->getLL('noRecordsOfTypeOnThisPage'), $lang->sL($GLOBALS['TCA'][$table]['ctrl']['title']));
301
                } else {
302
                    $ll = sprintf($lang->getLL('noRecordsOfTypeOnThisPage'), $GLOBALS['TCA'][$table]['ctrl']['title']);
303
304
305
306
                }
            } else {
                $ll = $lang->getLL('noRecordsOnThisPage');
            }
307
            $flashMessage = GeneralUtility::makeInstance(
308
                FlashMessage::class,
309
                $ll,
310
311
                '',
                FlashMessage::INFO
312
            );
313
            unset($ll);
314
            /** @var \TYPO3\CMS\Core\Messaging\FlashMessageService $flashMessageService */
315
            $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
316
            /** @var \TYPO3\CMS\Core\Messaging\FlashMessageQueue $defaultFlashMessageQueue */
317
318
            $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
            $defaultFlashMessageQueue->enqueue($flashMessage);
319
320
        }

321
322
323
        $body .= '<form action="' . htmlspecialchars($dblist->listURL()) . '" method="post" name="dblistForm">';
        $body .= $output;
        $body .= '<input type="hidden" name="cmd_table" /><input type="hidden" name="cmd" /></form>';
324
        // If a listing was produced, create the page footer with search form etc:
325
        if ($tableOutput) {
326
327
            // Making field select box (when extended view for a single table is enabled):
            if ($dblist->table) {
328
                $body .= $dblist->fieldSelectBox($dblist->table);
329
330
            }
            // Adding checkbox options for extended listing and clipboard display:
331
            $body .= '
332
333

					<!--
334
						Listing options for extended view and clipboard view
335
					-->
336
					<div class="typo3-listOptions">
337
						<form action="" method="post">';
338

339
            // Add "display bigControlPanel" checkbox:
340
341
            if ($modTSconfig['properties']['enableDisplayBigControlPanel'] === 'selectable') {
                $body .= '<div class="checkbox">' .
342
                    '<label for="checkLargeControl">' .
343
                    BackendUtility::getFuncCheck($id, 'SET[bigControlPanel]', $MOD_SETTINGS['bigControlPanel'] ?? '', '', $table ? '&table=' . $table : '', 'id="checkLargeControl"') .
344
                    BackendUtility::wrapInHelp('xMOD_csh_corebe', 'list_options', htmlspecialchars($lang->getLL('largeControl'))) .
345
346
347
348
349
                    '</label>' .
                    '</div>';
            }

            // Add "clipboard" checkbox:
350
            if ($modTSconfig['properties']['enableClipBoard'] === 'selectable') {
351
                if ($dblist->showClipboard) {
352
                    $body .= '<div class="checkbox">' .
353
                        '<label for="checkShowClipBoard">' .
354
                        BackendUtility::getFuncCheck($id, 'SET[clipBoard]', $MOD_SETTINGS['clipBoard'], '', $table ? '&table=' . $table : '', 'id="checkShowClipBoard"') .
355
                        BackendUtility::wrapInHelp('xMOD_csh_corebe', 'list_options', htmlspecialchars($lang->getLL('showClipBoard'))) .
356
357
358
359
360
                        '</label>' .
                        '</div>';
                }
            }

361
            $body .= '
362
363
						</form>
					</div>';
364
365
        }
        // Printing clipboard if enabled
366
367
        if ($MOD_SETTINGS['clipBoard'] && $dblist->showClipboard && ($tableOutput || $dblist->clipObj->hasElements())) {
            $body .= '<div class="db_list-dashboard">' . $dblist->clipObj->printClipboard() . '</div>';
368
369
        }
        // Additional footer content
370
        $body .= $additionalRecordListEvent->getAdditionalContentBelow();
371
        // Setting up the buttons for docheader
372
        $dblist->getDocHeaderButtons($this->moduleTemplate, $request);
373
        // search box toolbar
374
        $content = '';
375
        if (!($modTSconfig['properties']['disableSearchBox'] ?? false) && ($tableOutput || !empty($dblist->searchString))) {
376
            $content .= $dblist->getSearchBox();
377
            $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ToggleSearchToolbox');
378
379
380
381
382

            $searchButton = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar()->makeLinkButton();
            $searchButton
                ->setHref('#')
                ->setClasses('t3js-toggle-search-toolbox')
383
                ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.title.searchIcon'))
384
                ->setIcon($this->iconFactory->getIcon('actions-search', Icon::SIZE_SMALL));
385
386
387
388
389
            $this->moduleTemplate->getDocHeaderComponent()->getButtonBar()->addButton(
                $searchButton,
                ButtonBar::BUTTON_POSITION_LEFT,
                90
            );
390
        }
391

392
393
        if ($pageinfo) {
            $this->moduleTemplate->getDocHeaderComponent()->setMetaInformation($pageinfo);
394
395
        }

396
        // Build the <body> for the module
397
        $content .= $body;
398
        $this->moduleTemplate->setContent($content);
399
        return new HtmlResponse($this->moduleTemplate->renderContent());
400
401
    }

402
403
404
405
406
407
    /**
     * Make selector box for creating new translation in a language
     * Displays only languages which are not yet present for the current page and
     * that are not disabled with page TS.
     *
     * @param int $id Page id for which to create a new translation record of pages
408
     * @return string HTML <select> element (if there were items for the box anyways...)
409
410
411
     */
    protected function languageSelector(int $id): string
    {
412
413
414
415
416
417
418
        if (!$this->getBackendUserAuthentication()->check('tables_modify', 'pages')) {
            return '';
        }
        $availableTranslations = [];
        foreach ($this->siteLanguages as $siteLanguage) {
            if ($siteLanguage->getLanguageId() === 0) {
                continue;
419
            }
420
421
422
423
424
425
426
427
            $availableTranslations[$siteLanguage->getLanguageId()] = $siteLanguage->getTitle();
        }
        // Then, subtract the languages which are already on the page:
        $localizationParentField = $GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField'];
        $languageField = $GLOBALS['TCA']['pages']['ctrl']['languageField'];
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
        $queryBuilder->getRestrictions()->removeAll()
            ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
428
            ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, (int)$this->getBackendUserAuthentication()->workspace));
429
430
431
432
433
        $statement = $queryBuilder->select('uid', $languageField)
            ->from('pages')
            ->where(
                $queryBuilder->expr()->eq(
                    $localizationParentField,
434
                    $queryBuilder->createNamedParameter($id, \PDO::PARAM_INT)
435
                )
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
            )
            ->execute();
        while ($pageTranslation = $statement->fetch()) {
            unset($availableTranslations[(int)$pageTranslation[$languageField]]);
        }
        // If any languages are left, make selector:
        if (!empty($availableTranslations)) {
            $output = '<option value="">' . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_layout.xlf:new_language')) . '</option>';
            foreach ($availableTranslations as $languageUid => $languageTitle) {
                // Build localize command URL to DataHandler (tce_db)
                // which redirects to FormEngine (record_edit)
                // which, when finished editing should return back to the current page (returnUrl)
                $parameters = [
                    'justLocalized' => 'pages:' . $id . ':' . $languageUid,
                    'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')
                ];
452
                $redirectUrl = (string)$this->uriBuilder->buildUriFromRoute('record_edit', $parameters);
453
454
455
456
                $params = [];
                $params['redirect'] = $redirectUrl;
                $params['cmd']['pages'][$id]['localize'] = $languageUid;
                $targetUrl = (string)$this->uriBuilder->buildUriFromRoute('tce_db', $params);
457
                $output .= '<option value="' . htmlspecialchars($targetUrl) . '">' . htmlspecialchars($languageTitle) . '</option>';
458
            }
459
460
461

            return '<div class="form-inline form-inline-spaced">'
                . '<div class="form-group">'
462
                . '<select class="form-select" name="createNewLanguage" data-global-event="change" data-action-navigate="$value">'
463
464
                . $output
                . '</select></div></div>';
465
466
467
468
        }
        return '';
    }

469
470
471
    /**
     * @return BackendUserAuthentication
     */
472
    protected function getBackendUserAuthentication(): BackendUserAuthentication
473
474
475
476
477
478
479
    {
        return $GLOBALS['BE_USER'];
    }

    /**
     * @return LanguageService
     */
480
    protected function getLanguageService(): LanguageService
481
482
483
    {
        return $GLOBALS['LANG'];
    }
484
}