2 declare(strict_types = 1);
18 use Psr\Http\Message\ResponseInterface;
19 use Psr\Http\Message\ServerRequestInterface;
120 $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
126 $this->hiddenRecords = GeneralUtility::intExplode(
128 $userTsConfig[
'options.'][
'hideRecords.'][
'pages'] ??
'',
131 $this->backgroundColors = $userTsConfig[
'options.'][
'pageTree.'][
'backgroundColor.'] ?? [];
132 $this->addIdAsPrefix = (bool)($userTsConfig[
'options.'][
'pageTree.'][
'showPageIdWithTitle'] ??
false);
133 $this->addDomainName = (bool)($userTsConfig[
'options.'][
'pageTree.'][
'showDomainNameWithTitle'] ??
false);
134 $this->useNavTitle = (bool)($userTsConfig[
'options.'][
'pageTree.'][
'showNavTitle'] ??
false);
135 $this->showMountPathAboveMounts = (bool)($userTsConfig[
'options.'][
'pageTree.'][
'showPathAboveMounts'] ??
false);
136 $backendUserConfiguration = GeneralUtility::makeInstance(BackendUserConfiguration::class);
137 $this->expandedState = $backendUserConfiguration->get(
'BackendComponents.States.Pagetree');
138 if (is_object($this->expandedState) && is_object($this->expandedState->stateHash)) {
139 $this->expandedState = (array)$this->expandedState->stateHash;
141 $this->expandedState = $this->expandedState[
'stateHash'] ?: [];
153 'allowRecursiveDelete' => !empty($this->
getBackendUser()->uc[
'recursiveDelete']),
160 return new JsonResponse($configuration);
174 $doktypeLabelMap = [];
175 foreach (
$GLOBALS[
'TCA'][
'pages'][
'columns'][
'doktype'][
'config'][
'items'] as $doktypeItemConfig) {
176 if ($doktypeItemConfig[1] ===
'--div--') {
179 $doktypeLabelMap[$doktypeItemConfig[1]] = $doktypeItemConfig[0];
181 $doktypes = GeneralUtility::intExplode(
',', $backendUser->getTSConfig()[
'options.'][
'pageTree.'][
'doktypesToShowInNewPageDragArea'] ??
'',
true);
183 $allowedDoktypes = GeneralUtility::intExplode(
',', $backendUser->groupData[
'pagetypes_select'],
true);
184 $isAdmin = $backendUser->isAdmin();
186 if (!$isAdmin && empty($allowedDoktypes)) {
189 foreach ($doktypes as $doktype) {
190 if (!$isAdmin && !in_array($doktype, $allowedDoktypes,
true)) {
195 'nodeType' => $doktype,
196 'icon' =>
$GLOBALS[
'TCA'][
'pages'][
'ctrl'][
'typeicon_classes'][$doktype] ??
'',
210 public function fetchDataAction(ServerRequestInterface $request): ResponseInterface
215 if (!empty($request->getQueryParams()[
'pid'])) {
218 $mountPid = (int)($request->getQueryParams()[
'mount'] ?? 0);
219 $parentDepth = (int)($request->getQueryParams()[
'pidDepth'] ?? 0);
221 foreach ($entryPoints as $page) {
222 $items = array_merge($items, $this->
pagesToFlatArray($page, $mountPid, $parentDepth));
226 foreach ($entryPoints as $page) {
227 $items = array_merge($items, $this->
pagesToFlatArray($page, (
int)$page[
'uid']));
240 public function filterDataAction(ServerRequestInterface $request): ResponseInterface
242 $searchQuery = $request->getQueryParams()[
'q'] ??
'';
243 if (trim($searchQuery) ===
'') {
248 $this->expandAllNodes =
true;
253 foreach ($entryPoints as $page) {
255 $items = array_merge($items, $this->
pagesToFlatArray($page, (
int)$page[
'uid']));
271 if (empty($request->getParsedBody()[
'pid'])) {
272 throw new \RuntimeException(
273 'Required "pid" parameter is missing.',
277 $pid = (int)$request->getParsedBody()[
'pid'];
297 protected function pagesToFlatArray(array $page,
int $entryPoint,
int $depth = 0, array $inheritedData = []): array
300 $pageId = (int)$page[
'uid'];
301 if (in_array($pageId, $this->hiddenRecords,
true)) {
304 if ($pageId === 0 && !$backendUser->isAdmin()) {
308 $stopPageTree = !empty($page[
'php_tree_stop']) && $depth > 0;
309 $identifier = $entryPoint .
'_' . $pageId;
310 $expanded = !empty($page[
'expanded'])
311 || (isset($this->expandedState[$identifier]) && $this->expandedState[$identifier])
312 || $this->expandAllNodes;
314 $backgroundColor = !empty($this->backgroundColors[$pageId]) ? $this->backgroundColors[$pageId] : ($inheritedData[
'backgroundColor'] ??
'');
318 $nameSourceField =
'title';
319 $visibleText = $page[
'title'];
327 if ($this->useNavTitle && trim($page[
'nav_title'] ??
'') !==
'') {
328 $nameSourceField =
'nav_title';
329 $visibleText = $page[
'nav_title'];
331 if (trim($visibleText) ===
'') {
332 $visibleText = htmlspecialchars(
'[' . $this->
getLanguageService()->sL(
'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.no_title') .
']');
335 if ($this->addDomainName && $page[
'is_siteroot']) {
337 $suffix = $domain !==
'' ?
' [' . $domain .
']' :
'';
341 if (is_array($lockInfo)) {
342 $tooltip .=
' - ' . $lockInfo[
'msg'];
344 if ($this->addIdAsPrefix) {
345 $prefix = htmlspecialchars(
'[' . $pageId .
'] ');
351 'stateIdentifier' => $identifier,
352 'identifier' => $pageId,
354 'tip' => htmlspecialchars($tooltip),
355 'icon' => $icon->getIdentifier(),
356 'name' => $visibleText,
357 'nameSourceField' => $nameSourceField,
358 'alias' => htmlspecialchars($page[
'alias'] ??
''),
359 'selectable' =>
true,
361 'mountPoint' => $entryPoint,
362 'workspaceId' => !empty($page[
't3ver_oid']) ? $page[
't3ver_oid'] : $pageId,
365 && $backendUser->check(
'tables_modify',
'pages')
366 && $backendUser->checkLanguageAccess(0)
370 $item[
'hasChildren'] =
true;
371 if ($depth >= $this->levelsToFetch) {
375 if (!empty($prefix)) {
376 $item[
'prefix'] = htmlspecialchars($prefix);
378 if (!empty($suffix)) {
379 $item[
'suffix'] = htmlspecialchars($suffix);
381 if (is_array($lockInfo)) {
382 $item[
'locked'] =
true;
384 if ($icon->getOverlayIcon()) {
385 $item[
'overlayIcon'] = $icon->getOverlayIcon()->getIdentifier();
387 if ($expanded && is_array($page[
'_children']) && !empty($page[
'_children'])) {
388 $item[
'expanded'] = $expanded;
390 if ($backgroundColor) {
391 $item[
'backgroundColor'] = htmlspecialchars($backgroundColor);
394 $item[
'stopPageTree'] = $stopPageTree;
397 if (!empty($class)) {
398 $item[
'class'] = $class;
401 $item[
'isMountPoint'] =
true;
403 if ($this->showMountPathAboveMounts) {
409 if (!$stopPageTree && is_array($page[
'_children']) && !empty($page[
'_children']) && ($depth < $this->levelsToFetch || $expanded)) {
410 $items[key($items)][
'loaded'] =
true;
411 foreach ($page[
'_children'] as $child) {
412 $items = array_merge($items, $this->
pagesToFlatArray($child, $entryPoint, $depth + 1, [
'backgroundColor' => $backgroundColor]));
421 $userTsConfig = $backendUser->getTSConfig();
422 $excludedDocumentTypes = GeneralUtility::intExplode(
',', $userTsConfig[
'options.'][
'pageTree.'][
'excludeDoktypes'] ??
'',
true);
424 $additionalQueryRestrictions = [];
425 if (!empty($excludedDocumentTypes)) {
426 $additionalQueryRestrictions[] = GeneralUtility::makeInstance(DocumentTypeExclusionRestriction::class, $excludedDocumentTypes);
429 return GeneralUtility::makeInstance(
430 PageTreeRepository::class,
431 (
int)$backendUser->workspace,
433 $additionalQueryRestrictions
447 $entryPointId = $startPid > 0 ? $startPid : (int)($backendUser->uc[
'pageTree_temporaryMountPoint'] ?? 0);
448 if ($entryPointId > 0) {
449 $entryPointIds = [$entryPointId];
452 $entryPointIds = array_map(
'intval', $backendUser->returnWebmounts());
453 $entryPointIds = array_unique($entryPointIds);
454 if (empty($entryPointIds)) {
458 $entryPointIds = [0];
461 if (empty($entryPointIds)) {
468 $this->levelsToFetch = 999;
469 $repository->fetchFilteredTree(
476 $entryPointRecords = [];
477 foreach ($entryPointIds as $k => $entryPointId) {
478 if (in_array($entryPointId, $this->hiddenRecords,
true)) {
482 if (!empty($this->backgroundColors) && is_array($this->backgroundColors)) {
484 $entryPointRootLine = GeneralUtility::makeInstance(RootlineUtility::class, $entryPointId)->get();
485 }
catch (RootLineException $e) {
486 $entryPointRootLine = [];
488 foreach ($entryPointRootLine as $rootLineEntry) {
489 $parentUid = $rootLineEntry[
'uid'];
490 if (!empty($this->backgroundColors[$parentUid]) && empty($this->backgroundColors[$entryPointId])) {
491 $this->backgroundColors[$entryPointId] = $this->backgroundColors[$parentUid];
495 if ($entryPointId === 0) {
496 $entryPointRecord = [
498 'title' =>
$GLOBALS[
'TYPO3_CONF_VARS'][
'SYS'][
'sitename'] ?:
'TYPO3'
503 if ($entryPointRecord !==
null && !$this->
getBackendUser()->isInWebMount($entryPointId)) {
504 $entryPointRecord =
null;
507 if ($entryPointRecord) {
508 $entryPointRecord[
'uid'] = (int)$entryPointRecord[
'uid'];
510 $entryPointRecord = $repository->getTreeLevels($entryPointRecord, $this->levelsToFetch);
512 $entryPointRecord = $repository->getTree((
int)$entryPointRecord[
'uid'],
null, $entryPointIds,
true);
516 if (is_array($entryPointRecord) && !empty($entryPointRecord)) {
517 $entryPointRecords[$k] = $entryPointRecord;
521 return $entryPointRecords;
533 $siteFinder = GeneralUtility::makeInstance(SiteFinder::class);
535 $site = $siteFinder->getSiteByRootPageId($pageId);
536 $domain = (string)$site->getBase();
539 $pseudoSiteFinder = GeneralUtility::makeInstance(PseudoSiteFinder::class);
541 $site = $pseudoSiteFinder->getSiteByRootPageId($pageId);
542 $domain = trim((
string)$site->getBase(),
'/');
543 }
catch (SiteNotFoundException $e) {
563 array_shift($rootline);
565 foreach ($rootline as $rootlineElement) {
567 $text = $record[
'title'];
568 if ($this->useNavTitle && trim($record[
'nav_title'] ??
'') !==
'') {
569 $text = $record[
'nav_title'];
571 $path[] = htmlspecialchars($text);
573 return '/' . implode(
'/', $path);
586 if ($page[
'uid'] === 0) {
591 if ($page[
't3ver_oid'] > 0 && (
int)$page[
't3ver_wsid'] === $workspaceId) {
592 $classes[] =
'ver-element';
593 $classes[] =
'ver-versions';
597 $page[
't3ver_oid'] ?: $page[
'uid']
600 $classes[] =
'ver-versions';
604 return implode(
' ', $classes);
615 return $backendUser->isAdmin()
616 || ($backendUser->check(
'tables_modify',
'pages') && $backendUser->checkLanguageAccess(0));
626 $mountPoints = (int)($this->
getBackendUser()->uc[
'pageTree_temporaryMountPoint'] ?? 0);
628 $mountPoints = array_map(
'intval', $this->
getBackendUser()->returnWebmounts());
629 return array_unique($mountPoints);
631 return [$mountPoints];
639 return GeneralUtility::makeInstance(WorkspaceService::class);