Skip to content

Commit

Permalink
[TASK] Cleanup PageService and cover with tests
Browse files Browse the repository at this point in the history
  • Loading branch information
NamelessCoder committed Jan 24, 2023
1 parent ae3a13c commit d87b881
Show file tree
Hide file tree
Showing 4 changed files with 390 additions and 127 deletions.
186 changes: 64 additions & 122 deletions Classes/Service/PageService.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,11 @@

use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Context\LanguageAspect;
use TYPO3\CMS\Core\Domain\Repository\PageRepository;
use TYPO3\CMS\Core\SingletonInterface;
use TYPO3\CMS\Core\Type\Bitmask\PageTranslationVisibility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\RootlineUtility;
use TYPO3\CMS\Core\Utility\VersionNumberUtility;
use TYPO3\CMS\Frontend\Page\PageRepository;

/**
* Page Service
Expand All @@ -30,35 +29,8 @@ class PageService implements SingletonInterface
{
const DOKTYPE_MOVE_TO_PLACEHOLDER = 0;

/**
* @var array
*/
protected static $cachedPages = [];

/**
* @var array
*/
protected static $cachedMenus = [];

/**
* @var array
*/
protected static $cachedRootlines = [];

/**
* @param string $constantName
* @return mixed
*/
public function readPageRepositoryConstant(string $constantName)
{
if (class_exists(\TYPO3\CMS\Core\Domain\Repository\PageRepository::class)) {
$class = \TYPO3\CMS\Core\Domain\Repository\PageRepository::class;
} else {
$class = \TYPO3\CMS\Frontend\Page\PageRepository::class;
}

return constant($class . '::' . $constantName);
}
protected static array $cachedPages = [];
protected static array $cachedMenus = [];

public function getMenu(
int $pageUid,
Expand All @@ -70,15 +42,15 @@ public function getMenu(
$pageRepository = $this->getPageRepository();
$pageConstraints = $this->getPageConstraints($excludePages, $includeNotInMenu, $includeMenuSeparator);
$cacheKey = md5($pageUid . $pageConstraints . (integer) $disableGroupAccessCheck);
if (false === isset(static::$cachedMenus[$cacheKey])) {
if (true === (boolean) $disableGroupAccessCheck) {
if (!isset(static::$cachedMenus[$cacheKey])) {
if ($disableGroupAccessCheck) {
$pageRepository->where_groupAccess = '';
}

static::$cachedMenus[$cacheKey] = array_filter(
$pageRepository->getMenu($pageUid, '*', 'sorting', $pageConstraints),
function ($page) {
return $this->hidePageForLanguageUid($page) === false;
return !$this->hidePageForLanguageUid($page);
}
);
}
Expand All @@ -89,7 +61,7 @@ function ($page) {
public function getPage(int $pageUid, bool $disableGroupAccessCheck = false): array
{
$cacheKey = md5($pageUid . (integer) $disableGroupAccessCheck);
if (false === isset(static::$cachedPages[$cacheKey])) {
if (!isset(static::$cachedPages[$cacheKey])) {
static::$cachedPages[$cacheKey] = $this->getPageRepository()->getPage($pageUid, $disableGroupAccessCheck);
}

Expand All @@ -98,32 +70,18 @@ public function getPage(int $pageUid, bool $disableGroupAccessCheck = false): ar

public function getRootLine(
?int $pageUid = null,
bool $reverse = false,
bool $disableGroupAccessCheck = false
bool $reverse = false
): array {
if (null === $pageUid) {
$pageUid = $GLOBALS['TSFE']->id;
}
$cacheKey = md5($pageUid . (integer) $reverse . (integer) $disableGroupAccessCheck);
if (false === isset(static::$cachedRootlines[$cacheKey])) {
$pageRepository = $this->getPageRepository();
if (class_exists(RootlineUtility::class)) {
$rootline = (new RootlineUtility($pageUid))->get();
} elseif (method_exists($pageRepository, 'getRootLine')) {
if (true === (boolean) $disableGroupAccessCheck) {
$pageRepository->where_groupAccess = '';
}
$rootline = $pageRepository->getRootLine($pageUid);
} else {
$rootline = [];
}
if (true === $reverse) {
$rootline = array_reverse($rootline);
}
static::$cachedRootlines[$cacheKey] = $rootline;
/** @var RootlineUtility $rootLineUtility */
$rootLineUtility = GeneralUtility::makeInstance(RootlineUtility::class, $pageUid);
$rootline = $rootLineUtility->get();
if ($reverse) {
$rootline = array_reverse($rootline);
}

return static::$cachedRootlines[$cacheKey];
return $rootline;
}

protected function getPageConstraints(
Expand All @@ -134,19 +92,19 @@ protected function getPageConstraints(
$constraints = [];

$constraints[] = 'doktype NOT IN ('
. $this->readPageRepositoryConstant('DOKTYPE_BE_USER_SECTION')
. PageRepository::DOKTYPE_BE_USER_SECTION
. ','
. $this->readPageRepositoryConstant('DOKTYPE_RECYCLER')
. PageRepository::DOKTYPE_RECYCLER
. ','
. $this->readPageRepositoryConstant('DOKTYPE_SYSFOLDER')
. PageRepository::DOKTYPE_SYSFOLDER
. ')';

if ($includeNotInMenu === false) {
$constraints[] = 'nav_hide = 0';
}

if ($includeMenuSeparator === false) {
$constraints[] = 'doktype != ' . $this->readPageRepositoryConstant('DOKTYPE_SPACER');
$constraints[] = 'doktype != ' . PageRepository::DOKTYPE_SPACER;
}

if (0 < count($excludePages)) {
Expand All @@ -168,19 +126,18 @@ public function hidePageForLanguageUid($page = null, int $languageUid = -1, bool
$pageUid = (0 === (integer) $page) ? $GLOBALS['TSFE']->id : (integer) $page;
$pageRecord = $this->getPage($pageUid);
}
if (-1 === (integer) $languageUid) {
if (-1 === $languageUid) {
$languageUid = $GLOBALS['TSFE']->sys_language_uid;
if (class_exists(LanguageAspect::class)) {
/** @var Context $context */
$context = GeneralUtility::makeInstance(Context::class);
/** @var LanguageAspect $languageAspect */
$languageAspect = $context->getAspect('language');
$languageUid = $languageAspect->getId();
} else {
$languageUid = $GLOBALS['TSFE']->sys_language_uid;
}
}

$l18nCfg = true === isset($pageRecord['l18n_cfg']) ? $pageRecord['l18n_cfg'] : 0;
$l18nCfg = isset($pageRecord['l18n_cfg']) ? $pageRecord['l18n_cfg'] : 0;
if (class_exists(PageTranslationVisibility::class)) {
/** @var PageTranslationVisibility $visibilityBitSet */
$visibilityBitSet = GeneralUtility::makeInstance(
Expand All @@ -201,39 +158,14 @@ public function hidePageForLanguageUid($page = null, int $languageUid = -1, bool
$translationAvailable = (0 !== count($pageOverlay));

return
(true === $hideIfNotTranslated && (0 !== $languageUid) && false === $translationAvailable) ||
(true === $hideIfDefaultLanguage && ((0 === $languageUid) || false === $translationAvailable)) ||
(false === $normalWhenNoLanguage && (0 !== $languageUid) && false === $translationAvailable);
}

/**
* @return \TYPO3\CMS\Frontend\Page\PageRepository|\TYPO3\CMS\Core\Domain\Repository\PageRepository
*/
public function getPageRepository()
{
return clone ($GLOBALS['TSFE']->sys_page ?? $this->getPageRepositoryForBackendContext());
}

/**
* @return \TYPO3\CMS\Frontend\Page\PageRepository|\TYPO3\CMS\Core\Domain\Repository\PageRepository
*/
protected function getPageRepositoryForBackendContext()
{
static $instance = null;
if ($instance === null) {
/** @var PageRepository|\TYPO3\CMS\Core\Domain\Repository\PageRepository $instance */
$instance = GeneralUtility::makeInstance(
class_exists(\TYPO3\CMS\Core\Domain\Repository\PageRepository::class)
? \TYPO3\CMS\Core\Domain\Repository\PageRepository::class
: \TYPO3\CMS\Frontend\Page\PageRepository::class
);
}
return $instance;
($hideIfNotTranslated && (0 !== $languageUid) && !$translationAvailable) ||
($hideIfDefaultLanguage && ((0 === $languageUid) || !$translationAvailable)) ||
(!$normalWhenNoLanguage && (0 !== $languageUid) && !$translationAvailable);
}

public function getItemLink(array $page, bool $forceAbsoluteUrl = false): string
{
if ((integer) $page['doktype'] === $this->readPageRepositoryConstant('DOKTYPE_LINK')) {
if ((integer) $page['doktype'] === PageRepository::DOKTYPE_LINK) {
$parameter = $this->getPageRepository()->getExtURL($page);
} else {
$parameter = $page['uid'];
Expand All @@ -244,9 +176,6 @@ public function getItemLink(array $page, bool $forceAbsoluteUrl = false): string
'additionalParams' => '',
'forceAbsoluteUrl' => $forceAbsoluteUrl,
];
if (version_compare(VersionNumberUtility::getCurrentTypo3Version(), '9.5', '<')) {
$config['useCacheHash'] = false;
}

return $GLOBALS['TSFE']->cObj->typoLink('', $config);
}
Expand All @@ -262,34 +191,28 @@ public function isAccessGranted(array $page): bool
return true;
}

$groups = explode(',', $page['fe_group']);
$groups = GeneralUtility::intExplode(',', (string) $page['fe_group']);

$hide = (in_array(-1, $groups));
$show = (in_array(-2, $groups));

$showPageAtAnyLogin = (in_array(-2, $groups));
$hidePageAtAnyLogin = (in_array(-1, $groups));
$userIsLoggedIn = (is_array($GLOBALS['TSFE']->fe_user->user));
$userGroups = $GLOBALS['TSFE']->fe_user->groupData['uid'];
$userIsInGrantedGroups = (0 < count(array_intersect($userGroups, $groups)));

if ((false === $userIsLoggedIn && true === $hidePageAtAnyLogin) ||
(true === $userIsLoggedIn && true === $showPageAtAnyLogin) ||
(true === $userIsLoggedIn && true === $userIsInGrantedGroups)
) {
return true;
}

return false;
return (!$userIsLoggedIn && $hide) || ($userIsLoggedIn && $show) || ($userIsLoggedIn && $userIsInGrantedGroups);
}

public function isCurrent(int $pageUid): bool
{
return ((integer) $pageUid === (integer) $GLOBALS['TSFE']->id);
return ($pageUid === (integer) $GLOBALS['TSFE']->id);
}

public function isActive(int $pageUid, bool $showAccessProtected = false): bool
public function isActive(int $pageUid): bool
{
$rootLineData = $this->getRootLine(null, false, $showAccessProtected);
$rootLineData = $this->getRootLine();
foreach ($rootLineData as $page) {
if ((integer) $page['uid'] === (integer) $pageUid) {
if ((integer) $page['uid'] === $pageUid) {
return true;
}
}
Expand All @@ -300,7 +223,7 @@ public function isActive(int $pageUid, bool $showAccessProtected = false): bool
public function shouldUseShortcutTarget(array $arguments): bool
{
$useShortcutTarget = (boolean) $arguments['useShortcutData'];
if ($arguments['useShortcutTarget'] !== null) {
if (array_key_exists('useShortcutTarget', $arguments)) {
$useShortcutTarget = (boolean) $arguments['useShortcutTarget'];
}

Expand All @@ -310,7 +233,7 @@ public function shouldUseShortcutTarget(array $arguments): bool
public function shouldUseShortcutUid(array $arguments): bool
{
$useShortcutUid = (boolean) $arguments['useShortcutData'];
if ($arguments['useShortcutUid'] !== null) {
if (array_key_exists('useShortcutUid', $arguments)) {
$useShortcutUid = (boolean) $arguments['useShortcutUid'];
}

Expand All @@ -324,30 +247,49 @@ public function shouldUseShortcutUid(array $arguments): bool
*/
public function getShortcutTargetPage(array $page): ?array
{
if ((integer) $page['doktype'] !== $this->readPageRepositoryConstant('DOKTYPE_SHORTCUT')) {
if ((integer) $page['doktype'] !== PageRepository::DOKTYPE_SHORTCUT) {
return null;
}
$originalPageUid = $page['uid'];
switch ($page['shortcut_mode']) {
case 3:
// mode: parent page of current page (using PID of current page)
case PageRepository::SHORTCUT_MODE_PARENT_PAGE:
$targetPage = $this->getPage($page['pid']);
break;
case 2:
// mode: random subpage of selected or current page
case PageRepository::SHORTCUT_MODE_RANDOM_SUBPAGE:
$menu = $this->getMenu($page['shortcut'] > 0 ? $page['shortcut'] : $originalPageUid);
$targetPage = (0 < count($menu)) ? $menu[array_rand($menu)] : $page;
break;
case 1:
// mode: first subpage of selected or current page
case PageRepository::SHORTCUT_MODE_FIRST_SUBPAGE:
$menu = $this->getMenu($page['shortcut'] > 0 ? $page['shortcut'] : $originalPageUid);
$targetPage = (0 < count($menu)) ? reset($menu) : $page;
break;
case 0:
case PageRepository::SHORTCUT_MODE_NONE:
default:
// mode: selected page
$targetPage = $this->getPage($page['shortcut']);
}
return $targetPage;
}

/**
* @return PageRepository
* @codeCoverageIgnore
*/
public function getPageRepository()
{
return clone ($GLOBALS['TSFE']->sys_page ?? $this->getPageRepositoryForBackendContext());
}

/**
* @return PageRepository
* @codeCoverageIgnore
*/
protected function getPageRepositoryForBackendContext()
{
static $instance = null;
if ($instance === null) {
/** @var PageRepository $instance */
$instance = GeneralUtility::makeInstance(PageRepository::class);
}
return $instance;
}
}
8 changes: 4 additions & 4 deletions Classes/ViewHelpers/Menu/AbstractMenuViewHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use FluidTYPO3\Vhs\Service\PageService;
use FluidTYPO3\Vhs\Traits\PageRecordViewHelperTrait;
use FluidTYPO3\Vhs\Traits\TagViewHelperTrait;
use TYPO3\CMS\Core\Domain\Repository\PageRepository;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;

Expand Down Expand Up @@ -205,8 +206,7 @@ public function render()
$menu = $this->parseMenu($pages);
$rootLine = $this->pageService->getRootLine(
$this->arguments['pageUid'],
$this->arguments['reverse'] ?? false,
$this->arguments['showAccessProtected']
$this->arguments['reverse'] ?? false
);
$this->cleanupSubmenuVariables();
$this->cleanTemplateVariableContainer();
Expand Down Expand Up @@ -329,7 +329,7 @@ protected function autoRender(array $menu, int $level = 1): string

protected function renderItemLink(array $page): string
{
$isSpacer = ($page['doktype'] === $this->pageService->readPageRepositoryConstant('DOKTYPE_SPACER'));
$isSpacer = $page['doktype'] === PageRepository::DOKTYPE_SPACER;
$isCurrent = (boolean) $page['current'];
$isActive = (boolean) $page['active'];
$linkCurrent = (boolean) $this->arguments['linkCurrent'];
Expand Down Expand Up @@ -427,7 +427,7 @@ public function parseMenu(array $pages): array
$pages[$index]['uid'] = $targetPage['uid'];
}
}
if (true === $this->pageService->isActive($originalPageUid, $showAccessProtected)) {
if (true === $this->pageService->isActive($originalPageUid)) {
$pages[$index]['active'] = true;
$class[] = $this->arguments['classActive'];
} else {
Expand Down
3 changes: 2 additions & 1 deletion Classes/ViewHelpers/Page/BreadCrumbViewHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*/

use FluidTYPO3\Vhs\ViewHelpers\Menu\AbstractMenuViewHelper;
use TYPO3\CMS\Core\Domain\Repository\PageRepository;

/**
* ViewHelper to make a breadcrumb link set from a pageUid, automatic or manual.
Expand Down Expand Up @@ -52,7 +53,7 @@ public function render()
$rawRootLineData = array_slice($rawRootLineData, $entryLevel, $endLevel);
$rootLineData = [];
$showHidden = (boolean) $this->arguments['showHiddenInMenu'];
$spacerDoktype = $this->pageService->readPageRepositoryConstant('DOKTYPE_SPACER');
$spacerDoktype = PageRepository::DOKTYPE_SPACER;
foreach ($rawRootLineData as $record) {
$isHidden = (boolean) $record['nav_hide'];

Expand Down
Loading

0 comments on commit d87b881

Please sign in to comment.