Initial commit

This commit is contained in:
2018-04-02 08:07:38 +02:00
commit 7330c1ed3e
2054 changed files with 405203 additions and 0 deletions

View File

@@ -0,0 +1,34 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Page;
/*
* This file is part of the FluidTYPO3/Vhs project under GPLv2 or later.
*
* For the full copyright and license information, please read the
* LICENSE.md file that was distributed with this source code.
*/
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper;
/**
* Returns a full, absolute URL to this page with all arguments
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers\Page
*/
class AbsoluteUrlViewHelper extends AbstractViewHelper {
/**
* @return string
*/
public function render() {
$url = GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL');
if (0 !== strpos($url, GeneralUtility::getIndpEnv('TYPO3_SITE_URL'))) {
$url = GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . $url;
}
return $url;
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Page;
/*
* This file is part of the FluidTYPO3/Vhs project under GPLv2 or later.
*
* For the full copyright and license information, please read the
* LICENSE.md file that was distributed with this source code.
*/
use FluidTYPO3\Vhs\ViewHelpers\Page\Menu\AbstractMenuViewHelper;
/**
* ViewHelper to make a breadcrumb link set from a pageUid, automatic or manual
*
* @author Claus Due <claus@namelesscoder.net>
* @author Björn Fromme <fromeme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Page
*/
class BreadCrumbViewHelper extends AbstractMenuViewHelper {
/**
* @return void
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerArgument('pageUid', 'integer', 'Optional parent page UID to use as top level of menu. If left out will be detected from rootLine using $entryLevel.', FALSE, NULL);
$this->registerArgument('endLevel', 'integer', 'Optional deepest level of rendering. If left out all levels up to the current are rendered.', FALSE, NULL);
$this->overrideArgument('as', 'string', 'If used, stores the menu pages as an array in a variable named after this value and renders the tag content. If the tag content is empty automatic rendering is triggered.', FALSE, 'breadcrumb');
}
/**
* @return string
*/
public function render() {
$pageUid = $this->arguments['pageUid'] > 0 ? $this->arguments['pageUid'] : $GLOBALS['TSFE']->id;
$entryLevel = $this->arguments['entryLevel'];
$endLevel = $this->arguments['endLevel'];
$rawRootLineData = $this->pageSelect->getRootLine($pageUid);
$rawRootLineData = array_reverse($rawRootLineData);
$rawRootLineData = array_slice($rawRootLineData, $entryLevel, $endLevel);
$rootLineData = $rawRootLineData;
if (FALSE === (boolean) $this->arguments['showHiddenInMenu']) {
$rootLineData = array();
foreach ($rawRootLineData as $record) {
if (FALSE === (boolean) $record['nav_hide']) {
array_push($rootLineData, $record);
}
}
}
$rootLine = $this->parseMenu($rootLineData, $rootLineData);
if (0 === count($rootLine)) {
return NULL;
}
$this->backupVariables();
$this->templateVariableContainer->add($this->arguments['as'], $rootLine);
$output = $this->renderContent($rootLine);
$this->templateVariableContainer->remove($this->arguments['as']);
$this->restoreVariables();
return $output;
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Page;
/*
* This file is part of the FluidTYPO3/Vhs project under GPLv2 or later.
*
* For the full copyright and license information, please read the
* LICENSE.md file that was distributed with this source code.
*/
use FluidTYPO3\Vhs\ViewHelpers\Asset\AbstractAssetViewHelper;
/**
* ViewHelper used to place header blocks in document footer
*
* @package Vhs
* @subpackage ViewHelpers\Page
*/
class FooterViewHelper extends AbstractAssetViewHelper {
/**
* Render method
*
* @return void
*/
public function render() {
if ('BE' === TYPO3_MODE) {
return;
}
$content = $this->getContent();
$GLOBALS['TSFE']->getPageRenderer()->addFooterData($content);
}
}

View File

@@ -0,0 +1,130 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Page\Header;
/*
* This file is part of the FluidTYPO3/Vhs project under GPLv2 or later.
*
* For the full copyright and license information, please read the
* LICENSE.md file that was distributed with this source code.
*/
use FluidTYPO3\Vhs\Service\PageSelectService;
use TYPO3\CMS\Core\Page\PageRenderer;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder;
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper;
/**
* Returns the all alternate urls.
*
* @author Danilo Bürger <danilo.buerger@hmspl.de>, Heimspiel GmbH
* @package Vhs
* @subpackage ViewHelpers\Page\Header
*/
class AlternateViewHelper extends AbstractViewHelper {
/**
* @var PageSelectService
*/
protected $pageSelect;
/**
* @var ObjectManagerInterface
*/
protected $objectManager;
/**
* @var \TYPO3\CMS\Fluid\Core\ViewHelper\TagBuilder
*/
protected $tagBuilder;
/**
* @param PageSelectService $pageSelectService
* @return void
*/
public function injectPageSelectService(PageSelectService $pageSelectService) {
$this->pageSelect = $pageSelectService;
}
/**
* @param ObjectManagerInterface $objectManager
* @return void
*/
public function injectObjectManager(ObjectManagerInterface $objectManager) {
$this->objectManager = $objectManager;
$this->tagBuilder = $this->objectManager->get('TYPO3\\CMS\\Fluid\\Core\\ViewHelper\\TagBuilder');
}
/**
* Initialize
*
* @return void
*/
public function initializeArguments() {
$this->registerArgument('languages', 'mixed', 'The languages (either CSV, array or implementing Traversable)', TRUE);
$this->registerArgument('pageUid', 'integer', 'The page uid to check', FALSE, 0);
$this->registerArgument('normalWhenNoLanguage', 'boolean', 'If TRUE, a missing page overlay should be ignored', FALSE, FALSE);
}
/**
* @return string
*/
public function render() {
if ('BE' === TYPO3_MODE) {
return '';
}
$languages = $this->arguments['languages'];
if (TRUE === $languages instanceof \Traversable) {
$languages = iterator_to_array($languages);
} elseif (TRUE === is_string($languages)) {
$languages = GeneralUtility::trimExplode(',', $languages, TRUE);
} else {
$languages = (array) $languages;
}
$pageUid = intval($this->arguments['pageUid']);
$normalWhenNoLanguage = $this->arguments['normalWhenNoLanguage'];
if (0 === $pageUid) {
$pageUid = $GLOBALS['TSFE']->id;
}
/** @var UriBuilder $uriBuilder */
$uriBuilder = $this->controllerContext->getUriBuilder();
$uriBuilder = $uriBuilder->reset()
->setTargetPageUid($pageUid)
->setCreateAbsoluteUri(TRUE);
$this->tagBuilder->reset();
$this->tagBuilder->setTagName('link');
$this->tagBuilder->addAttribute('rel', 'alternate');
/** @var PageRenderer $pageRenderer */
$pageRenderer = $GLOBALS['TSFE']->getPageRenderer();
$usePageRenderer = (1 !== intval($GLOBALS['TSFE']->config['config']['disableAllHeaderCode']));
$output = '';
foreach ($languages as $languageUid => $languageName) {
if (FALSE === $this->pageSelect->hidePageForLanguageUid($pageUid, $languageUid, $normalWhenNoLanguage)) {
$uri = $uriBuilder->setArguments(array('L' => $languageUid))->build();
$this->tagBuilder->addAttribute('href', $uri);
$this->tagBuilder->addAttribute('hreflang', $languageName);
$renderedTag = $this->tagBuilder->render();
if (TRUE === $usePageRenderer) {
$pageRenderer->addMetaTag($renderedTag);
} else {
$output .= $renderedTag . LF;
}
}
}
if (FALSE === $usePageRenderer) {
return trim($output);
}
return '';
}
}

View File

@@ -0,0 +1,88 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Page\Header;
/*
* This file is part of the FluidTYPO3/Vhs project under GPLv2 or later.
*
* For the full copyright and license information, please read the
* LICENSE.md file that was distributed with this source code.
*/
use FluidTYPO3\Vhs\Service\PageSelectService;
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
/**
* Returns the current canonical url in a link tag.
*
* @author Danilo Bürger <danilo.buerger@hmspl.de>, Heimspiel GmbH
* @package Vhs
* @subpackage ViewHelpers\Page\Header
*/
class CanonicalViewHelper extends AbstractTagBasedViewHelper {
/**
* @var PageSelectService
*/
protected $pageSelect;
/**
* @param PageSelectService $pageSelectService
* @return void
*/
public function injectPageSelectService(PageSelectService $pageSelectService) {
$this->pageSelect = $pageSelectService;
}
/**
* @var string
*/
protected $tagName = 'link';
/**
* Initialize
*/
public function initializeArguments() {
$this->registerUniversalTagAttributes();
$this->registerArgument('pageUid', 'integer', 'The page uid to check', FALSE, 0);
$this->registerArgument('normalWhenNoLanguage', 'boolean', 'DEPRECATED: Visibility is now handled by core\'s typolink function.', FALSE);
}
/**
* @return mixed
*/
public function render() {
if ('BE' === TYPO3_MODE) {
return '';
}
$pageUid = $this->arguments['pageUid'];
if (0 === $pageUid) {
$pageUid = $GLOBALS['TSFE']->id;
}
$configuration = array(
'parameter' => $pageUid,
'forceAbsoluteUrl' => 1,
);
$uri = $GLOBALS['TSFE']->cObj->typoLink_URL($configuration);
if (TRUE === empty($uri)) {
return '';
}
$uri = $GLOBALS['TSFE']->baseUrlWrap($uri);
$this->tag->addAttribute('rel', 'canonical');
$this->tag->addAttribute('href', $uri);
$renderedTag = $this->tag->render();
if (1 === intval($GLOBALS['TSFE']->config['config']['disableAllHeaderCode'])) {
return $renderedTag;
}
$GLOBALS['TSFE']->getPageRenderer()->addMetaTag($renderedTag);
}
}

View File

@@ -0,0 +1,57 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Page\Header;
/*
* This file is part of the FluidTYPO3/Vhs project under GPLv2 or later.
*
* For the full copyright and license information, please read the
* LICENSE.md file that was distributed with this source code.
*/
use FluidTYPO3\Vhs\Traits\TagViewHelperTrait;
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
/**
* ViewHelper used to render a link tag in the `<head>` section of the page.
* If you use the ViewHelper in a plugin, the plugin and its action have to
* be cached!
*
* @author Georg Ringer
* @package Vhs
* @subpackage ViewHelpers\Page\Header
*/
class LinkViewHelper extends AbstractTagBasedViewHelper {
use TagViewHelperTrait;
/**
* @var string
*/
protected $tagName = 'link';
/**
* Arguments initialization
*
* @return void
*/
public function initializeArguments() {
$this->registerTagAttribute('rel', 'string', 'Property: rel');
$this->registerTagAttribute('href', 'string', 'Property: href');
$this->registerTagAttribute('type', 'string', 'Property: type');
$this->registerTagAttribute('lang', 'string', 'Property: lang');
$this->registerTagAttribute('dir', 'string', 'Property: dir');
}
/**
* Render method
*
* @return void
*/
public function render() {
if ('BE' === TYPO3_MODE) {
return;
}
$GLOBALS['TSFE']->getPageRenderer()->addMetaTag($this->renderTag($this->tagName));
}
}

View File

@@ -0,0 +1,63 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Page\Header;
/*
* This file is part of the FluidTYPO3/Vhs project under GPLv2 or later.
*
* For the full copyright and license information, please read the
* LICENSE.md file that was distributed with this source code.
*/
use FluidTYPO3\Vhs\Traits\TagViewHelperTrait;
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
/**
* ViewHelper used to render a meta tag
*
* If you use the ViewHelper in a plugin it has to be USER
* not USER_INT, what means it has to be cached!
*
* @author Georg Ringer
* @package Vhs
* @subpackage ViewHelpers\Page\Header
*/
class MetaViewHelper extends AbstractTagBasedViewHelper {
use TagViewHelperTrait;
/**
* @var string
*/
protected $tagName = 'meta';
/**
* Arguments initialization
*
* @return void
*/
public function initializeArguments() {
$this->registerTagAttribute('name', 'string', 'Name property of meta tag');
$this->registerTagAttribute('property', 'string', 'Property of meta tag');
$this->registerTagAttribute('content', 'string', 'Content of meta tag');
$this->registerTagAttribute('http-equiv', 'string', 'Property: http-equiv');
$this->registerTagAttribute('scheme', 'string', 'Property: scheme');
$this->registerTagAttribute('lang', 'string', 'Property: lang');
$this->registerTagAttribute('dir', 'string', 'Property: dir');
}
/**
* Render method
*
* @return void
*/
public function render() {
if ('BE' === TYPO3_MODE) {
return;
}
if (TRUE === isset($this->arguments['content']) && FALSE === empty($this->arguments['content'])) {
$GLOBALS['TSFE']->getPageRenderer()
->addMetaTag($this->renderTag($this->tagName, NULL, array('content' => $this->arguments['content'])));
}
}
}

View File

@@ -0,0 +1,81 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Page\Header;
/*
* This file is part of the FluidTYPO3/Vhs project under GPLv2 or later.
*
* For the full copyright and license information, please read the
* LICENSE.md file that was distributed with this source code.
*/
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper;
/**
* ### ViewHelper used to override page title
*
* This ViewHelper uses the TYPO3 PageRenderer to set the
* page title - with everything this implies regarding
* support for TypoScript settings.
*
* Specifically you should note the setting `config.noPageTitle`
* which must be set to either 1 (one) in case no other source
* defines the page title (it's likely that at least one does),
* or 2 (two) to indicate that the TS-controlled page title
* must be disabled. A value of 2 (two) ensures that the title
* used in this ViewHelper will be used in the rendered page.
*
* If you use the ViewHelper in a plugin it has to be USER
* not USER_INT, what means it has to be cached!
*
* #### Why can I not forcibly override the title?
*
* This has been opted out with full intention. The reasoning
* behind not allowing a Fluid template to forcibly override the
* page title that may be set through TypoScript is that many
* other extensions (mainly SEO-focused ones) will be setting
* and manipulating the page title - and if overridden in a
* template file using a ViewHelper, it would be almost impossible
* to detect unless you already know exactly where to look.
* Enforcing use of the core behavior is the only way to ensure
* that this ViewHelper can coexist with other extensions in
* a fully controllable way.
*
* @author Georg Ringer
* @package Vhs
* @subpackage ViewHelpers\Page\Header
*/
class TitleViewHelper extends AbstractViewHelper {
/**
* Arguments initialization
*
* @return void
*/
public function initializeArguments() {
$this->registerArgument('title', 'string', 'Title tag content');
$this->registerArgument('whitespaceString', 'string', 'String used to replace groups of white space characters, one replacement inserted per group', FALSE, ' ');
$this->registerArgument('setIndexedDocTitle', 'boolean', 'Set indexed doc title to title', FALSE, FALSE);
}
/**
* Render method
*
* @return void
*/
public function render() {
if ('BE' === TYPO3_MODE) {
return;
}
if (FALSE === empty($this->arguments['title'])) {
$title = $this->arguments['title'];
} else {
$title = $this->renderChildren();
}
$title = trim(preg_replace('/\s+/', $this->arguments['whitespaceString'], $title), $this->arguments['whitespaceString']);
$GLOBALS['TSFE']->getPageRenderer()->setTitle($title);
if (TRUE === $this->arguments['setIndexedDocTitle']) {
$GLOBALS['TSFE']->indexedDocTitle = $title;
}
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Page;
/*
* This file is part of the FluidTYPO3/Vhs project under GPLv2 or later.
*
* For the full copyright and license information, please read the
* LICENSE.md file that was distributed with this source code.
*/
use FluidTYPO3\Vhs\ViewHelpers\Asset\AbstractAssetViewHelper;
/**
* ViewHelper used to place header blocks in document header
*
* @package Vhs
* @subpackage ViewHelpers\Page
*/
class HeaderViewHelper extends AbstractAssetViewHelper {
/**
* Render method
*
* @return void
*/
public function render() {
if ('BE' === TYPO3_MODE) {
return;
}
$content = $this->getContent();
$name = $this->getName();
$overwrite = $this->getOverwrite();
if (TRUE === isset($GLOBALS['TSFE']->additionalHeaderData[$name]) && FALSE === $overwrite) {
return;
}
$GLOBALS['TSFE']->additionalHeaderData[$name] = $content;
}
}

View File

@@ -0,0 +1,89 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Page;
/*
* This file is part of the FluidTYPO3/Vhs project under GPLv2 or later.
*
* For the full copyright and license information, please read the
* LICENSE.md file that was distributed with this source code.
*/
use FluidTYPO3\Vhs\Service\PageSelectService;
use FluidTYPO3\Vhs\Traits\TemplateVariableViewHelperTrait;
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper;
/**
* ViewHelper to access data of the current page record
*
* @author Björn Fromme <fromeme@dreipunktnull.com>, dreipunktnull
* @author Danilo Bürger <danilo.buerger@hmspl.de>, Heimspiel GmbH
* @package Vhs
* @subpackage ViewHelpers\Page
*/
class InfoViewHelper extends AbstractViewHelper {
use TemplateVariableViewHelperTrait;
/**
* @var PageSelectService
*/
protected $pageSelect;
/**
* @param PageSelectService $pageSelect
* @return void
*/
public function injectPageSelectService(PageSelectService $pageSelect) {
$this->pageSelect = $pageSelect;
}
/**
* @return void
*/
public function initializeArguments() {
$this->registerAsArgument();
$this->registerArgument('pageUid', 'integer', 'If specified, this UID will be used to fetch page data instead of using the current page.', FALSE, 0);
$this->registerArgument('field', 'string', 'If specified, only this field will be returned/assigned instead of the complete page record.', FALSE, NULL);
}
/**
* @return mixed
*/
public function render() {
// Get page via pageUid argument or current id
$pageUid = intval($this->arguments['pageUid']);
if (0 === $pageUid) {
$pageUid = $GLOBALS['TSFE']->id;
}
$page = $this->pageSelect->getPage($pageUid);
// Add the page overlay
$languageUid = intval($GLOBALS['TSFE']->sys_language_uid);
if (0 !== $languageUid) {
$pageOverlay = $this->pageSelect->getPageOverlay($pageUid, $languageUid);
if (TRUE === is_array($pageOverlay)) {
if (TRUE === method_exists('TYPO3\\CMS\\Core\\Utility\\ArrayUtility', 'mergeRecursiveWithOverrule')) {
ArrayUtility::mergeRecursiveWithOverrule($page, $pageOverlay, FALSE, FALSE);
} else {
$page = GeneralUtility::array_merge_recursive_overrule($page, $pageOverlay, FALSE, FALSE);
}
}
}
$content = NULL;
// Check if field should be returned or assigned
$field = $this->arguments['field'];
if (TRUE === empty($field)) {
$content = $page;
} elseif (TRUE === isset($page[$field])) {
$content = $page[$field];
}
return $this->renderChildrenWithVariableOrReturnInput($content);
}
}

View File

@@ -0,0 +1,331 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Page;
/*
* This file is part of the FluidTYPO3/Vhs project under GPLv2 or later.
*
* For the full copyright and license information, please read the
* LICENSE.md file that was distributed with this source code.
*/
use FluidTYPO3\Vhs\Traits\ArrayConsumingViewHelperTrait;
use FluidTYPO3\Vhs\Utility\CoreUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
/**
* ViewHelper for rendering TYPO3 menus in Fluid
* Require the extension static_info_table
*
* @author Dominic Garms, DMFmedia GmbH
* @package Vhs
* @subpackage ViewHelpers/Page
*/
class LanguageMenuViewHelper extends AbstractTagBasedViewHelper {
use ArrayConsumingViewHelperTrait;
/**
* @var array
*/
protected $languageMenu = array();
/**
* @var integer
*/
protected $defaultLangUid = 0;
/**
* @var string
*/
protected $tagName = 'ul';
/**
* @var \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
*/
protected $cObj;
/**
* Initialize
* @return void
*/
public function initializeArguments() {
$this->registerUniversalTagAttributes();
$this->registerArgument('tagName', 'string', 'Tag name to use for enclosing container, list and flags (not finished) only', FALSE, 'ul');
$this->registerArgument('tagNameChildren', 'string', 'Tag name to use for child nodes surrounding links, list and flags only', FALSE, 'li');
$this->registerArgument('defaultIsoFlag', 'string', 'ISO code of the default flag', FALSE, 'gb');
$this->registerArgument('defaultLanguageLabel', 'string', 'Label for the default language', FALSE, 'English');
$this->registerArgument('order', 'mixed', 'Orders the languageIds after this list', FALSE, '');
$this->registerArgument('labelOverwrite', 'mixed', 'Overrides language labels', FALSE, '');
$this->registerArgument('hideNotTranslated', 'boolean', 'Hides languageIDs which are not translated', FALSE, FALSE);
$this->registerArgument('layout', 'string', 'How to render links when using autorendering. Possible selections: name,flag - use fx "name" or "flag,name" or "name,flag"', FALSE, 'flag,name');
$this->registerArgument('useCHash', 'boolean', 'Use cHash for typolink', FALSE, TRUE);
$this->registerArgument('flagPath', 'string', 'Overwrites the path to the flag folder', FALSE, '');
$this->registerArgument('flagImageType', 'string', 'Sets type of flag image: png, gif, jpeg', FALSE, 'png');
$this->registerArgument('linkCurrent', 'boolean', 'Sets flag to link current language or not', FALSE, TRUE);
$this->registerArgument('classCurrent', 'string', 'Sets the class, by which the current language will be marked', FALSE, 'current');
$this->registerArgument('as', 'string', 'If used, stores the menu pages as an array in a variable named according to this value and renders the tag content - which means automatic rendering is disabled if this attribute is used', FALSE, 'languageMenu');
$this->registerArgument('pageUid', 'integer', 'Optional page uid to use.', FALSE, 0);
$this->registerArgument('configuration', 'array', 'Additional typoLink configuration', FALSE, array());
$this->registerArgument('excludeQueryVars', 'string', 'Comma-separate list of variables to exclude', FALSE, '');
}
/**
* Render method
*
* @return string
*/
public function render() {
if (FALSE === is_object($GLOBALS['TSFE']->sys_page)) {
return NULL;
}
$this->cObj = GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\ContentObject\\ContentObjectRenderer');
$this->tagName = $this->arguments['tagName'];
// to set the tagName we should call initialize()
$this->initialize();
$this->languageMenu = $this->parseLanguageMenu($this->arguments['order'], $this->arguments['labelOverwrite']);
$this->templateVariableContainer->add($this->arguments['as'], $this->languageMenu);
$content = $this->renderChildren();
$this->templateVariableContainer->remove($this->arguments['as']);
if (0 === strlen(trim($content))) {
$content = $this->autoRender($this->languageMenu);
}
return $content;
}
/**
* Automatically render a language menu
*
* @return string
*/
protected function autoRender() {
$content = $this->getLanguageMenu();
$content = trim($content);
if (FALSE === empty($content)) {
$this->tag->setContent($content);
$content = $this->tag->render();
}
return $content;
}
/**
* Get layout 0 (default): list
*
* @return string
*/
protected function getLanguageMenu() {
$tagName = $this->arguments['tagNameChildren'];
$html = array();
$itemCount = count($this->languageMenu);
foreach ($this->languageMenu as $index => $var) {
$class = '';
$classes = array();
if (TRUE === (boolean) $var['inactive']) {
$classes[] = 'inactive';
}
if (TRUE === (boolean) $var['current']) {
$classes[] = $this->arguments['classCurrent'];
}
if (0 === $index) {
$classes[] = 'first';
} elseif (($itemCount - 1) === $index) {
$classes[] = 'last';
}
if (0 < count($classes)) {
$class = ' class="' . implode(' ', $classes) . '" ';
}
if (TRUE === (boolean) $var['current'] && FALSE === (boolean) $this->arguments['linkCurrent']) {
$html[] = '<' . $tagName . $class . '>' . $this->getLayout($var) . '</' . $tagName . '>';
} else {
$html[] = '<' . $tagName . $class . '><a href="' . htmlspecialchars($var['url']) . '">' . $this->getLayout($var) . '</a></' . $tagName . '>';
}
}
return implode(LF, $html);
}
/**
* Returns the flag source
*
* @param string $iso
* @return string
*/
protected function getLanguageFlagSrc($iso) {
if ('' !== $this->arguments['flagPath']) {
$path = trim($this->arguments['flagPath']);
} else {
$path = CoreUtility::getLanguageFlagIconPath();
}
$imgType = trim($this->arguments['flagImageType']);
$img = $path . $iso . '.' . $imgType;
return $img;
}
/**
* Return the layout: flag & text, flags only or text only
*
* @param array $language
* @return string
*/
protected function getLayout(array $language) {
$flagImage = FALSE !== stripos($this->arguments['layout'], 'flag') ? $this->getFlagImage($language) : '';
$label = $language['label'];
switch ($this->arguments['layout']) {
case 'flag':
$html = $flagImage;
break;
case 'name':
$html = $label;
break;
case 'name,flag':
$html = $label;
if ('' !== $flagImage) {
$html .= '&nbsp;' . $flagImage;
}
break;
case 'flag,name':
default:
if ('' !== $flagImage) {
$html = $flagImage . '&nbsp;' . $label;
} else {
$html = $label;
}
}
return $html;
}
/**
* Render the flag image for autorenderer
*
* @param array $language
* @return string
*/
protected function getFlagImage(array $language) {
$conf = array(
'file' => $language['flagSrc'],
'altText' => $language['label'],
'titleText' => $language['label']
);
return $this->cObj->render($this->cObj->getContentObject('IMAGE'), $conf);
}
/**
* Sets all parameter for langMenu
*
* @return array
*/
protected function parseLanguageMenu() {
$order = $this->arguments['order'] ? GeneralUtility::trimExplode(',', $this->arguments['order']) : '';
$labelOverwrite = $this->arguments['labelOverwrite'] ? GeneralUtility::trimExplode(',', $this->arguments['labelOverwrite']) : '';
$languageMenu = array();
$tempArray = array();
$tempArray[0] = array(
'label' => $this->arguments['defaultLanguageLabel'],
'flag' => $this->arguments['defaultIsoFlag']
);
$select = 'uid, title, flag';
$from = 'sys_language';
$where = '1=1' . $this->cObj->enableFields('sys_language');
$sysLanguage = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows($select, $from, $where);
foreach ($sysLanguage as $value) {
$tempArray[$value['uid']] = array(
'label' => $value['title'],
'flag' => $value['flag'],
);
}
// reorders languageMenu
if (FALSE === empty($order)) {
foreach ($order as $value) {
$languageMenu[$value] = $tempArray[$value];
}
} else {
$languageMenu = $tempArray;
}
// overwrite of label
if (FALSE === empty($labelOverwrite)) {
$i = 0;
foreach ($languageMenu as $key => $value) {
$languageMenu[$key]['label'] = $labelOverwrite[$i];
$i++;
}
}
// Select all pages_language_overlay records on the current page. Each represents a possibility for a language.
$pageArray = array();
$table = 'pages_language_overlay';
$whereClause = 'pid=' . $this->getPageUid() . ' ';
$whereClause .= $GLOBALS['TSFE']->sys_page->enableFields($table);
$sysLang = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('DISTINCT sys_language_uid', $table, $whereClause);
if (FALSE === empty($sysLang)) {
foreach ($sysLang as $val) {
$pageArray[$val['sys_language_uid']] = $val['sys_language_uid'];
}
}
foreach ($languageMenu as $key => $value) {
$current = $GLOBALS['TSFE']->sys_language_uid === (integer) $key ? 1 : 0;
$inactive = $pageArray[$key] || (integer) $key === $this->defaultLangUid ? 0 : 1;
$url = $this->getLanguageUrl($key, $inactive);
if (TRUE === empty($url)) {
$url = GeneralUtility::getIndpEnv('REQUEST_URI');
}
$languageMenu[$key]['current'] = $current;
$languageMenu[$key]['inactive'] = $inactive;
$languageMenu[$key]['url'] = $url;
$languageMenu[$key]['flagSrc'] = $this->getLanguageFlagSrc($value['flag']);
if (TRUE === (boolean) $this->arguments['hideNotTranslated'] && TRUE === (boolean) $inactive) {
unset($languageMenu[$key]);
}
}
return $languageMenu;
}
/**
* Get link of language menu entry
*
* @param $uid
* @return string
*/
protected function getLanguageUrl($uid) {
$excludedVars = trim((string) $this->arguments['excludeQueryVars']);
$config = array(
'parameter' => $this->getPageUid(),
'returnLast' => 'url',
'additionalParams' => '&L=' . $uid,
'useCacheHash' => $this->arguments['useCHash'],
'addQueryString' => 'GET',
'addQueryString.' => array(
'exclude' => 'id,L,cHash' . ($excludedVars ? ',' . $excludedVars : '')
)
);
if (TRUE === is_array($this->arguments['configuration'])) {
$config = $this->mergeArrays($config, $this->arguments['configuration']);
}
return $this->cObj->typoLink('', $config);
}
/**
* Get page via pageUid argument or current id
*
* @return integer
*/
protected function getPageUid() {
$pageUid = (integer) $this->arguments['pageUid'];
if (0 === $pageUid) {
$pageUid = $GLOBALS['TSFE']->id;
}
return (integer) $pageUid;
}
}

View File

@@ -0,0 +1,89 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Page;
/*
* This file is part of the FluidTYPO3/Vhs project under GPLv2 or later.
*
* For the full copyright and license information, please read the
* LICENSE.md file that was distributed with this source code.
*/
use FluidTYPO3\Vhs\Service\PageSelectService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper;
/**
* Returns the current language from languages depending on l18n settings.
*
* @author Danilo Bürger <danilo.buerger@hmspl.de>, Heimspiel GmbH
* @package Vhs
* @subpackage ViewHelpers\Page
*/
class LanguageViewHelper extends AbstractViewHelper {
/**
* @var PageSelectService
*/
protected $pageSelect;
/**
* @param PageSelectService $pageSelectService
* @return void
*/
public function injectPageSelectService(PageSelectService $pageSelectService) {
$this->pageSelect = $pageSelectService;
}
/**
* Initialize
*
* @return void
*/
public function initializeArguments() {
$this->registerArgument('languages', 'mixed', 'The languages (either CSV, array or implementing Traversable)', FALSE);
$this->registerArgument('pageUid', 'integer', 'The page uid to check', FALSE, 0);
$this->registerArgument('normalWhenNoLanguage', 'boolean', 'If TRUE, a missing page overlay should be ignored', FALSE, FALSE);
}
/**
* @return string
*/
public function render() {
if ('BE' === TYPO3_MODE) {
return '';
}
$languages = $this->arguments['languages'];
if (TRUE === $languages instanceof \Traversable) {
$languages = iterator_to_array($languages);
} elseif (TRUE === is_string($languages)) {
$languages = GeneralUtility::trimExplode(',', $languages, TRUE);
} else {
$languages = (array) $languages;
}
$pageUid = intval($this->arguments['pageUid']);
$normalWhenNoLanguage = $this->arguments['normalWhenNoLanguage'];
if (0 === $pageUid) {
$pageUid = $GLOBALS['TSFE']->id;
}
$currentLanguageUid = $GLOBALS['TSFE']->sys_language_uid;
$languageUid = 0;
if (FALSE === $this->pageSelect->hidePageForLanguageUid($pageUid, $currentLanguageUid, $normalWhenNoLanguage)) {
$languageUid = $currentLanguageUid;
} elseif (0 !== $currentLanguageUid) {
if (TRUE === $this->pageSelect->hidePageForLanguageUid($pageUid, 0, $normalWhenNoLanguage)) {
return '';
}
}
if (FALSE === empty($languages[$languageUid])) {
return $languages[$languageUid];
}
return $languageUid;
}
}

View File

@@ -0,0 +1,188 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Page;
/*
* This file is part of the FluidTYPO3/Vhs project under GPLv2 or later.
*
* For the full copyright and license information, please read the
* LICENSE.md file that was distributed with this source code.
*/
use FluidTYPO3\Vhs\Service\PageSelectService;
use FluidTYPO3\Vhs\Traits\TemplateVariableViewHelperTrait;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
/**
* ### Page: Link ViewHelper
*
* Viewhelper for rendering page links
*
* This viewhelper behaves identically to Fluid's link viewhelper
* except for it fetches the title of the provided page UID and inserts
* it as linktext if that is omitted. The link will not render at all
* if the requested page is not translated in the current language.
*
* Automatic linktext: <v:page.link pageUid="UID" />
* Manual linktext: <v:page.link pageUid="UID">linktext</v:page.link>
*
* @author Björn Fromme <fromeme@dreipunktnull.com>, dreipunktnull
* @author Danilo Bürger <danilo.buerger@hmspl.de>, Heimspiel GmbH
* @package Vhs
* @subpackage ViewHelpers\Page
*/
class LinkViewHelper extends AbstractTagBasedViewHelper {
use TemplateVariableViewHelperTrait;
/**
* @var PageSelectService
*/
protected $pageSelect;
/**
* @param PageSelectService $pageSelect
* @return void
*/
public function injectPageSelectService(PageSelectService $pageSelect) {
$this->pageSelect = $pageSelect;
}
/**
* @var string
*/
protected $tagName = 'a';
/**
* Arguments initialization
*
* @return void
*/
public function initializeArguments() {
$this->registerUniversalTagAttributes();
$this->registerTagAttribute('target', 'string', 'Target of link', FALSE);
$this->registerTagAttribute('rel', 'string', 'Specifies the relationship between the current document and the linked document', FALSE);
$this->registerArgument('pageUid', 'integer', 'UID of the page to create the link and fetch the title for.', FALSE, 0);
$this->registerArgument('additionalParams', 'array', 'Query parameters to be attached to the resulting URI', FALSE, array());
$this->registerArgument('pageType', 'integer', 'Type of the target page. See typolink.parameter', FALSE, 0);
$this->registerArgument('noCache', 'boolean', 'When TRUE disables caching for the target page. You should not need this.', FALSE, FALSE);
$this->registerArgument('noCacheHash', 'boolean', 'When TRUE supresses the cHash query parameter created by TypoLink. You should not need this.', FALSE, FALSE);
$this->registerArgument('section', 'string', 'The anchor to be added to the URI', FALSE, '');
$this->registerArgument('linkAccessRestrictedPages', 'boolean', 'When TRUE, links pointing to access restricted pages will still link' .
'to the page even though the page cannot be accessed.', FALSE, FALSE);
$this->registerArgument('absolute', 'boolean', 'When TRUE, the URI of the rendered link is absolute', FALSE, FALSE);
$this->registerArgument('addQueryString', 'boolean', 'When TRUE, the current query parameters will be kept in the URI', FALSE, FALSE);
$this->registerArgument('argumentsToBeExcludedFromQueryString', 'array', 'Arguments to be removed from the URI. Only active if $addQueryString = TRUE', FALSE, array());
$this->registerArgument('titleFields', 'string', 'CSV list of fields to use as link label - default is "nav_title,title", change to' .
'for example "tx_myext_somefield,subtitle,nav_title,title". The first field that contains text will be used. Field value resolved' .
'AFTER page field overlays.', FALSE, 'nav_title,title');
$this->registerArgument('pageTitleAs', 'string', 'When rendering child content, supplies page title as variable.', FALSE, NULL);
}
/**
* Render method
* @return NULL|string
*/
public function render() {
// Check if link wizard link
$pageUid = $this->arguments['pageUid'];
$additionalParameters = (array) $this->arguments['additionalParams'];
if (FALSE === is_numeric($pageUid)) {
$linkConfig = GeneralUtility::unQuoteFilenames($pageUid, TRUE);
if (TRUE === isset($linkConfig[0])) {
$pageUid = $linkConfig[0];
}
if (TRUE === isset($linkConfig[1]) && '-' !== $linkConfig[1]) {
$this->tag->addAttribute('target', $linkConfig[1]);
}
if (TRUE === isset($linkConfig[2]) && '-' !== $linkConfig[2]) {
$this->tag->addAttribute('class', $linkConfig[2]);
}
if (TRUE === isset($linkConfig[3]) && '-' !== $linkConfig[3]) {
$this->tag->addAttribute('title', $linkConfig[3]);
}
if (TRUE === isset($linkConfig[4]) && '-' !== $linkConfig[4]) {
$additionalParametersString = trim($linkConfig[4], '&');
$additionalParametersArray = GeneralUtility::trimExplode('&', $additionalParametersString);
foreach ($additionalParametersArray as $parameter) {
list($key, $value) = GeneralUtility::trimExplode('=', $parameter);
$additionalParameters[$key] = $value;
}
}
}
// Get page via pageUid argument or current id
$pageUid = intval($pageUid);
if (0 === $pageUid) {
$pageUid = $GLOBALS['TSFE']->id;
}
$page = $this->pageSelect->getPage($pageUid);
if (TRUE === empty($page)) {
return NULL;
}
// Do not render the link, if the page should be hidden
$currentLanguageUid = $GLOBALS['TSFE']->sys_language_uid;
$hidePage = $this->pageSelect->hidePageForLanguageUid($pageUid, $currentLanguageUid);
if (TRUE === $hidePage) {
return NULL;
}
// Get the title from the page or page overlay
$title = $this->getTitleValue($page);
if (0 < $currentLanguageUid) {
$pageOverlay = $this->pageSelect->getPageOverlay($pageUid, $currentLanguageUid);
$translatedTitle = $this->getTitleValue($pageOverlay);
if (FALSE === empty($translatedTitle)) {
$title = $translatedTitle;
}
}
// Check if we should assign page title to the template variable container
$pageTitleAs = $this->arguments['pageTitleAs'];
if (FALSE === empty($pageTitleAs)) {
$variables = array($pageTitleAs => $title);
} else {
$variables = array();
}
// Render childs to see if an alternative title content should be used
$renderedTitle = $this->renderChildrenWithVariables($variables);
if (FALSE === empty($renderedTitle)) {
$title = $renderedTitle;
}
$uriBuilder = $this->controllerContext->getUriBuilder();
$uri = $uriBuilder->reset()
->setTargetPageUid($pageUid)
->setTargetPageType($this->arguments['pageType'])
->setNoCache($this->arguments['noCache'])
->setUseCacheHash(!$this->arguments['noCacheHash'])
->setSection($this->arguments['section'])
->setLinkAccessRestrictedPages($this->arguments['linkAccessRestrictedPages'])
->setArguments($additionalParameters)
->setCreateAbsoluteUri($this->arguments['absolute'])
->setAddQueryString($this->arguments['addQueryString'])
->setArgumentsToBeExcludedFromQueryString((array) $this->arguments['argumentsToBeExcludedFromQueryString'])
->build();
$this->tag->addAttribute('href', $uri);
$this->tag->setContent($title);
return $this->tag->render();
}
/**
* @param array $record
* @return string
*/
private function getTitleValue($record) {
$titleFieldList = GeneralUtility::trimExplode(',', $this->arguments['titleFields']);
foreach ($titleFieldList as $titleFieldName) {
if (FALSE === empty($record[$titleFieldName])) {
return $record[$titleFieldName];
}
}
return '';
}
}

View File

@@ -0,0 +1,742 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Page\Menu;
/*
* This file is part of the FluidTYPO3/Vhs project under GPLv2 or later.
*
* For the full copyright and license information, please read the
* LICENSE.md file that was distributed with this source code.
*/
use FluidTYPO3\Vhs\Service\PageSelectService;
use FluidTYPO3\Vhs\Traits\TagViewHelperTrait;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
use TYPO3\CMS\Frontend\Page\PageRepository;
/**
* Base class for menu rendering ViewHelpers
*
* @author Claus Due <claus@namelesscoder.net>
* @author Björn Fromme <fromeme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Page\Menu
*/
abstract class AbstractMenuViewHelper extends AbstractTagBasedViewHelper {
use TagViewHelperTrait;
/**
* @var string
*/
protected $tagName = 'ul';
/**
* @var array
*/
private $backupValues = array();
/**
* @var boolean
*/
private $original = TRUE;
/**
* @var PageSelectService
*/
protected $pageSelect;
/**
* @param PageSelectService $pageSelectService
* @return void
*/
public function injectPageSelectService(PageSelectService $pageSelectService) {
$this->pageSelect = $pageSelectService;
}
/**
* Initialize
* @return void
*/
public function initializeArguments() {
$this->registerUniversalTagAttributes();
$this->registerArgument('tagName', 'string', 'Tag name to use for enclsing container', FALSE, 'ul');
$this->registerArgument('tagNameChildren', 'string', 'Tag name to use for child nodes surrounding links. If set to "a" enables non-wrapping mode.', FALSE, 'li');
$this->registerArgument('entryLevel', 'integer', 'Optional entryLevel TS equivalent of the menu', FALSE, 0);
$this->registerArgument('levels', 'integer', 'Number of levels to render - setting this to a number higher than 1 (one) will expand menu items that are active, to a depth of $levels starting from $entryLevel', FALSE, 1);
$this->registerArgument('divider', 'string', 'Optional divider to insert between each menu item. Note that this does not mix well with automatic rendering due to the use of an ul > li structure', FALSE, NULL);
$this->registerArgument('expandAll', 'boolean', 'If TRUE and $levels > 1 then expands all (not just the active) menu items which have submenus', FALSE, FALSE);
$this->registerArgument('classActive', 'string', 'Optional class name to add to active links', FALSE, 'active');
$this->registerArgument('classCurrent', 'string', 'Optional class name to add to current link', FALSE, 'current');
$this->registerArgument('classHasSubpages', 'string', 'Optional class name to add to links which have subpages', FALSE, 'sub');
$this->registerArgument('useShortcutUid', 'boolean', 'If TRUE, substitutes the link UID of a shortcut with the target page UID (and thus avoiding redirects) but does not change other data - which is done by using useShortcutData.', FALSE, FALSE);
$this->registerArgument('useShortcutTarget', 'boolean', 'Optional param for using shortcut target instead of shortcut itself for current link', FALSE, NULL);
$this->registerArgument('useShortcutData', 'boolean', 'Shortcut to set useShortcutTarget and useShortcutData simultaneously', FALSE, NULL);
$this->registerArgument('classFirst', 'string', 'Optional class name for the first menu elment', FALSE, '');
$this->registerArgument('classLast', 'string', 'Optional class name for the last menu elment', FALSE, '');
$this->registerArgument('substElementUid', 'boolean', 'Optional parameter for wrapping the link with the uid of the page', FALSE, '');
$this->registerArgument('includeSpacers', 'boolean', 'Wether or not to include menu spacers in the page select query', FALSE, FALSE);
$this->registerArgument('resolveExclude', 'boolean', 'Exclude link if realurl/cooluri flag tx_realurl_exclude is set', FALSE, FALSE);
$this->registerArgument('showHidden', 'boolean', 'DEPRECATED - IGNORED. FIELD IS AN ENABLE-FIELD WHICH MUST BE RESPECTED. Include disabled pages into the menu', FALSE, FALSE);
$this->registerArgument('showHiddenInMenu', 'boolean', 'Include pages that are set to be hidden in menus', FALSE, FALSE);
$this->registerArgument('showCurrent', 'boolean', 'If FALSE, does not display the current page', FALSE, TRUE);
$this->registerArgument('linkCurrent', 'boolean', 'If FALSE, does not wrap the current page in a link', FALSE, TRUE);
$this->registerArgument('linkActive', 'boolean', 'If FALSE, does not wrap with links the titles of pages that are active in the rootline', FALSE, TRUE);
$this->registerArgument('titleFields', 'string', 'CSV list of fields to use as link label - default is "nav_title,title", change to for example "tx_myext_somefield,subtitle,nav_title,title". The first field that contains text will be used. Field value resolved AFTER page field overlays.', FALSE, 'nav_title,title');
$this->registerArgument('doktypes', 'mixed', 'CSV list or array of allowed doktypes from constant names or integer values, i.e. 1,254 or DEFAULT,SYSFOLDER,SHORTCUT or just default,sysfolder,shortcut');
$this->registerArgument('excludeSubpageTypes', 'mixed', 'CSV list or array of doktypes to not consider as subpages. Can be constant names or integer values, i.e. 1,254 or DEFAULT,SYSFOLDER,SHORTCUT or just default,sysfolder,shortcut', FALSE, 'SYSFOLDER');
$this->registerArgument('deferred', 'boolean', 'If TRUE, does not output the tag content UNLESS a v:page.menu.deferred child ViewHelper is both used and triggered. This allows you to create advanced conditions while still using automatic rendering', FALSE, FALSE);
$this->registerArgument('as', 'string', 'If used, stores the menu pages as an array in a variable named after this value and renders the tag content. If the tag content is empty automatic rendering is triggered.', FALSE, 'menu');
$this->registerArgument('rootLineAs', 'string', 'If used, stores the menu root line as an array in a variable named according to this value and renders the tag content - which means automatic rendering is disabled if this attribute is used', FALSE, 'rootLine');
$this->registerArgument('excludePages', 'mixed', 'Page UIDs to exclude from the menu. Can be CSV, array or an object implementing Traversable.', FALSE, '');
$this->registerArgument('includeAnchorTitle', 'boolean', 'If TRUE, includes the page title as title attribute on the anchor.', FALSE, TRUE);
$this->registerArgument('forceAbsoluteUrl', 'boolean', 'If TRUE, the menu will be rendered with absolute URLs', FALSE, FALSE);
}
/**
* Initialize variables used by the submenu instance recycler. Variables set here
* may be read by the Page / Menu / Sub ViewHelper which then automatically repeats
* rendering using the exact same arguments but with a new page UID as starting page.
* Note that the submenu VieWHelper is only capable of recycling one type of menu at
* a time - for example, a List menu nested inside a regular Menu ViewHelper will
* simply start another menu rendering completely separate from the parent menu.
* @return NULL
*/
protected function initalizeSubmenuVariables() {
if (FALSE === $this->original) {
return NULL;
}
$variables = $this->templateVariableContainer->getAll();
$this->viewHelperVariableContainer->addOrUpdate('FluidTYPO3\Vhs\ViewHelpers\Page\Menu\AbstractMenuViewHelper', 'parentInstance', $this);
$this->viewHelperVariableContainer->addOrUpdate('FluidTYPO3\Vhs\ViewHelpers\Page\Menu\AbstractMenuViewHelper', 'variables', $variables);
}
/**
* @param boolean $original
* @return void
*/
public function setOriginal($original) {
$this->original = (boolean) $original;
}
/**
* @return NULL
*/
protected function cleanupSubmenuVariables() {
if (FALSE === $this->original) {
return NULL;
}
if (FALSE === $this->viewHelperVariableContainer->exists('FluidTYPO3\Vhs\ViewHelpers\Page\Menu\AbstractMenuViewHelper', 'parentInstance')) {
return NULL;
}
$this->viewHelperVariableContainer->remove('FluidTYPO3\Vhs\ViewHelpers\Page\Menu\AbstractMenuViewHelper', 'parentInstance');
$this->viewHelperVariableContainer->remove('FluidTYPO3\Vhs\ViewHelpers\Page\Menu\AbstractMenuViewHelper', 'variables');
}
/**
* Retrieves a stored, if any, parent instance of a menu. Although only implemented by
* the Page / Menu / Sub ViewHelper, placing this method in this abstract class instead
* will allow custom menu ViewHelpers to work as sub menu ViewHelpers without being
* forced to implement their own variable retrieval or subclass Page / Menu / Sub.
* Returns NULL if no parent exists.
* @param integer $pageUid UID of page that's the new parent page, overridden in arguments of cloned and recycled menu ViewHelper instance
* @return AbstractMenuViewHelper|NULL
*/
protected function retrieveReconfiguredParentMenuInstance($pageUid) {
if (FALSE === $this->viewHelperVariableContainer->exists('FluidTYPO3\Vhs\ViewHelpers\Page\Menu\AbstractMenuViewHelper', 'parentInstance')) {
return NULL;
}
$parentInstance = $this->viewHelperVariableContainer->get('FluidTYPO3\\Vhs\\ViewHelpers\\Page\\Menu\\AbstractMenuViewHelper', 'parentInstance');
$arguments = $parentInstance->getArguments();
$arguments['pageUid'] = $pageUid;
$parentInstance->setArguments($arguments);
return $parentInstance;
}
/**
* @return void
*/
protected function cleanTemplateVariableContainer() {
if (FALSE === $this->viewHelperVariableContainer->exists('FluidTYPO3\\Vhs\\ViewHelpers\\Page\\Menu\\AbstractMenuViewHelper', 'variables')) {
return;
}
$storedVariables = $this->viewHelperVariableContainer->get('FluidTYPO3\\Vhs\\ViewHelpers\\Page\\Menu\\AbstractMenuViewHelper', 'variables');
foreach ($this->templateVariableContainer->getAll() as $variableName => $value) {
$this->backupValues[$variableName] = $value;
$this->templateVariableContainer->remove($variableName);
}
foreach ($storedVariables as $variableName => $value) {
$this->templateVariableContainer->add($variableName, $value);
}
}
/**
* @return array
*/
public function getArguments() {
if (FALSE === is_array($this->arguments)) {
return $this->arguments->toArray();
}
return $this->arguments;
}
/**
* @return boolean
*/
protected function shouldUseShortcutTarget() {
$useShortcutTarget = (boolean) $this->arguments['useShortcutData'];
if (TRUE === $this->hasArgument('useShortcutTarget')) {
$useShortcutTarget = (boolean) $this->arguments['useShortcutTarget'];
}
return $useShortcutTarget;
}
/**
* @return boolean
*/
protected function shouldUseShortcutUid() {
$useShortcutUid = (boolean) $this->arguments['useShortcutData'];
if (TRUE === $this->hasArgument('useShortcutUid')) {
$useShortcutUid = (boolean) $this->arguments['useShortcutUid'];
}
return $useShortcutUid;
}
/**
* @param integer $pageUid
* @return boolean
*/
protected function isCurrent($pageUid) {
return (boolean) ((integer) $pageUid === (integer) $GLOBALS['TSFE']->id);
}
/**
* @param integer $pageUid
* @param array $rootLine
* @param integer $originalPageUid
* @return boolean
*/
protected function isActive($pageUid, $rootLine, $originalPageUid = NULL) {
if (NULL !== $originalPageUid && $pageUid !== $originalPageUid) {
$pageUid = $originalPageUid;
}
foreach ($rootLine as $page) {
if ((integer) $page['uid'] === (integer) $pageUid) {
return TRUE;
}
}
return FALSE;
}
/**
* Get a list from allowed doktypes for pages
*
* @return array
*/
protected function allowedDoktypeList() {
if (TRUE === isset($this->arguments['doktypes']) && FALSE === empty($this->arguments['doktypes'])) {
$types = $this->parseDoktypeList($this->arguments['doktypes']);
} else {
$types = array(
PageSelectService::DOKTYPE_MOVE_TO_PLACEHOLDER,
PageRepository::DOKTYPE_DEFAULT,
PageRepository::DOKTYPE_LINK,
PageRepository::DOKTYPE_SHORTCUT,
PageRepository::DOKTYPE_MOUNTPOINT,
);
}
if (TRUE === (boolean) $this->arguments['includeSpacers'] && FALSE === in_array(PageRepository::DOKTYPE_SPACER, $types)) {
array_push($types, PageRepository::DOKTYPE_SPACER);
}
return $types;
}
/**
* Parses the provided CSV list or array of doktypes to
* return an array of integers
*
* @param mixed $doktypes
* @return array
*/
protected function parseDoktypeList($doktypes) {
if (TRUE === is_array($doktypes)) {
$types = $doktypes;
} else {
$types = GeneralUtility::trimExplode(',', $doktypes);
}
$parsed = array();
foreach ($types as $index => $type) {
if (FALSE === ctype_digit($type)) {
$typeNumber = constant('TYPO3\CMS\Frontend\Page\PageRepository::DOKTYPE_' . strtoupper($type));
if (NULL !== $typeNumber) {
$parsed[$index] = $typeNumber;
}
} else {
$parsed[$index] = intval($type);
}
}
return $parsed;
}
/**
* @param array $page
* @return string
*/
protected function getItemTitle($page) {
$titleFieldList = GeneralUtility::trimExplode(',', $this->arguments['titleFields']);
foreach ($titleFieldList as $titleFieldName) {
if (FALSE === empty($page[$titleFieldName])) {
return $page[$titleFieldName];
}
}
return $page['title'];
}
/**
* Get the combined item CSS class based on menu item state and VH arguments
*
* @param array $pageRow
* @return array
*/
protected function getItemClass($pageRow) {
$class = array();
if (TRUE === (boolean) $pageRow['active']) {
$class[] = $this->arguments['classActive'];
}
if (TRUE === (boolean) $pageRow['current']) {
$class[] = $this->arguments['classCurrent'];
}
if (TRUE === (boolean) $pageRow['hasSubPages']) {
$class[] = $this->arguments['classHasSubpages'];
}
return $class;
}
/**
* Create the href of a link for a page record respecting
* a possible shortcut UID or mountpoint
*
* @param array $page
* @return string
*/
protected function getItemLink($page) {
$doktype = (integer) $page['doktype'];
if (PageRepository::DOKTYPE_SPACER === $doktype) {
return '';
}
$shortcut = (PageRepository::DOKTYPE_SHORTCUT === $doktype ? $page['shortcut'] : $page['url']);
$isShortcutOrLink = PageRepository::DOKTYPE_SHORTCUT === $doktype || PageRepository::DOKTYPE_LINK === $doktype;
$useShortcutTarget = $this->shouldUseShortcutTarget();
$pageUid = $page['uid'];
if (TRUE === $isShortcutOrLink && TRUE === $useShortcutTarget && 0 < $shortcut) {
$pageUid = $shortcut;
}
if (TRUE === (PageRepository::DOKTYPE_MOUNTPOINT === $doktype)) {
$pageUid = $page['mountedPageUid'];
}
$forceAbsoluteUrl = (boolean) $this->arguments['forceAbsoluteUrl'];
$config = array(
'parameter' => $pageUid,
'returnLast' => 'url',
'additionalParams' => '',
'useCacheHash' => FALSE,
'forceAbsoluteUrl' => $forceAbsoluteUrl,
);
// Append mountpoint parameter to urls of pages of a mounted subtree
$mountPointParameter = NULL;
if (FALSE === empty($page['mountPointParameter'])) {
$mountPointParameter = $page['mountPointParameter'];
}
if (FALSE === empty($page['_MP_PARAM'])) {
$mountPointParameter = $page['_MP_PARAM'];
}
if (NULL !== $mountPointParameter) {
$config['additionalParams'] = '&MP=' . $mountPointParameter;
}
return $GLOBALS['TSFE']->cObj->typoLink('', $config);
}
/**
* Returns submenu for provided UID respecting
* possible subpage types to exclude
*
* @param integer $pageUid
* @return array
*/
protected function getSubmenu($pageUid) {
$where = '';
if (NULL !== $this->arguments['excludeSubpageTypes'] && FALSE === empty($this->arguments['excludeSubpageTypes'])) {
$excludeSubpageTypes = $this->parseDoktypeList($this->arguments['excludeSubpageTypes']);
if (0 < count($excludeSubpageTypes)) {
$where .= ' AND doktype NOT IN (' . implode(',', $excludeSubpageTypes) . ')';
}
}
return $this->getMenu($pageUid, $where);
}
/**
* @param array $page
* @param array $rootLine
* @param array $parentPage
* @return array
*/
protected function getMenuItemEntry($page, $rootLine, array $parentPage = NULL) {
$getLL = $GLOBALS['TSFE']->sys_language_uid;
$page['originalPageUid'] = $page['uid'];
$overlayPageUid = $page['uid'];
$pageUid = $page['uid'];
$targetPage = NULL;
$doktype = (integer) $page['doktype'];
if (NULL !== $parentPage && TRUE === isset($parentPage['_MP_PARAM'])) {
$page['mountPointParameter'] = $parentPage['_MP_PARAM'];
}
if (PageRepository::DOKTYPE_MOUNTPOINT === $doktype) {
$mountInfo = $GLOBALS['TSFE']->sys_page->getMountPointInfo($page['uid'], $page);
$page['mountedPageUid'] = $mountInfo['mount_pid'];
$page['mountPointParameter'] = $mountInfo['MPvar'];
} elseif (PageRepository::DOKTYPE_SHORTCUT === $doktype) {
switch ($page['shortcut_mode']) {
case 3:
// mode: parent page of current page (using PID of current page)
$targetPage = $this->pageSelect->getPage($page['pid']);
if ((integer) $page['pid'] === (integer) $GLOBALS['TSFE']->id) {
array_push($rootLine, $page);
}
break;
case 2:
// mode: random subpage of selected or current page
$menu = $this->getMenu($page['shortcut'] > 0 ? $page['shortcut'] : $pageUid);
$targetPage = count($menu) > 0 ? $menu[array_rand($menu)] : $page;
break;
case 1:
// mode: first subpage of selected or current page
$menu = $this->getMenu($page['shortcut'] > 0 ? $page['shortcut'] : $pageUid);
$targetPage = count($menu) > 0 ? reset($menu) : $page;
break;
case 0:
default:
$targetPage = $this->pageSelect->getPage($page['shortcut']);
}
if (TRUE === (boolean) $this->shouldUseShortcutTarget()) {
// overwrite current page data with shortcut page data
$page = $targetPage;
$overlayPageUid = $targetPage['uid'];
}
if (TRUE === (boolean) $this->shouldUseShortcutUid()) {
// overwrite current page UID
$page['uid'] = $targetPage['uid'];
}
}
if (0 < $getLL) {
$pageOverlay = $this->pageSelect->getPageOverlay($overlayPageUid, $getLL);
foreach ($pageOverlay as $name => $value) {
$page[$name] = $value;
}
}
$page['hasSubPages'] = (0 < count($this->getSubmenu($page['originalPageUid'])));
$page['active'] = $this->isActive($page['uid'], $rootLine, $page['originalPageUid']);
$page['current'] = $this->isCurrent($page['uid']);
$page['link'] = $this->getItemLink($page);
$page['linktext'] = $this->getItemTitle($page);
$page['class'] = implode(' ', $this->getItemClass($page));
$page['doktype'] = $doktype;
if (PageRepository::DOKTYPE_LINK === $doktype) {
$urlTypes = array(
'1' => 'http://',
'4' => 'https://',
'2' => 'ftp://',
'3' => 'mailto:'
);
$page['link'] = $urlTypes[$page['urltype']] . $page['url'];
}
return $page;
}
/**
* Filter the fetched menu according to visibility etc.
*
* @param array $menu
* @param array $rootLine
* @param array $parentPage
* @return array
*/
protected function parseMenu($menu, $rootLine, array $parentPage = NULL) {
$classFirst = $this->arguments['classFirst'];
$classLast = $this->arguments['classLast'];
$filtered = array();
$allowedDocumentTypes = $this->allowedDoktypeList();
foreach ($menu as $uid => $page) {
if (TRUE === isset($page['tx_realurl_exclude']) && TRUE === (boolean) $page['tx_realurl_exclude'] && TRUE === (boolean) $this->arguments['resolveExclude']) {
continue;
} elseif (TRUE === isset($page['tx_cooluri_exclude']) && TRUE === (boolean) $page['tx_cooluri_exclude'] && TRUE === (boolean) $this->arguments['resolveExclude']) {
continue;
} elseif (TRUE === in_array($page['doktype'], $allowedDocumentTypes)) {
$filtered[$uid] = $this->getMenuItemEntry($page, $rootLine, $parentPage);
}
}
$length = count($filtered);
if (0 < $length) {
$idx = 1;
foreach ($filtered as $uid => $page) {
if (1 === $idx) {
$filtered[$uid]['class'] = trim($filtered[$uid]['class'] . ' ' . $classFirst);
}
if ($length === $idx) {
$filtered[$uid]['class'] = trim($filtered[$uid]['class'] . ' ' . $classLast);
}
$idx++;
}
}
return $filtered;
}
/**
* Automatically render a menu
*
* @param array $menu
* @param integer $level
* @return string
*/
protected function autoRender($menu, $level = 1) {
$tagName = $this->arguments['tagNameChildren'];
$this->tag->setTagName($this->getWrappingTagName());
$substElementUid = $this->arguments['substElementUid'];
$linkCurrent = (boolean) $this->arguments['linkCurrent'];
$linkActive = (boolean) $this->arguments['linkActive'];
$showCurrent = (boolean) $this->arguments['showCurrent'];
$expandAll = (boolean) $this->arguments['expandAll'];
$maxLevels = (integer) $this->arguments['levels'];
$includeAnchorTitle = (boolean) $this->arguments['includeAnchorTitle'];
$html = array();
$itemsRendered = 0;
$numberOfItems = count($menu);
$includedPages = array();
foreach ($menu as $page) {
if (TRUE === (boolean) $page['current'] && FALSE === $showCurrent) {
continue;
}
$class = trim($page['class']) != '' ? ' class="' . $page['class'] . '"' : '';
$elementId = $substElementUid ? ' id="elem_' . $page['uid'] . '"' : '';
$target = $page['target'] != '' ? ' target="' . $page['target'] . '"' : '';
if (FALSE === $this->isNonWrappingMode()) {
$html[] = '<' . $tagName . $elementId . $class . '>';
}
$isSpacer = ($page['doktype'] === PageRepository::DOKTYPE_SPACER);
$isCurrent = (boolean) $page['current'];
$isActive = (boolean) $page['active'];
if (TRUE === $isSpacer || (TRUE === $isCurrent && FALSE === $linkCurrent) || (TRUE === $isActive && FALSE === $linkActive)) {
$html[] = htmlspecialchars($page['linktext']);
} elseif (TRUE === $includeAnchorTitle) {
$html[] = sprintf('<a href="%s" title="%s"%s%s>%s</a>', $page['link'], htmlspecialchars($page['title']), $class, $target, htmlspecialchars($page['linktext']));
} else {
$html[] = sprintf('<a href="%s"%s%s>%s</a>', $page['link'], $class, $target, htmlspecialchars($page['linktext']));
}
if ((TRUE === (boolean) $page['active'] || TRUE === $expandAll) && TRUE === (boolean) $page['hasSubPages'] && $level < $maxLevels) {
$pageUid = (TRUE === isset($page['mountedPageUid'])) ? $page['mountedPageUid'] : $page['originalPageUid'];
$rootLineData = $this->pageSelect->getRootLine();
$subMenuData = $this->getMenu($pageUid);
$subMenu = $this->parseMenu($subMenuData, $rootLineData, $page);
$renderedSubMenu = $this->autoRender($subMenu, $level + 1);
$parentTagId = $this->tag->getAttribute('id');
if (FALSE === empty($parentTagId)) {
$this->tag->addAttribute('id', $parentTagId . '-lvl-' . strval($level));
}
$this->tag->setTagName($this->getWrappingTagName());
$this->tag->setContent($renderedSubMenu);
$this->tag->addAttribute('class', ($this->arguments['class'] ? $this->arguments['class'] . ' lvl-' : 'lvl-') . strval($level));
$html[] = $this->tag->render();
$this->tag->addAttribute('class', $this->arguments['class']);
if (FALSE === empty($parentTagId)) {
$this->tag->addAttribute('id', $parentTagId);
}
array_push($includedPages, $page);
}
if (FALSE === $this->isNonWrappingMode()) {
$html[] = '</' . $tagName . '>';
}
$itemsRendered++;
if (TRUE === isset($this->arguments['divider']) && $itemsRendered < $numberOfItems) {
$html[] = $this->arguments['divider'];
}
}
$content = implode(LF, $html);
return $content;
}
/**
* Returns the wrapping tag to use
*
* @return string
*/
public function getWrappingTagName() {
return $this->isNonWrappingMode() ? 'nav' : $this->arguments['tagName'];
}
/**
* Returns TRUE for non-wrapping mode which is triggered
* by setting tagNameChildren to 'a'
*
* @return boolean
*/
public function isNonWrappingMode() {
return (boolean) ('a' === strtolower($this->arguments['tagNameChildren']));
}
/**
* Saves copies of all template variables while rendering
* the menu
*
* @return void
*/
public function backupVariables() {
$backups = array($this->arguments['as'], $this->arguments['rootLineAs']);
foreach ($backups as $var) {
if (TRUE === $this->templateVariableContainer->exists($var)) {
$this->backupValues[$var] = $this->templateVariableContainer->get($var);
$this->templateVariableContainer->remove($var);
}
}
}
/**
* Restores all saved template variables
*
* @return void
*/
public function restoreVariables() {
if (0 < count($this->backupValues)) {
foreach ($this->backupValues as $var => $value) {
if (FALSE === $this->templateVariableContainer->exists($var)) {
$this->templateVariableContainer->add($var, $value);
}
}
}
}
/**
* Renders the tag's content or if omitted auto
* renders the menu for the provided arguments
*
* @param array $menu
* @return string
*/
public function renderContent($menu) {
$deferredRendering = (boolean) $this->arguments['deferred'];
if (0 === count($menu) && FALSE === $deferredRendering) {
return NULL;
}
if (TRUE === $deferredRendering) {
$tagContent = $this->autoRender($menu);
$this->tag->setContent($tagContent);
$deferredContent = $this->tag->render();
$this->viewHelperVariableContainer->addOrUpdate(
'FluidTYPO3\Vhs\ViewHelpers\Page\Menu\AbstractMenuViewHelper', 'deferredString', $deferredContent
);
$this->viewHelperVariableContainer->addOrUpdate(
'FluidTYPO3\Vhs\ViewHelpers\Page\Menu\AbstractMenuViewHelper', 'deferredArray', $menu
);
$output = $this->renderChildren();
$this->unsetDeferredVariableStorage();
} else {
$content = $this->renderChildren();
if (0 < strlen(trim($content))) {
$output = $content;
} else {
$output = $this->renderTag($this->getWrappingTagName(), $this->autoRender($menu));
}
}
return $output;
}
/**
* Render method
*
* @return string
*/
public function render() {
$pageUid = $this->arguments['pageUid'];
$rootLineData = $this->pageSelect->getRootLine();
$entryLevel = (integer) $this->arguments['entryLevel'];
if (0 > $entryLevel) {
$entryLevel = count($rootLineData) + $entryLevel;
}
if (TRUE === empty($pageUid)) {
if (NULL !== $rootLineData[$entryLevel]['uid']) {
$pageUid = $rootLineData[$entryLevel]['uid'];
} else {
return '';
}
}
$menuData = $this->getMenu($pageUid);
$menu = $this->parseMenu($menuData, $rootLineData);
$rootLine = $this->parseMenu($rootLineData, $rootLineData);
$this->cleanupSubmenuVariables();
$this->cleanTemplateVariableContainer();
$this->backupVariables();
$this->templateVariableContainer->add($this->arguments['as'], $menu);
$this->templateVariableContainer->add($this->arguments['rootLineAs'], $rootLine);
$this->initalizeSubmenuVariables();
$output = $this->renderContent($menu);
$this->cleanupSubmenuVariables();
$this->templateVariableContainer->remove($this->arguments['as']);
$this->templateVariableContainer->remove($this->arguments['rootLineAs']);
$this->restoreVariables();
return $output;
}
/**
* @param $pageUid
* @param string $where
* @return array
*/
public function getMenu($pageUid, $where = '') {
$excludePages = $this->processPagesArgument($this->arguments['excludePages']);
$showHiddenInMenu = (boolean) $this->arguments['showHiddenInMenu'];
$allowedDoktypeList = $this->allowedDoktypeList();
$menuData = $this->pageSelect->getMenu($pageUid, $excludePages, $where, $showHiddenInMenu, FALSE, $allowedDoktypeList);
foreach ($menuData as $key => $page) {
if (TRUE === $this->pageSelect->hidePageForLanguageUid($page['uid'], $GLOBALS['TSFE']->sys_language_uid)) {
unset($menuData[$key]);
}
}
return $menuData;
}
/**
* Returns array of page UIDs from provided pages
*
* @param mixed $pages
* @return array
*/
public function processPagesArgument($pages = NULL) {
if (NULL === $pages) {
$pages = $this->arguments['pages'];
}
if (TRUE === $pages instanceof \Traversable) {
$pages = iterator_to_array($pages);
} elseif (TRUE === is_string($pages)) {
$pages = GeneralUtility::trimExplode(',', $pages, TRUE);
} elseif (TRUE === is_integer($pages)) {
$pages = (array) $pages;
}
if (FALSE === is_array($pages)) {
return array();
}
return $pages;
}
/**
* @return void
*/
protected function unsetDeferredVariableStorage() {
if (TRUE === $this->viewHelperVariableContainer->exists('FluidTYPO3\Vhs\ViewHelpers\Page\Menu\AbstractMenuViewHelper', 'deferredString')) {
$this->viewHelperVariableContainer->remove('FluidTYPO3\Vhs\ViewHelpers\Page\Menu\AbstractMenuViewHelper', 'deferredString');
$this->viewHelperVariableContainer->remove('FluidTYPO3\Vhs\ViewHelpers\Page\Menu\AbstractMenuViewHelper', 'deferredArray');
}
}
}

View File

@@ -0,0 +1,144 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Page\Menu;
/*
* This file is part of the FluidTYPO3/Vhs project under GPLv2 or later.
*
* For the full copyright and license information, please read the
* LICENSE.md file that was distributed with this source code.
*/
/**
* ### Page: Browse Menu ViewHelper
*
* ViewHelper for rendering TYPO3 browse menus in Fluid
*
* Renders links to browse inside a menu branch including
* first, previous, next, last and up to the parent page.
* Supports both automatic, tag-based rendering (which
* defaults to `ul > li` with options to set both the
* parent and child tag names. When using manual rendering
* a range of support CSS classes are available along
* with each page record.
*
* @author Björn Fromme <fromeme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Page
*/
class BrowseViewHelper extends AbstractMenuViewHelper {
/**
* @var array
*/
protected $backups = array('menu');
/**
* @return void
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerArgument('labelFirst', 'string', 'Label for the "first" link', FALSE, 'first');
$this->registerArgument('labelLast', 'string', 'Label for the "last" link', FALSE, 'last');
$this->registerArgument('labelPrevious', 'string', 'Label for the "previous" link', FALSE, 'previous');
$this->registerArgument('labelNext', 'string', 'Label for the "next" link', FALSE, 'next');
$this->registerArgument('labelUp', 'string', 'Label for the "up" link', FALSE, 'up');
$this->registerArgument('renderFirst', 'boolean', 'If set to FALSE the "first" link will not be rendered', FALSE, TRUE);
$this->registerArgument('renderLast', 'boolean', 'If set to FALSE the "last" link will not be rendered', FALSE, TRUE);
$this->registerArgument('renderUp', 'boolean', 'If set to FALSE the "up" link will not be rendered', FALSE, TRUE);
$this->registerArgument('usePageTitles', 'boolean', 'If set to TRUE, uses target page titles instead of "next", "previous" etc. labels', FALSE, FALSE);
$this->registerArgument('pageUid', 'integer', 'Optional parent page UID to use as top level of menu. If unspecified, current page UID is used', FALSE, NULL);
$this->registerArgument('currentPageUid', 'integer', 'Optional page UID to use as current page. If unspecified, current page UID from globals is used', FALSE, NULL);
}
/**
* Render method
*
* @return string
*/
public function render() {
$pageUid = (integer) (NULL !== $this->arguments['pageUid'] ? $this->arguments['pageUid'] : $GLOBALS['TSFE']->id);
$currentUid = (integer) (NULL !== $this->arguments['currentPageUid'] ? $this->arguments['currentPageUid'] : $GLOBALS['TSFE']->id);
$currentPage = $this->pageSelect->getPage($currentUid);
$rootLineData = $this->pageSelect->getRootLine($pageUid);
$parentUid = (integer) (NULL !== $this->arguments['pageUid'] ? $pageUid : $currentPage['pid']);
$parentPage = $this->pageSelect->getPage($parentUid);
$menuData = $this->getMenu($parentUid);
$pageUids = array_keys($menuData);
$uidCount = count($pageUids);
$firstUid = $pageUids[0];
$lastUid = $pageUids[$uidCount - 1];
$nextUid = NULL;
$prevUid = NULL;
for ($i = 0; $i < $uidCount; $i++) {
if ((integer) $pageUids[$i] === $currentUid) {
if ($i > 0) {
$prevUid = $pageUids[$i - 1];
}
if ($i < $uidCount) {
$nextUid = $pageUids[$i + 1];
}
break;
}
}
$pages = array();
if (TRUE === (boolean) $this->arguments['renderFirst']) {
$pages['first'] = $menuData[$firstUid];
}
if (NULL !== $prevUid) {
$pages['prev'] = $menuData[$prevUid];
}
if (TRUE === (boolean) $this->arguments['renderUp']) {
$pages['up'] = $parentPage;
}
if (NULL !== $nextUid) {
$pages['next'] = $menuData[$nextUid];
}
if (TRUE === (boolean) $this->arguments['renderLast']) {
$pages['last'] = $menuData[$lastUid];
}
$menuItems = $this->parseMenu($pages, $rootLineData);
$menu = array();
if (TRUE === isset($pages['first'])) {
$menu['first'] = $menuItems['first'];
$menu['first']['linktext'] = $this->getCustomLabelOrPageTitle('labelFirst', $menuItems['first']);
}
if (TRUE === isset($pages['prev'])) {
$menu['prev'] = $menuItems['prev'];
$menu['prev']['linktext'] = $this->getCustomLabelOrPageTitle('labelPrevious', $menuItems['prev']);
}
if (TRUE === isset($pages['up'])) {
$menu['up'] = $menuItems['up'];
$menu['up']['linktext'] = $this->getCustomLabelOrPageTitle('labelUp', $menuItems['up']);
}
if (TRUE === isset($pages['next'])) {
$menu['next'] = $menuItems['next'];
$menu['next']['linktext'] = $this->getCustomLabelOrPageTitle('labelNext', $menuItems['next']);
}
if (TRUE === isset($pages['last'])) {
$menu['last'] = $menuItems['last'];
$menu['last']['linktext'] = $this->getCustomLabelOrPageTitle('labelLast', $menuItems['last']);
}
$this->backupVariables();
$this->templateVariableContainer->add($this->arguments['as'], $menu);
$output = $this->renderContent($menu);
$this->templateVariableContainer->remove($this->arguments['as']);
$this->restoreVariables();
return $output;
}
/**
* @todo Consider moving this, and argument usePageTitles, to AbstractMenuViewHelper; however, this is not merited by the current Menu VH collection
* @param string $labelName
* @param array $pageRecord
* @return string
*/
protected function getCustomLabelOrPageTitle($labelName, $pageRecord) {
$title = $this->arguments[$labelName];
if (TRUE === $this->arguments['usePageTitles']) {
$title = $this->getItemTitle($pageRecord);
}
return $title;
}
}

View File

@@ -0,0 +1,67 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Page\Menu;
/*
* This file is part of the FluidTYPO3/Vhs project under GPLv2 or later.
*
* For the full copyright and license information, please read the
* LICENSE.md file that was distributed with this source code.
*/
/**
* ### Page: Deferred menu rendering ViewHelper
*
* Place this ViewHelper inside any other ViewHelper which
* has been configured with the `deferred` attribute set to
* TRUE - this will cause the output of the parent to only
* contain the content of this ViewHelper.
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers\Page
*/
class DeferredViewHelper extends AbstractMenuViewHelper {
/**
* Initialize
* @return void
*/
public function initializeArguments() {
parent::initializeArguments();
$this->overrideArgument('as', 'string', 'If used, stores the menu pages as an array in a variable named according to this value and renders the tag content - which means automatic rendering is disabled if this attribute is used', FALSE, NULL);
}
/**
* @return string
* @throws \Exception
*/
public function render() {
$as = $this->arguments['as'];
if (FALSE === $this->viewHelperVariableContainer->exists('FluidTYPO3\Vhs\ViewHelpers\Page\Menu\AbstractMenuViewHelper', 'deferredArray')) {
return NULL;
}
if (FALSE === $this->viewHelperVariableContainer->exists('FluidTYPO3\Vhs\ViewHelpers\Page\Menu\AbstractMenuViewHelper', 'deferredString')) {
return NULL;
}
if (NULL === $as) {
$content = $this->viewHelperVariableContainer->get('FluidTYPO3\\Vhs\\ViewHelpers\\Page\\Menu\\AbstractMenuViewHelper', 'deferredString');
$this->unsetDeferredVariableStorage();
return $content;
} elseif (TRUE === empty($as)) {
throw new \Exception('An "as" attribute was used but was empty - use a proper string value', 1370096373);
}
if (TRUE === $this->templateVariableContainer->exists($as)) {
$backupVariable = $this->templateVariableContainer->get($as);
$this->templateVariableContainer->remove($as);
}
$this->templateVariableContainer->add($as, $this->viewHelperVariableContainer->get('FluidTYPO3\\Vhs\\ViewHelpers\\Page\\Menu\\AbstractMenuViewHelper', 'deferredArray'));
$this->unsetDeferredVariableStorage();
$content = $this->renderChildren();
$this->templateVariableContainer->remove($as);
if (TRUE === isset($backupVariable)) {
$this->templateVariableContainer->add($as, $backupVariable);
}
return $content;
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Page\Menu;
/*
* This file is part of the FluidTYPO3/Vhs project under GPLv2 or later.
*
* For the full copyright and license information, please read the
* LICENSE.md file that was distributed with this source code.
*/
/**
* ### Page: Directory Menu ViewHelper
*
* ViewHelper for rendering TYPO3 list menus in Fluid
*
* Supports both automatic, tag-based rendering (which
* defaults to `ul > li` with options to set both the
* parent and child tag names. When using manual rendering
* a range of support CSS classes are available along
* with each page record.
*
* @author Björn Fromme <fromeme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Page
*/
class DirectoryViewHelper extends AbstractMenuViewHelper {
/**
* @var array
*/
protected $backups = array('menu');
/**
* @return void
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerArgument('pages', 'mixed', 'Parent page UIDs of subpages to include in the menu. Can be CSV, array or an object implementing Traversable.', TRUE);
}
/**
* Render method
*
* @return string
*/
public function render() {
$pages = $this->processPagesArgument();
if (0 === count($pages)) {
return '';
}
$menuData = array();
$rootLineData = $this->pageSelect->getRootLine();
foreach ($pages as $pageUid) {
$menuData = array_merge($menuData, $this->getMenu($pageUid));
}
$menu = $this->parseMenu($menuData, $rootLineData);
$this->backupVariables();
$this->templateVariableContainer->add($this->arguments['as'], $menu);
$output = $this->renderContent($menu);
$this->templateVariableContainer->remove($this->arguments['as']);
$this->restoreVariables();
return $output;
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Page\Menu;
/*
* This file is part of the FluidTYPO3/Vhs project under GPLv2 or later.
*
* For the full copyright and license information, please read the
* LICENSE.md file that was distributed with this source code.
*/
/**
* ### Page: List Menu ViewHelper
*
* ViewHelper for rendering TYPO3 list menus in Fluid
*
* Supports both automatic, tag-based rendering (which
* defaults to `ul > li` with options to set both the
* parent and child tag names. When using manual rendering
* a range of support CSS classes are available along
* with each page record.
*
* @author Björn Fromme <fromeme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Page
*/
class ListViewHelper extends AbstractMenuViewHelper {
/**
* @var array
*/
protected $backups = array('menu');
/**
* @return void
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerArgument('pages', 'mixed', 'Page UIDs to include in the menu. Can be CSV, array or an object implementing Traversable.', TRUE);
}
/**
* Render method
*
* @return string
*/
public function render() {
$pages = $this->processPagesArgument();
if (0 === count($pages)) {
return '';
}
$menuData = array();
$rootLineData = $this->pageSelect->getRootLine();
foreach ($pages as $pageUid) {
$menuData[] = $this->pageSelect->getPage($pageUid);
}
$menu = $this->parseMenu($menuData, $rootLineData);
$this->backupVariables();
$this->templateVariableContainer->add($this->arguments['as'], $menu);
$output = $this->renderContent($menu);
$this->templateVariableContainer->remove($this->arguments['as']);
$this->restoreVariables();
return $output;
}
}

View File

@@ -0,0 +1,74 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Page\Menu;
/*
* This file is part of the FluidTYPO3/Vhs project under GPLv2 or later.
*
* For the full copyright and license information, please read the
* LICENSE.md file that was distributed with this source code.
*/
/**
* ### Page: Auto Sub Menu ViewHelper
*
* Recycles the parent menu ViewHelper instance, resetting the
* page UID used as starting point and repeating rendering of
* the exact same tag content.
*
* Used in custom menu rendering to indicate where a submenu is
* to be rendered; accepts only a single argument called `pageUid`
* which defines the new starting page UID that is used in the
* recycled parent menu instance.
*
* @author Björn Fromme <fromeme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Page
*/
class SubViewHelper extends AbstractMenuViewHelper {
/**
* @return void
*/
public function initializeArguments() {
$this->registerArgument('pageUid', 'mixed', 'Page UID to be overridden in the recycled rendering of the parent instance, if one exists', TRUE);
}
/**
* Render method
*
* @return string
*/
public function render() {
$pageUid = $this->arguments['pageUid'];
$parentInstance = $this->retrieveReconfiguredParentMenuInstance($pageUid);
if (NULL === $parentInstance) {
return '';
}
$parentArguments = $parentInstance->getArguments();
$currentPageRootLine = $this->pageSelect->getRootLine();
$isActive = $this->isActive($pageUid, $currentPageRootLine);
// Note about next case: although $isCurrent in most cases implies $isActive, cases where the menu item
// that is being rendered is in fact the current page but is NOT part of the rootline of the menu being
// rendered - which is expected for example if using a page setting to render a different page in menus.
// This means that the following check although it appears redundant, it is in fact not.
$isCurrent = $this->isCurrent($pageUid);
$isExpanded = (boolean) (TRUE === (boolean) $parentArguments['expandAll']);
$shouldRender = (boolean) (TRUE === $isActive || TRUE === $isCurrent || TRUE === $isExpanded);
if (FALSE === $shouldRender) {
return '';
}
// retrieve the set of template variables which were in play when the parent menu VH started rendering.
$variables = $this->viewHelperVariableContainer->get('FluidTYPO3\\Vhs\\ViewHelpers\\Page\\Menu\\AbstractMenuViewHelper', 'variables');
$parentInstance->setOriginal(FALSE);
$content = $parentInstance->render();
// restore the previous set of variables after they most likely have changed during the render() above.
foreach ($variables as $name => $value) {
if (TRUE === $this->templateVariableContainer->exists($name)) {
$this->templateVariableContainer->remove($name);
$this->templateVariableContainer->add($name, $value);
}
}
return $content;
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Page;
/*
* This file is part of the FluidTYPO3/Vhs project under GPLv2 or later.
*
* For the full copyright and license information, please read the
* LICENSE.md file that was distributed with this source code.
*/
use FluidTYPO3\Vhs\ViewHelpers\Page\Menu\AbstractMenuViewHelper;
/**
* ### Page: Menu ViewHelper
*
* ViewHelper for rendering TYPO3 menus in Fluid
*
* Supports both automatic, tag-based rendering (which
* defaults to `ul > li` with options to set both the
* parent and child tag names. When using manual rendering
* a range of support CSS classes are available along
* with each page record.
*
* @author Claus Due <claus@namelesscoder.net>
* @author Björn Fromme <fromeme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Page
*/
class MenuViewHelper extends AbstractMenuViewHelper {
/**
* @return void
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerArgument('pageUid', 'integer', 'Optional parent page UID to use as top level of menu. If left out will be detected from rootLine using $entryLevel', FALSE, NULL);
}
}

View File

@@ -0,0 +1,120 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Page\Resources;
/*
* This file is part of the FluidTYPO3/Vhs project under GPLv2 or later.
*
* For the full copyright and license information, please read the
* LICENSE.md file that was distributed with this source code.
*/
use FluidTYPO3\Vhs\ViewHelpers\Resource\Record\FalViewHelper as ResourcesFalViewHelper;
use FluidTYPO3\Vhs\Traits\SlideViewHelperTrait;
use TYPO3\CMS\Core\Utility\ArrayUtility;
/**
* @author Danilo Bürger <danilo.buerger@hmspl.de>, Heimspiel GmbH
* @package Vhs
* @subpackage ViewHelpers\Page\Resources
*/
class FalViewHelper extends ResourcesFalViewHelper {
use SlideViewHelperTrait;
const defaultTable = 'pages';
const defaultField = 'media';
/**
* @var string
*/
protected $table = self::defaultTable;
/**
* @var string
*/
protected $field = self::defaultField;
/**
* Initialize arguments.
*
* @return void
* @api
*/
public function initializeArguments() {
parent::initializeArguments();
$this->overrideArgument('table', 'string', 'The table to lookup records.', FALSE, self::defaultTable);
$this->overrideArgument('field', 'string', 'The field of the table associated to resources.', FALSE, self::defaultField);
$this->registerSlideArguments();
}
/**
* @param integer $pageUid
* @param integer $limit
* @return array
*/
protected function getSlideRecordsFromPage($pageUid, $limit) {
$pageRecord = $this->getRecord($pageUid);
if (!$this->isDefaultLanguage()) {
$localisation = $this->getDatabaseConntection()->exec_SELECTgetSingleRow(
'*',
'pages_language_overlay',
"pid = '" . $pageRecord['uid'] . "' AND sys_language_uid = '" . $this->getCurrentLanguageUid() . "'"
);
if (TRUE === is_array($localisation)) {
ArrayUtility::mergeRecursiveWithOverrule($pageRecord, $localisation);
}
}
$resources = $this->getResources($pageRecord);
if (NULL !== $limit && count($resources) > $limit) {
$resources = array_slice($resources, 0, $limit);
}
return $resources;
}
/**
* @return boolean
*/
protected function isDefaultLanguage() {
return (boolean) $this->getCurrentLanguageUid() === 0;
}
/**
* @return integer
*/
protected function getCurrentLanguageUid() {
return (integer) $GLOBALS['TSFE']->sys_language_uid;
}
/**
* AbstractRecordResource usually uses the current cObj as reference,
* but the page is needed here
*
* @return array
*/
public function getActiveRecord() {
return $GLOBALS['TSFE']->page;
}
/**
* @return mixed
* @throws Exception
*/
public function render() {
$record = $this->arguments['record'];
$uid = $this->arguments['uid'];
if (NULL === $uid) {
if (NULL === $record) {
$record = $this->getActiveRecord();
}
$uid = $record['uid'];
}
if (NULL === $uid) {
throw new Exception('No record was found. The "record" or "uid" argument must be specified.', 1384611413);
}
$resources = $this->getSlideRecords($uid);
return $this->renderChildrenWithVariableOrReturnInput($resources);
}
}

View File

@@ -0,0 +1,46 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Page;
/*
* This file is part of the FluidTYPO3/Vhs project under GPLv2 or later.
*
* For the full copyright and license information, please read the
* LICENSE.md file that was distributed with this source code.
*/
use FluidTYPO3\Vhs\ViewHelpers\Resource\RecordViewHelper;
/**
* @author Danilo Bürger <danilo.buerger@hmspl.de>, Heimspiel GmbH
* @package Vhs
* @subpackage ViewHelpers\Page
*/
class ResourcesViewHelper extends RecordViewHelper {
const defaultTable = 'pages';
const defaultField = 'media';
/**
* @var string
*/
protected $table = self::defaultTable;
/**
* @var string
*/
protected $field = self::defaultField;
/**
* Initialize arguments.
*
* @return void
* @api
*/
public function initializeArguments() {
parent::initializeArguments();
$this->overrideArgument('table', 'string', 'The table to lookup records.', FALSE, self::defaultTable);
$this->overrideArgument('field', 'string', 'The field of the table associated to resources.', FALSE, self::defaultField);
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Page;
/*
* This file is part of the FluidTYPO3/Vhs project under GPLv2 or later.
*
* For the full copyright and license information, please read the
* LICENSE.md file that was distributed with this source code.
*/
use FluidTYPO3\Vhs\Service\PageSelectService;
use FluidTYPO3\Vhs\Traits\TemplateVariableViewHelperTrait;
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper;
/**
* ViewHelper to get the rootline of a page
*
* @author Danilo Bürger <danilo.buerger@hmspl.de>, Heimspiel GmbH
* @package Vhs
* @subpackage ViewHelpers\Page
*/
class RootlineViewHelper extends AbstractViewHelper {
use TemplateVariableViewHelperTrait;
/**
* @var PageSelectService
*/
protected $pageSelect;
/**
* @param PageSelectService $pageSelectService
* @return void
*/
public function injectPageSelectService(PageSelectService $pageSelectService) {
$this->pageSelect = $pageSelectService;
}
/**
* Initialize arguments.
*
* @return void
* @api
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerAsArgument();
$this->registerArgument('pageUid', 'integer', 'Optional page uid to use.', FALSE, 0);
}
/**
* @return mixed
*/
public function render() {
$pageUid = $this->arguments['pageUid'];
if (0 === $pageUid) {
$pageUid = $GLOBALS['TSFE']->id;
}
$rootLineData = $this->pageSelect->getRootLine($pageUid);
return $this->renderChildrenWithVariableOrReturnInput($rootLineData);
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Page;
/*
* This file is part of the FluidTYPO3/Vhs project under GPLv2 or later.
*
* For the full copyright and license information, please read the
* LICENSE.md file that was distributed with this source code.
*/
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper;
/**
* ### Page: Static Prefix
*
* Use this ViewHelper to read the contents of the `plugin.tx_vhs.settings.prependPath`
* TypoScript location - this setting stores the static prefix which gets added to all
* relative resource URIs generated by VHS; whenever you require a ViewHelper which
* does not respect this setting you can use this ViewHelper to prepend that setting
* after the value is returned from the other ViewHelper.
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers\Page
*/
class StaticPrefixViewHelper extends AbstractViewHelper {
/**
* @return string
*/
public function render() {
if (FALSE === empty($GLOBALS['TSFE']->tmpl->setup['plugin.']['tx_vhs.']['settings.']['prependPath'])) {
return $GLOBALS['TSFE']->tmpl->setup['plugin.']['tx_vhs.']['settings.']['prependPath'];
}
return '';
}
}