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,82 @@
<?php
namespace FluidTYPO3\Vhs\Traits;
/*
* 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\ArrayUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
use TYPO3\CMS\Fluid\Core\ViewHelper\Exception;
/**
* Class ArrayConsumingViewHelperTrait
*
* Trait implemented by ViewHelpers that operate with
* arrays, ArrayAccess, Iterator etc. instances.
*
* Contains the following main responsibilities:
*
* - retrieving an argument either from arguments or from
* tag contents while also converting it to array.
* - merge arrays with a switch to respect TYPO3 version.
*/
trait ArrayConsumingViewHelperTrait {
/**
* Override of VhsViewHelperTrait equivalent. Does what
* that function does, but also ensures an array return.
*
* @param string $argumentName
* @return mixed
*/
protected function getArgumentFromArgumentsOrTagContentAndConvertToArray($argumentName) {
if (FALSE === $this->hasArgument($argumentName)) {
$value = $this->renderChildren();
} else {
$value = $this->arguments[$argumentName];
}
return $this->arrayFromArrayOrTraversableOrCSV($value);
}
/**
* @param mixed $candidate
* @param boolean $useKeys
*
* @return array
* @throws Exception
*/
protected function arrayFromArrayOrTraversableOrCSV($candidate, $useKeys = TRUE) {
if (TRUE === $candidate instanceof \Traversable) {
return iterator_to_array($candidate, $useKeys);
} elseif (TRUE === $candidate instanceof QueryResultInterface) {
/** @var QueryResultInterface $candidate */
return $candidate->toArray();
}
if (TRUE === is_string($candidate)) {
return GeneralUtility::trimExplode(',', $candidate, TRUE);
} elseif (TRUE === is_array($candidate)) {
return $candidate;
}
throw new Exception('Unsupported input type; cannot convert to array!');
}
/**
* @param $array1
* @param $array2
* @return array
*/
protected function mergeArrays($array1, $array2) {
if (6.2 <= (float) substr(TYPO3_version, 0, 3)) {
ArrayUtility::mergeRecursiveWithOverrule($array1, $array2);
return $array1;
} else {
return GeneralUtility::array_merge_recursive_overrule($array1, $array2);
}
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace FluidTYPO3\Vhs\Traits;
/*
* 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.
*/
/**
* Class BasicViewHelperTrait
*
* Trait implemented by ViewHelpers which require access
* to generic functions.
*
* Has the following main responsibilities:
*
* - generic method to get either an argument or if that
* argument is not specified, retrieve the tag contents.
*/
trait BasicViewHelperTrait {
/**
* Retrieve an argument either from arguments if
* specified there, else from tag content.
*
* @param string $argumentName
* @return mixed
*/
protected function getArgumentFromArgumentsOrTagContent($argumentName) {
if (FALSE === $this->hasArgument($argumentName)) {
$value = $this->renderChildren();
} else {
$value = $this->arguments[$argumentName];
}
return $value;
}
}

View File

@@ -0,0 +1,132 @@
<?php
namespace FluidTYPO3\Vhs\Traits;
/*
* 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.
*/
/**
* This trait can be used by viewhelpers that generate image tags
* to add srcsets based to the imagetag for better responsiveness
*/
trait ConditionViewHelperTrait {
/**
* renders <f:then> child if $condition is true, otherwise renders <f:else> child.
*
* @return string the rendered string
* @api
*/
public function render() {
if (static::evaluateCondition($this->arguments)) {
return $this->renderThenChild();
} else {
return $this->renderElseChild();
}
}
/**
* Default implementation for use in compiled templates
*
* TODO: remove at some point, because this is only here for legacy reasons.
* the AbstractConditionViewHelper in 6.2.* doesn't have a default render
* method. 7.2+ on the other hand provides basically exactly this method here
* luckily it's backwards compatible out of the box.
* tl;dr -> remove after expiration of support for anything below 7.2
*
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param \TYPO3\CMS\Fluid\Core\Rendering\RenderingContextInterface $renderingContext
* @return mixed
*/
static public function renderStatic(array $arguments, \Closure $renderChildrenClosure, \TYPO3\CMS\Fluid\Core\Rendering\RenderingContextInterface $renderingContext) {
$hasEvaluated = TRUE;
if (static::evaluateCondition($arguments)) {
$result = static::renderStaticThenChild($arguments, $hasEvaluated);
if ($hasEvaluated) {
return $result;
}
return $renderChildrenClosure();
} else {
$result = static::renderStaticElseChild($arguments, $hasEvaluated);
if ($hasEvaluated) {
return $result;
}
}
return '';
}
/**
* Statically evalute "then" children.
* The "$hasEvaluated" argument is there to distinguish the case that "then" returned NULL or was not evaluated.
*
* TODO: remove at some point, because this is only here for legacy reasons.
* the AbstractConditionViewHelper in 6.2.* doesn't have a default render
* method. 7.2+ on the other hand provides basically exactly this method here
* luckily it's backwards compatible out of the box.
* tl;dr -> remove after expiration of support for anything below 7.2
*
* @param array $arguments ViewHelper arguments
* @param bool $hasEvaluated Can be used to check if the "then" child was actually evaluated by this method.
* @return string
*/
static protected function renderStaticThenChild($arguments, &$hasEvaluated) {
if (isset($arguments['then'])) {
return $arguments['then'];
}
if (isset($arguments['__thenClosure'])) {
$thenClosure = $arguments['__thenClosure'];
return $thenClosure();
} elseif (isset($arguments['__elseClosure'])) {
return '';
}
$hasEvaluated = FALSE;
}
/**
* Statically evalute "else" children.
* The "$hasEvaluated" argument is there to distinguish the case that "else" returned NULL or was not evaluated.
*
* TODO: remove at some point, because this is only here for legacy reasons.
* the AbstractConditionViewHelper in 6.2.* doesn't have a default render
* method. 7.2+ on the other hand provides basically exactly this method here
* luckily it's backwards compatible out of the box.
* tl;dr -> remove after expiration of support for anything below 7.2
*
* @param array $arguments ViewHelper arguments
* @param bool $hasEvaluated Can be used to check if the "else" child was actually evaluated by this method.
* @return string
*/
static protected function renderStaticElseChild($arguments, &$hasEvaluated) {
if (isset($arguments['else'])) {
return $arguments['else'];
}
if (isset($arguments['__elseClosure'])) {
$elseClosure = $arguments['__elseClosure'];
return $elseClosure();
}
$hasEvaluated = FALSE;
}
/**
* This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality.
*
* TODO: remove at some point, because this is only here for legacy reasons.
* the AbstractConditionViewHelper in 6.2.* doesn't have a default render
* method. 7.2+ on the other hand provides basically exactly this method here
* luckily it's backwards compatible out of the box.
* tl;dr -> remove after expiration of support for anything below 7.2
*
* @param array $arguments ViewHelper arguments to evaluate the condition for this ViewHelper, allows for flexiblity in overriding this method.
* @return bool
*/
static protected function evaluateCondition($arguments = NULL) {
return (isset($arguments['condition']) && $arguments['condition']);
}
}

View File

@@ -0,0 +1,137 @@
<?php
namespace FluidTYPO3\Vhs\Traits;
/*
* 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;
/**
* Class SlideViewHelperTrait
*
* Trait implemented by ViewHelpers that wants some kind of
* records to be optionally looked up and/or collected from
* the current page and pages up the rootline. ViewHelpers must
* implement the getSlideRecordsFromPage method which looks up
* resources for a single page.
*
* Has the following main responsibilities:
* - register arguments common for sliding
* - method to get records with sliding according
* to the ViewHelper arguments
*/
trait SlideViewHelperTrait {
/**
* Default initialisation of arguments - will be used
* if the implementing ViewHelper does not itself define
* this method.
*
* @return void
*/
public function initializeArguments() {
$this->registerSlideArguments();
}
/**
* Register the "limit", "slide", "slideCollect" and "slideCollectReverse"
* arguments which are consumed by getSlideRecords.
* Should be used inside registerArguments().
*
* @return void
* @api
*/
protected function registerSlideArguments() {
$this->registerArgument('limit', 'integer', 'Optional limit to the total number of records to render');
$this->registerArgument('slide', 'integer', 'Enables Record Sliding - amount of levels which shall get walked up the rootline, including the current page. For infinite sliding (till the rootpage) set to -1. Only the first PID which has at minimum one record is used', FALSE, 0);
$this->registerArgument('slideCollect', 'integer', 'If TRUE, content is collected up the root line. If FALSE, only the first PID which has content is used. If greater than zero, this value overrides $slide.', FALSE, 0);
$this->registerArgument('slideCollectReverse', 'boolean', 'Normally when collecting records the elements from the actual page get shown on the top and those from the parent pages below those. You can invert this behaviour (actual page elements at bottom) by setting this flag))', FALSE, 0);
}
/**
* @return \FluidTYPO3\Vhs\Service\PageSelectService
*/
protected function getPageSelectService() {
$objectManager = GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Object\\ObjectManager');
return $objectManager->get('FluidTYPO3\\Vhs\\Service\\PageSelectService');
}
/**
* Get a number of records from a page for sliding
*
* @param integer $pageUid PID to get the records from
* @param integer $limit number of records to get at maximum
*/
abstract protected function getSlideRecordsFromPage($pageUid, $limit);
/**
* Get records, optionally sliding up the page rootline
*
* @param integer $pageUid
* @param integer $limit
* @return array
* @api
*/
protected function getSlideRecords($pageUid, $limit = NULL) {
if (NULL === $limit && FALSE === empty($this->arguments['limit'])) {
$limit = (integer) $this->arguments['limit'];
}
$slide = (integer) $this->arguments['slide'];
$slideCollectReverse = (boolean) $this->arguments['slideCollectReverse'];
$slideCollect = (integer) $this->arguments['slideCollect'];
if (0 < $slideCollect) {
$slide = $slideCollect;
}
// find out which storage page UIDs to read from, respecting slide depth
$storagePageUids = array();
if (0 === $slide) {
$storagePageUids[] = $pageUid;
} else {
$reverse = FALSE;
if (TRUE === $slideCollectReverse && 0 !== $slideCollect) {
$reverse = TRUE;
}
$rootLine = $this->getPageSelectService()->getRootLine($pageUid, NULL, $reverse);
if (-1 !== $slide) {
if (TRUE === $reverse) {
$rootLine = array_slice($rootLine, - $slide);
} else {
$rootLine = array_slice($rootLine, 0, $slide);
}
}
foreach ($rootLine as $page) {
$storagePageUids[] = (integer) $page['uid'];
}
}
// select records, respecting slide and slideCollect.
$records = array();
do {
$storagePageUid = array_shift($storagePageUids);
$limitRemaining = NULL;
if (NULL !== $limit) {
$limitRemaining = $limit - count($records);
if (0 >= $limitRemaining) {
break;
}
}
$recordsFromPageUid = $this->getSlideRecordsFromPage($storagePageUid, $limitRemaining);
if (0 < count($recordsFromPageUid)) {
$records = array_merge($records, $recordsFromPageUid);
if (0 === $slideCollect) {
// stop collecting because argument said so and we've gotten at least one record now.
break;
}
}
} while (FALSE === empty($storagePageUids));
return $records;
}
}

View File

@@ -0,0 +1,113 @@
<?php
namespace FluidTYPO3\Vhs\Traits;
use FluidTYPO3\Vhs\Utility\FrontendSimulationUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
/*
* 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.
*/
/**
* This trait can be used by viewhelpers that generate image tags
* to add srcsets based to the imagetag for better responsiveness
*/
trait SourceSetViewHelperTrait {
/**
* used to attach srcset variants of a given image to the specified tag
*
* @param \TYPO3\CMS\Fluid\Core\ViewHelper\TagBuilder $tag the tag to add the srcset as argument
* @param string $src image path to render srcsets for
* @return array
*/
public function addSourceSet($tag, $src) {
$srcsets = $this->getSourceSetWidths();
if ('BE' === TYPO3_MODE) {
FrontendSimulationUtility::simulateFrontendEnvironment();
}
$format = $this->arguments['format'];
$quality = $this->arguments['quality'];
$treatIdAsReference = (boolean) $this->arguments['treatIdAsReference'];
$imageSources = array();
$srcsetVariants = array();
foreach ($srcsets as $key => $width) {
$srcsetVariant = $this->getImgResource($src, $width, $format, $quality, $treatIdAsReference);
$srcsetVariantSrc = rawurldecode($srcsetVariant[3]);
$srcsetVariantSrc = $this->preprocessSourceUri(GeneralUtility::rawUrlEncodeFP($srcsetVariantSrc));
$imageSources[$srcsetVariant[0]] = array(
'src' => $srcsetVariantSrc,
'width' => $srcsetVariant[0],
'height' => $srcsetVariant[1],
);
$srcsetVariants[$srcsetVariant[0]] = $srcsetVariantSrc . ' ' . $srcsetVariant[0] . 'w';
}
$tag->addAttribute('srcset', implode(',', $srcsetVariants));
if ('BE' === TYPO3_MODE) {
FrontendSimulationUtility::resetFrontendEnvironment();
}
return $imageSources;
}
/**
* generates a copy of a give image with a specific width
*
* @param string $src path of the image to convert
* @param integer $width width to convert the image to
* @param string $format format of the resulting copy
* @param string $quality quality of the resulting copy
* @param string $treatIdAsReference given src argument is a sys_file_reference record
* @param array $params additional params for the image rendering
* @return string
*/
public function getImgResource($src, $width, $format, $quality, $treatIdAsReference, $params = NULL) {
$setup = array(
'width' => $width,
'treatIdAsReference' => $treatIdAsReference
);
if (FALSE === empty($format)) {
$setup['ext'] = $format;
}
if (0 < intval($quality)) {
$quality = MathUtility::forceIntegerInRange($quality, 10, 100, 75);
$setup['params'] .= ' -quality ' . $quality;
}
if ('BE' === TYPO3_MODE && '../' === substr($src, 0, 3)) {
$src = substr($src, 3);
}
return $this->contentObject->getImgResource($src, $setup);
}
/**
* returns an array of srcsets based on the mixed ViewHelper
* input (list, csv, array, iterator)
*
* @return array
*/
public function getSourceSetWidths() {
$srcsets = $this->arguments['srcset'];
if (TRUE === $srcsets instanceof \Traversable) {
$srcsets = iterator_to_array($srcsets);
} elseif (TRUE === is_string($srcsets)) {
$srcsets = GeneralUtility::trimExplode(',', $srcsets, TRUE);
} else {
$srcsets = (array) $srcsets;
}
return $srcsets;
}
}

View File

@@ -0,0 +1,123 @@
<?php
namespace FluidTYPO3\Vhs\Traits;
/*
* 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.
*/
/**
* Class TagViewHelperTrait
*
* Trait implemented by ViewHelpers which require access
* to functions dealing with tag generation.
*
* Has the following main responsibilities:
*
* - register additional HTML5-specific attributes for tag
* based ViewHelpers
* - custom rendering method which applies those attributes.
*/
trait TagViewHelperTrait {
/**
* Default implementation to register only the tag
* arguments along with universal attributes.
*
* @return void
*/
public function registerArguments() {
$this->registerUniversalTagAttributes();
}
/**
* Registers all standard and HTML5 universal attributes.
* Should be used inside registerArguments();
*
* @return void
* @api
*/
protected function registerUniversalTagAttributes() {
parent::registerUniversalTagAttributes();
$this->registerArgument('forceClosingTag', 'boolean', 'If TRUE, forces the created tag to use a closing tag. If FALSE, allows self-closing tags.', FALSE, FALSE);
$this->registerArgument('hideIfEmpty', 'boolean', 'Hide the tag completely if there is no tag content', FALSE, FALSE);
$this->registerTagAttribute('contenteditable', 'string', 'Specifies whether the contents of the element are editable.');
$this->registerTagAttribute('contextmenu', 'string', 'The value of the id attribute on the menu with which to associate the element as a context menu.');
$this->registerTagAttribute('draggable', 'string', 'Specifies whether the element is draggable.');
$this->registerTagAttribute('dropzone', 'string', 'Specifies what types of content can be dropped on the element, and instructs the UA about which actions to take with content when it is dropped on the element.');
$this->registerTagAttribute('translate', 'string', 'Specifies whether an elements attribute values and contents of its children are to be translated when the page is localized, or whether to leave them unchanged.');
$this->registerTagAttribute('spellcheck', 'string', 'Specifies whether the element represents an element whose contents are subject to spell checking and grammar checking.');
$this->registerTagAttribute('hidden', 'string', 'Specifies that the element represents an element that is not yet, or is no longer, relevant.');
}
/**
* Renders the provided tag with the given name and any
* (additional) attributes not already provided as arguments.
*
* @param string $tagName
* @param mixed $content
* @param array $attributes
* @param array $nonEmptyAttributes
* @return string
*/
protected function renderTag($tagName, $content = NULL, array $attributes = array(), array $nonEmptyAttributes = array('id', 'class')) {
$trimmedContent = trim((string) $content);
$forceClosingTag = (boolean) $this->arguments['forceClosingTag'];
if (TRUE === empty($trimmedContent) && TRUE === (boolean) $this->arguments['hideIfEmpty']) {
return '';
}
if ('none' === $tagName || TRUE === empty($tagName)) {
// skip building a tag if special keyword "none" is used, or tag name is empty
return $trimmedContent;
}
$this->tag->setTagName($tagName);
$this->tag->addAttributes($attributes);
$this->tag->forceClosingTag($forceClosingTag);
if (NULL !== $content) {
$this->tag->setContent($content);
}
// process some attributes differently - if empty, remove the property:
foreach ($nonEmptyAttributes as $propertyName) {
$value = $this->arguments[$propertyName];
if (TRUE === empty($value)) {
$this->tag->removeAttribute($propertyName);
} else {
$this->tag->addAttribute($propertyName, $value);
}
}
return $this->tag->render();
}
/**
* Renders the provided tag and optionally appends or prepends
* it to the main tag's content depending on 'mode' which can
* be one of 'none', 'append' or 'prepend'
*
* @param string $tagName
* @param array $attributes
* @param boolean $forceClosingTag
* @param string $mode
* @return string
*/
protected function renderChildTag($tagName, $attributes = array(), $forceClosingTag = FALSE, $mode = 'none') {
$tagBuilder = clone $this->tag;
$tagBuilder->reset();
$tagBuilder->setTagName($tagName);
$tagBuilder->addAttributes($attributes);
$tagBuilder->forceClosingTag($forceClosingTag);
$childTag = $tagBuilder->render();
if ('append' === $mode || 'prepend' === $mode) {
$content = $this->tag->getContent();
if ('append' === $mode) {
$content = $content . $childTag;
} else {
$content = $childTag . $content;
}
$this->tag->setContent($content);
}
return $childTag;
}
}

View File

@@ -0,0 +1,139 @@
<?php
namespace FluidTYPO3\Vhs\Traits;
/*
* 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.
*/
/**
* Class TemplateVariableViewHelperTrait
*
* Trait implementable by ViewHelpers which operate with
* template variables in one way or another. Contains
* the following main responsibilities:
*
* - A generic "as" argument solution
* - A method to render child content with automatically
* backed up variables specified in an array.
*/
trait TemplateVariableViewHelperTrait {
/**
* Default initialisation of arguments - will be used
* if the implementing ViewHelper does not itself define
* this method. The default behavior is to only register
* the "as" argument.
*
* @return void
*/
public function initializeArguments() {
$this->registerAsArgument();
}
/**
* Registers the "as" argument for use with the
* implementing ViewHelper.
*
* @return void
*/
protected function registerAsArgument() {
$this->registerArgument('as', 'string', 'Template variable name to assign; if not specified the ViewHelper returns the variable instead.');
}
/**
* @return mixed
*/
protected function renderChildrenWithVariableOrReturnInput($variable = NULL) {
$as = $this->arguments['as'];
if (TRUE === empty($as)) {
return $variable;
} else {
$variables = array($as => $variable);
$content = static::renderChildrenWithVariables($variables);
}
return $content;
}
/**
* @param \TYPO3\CMS\Fluid\Core\Rendering\RenderingContextInterface $renderingContext
* @param \Closure $renderChildrenClosure
* @return mixed
*/
protected static function renderChildrenWithVariableOrReturnInputStatic($variable = NULL, $as, $renderingContext = NULL, $renderChildrenClosure = NULL) {
if (TRUE === empty($as)) {
return $variable;
} else {
$variables = array($as => $variable);
$content = static::renderChildrenWithVariablesStatic($variables, $renderingContext->getTemplateVariableContainer(), $renderChildrenClosure);
}
return $content;
}
/**
* Renders tag content of ViewHelper and inserts variables
* in $variables into $variableContainer while keeping backups
* of each existing variable, restoring it after rendering.
* Returns the output of the renderChildren() method on $viewHelper.
*
* @param array $variables
* @return mixed
*/
protected function renderChildrenWithVariables(array $variables) {
return self::renderChildrenWithVariablesStatic($variables, $this->templateVariableContainer, $this->buildRenderChildrenClosure());
}
/**
* Renders tag content of ViewHelper and inserts variables
* in $variables into $variableContainer while keeping backups
* of each existing variable, restoring it after rendering.
* Returns the output of the renderChildren() method on $viewHelper.
*
* @param array $variables
* @param \TYPO3\CMS\Fluid\Core\ViewHelper\TemplateVariableContainer $templateVariableContainer
* @param \Closure $renderChildrenClosure
* @return mixed
*/
protected static function renderChildrenWithVariablesStatic(array $variables, $templateVariableContainer, $renderChildrenClosure) {
$backups = self::backupVariables($variables, $templateVariableContainer);
$content = $renderChildrenClosure();
self::restoreVariables($variables, $backups, $templateVariableContainer);
return $content;
}
/**
* @param array $variables
* @param \TYPO3\CMS\Fluid\Core\ViewHelper\TemplateVariableContainer $templateVariableContainer
* @param \Closure $renderChildrenClosure
* @return array
*/
private static function backupVariables(array $variables, $templateVariableContainer) {
$backups = array();
foreach ($variables as $variableName => $variableValue) {
if (TRUE === $templateVariableContainer->exists($variableName)) {
$backups[$variableName] = $templateVariableContainer->get($variableName);
$templateVariableContainer->remove($variableName);
}
$templateVariableContainer->add($variableName, $variableValue);
}
return $backups;
}
/**
* @param array $variables
* @param array $backups
* @param \TYPO3\CMS\Fluid\Core\ViewHelper\TemplateVariableContainer $templateVariableContainer
* @return void
*/
private static function restoreVariables(array $variables, array $backups, $templateVariableContainer) {
foreach ($variables as $variableName => $variableValue) {
$templateVariableContainer->remove($variableName);
if (TRUE === isset($backups[$variableName])) {
$templateVariableContainer->add($variableName, $variableValue);
}
}
}
}