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,611 @@
<?php
namespace FluidTYPO3\Vhs;
/*
* 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\AssetInterface;
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
/**
* ### Asset
*
* Class to create Assets in PHP.
*
* ### Examples:
*
* $asset = $this->objectManager->get('FluidTYPO3\\Vhs\\Asset');
* // OR you can use the static factory method which works anywhere
* // including outside of Extbase.
* $asset = \FluidTYPO3\Vhs\Asset::getInstance();
* $asset->setPath('fileadmin/test.js')->setStandalone(TRUE)->finalize();
*
* Or simply:
*
* $this->objectManager->get('FluidTYPO3\\Vhs\\Asset')->setPath('...')->finalize();
*
* And you can create clean instances:
*
*
* Depending on how you need to access the Asset afterwards, you will
* want wo switch between these methods.
*
* Or if you have all settings in an array (with members named according to
* the properties on this class:
*
* \FluidTYPO3\Vhs\Asset::createFromSettings($settings)->finalize();
*
* Finally, if your Asset is file based, VHS can perform a few detections to
* set initial attributes like standalone, external (if file contains protocol),
* type (based on extension) and name (base name of file).
*
* \FluidTYPO3\Vhs\Asset::createFromFile($filePathAndFilename);
* \FluidTYPO3\Vhs\Asset::createFromUrl($urlToExternalFile);
*
* You can chain all setters on the class before finally calling finalize() to
* register the Asset (you can still modify the Asset afterwards, but an Asset
* that has not been finalize()'ed will never show up or be processed - which
* is a lot friendlier than requiring you to use remove() on unwanted Assets).
*
* > Note: the "createFrom..." suite of methods automatically calls finalize()
* > on your Asset just before returning it. You can of course keep modifying
* > the instance after it is returned - but when using a "createFrom"... method
* > VHS assumes you always want your Asset included in the output.
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
*/
class Asset implements AssetInterface {
/**
* @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
*/
protected $configurationManager;
/**
* @var array
*/
protected $dependencies = array();
/**
* @var string
*/
protected $type = NULL;
/**
* @var string
*/
protected $name = NULL;
/**
* @var string
*/
protected $content = NULL;
/**
* @var string
*/
protected $path = NULL;
/**
* @var boolean
*/
protected $namedChunks = FALSE;
/**
* @var boolean
*/
protected $movable = TRUE;
/**
* @var boolean
*/
protected $removed = FALSE;
/**
* @var boolean
*/
protected $fluid = FALSE;
/**
* @var array
*/
protected $variables = array();
/**
* @var array
*/
protected $settings = array();
/**
* @var boolean
*/
protected $external = FALSE;
/**
* @var boolean
*/
protected $standalone = FALSE;
/**
* @var boolean
*/
protected $rewrite = TRUE;
/**
* @var array
*/
private static $settingsCache = NULL;
/**
* @param \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager
* @return void
*/
public function injectConfigurationManager(ConfigurationManagerInterface $configurationManager) {
$this->configurationManager = $configurationManager;
}
/**
* @return Asset
*/
public static function getInstance() {
/** @var $asset Asset */
$asset = GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Object\\ObjectManager')->get('FluidTYPO3\\Vhs\\Asset');
return $asset;
}
/**
* @param array $settings
* @return Asset
*/
public static function createFromSettings(array $settings) {
if (TRUE === isset($settings['allowMoveToFooter'])) {
// @TODO: remove in 2.2 or 3.0 whichever comes first.
GeneralUtility::deprecationLog('Deprecated property "allowMoveToFooter" was used in VHS Asset settings ' .
'for asset named "' . $settings['name'] . '". Please correct this to use the proper "movable" attribute');
$settings['movable'] = $settings['allowMoveToFooter'];
unset($settings['allowMoveToFooter']);
}
if (TRUE === isset($settings['arguments'])) {
// @TODO: remove in 2.2 or 3.0 whichever comes first.
GeneralUtility::deprecationLog('Deprecated property "arguments" was used in VHS Asset settings ' .
'for asset named "' . $settings['name'] . '". Please correct this to use the proper "variables" attribute');
$settings['variables'] = $settings['arguments'];
unset($settings['arguments']);
}
$asset = self::getInstance();
foreach ($settings as $propertyName => $value) {
ObjectAccess::setProperty($asset, $propertyName, $value);
}
return $asset->finalize();
}
/**
* @param string $filePathAndFilename
* @return Asset
*/
public static function createFromFile($filePathAndFilename) {
$asset = self::getInstance();
$asset->setExternal(FALSE);
$asset->setPath($filePathAndFilename);
return $asset->finalize();
}
/**
* @param string $content
* @return Asset
*/
public static function createFromContent($content) {
$asset = self::getInstance();
$asset->setContent($content);
$asset->setName(md5($content));
return $asset->finalize();
}
/**
* @param string $url
* @return Asset
*/
public static function createFromUrl($url) {
$asset = self::getInstance();
$asset->setStandalone(TRUE);
$asset->setExternal(TRUE);
$asset->setPath($url);
return $asset->finalize();
}
/**
* Render method
*
* @return mixed
*/
public function render() {
return $this->build();
}
/**
* Build this asset. Override this method in the specific
* implementation of an Asset in order to:
*
* - if necessary compile the Asset (LESS, SASS, CoffeeScript etc)
* - make a final rendering decision based on arguments
*
* Note that within this function the ViewHelper and TemplateVariable
* Containers are not dependable, you cannot use the ControllerContext
* and RenderingContext and you should therefore also never call
* renderChildren from within this function. Anything else goes; CLI
* commands to build, caching implementations - you name it.
*
* @return mixed
*/
public function build() {
$path = $this->getPath();
if (TRUE === empty($path)) {
return $this->getContent();
}
$content = file_get_contents($path);
return $content;
}
/**
* @return Asset
*/
public function finalize() {
$name = $this->getName();
if (TRUE === empty($name)) {
$name = md5(spl_object_hash($this));
}
if (FALSE === isset($GLOBALS['VhsAssets']) || FALSE === is_array($GLOBALS['VhsAssets'])) {
$GLOBALS['VhsAssets'] = array();
}
$GLOBALS['VhsAssets'][$name] = $this;
return $this;
}
/**
* @return Asset
*/
public function remove() {
return $this->setRemoved(TRUE);
}
/**
* @return array
*/
public function getDependencies() {
return $this->dependencies;
}
/**
* @param array $dependencies
* @return Asset
*/
public function setDependencies($dependencies) {
$this->dependencies = $dependencies;
return $this;
}
/**
* @return string
*/
public function getType() {
return $this->type;
}
/**
* @param string $type
* @return Asset
*/
public function setType($type) {
$this->type = $type;
if ('css' == strtolower($type)) {
$this->setMovable(FALSE);
}
return $this;
}
/**
* @param boolean $external
* @return Asset
*/
public function setExternal($external) {
$this->external = $external;
return $this;
}
/**
* @return boolean
*/
public function getExternal() {
return $this->external;
}
/**
* @param boolean $rewrite
* @return Asset
*/
public function setRewrite($rewrite) {
$this->rewrite = $rewrite;
return $this;
}
/**
* @return boolean
*/
public function getRewrite() {
return $this->rewrite;
}
/**
* @param boolean $standalone
* @return Asset
*/
public function setStandalone($standalone) {
$this->standalone = $standalone;
return $this;
}
/**
* @return boolean
*/
public function getStandalone() {
return $this->standalone;
}
/**
* @return string
*/
public function getName() {
return $this->name;
}
/**
* @param string $name
* @return Asset
*/
public function setName($name) {
$this->name = $name;
return $this;
}
/**
* @return string
*/
public function getContent() {
$path = (0 === strpos($this->path, '/') ? $this->path : GeneralUtility::getFileAbsFileName($this->path));
if (TRUE === empty($this->content) && NULL !== $this->path && file_exists($path)) {
return file_get_contents($path);
}
return $this->content;
}
/**
* @param string $content
* @return Asset
*/
public function setContent($content) {
$this->content = $content;
return $this;
}
/**
* @return string
*/
public function getPath() {
return $this->path;
}
/**
* @param string $path
* @return Asset
*/
public function setPath($path) {
if (NULL === $path) {
$this->path = NULL;
return $this;
}
if (FALSE === strpos($path, '://') && 0 !== strpos($path, '/')) {
$path = GeneralUtility::getFileAbsFileName($path);
}
if (NULL === $this->type) {
$this->setType(pathinfo($path, PATHINFO_EXTENSION));
}
if (NULL === $this->name) {
$this->setName(pathinfo($path, PATHINFO_FILENAME));
}
$this->path = $path;
return $this;
}
/**
* @return boolean
*/
public function getNamedChunks() {
return $this->namedChunks;
}
/**
* @param boolean $namedChunks
* @return Asset
*/
public function setNamedChunks($namedChunks) {
$this->namedChunks = $namedChunks;
return $this;
}
/**
* @return boolean
*/
public function getFluid() {
return $this->fluid;
}
/**
* @param boolean $fluid
* @return Asset
*/
public function setFluid($fluid) {
$this->fluid = $fluid;
return $this;
}
/**
* @return array
*/
public function getVariables() {
return $this->variables;
}
/**
* @param array $variables
* @return Asset
*/
public function setVariables($variables) {
$this->variables = $variables;
return $this;
}
/**
* Returns the settings used by this particular Asset
* during inclusion. Public access allows later inspection
* of the TypoScript values which were applied to the Asset.
*
* @return array
*/
public function getSettings() {
if (NULL === self::$settingsCache) {
$allTypoScript = $this->configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FULL_TYPOSCRIPT);
$settingsExist = isset($allTypoScript['plugin.']['tx_vhs.']['settings.']);
if (TRUE === $settingsExist) {
self::$settingsCache = GeneralUtility::removeDotsFromTS($allTypoScript['plugin.']['tx_vhs.']['settings.']);
}
}
$settings = (array) self::$settingsCache;
$properties = get_class_vars(get_class($this));
foreach (array_keys($properties) as $propertyName) {
$properties[$propertyName] = $this->$propertyName;
}
if (TRUE === method_exists('TYPO3\\CMS\\Core\\Utility\\ArrayUtility', 'mergeRecursiveWithOverrule')) {
ArrayUtility::mergeRecursiveWithOverrule($settings, $this->settings);
ArrayUtility::mergeRecursiveWithOverrule($settings, $properties);
} else {
$settings = GeneralUtility::array_merge_recursive_overrule($settings, $this->settings);
$settings = GeneralUtility::array_merge_recursive_overrule($settings, $properties);
}
return $settings;
}
/**
* @param array $settings
* @return Asset
*/
public function setSettings($settings) {
$this->settings = $settings;
return $this;
}
/**
* @return array
*/
public function getAssetSettings() {
return $this->getSettings();
}
/**
* @return boolean
*/
public function getMovable() {
return $this->movable;
}
/**
* @param boolean $movable
* @return $this
*/
public function setMovable($movable) {
$this->movable = $movable;
return $this;
}
/**
* @return boolean
*/
public function getRemoved() {
return $this->removed;
}
/**
* @param boolean $removed
* @return Asset
*/
public function setRemoved($removed) {
$this->removed = $removed;
return $this;
}
/**
* Allows public access to debug this particular Asset
* instance later, when including the Asset in the page.
*
* @return array
*/
public function getDebugInformation() {
return $this->getSettings();
}
/**
* Returns TRUE of settings specify that the source of this
* Asset should be rendered as if it were a Fluid template,
* using variables from the "arguments" attribute
*
* @return boolean
*/
public function assertFluidEnabled() {
return $this->getFluid();
}
/**
* Returns TRUE if settings specify that the name of each Asset
* should be placed above the built content when placed in merged
* Asset cache files.
*
* @return boolean
*/
public function assertAddNameCommentWithChunk() {
return $this->getNamedChunks();
}
/**
* Returns TRUE if the current Asset should be debugged as commanded
* by settings in TypoScript an/ord ViewHelper attributes.
*
* @return boolean
*/
public function assertDebugEnabled() {
$settings = $this->getSettings();
$enabled = (TRUE === isset($settings['debug']) ? (boolean) $settings['debug'] : FALSE);
return $enabled;
}
/**
* @return boolean
*/
public function assertAllowedInFooter() {
return $this->getMovable();
}
/**
* @return boolean
*/
public function assertHasBeenRemoved() {
return $this->getRemoved();
}
}

View File

@@ -0,0 +1,680 @@
<?php
namespace FluidTYPO3\Vhs\Service;
/*
* 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\Asset;
use FluidTYPO3\Vhs\ViewHelpers\Asset\AssetInterface;
use TYPO3\CMS\Core\SingletonInterface;
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
use TYPO3\CMS\Extbase\Utility\DebuggerUtility;
/**
* Asset Handling Service
*
* Inject this Service in your class to access VHS Asset
* features - include assets etc.
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage Service
*/
class AssetService implements SingletonInterface {
/**
* @var boolean
*/
protected static $typoScriptAssetsBuilt = FALSE;
/**
* @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
*/
protected $configurationManager;
/**
* @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
*/
protected $objectManager;
/**
* @var array
*/
private static $settingsCache = NULL;
/**
* @var array
*/
private static $cachedDependencies = array();
/**
* @var boolean
*/
private static $cacheCleared = FALSE;
/**
* @param \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager
* @return void
*/
public function injectConfigurationManager(ConfigurationManagerInterface $configurationManager) {
$this->configurationManager = $configurationManager;
}
/**
* @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
* @return void
*/
public function injectObjectManager(ObjectManagerInterface $objectManager) {
$this->objectManager = $objectManager;
}
/**
* @param object $caller
* @param boolean $shouldUsePageCache
* @return boolean
*/
public function usePageCache($caller, $shouldUsePageCache) {
$this->buildAll(array(), $caller);
return $shouldUsePageCache;
}
/**
* @param array $parameters
* @param object $caller
* @param boolean $cached If TRUE, treats this inclusion as happening in a cached context
* @return void
*/
public function buildAll(array $parameters, $caller, $cached = TRUE) {
if (FALSE === $this->objectManager instanceof ObjectManager) {
$this->objectManager = GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Object\\ObjectManager');
$this->configurationManager = $this->objectManager->get('TYPO3\\CMS\\Extbase\\Configuration\\ConfigurationManagerInterface');
}
$settings = $this->getSettings();
$cached = (boolean) $cached;
$buildTypoScriptAssets = (FALSE === self::$typoScriptAssetsBuilt && (TRUE === $cached || TRUE === (boolean) $GLOBALS['TSFE']->no_cache));
if (TRUE === $buildTypoScriptAssets && TRUE === isset($settings['asset']) && TRUE === is_array($settings['asset'])) {
foreach ($settings['asset'] as $name => $typoScriptAsset) {
if (FALSE === isset($GLOBALS['VhsAssets'][$name]) && TRUE === is_array($typoScriptAsset)) {
if (FALSE === isset($typoScriptAsset['name'])) {
$typoScriptAsset['name'] = $name;
}
Asset::createFromSettings($typoScriptAsset);
}
}
self::$typoScriptAssetsBuilt = TRUE;
}
if (FALSE === isset($GLOBALS['VhsAssets']) || FALSE === is_array($GLOBALS['VhsAssets'])) {
return;
}
$assets = $GLOBALS['VhsAssets'];
$assets = $this->sortAssetsByDependency($assets);
$assets = $this->manipulateAssetsByTypoScriptSettings($assets);
$buildDebugRequested = (isset($settings['asset']['debugBuild']) && $settings['asset']['debugBuild'] > 0);
$assetDebugRequested = (isset($settings['asset']['debug']) && $settings['asset']['debug'] > 0);
$useDebugUtility = (isset($settings['asset']['useDebugUtility']) && $settings['asset']['useDebugUtility'] > 0) || FALSE === isset($settings['asset']['useDebugUtility']);
if (TRUE === ($buildDebugRequested || $assetDebugRequested)) {
if (TRUE === $useDebugUtility) {
DebuggerUtility::var_dump($assets);
} else {
echo var_export($assets, TRUE);
}
}
$this->placeAssetsInHeaderAndFooter($assets, $cached);
}
/**
* @param array $parameters
* @param object $caller
* @return void
*/
public function buildAllUncached(array $parameters, $caller) {
$content = $caller->content;
$matches = array();
preg_match_all('/\<\![\-]+\ VhsAssetsDependenciesLoaded ([^ ]+) [\-]+\>/i', $content, $matches);
foreach ($matches[1] as $key => $match) {
$extractedDependencies = explode(',', $matches[1][$key]);
self::$cachedDependencies = array_merge(self::$cachedDependencies, $extractedDependencies);
$content = str_replace($matches[0][$key], '', $content);
}
$caller->content = $content;
$this->buildAll($parameters, $caller, FALSE);
}
/**
* Returns the settings used by this particular Asset
* during inclusion. Public access allows later inspection
* of the TypoScript values which were applied to the Asset.
*
* @return array
*/
public function getSettings() {
if (NULL === self::$settingsCache) {
$allTypoScript = $this->configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FULL_TYPOSCRIPT);
$settingsExist = isset($allTypoScript['plugin.']['tx_vhs.']['settings.']);
if (FALSE === $settingsExist) {
// no settings exist, but don't allow a NULL value. This prevents cache clobbering.
self::$settingsCache = array();
} else {
self::$settingsCache = GeneralUtility::removeDotsFromTS($allTypoScript['plugin.']['tx_vhs.']['settings.']);
}
}
$settings = (array) self::$settingsCache;
return $settings;
}
/**
* @param AssetInterface[] $assets
* @param boolean $cached
* @return void
*/
protected function placeAssetsInHeaderAndFooter($assets, $cached) {
$settings = $this->getSettings();
$header = array();
$footer = array();
$footerRelocationEnabled = (TRUE === isset($settings['enableFooterRelocation']) && $settings['relocateToFooter'] > 0) || FALSE === isset($settings['enableFooterRelocation']);
foreach ($assets as $name => $asset) {
$variables = $asset->getVariables();
if (0 < count($variables)) {
$name .= '-' . md5(serialize($variables));
}
if (TRUE === ($this->assertAssetAllowedInFooter($asset) && $footerRelocationEnabled)) {
$footer[$name] = $asset;
} else {
$header[$name] = $asset;
}
}
if (FALSE === $cached) {
$uncachedSuffix = 'Uncached';
} else {
$uncachedSuffix = '';
$dependenciesString = '<!-- VhsAssetsDependenciesLoaded ' . implode(',', array_keys($assets)) . ' -->';
$this->insertAssetsAtMarker('DependenciesLoaded', $dependenciesString);
}
$this->insertAssetsAtMarker('Header' . $uncachedSuffix, $header);
$this->insertAssetsAtMarker('Footer' . $uncachedSuffix, $footer);
$GLOBALS['VhsAssets'] = array();
}
/**
* @param string $markerName
* @param mixed $assets
* @return void
*/
protected function insertAssetsAtMarker($markerName, $assets) {
$assetMarker = '<!-- VhsAssets' . $markerName . ' -->';
if (FALSE === strpos($GLOBALS['TSFE']->content, $assetMarker)) {
$inFooter = (boolean) (FALSE !== strpos($markerName, 'Footer'));
$tag = TRUE === $inFooter ? '</body>' : '</head>';
$GLOBALS['TSFE']->content = str_replace($tag, $assetMarker . LF . $tag, $GLOBALS['TSFE']->content);
}
if (TRUE === is_array($assets)) {
$chunk = $this->buildAssetsChunk($assets);
} else {
$chunk = $assets;
}
$GLOBALS['TSFE']->content = str_replace($assetMarker, $chunk, $GLOBALS['TSFE']->content);
}
/**
* @param array $assets
* @throws \RuntimeException
* @return string
*/
protected function buildAssetsChunk($assets) {
$spool = array();
foreach ($assets as $name => $asset) {
$assetSettings = $this->extractAssetSettings($asset);
$type = $assetSettings['type'];
if (FALSE === isset($spool[$type])) {
$spool[$type] = array();
}
$spool[$type][$name] = $asset;
}
$chunks = array();
foreach ($spool as $type => $spooledAssets) {
$chunk = array();
/** @var AssetInterface[] $spooledAssets */
foreach ($spooledAssets as $name => $asset) {
$assetSettings = $this->extractAssetSettings($asset);
$standalone = (boolean) $assetSettings['standalone'];
$external = (boolean) $assetSettings['external'];
$rewrite = (boolean) $assetSettings['rewrite'];
$path = $assetSettings['path'];
if (FALSE === $standalone) {
$chunk[$name] = $asset;
} else {
if (0 < count($chunk)) {
$mergedFileTag = $this->writeCachedMergedFileAndReturnTag($chunk, $type);
array_push($chunks, $mergedFileTag);
$chunk = array();
}
if (TRUE === empty($path)) {
$assetContent = $this->extractAssetContent($asset);
array_push($chunks, $this->generateTagForAssetType($type, $assetContent));
} else {
if (TRUE === $external) {
array_push($chunks, $this->generateTagForAssetType($type, NULL, $path));
} else {
if (TRUE === $rewrite) {
array_push($chunks, $this->writeCachedMergedFileAndReturnTag(array($name => $asset), $type));
} else {
$path = substr($path, strlen(PATH_site));
$path = $this->prefixPath($path);
array_push($chunks, $this->generateTagForAssetType($type, NULL, $path));
}
}
}
}
}
if (0 < count($chunk)) {
$mergedFileTag = $this->writeCachedMergedFileAndReturnTag($chunk, $type);
array_push($chunks, $mergedFileTag);
}
}
return implode(LF, $chunks);
}
/**
* @param AssetInterface[] $assets
* @param string $type
* @return string
*/
protected function writeCachedMergedFileAndReturnTag($assets, $type) {
$source = '';
$assetName = implode('-', array_keys($assets));
if (TRUE === isset($GLOBALS['TSFE']->tmpl->setup['plugin.']['tx_vhs.']['assets.']['mergedAssetsUseHashedFilename'])) {
if (TRUE === (boolean) $GLOBALS['TSFE']->tmpl->setup['plugin.']['tx_vhs.']['assets.']['mergedAssetsUseHashedFilename']) {
$assetName = md5($assetName);
}
}
$fileRelativePathAndFilename = 'typo3temp/vhs-assets-' . $assetName . '.' . $type;
$fileAbsolutePathAndFilename = GeneralUtility::getFileAbsFileName($fileRelativePathAndFilename);
if (
FALSE === file_exists($fileAbsolutePathAndFilename)
|| 0 === filemtime($fileAbsolutePathAndFilename)
|| TRUE === isset($GLOBALS['BE_USER'])
|| TRUE === (boolean) $GLOBALS['TSFE']->no_cache
|| TRUE === (boolean) $GLOBALS['TSFE']->page['no_cache']
) {
foreach ($assets as $name => $asset) {
$assetSettings = $this->extractAssetSettings($asset);
if (TRUE === (isset($assetSettings['namedChunks']) && 0 < $assetSettings['namedChunks']) || FALSE === isset($assetSettings['namedChunks'])) {
$source .= '/* ' . $name . ' */' . LF;
}
$source .= $this->extractAssetContent($asset) . LF;
// Put a return carriage between assets preventing broken content.
$source .= "\n";
}
$this->writeFile($fileAbsolutePathAndFilename, $source);
}
if (FALSE === empty($GLOBALS['TYPO3_CONF_VARS']['FE']['versionNumberInFilename'])) {
$timestampMode = $GLOBALS['TYPO3_CONF_VARS']['FE']['versionNumberInFilename'];
if (TRUE === file_exists($fileRelativePathAndFilename)) {
$lastModificationTime = filemtime($fileRelativePathAndFilename);
if ('querystring' === $timestampMode) {
$fileRelativePathAndFilename .= '?' . $lastModificationTime;
} elseif ('embed' === $timestampMode) {
$fileRelativePathAndFilename = substr_replace($fileRelativePathAndFilename, '.' . $lastModificationTime, strrpos($fileRelativePathAndFilename, '.'), 0);
}
}
}
$fileRelativePathAndFilename = $this->prefixPath($fileRelativePathAndFilename);
return $this->generateTagForAssetType($type, NULL, $fileRelativePathAndFilename);
}
/**
* @param string $type
* @param string $content
* @param string $file
* @throws \RuntimeException
* @return string
*/
protected function generateTagForAssetType($type, $content, $file = NULL) {
/** @var \TYPO3\CMS\Fluid\Core\ViewHelper\TagBuilder $tagBuilder */
$tagBuilder = $this->objectManager->get('TYPO3\\CMS\\Fluid\\Core\\ViewHelper\\TagBuilder');
if (NULL === $file && TRUE === empty($content)) {
$content = '<!-- Empty tag content -->';
}
switch ($type) {
case 'js':
$tagBuilder->setTagName('script');
$tagBuilder->forceClosingTag(TRUE);
$tagBuilder->addAttribute('type', 'text/javascript');
if (NULL === $file) {
$tagBuilder->setContent($content);
} else {
$tagBuilder->addAttribute('src', $file);
}
break;
case 'css':
if (NULL === $file) {
$tagBuilder->setTagName('style');
$tagBuilder->forceClosingTag(TRUE);
$tagBuilder->addAttribute('type', 'text/css');
$tagBuilder->setContent($content);
} else {
$tagBuilder->forceClosingTag(FALSE);
$tagBuilder->setTagName('link');
$tagBuilder->addAttribute('rel', 'stylesheet');
$tagBuilder->addAttribute('href', $file);
}
break;
case 'meta':
$tagBuilder->forceClosingTag(FALSE);
$tagBuilder->setTagName('meta');
break;
default:
if (NULL === $file) {
return $content;
} else {
throw new \RuntimeException('Attempt to include file based asset with unknown type ("' . $type . '")', 1358645219);
}
break;
}
return $tagBuilder->render();
}
/**
* @param array $assets
* @return array
* @throws \RuntimeException
*/
protected function manipulateAssetsByTypoScriptSettings($assets) {
$settings = $this->getSettings();
if (FALSE === (isset($settings['asset']) || isset($settings['assetGroup']))) {
return $assets;
}
$filtered = array();
/** @var \FluidTYPO3\Vhs\Asset $asset */
foreach ($assets as $name => $asset) {
$assetSettings = $this->extractAssetSettings($asset);
$groupName = $assetSettings['group'];
$removed = (boolean) (TRUE === isset($assetSettings['removed']) ? $assetSettings['removed'] : FALSE);
if (TRUE === $removed) {
continue;
}
$localSettings = (array) $assetSettings;
if (TRUE === isset($settings['asset'])) {
$localSettings = $this->mergeArrays($localSettings, (array) $settings['asset']);
}
if (TRUE === isset($settings['asset'][$name])) {
$localSettings = $this->mergeArrays($localSettings, (array) $settings['asset'][$name]);
}
if (TRUE === isset($settings['assetGroup'][$groupName])) {
$localSettings = $this->mergeArrays($localSettings, (array) $settings['assetGroup'][$groupName]);
}
if (TRUE === $asset instanceof AssetInterface) {
$asset->setSettings($localSettings);
$filtered[$name] = $asset;
} else {
$filtered[$name] = $assetSettings;
}
}
return $filtered;
}
/**
* @param AssetInterface[] $assets
* @throws \RuntimeException
* @return AssetInterface[]
*/
protected function sortAssetsByDependency($assets) {
$placed = array();
$assetNames = (0 < count($assets)) ? array_combine(array_keys($assets), array_keys($assets)) : array();
while ($asset = array_shift($assets)) {
$postpone = FALSE;
/** @var AssetInterface $asset */
$assetSettings = $this->extractAssetSettings($asset);
$name = array_shift($assetNames);
$dependencies = $assetSettings['dependencies'];
if (FALSE === is_array($dependencies)) {
$dependencies = GeneralUtility::trimExplode(',', $assetSettings['dependencies'], TRUE);
}
foreach ($dependencies as $dependency) {
if (
TRUE === array_key_exists($dependency, $assets)
&& FALSE === isset($placed[$dependency])
&& FALSE === in_array($dependency, self::$cachedDependencies)
) {
// shove the Asset back to the end of the queue, the dependency has
// not yet been encountered and moving this item to the back of the
// queue ensures it will be encountered before re-encountering this
// specific Asset
if (0 === count($assets)) {
throw new \RuntimeException('Asset "' . $name . '" depends on "' . $dependency . '" but "' . $dependency . '" was not found', 1358603979);
}
$assets[$name] = $asset;
$assetNames[$name] = $name;
$postpone = TRUE;
}
}
if (FALSE === $postpone) {
$placed[$name] = $asset;
}
}
return $placed;
}
/**
* @param mixed $asset
* @return string
*/
protected function renderAssetAsFluidTemplate($asset) {
$settings = $this->extractAssetSettings($asset);
$variables = (TRUE === (isset($settings['variables']) && is_array($settings['variables'])) ? $settings['variables'] : array());
$contents = $this->buildAsset($asset);
$variables = GeneralUtility::removeDotsFromTS($variables);
/** @var \TYPO3\CMS\Fluid\View\StandaloneView $view */
$view = $this->objectManager->get('TYPO3\\CMS\\Fluid\\View\\StandaloneView');
$view->setTemplateSource($contents);
$view->assignMultiple($variables);
$content = $view->render();
return $content;
}
/**
* Prefix a path according to "absRefPrefix" TS configuration.
*
* @param string $fileRelativePathAndFilename
* @return string
*/
protected function prefixPath($fileRelativePathAndFilename) {
$settings = $this->getSettings();
$prefixPath = $settings['prependPath'];
if (FALSE === empty($GLOBALS['TSFE']->absRefPrefix) && TRUE === empty($prefixPath)) {
$fileRelativePathAndFilename = $GLOBALS['TSFE']->absRefPrefix . $fileRelativePathAndFilename;
} elseif (FALSE === empty($prefixPath)) {
$fileRelativePathAndFilename = $prefixPath . $fileRelativePathAndFilename;
}
return $fileRelativePathAndFilename;
}
/**
* Fixes the relative paths inside of url() references in CSS files
*
* @param string $contents Data to process
* @param string $originalDirectory Original location of file
* @return string Processed data
*/
protected function detectAndCopyFileReferences($contents, $originalDirectory) {
if (FALSE !== stripos($contents, 'url')) {
$regex = '/url(\\(\\s*["\']?(?!\\/)([^"\']+)["\']?\\s*\\))/iU';
$contents = $this->copyReferencedFilesAndReplacePaths($contents, $regex, $originalDirectory, '(\'|\')');
}
if (FALSE !== stripos($contents, '@import')) {
$regex = '/@import\\s*(["\']?(?!\\/)([^"\']+)["\']?)/i';
$contents = $this->copyReferencedFilesAndReplacePaths($contents, $regex, $originalDirectory, '"|"');
}
return $contents;
}
/**
* Finds and replaces all URLs by using a given regex
*
* @param string $contents Data to process
* @param string $regex Regex used to find URLs in content
* @param string $originalDirectory Original location to CSS file, if file based.
* @param string $wrap Wrap around replaced values
* @return string Processed data
*/
protected function copyReferencedFilesAndReplacePaths($contents, $regex, $originalDirectory, $wrap = '|') {
$matches = array();
$replacements = array();
$wrap = explode('|', $wrap);
preg_match_all($regex, $contents, $matches);
foreach ($matches[2] as $matchCount => $match) {
$match = trim($match, '\'" ');
if (FALSE === strpos($match, ':') && !preg_match('/url\\s*\\(/i', $match)) {
$checksum = md5($originalDirectory . $match);
if (0 < preg_match('/([^\?#]+)(.+)?/', $match, $items)) {
list(, $path, $suffix) = $items;
} else {
$path = $match;
$suffix = '';
}
$newPath = basename($path);
$extension = pathinfo($newPath, PATHINFO_EXTENSION);
$temporaryFileName = 'vhs-assets-css-' . $checksum . '.' . $extension;
$temporaryFile = constant('PATH_site') . 'typo3temp/' . $temporaryFileName;
$rawPath = GeneralUtility::getFileAbsFileName($originalDirectory . (TRUE === empty($originalDirectory) ? '' : '/')) . $path;
$realPath = realpath($rawPath);
if (FALSE === $realPath) {
GeneralUtility::sysLog('Asset at path "' . $rawPath . '" not found. Processing skipped.', 'vhs', GeneralUtility::SYSLOG_SEVERITY_WARNING);
} else {
if (FALSE === file_exists($temporaryFile)) {
copy($realPath, $temporaryFile);
}
$replacements[$matches[1][$matchCount]] = $wrap[0] . $temporaryFileName . $suffix . $wrap[1];
}
}
}
if (FALSE === empty($replacements)) {
$contents = str_replace(array_keys($replacements), array_values($replacements), $contents);
}
return $contents;
}
/**
* @param mixed $asset An Asset ViewHelper instance or an array containing an Asset definition
* @return boolean
*/
protected function assertAssetAllowedInFooter($asset) {
if (TRUE === $asset instanceof AssetInterface) {
return $asset->assertAllowedInFooter();
}
return (boolean) (TRUE === isset($asset['allowMoveToFooter']) ? $asset['allowMoveToFooter'] : TRUE);
}
/**
* @param mixed $asset An Asset ViewHelper instance or an array containing an Asset definition
* @return array
*/
protected function extractAssetSettings($asset) {
if (TRUE === $asset instanceof AssetInterface) {
return $asset->getAssetSettings();
}
return $asset;
}
/**
* @param mixed $asset An Asset ViewHelper instance or an array containing an Asset definition
* @return string
*/
protected function buildAsset($asset) {
if (TRUE === $asset instanceof AssetInterface) {
return $asset->build();
}
if (FALSE === isset($asset['path']) || TRUE === empty($asset['path'])) {
return (TRUE === isset($asset['content']) ? $asset['content'] : NULL);
}
if (TRUE === isset($asset['external']) && TRUE === (boolean) $asset['external']) {
$path = $asset['path'];
} else {
$path = GeneralUtility::getFileAbsFileName($asset['path']);
}
$content = file_get_contents($path);
return $content;
}
/**
* @param mixed $asset
* @throws \RuntimeException
* @return string
*/
protected function extractAssetContent($asset) {
$assetSettings = $this->extractAssetSettings($asset);
$fileRelativePathAndFilename = $assetSettings['path'];
$fileRelativePath = dirname($assetSettings['path']);
$absolutePathAndFilename = GeneralUtility::getFileAbsFileName($fileRelativePathAndFilename);
$isExternal = TRUE === isset($assetSettings['external']) && TRUE === (boolean) $assetSettings['external'];
$isFluidTemplate = TRUE === isset($assetSettings['fluid']) && TRUE === (boolean) $assetSettings['fluid'];
if (FALSE === empty($fileRelativePathAndFilename)) {
if (FALSE === $isExternal && FALSE === file_exists($absolutePathAndFilename)) {
throw new \RuntimeException('Asset "' . $absolutePathAndFilename . '" does not exist.');
}
if (TRUE === $isFluidTemplate) {
$content = $this->renderAssetAsFluidTemplate($asset);
} else {
$content = $this->buildAsset($asset);
}
} else {
$content = $this->buildAsset($asset);
}
if (('css' === $assetSettings['type']) && (TRUE === (boolean) $assetSettings['rewrite'])) {
$content = $this->detectAndCopyFileReferences($content, $fileRelativePath);
}
return $content;
}
/**
* @param array $parameters
* @return void
*/
public function clearCacheCommand($parameters) {
if (TRUE === self::$cacheCleared) {
return;
}
if ('all' !== $parameters['cacheCmd']) {
return;
}
$assetCacheFiles = glob(GeneralUtility::getFileAbsFileName('typo3temp/vhs-assets-*'));
if (FALSE === $assetCacheFiles) {
return;
}
foreach ($assetCacheFiles as $assetCacheFile) {
touch($assetCacheFile, 0);
}
self::$cacheCleared = TRUE;
}
/**
* @param string $file
* @param string $contents
*/
protected function writeFile($file, $contents) {
file_put_contents($file, $contents);
}
/**
* @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,218 @@
<?php
namespace FluidTYPO3\Vhs\Service;
/*
* 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\SingletonInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Frontend\Page\PageRepository;
/**
* Page Select Service
*
* Wrapper service for \TYPO3\CMS\Frontend\Page\PageRepository including static caches for
* menus, rootlines, pages and page overlays to be implemented in
* viewhelpers by replacing calls to \TYPO3\CMS\Frontend\Page\PageRepository::getMenu()
* and the like.
*
* @author Björn Fromme <fromme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage Service
*/
class PageSelectService implements SingletonInterface {
const DOKTYPE_MOVE_TO_PLACEHOLDER = 0;
/**
* @var PageRepository
*/
protected static $pageSelect;
/**
* @var array
*/
protected static $cachedPages = array();
/**
* @var array
*/
protected static $cachedOverlays = array();
/**
* @var array
*/
protected static $cachedMenus = array();
/**
* @var array
*/
protected static $cachedRootLines = array();
/**
* Initialize \TYPO3\CMS\Frontend\Page\PageRepository objects
*/
public function initializeObject() {
self::$pageSelect = $this->createPageSelectInstance();
}
/**
* @return PageRepository
*/
private function createPageSelectInstance() {
if (TRUE === is_array($GLOBALS['TSFE']->fe_user->user)
|| (TRUE === isset($GLOBALS['TSFE']->fe_user->groupData['uid']) && 0 < $GLOBALS['TSFE']->fe_user->groupData['uid'])) {
$groups = array(-2, 0);
} else {
$groups = array(-1, 0);
}
/** @var \TYPO3\CMS\Frontend\Page\PageRepository $pageSelect */
$pageSelect = GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\Page\\PageRepository');
if (TRUE === isset($GLOBALS['TSFE']->fe_user->groupData)) {
$groups = array_merge($groups, array_values($GLOBALS['TSFE']->fe_user->groupData['uid']));
}
$clauses = array();
foreach ($groups as $group) {
$clause = "fe_group = '" . $group . "' OR fe_group LIKE '" .
$group . ",%' OR fe_group LIKE '%," . $group . "' OR fe_group LIKE '%," . $group . ",%'";
array_push($clauses, $clause);
}
array_push($clauses, "fe_group = '' OR fe_group = '0'");
$pageSelect->where_groupAccess = ' AND (' . implode(' OR ', $clauses) . ')';
$pageSelect->versioningPreview = (boolean) 0 < $GLOBALS['BE_USER']->workspace;
$pageSelect->versioningWorkspaceId = (integer) $GLOBALS['BE_USER']->workspace;
return $pageSelect;
}
/**
* Wrapper for \TYPO3\CMS\Frontend\Page\PageRepository::getPage()
*
* @param integer $pageUid
* @return array
*/
public function getPage($pageUid = NULL) {
if (NULL === $pageUid) {
$pageUid = $GLOBALS['TSFE']->id;
}
if (FALSE === isset(self::$cachedPages[$pageUid])) {
self::$cachedPages[$pageUid] = self::$pageSelect->getPage($pageUid);
}
return self::$cachedPages[$pageUid];
}
/**
* Wrapper for \TYPO3\CMS\Frontend\Page\PageRepository::getPageOverlay()
*
* @param mixed $pageInput
* @param integer $languageUid
* @return array
*/
public function getPageOverlay($pageInput, $languageUid = -1) {
$key = md5(serialize($pageInput) . $languageUid);
if (FALSE === isset(self::$cachedOverlays[$key])) {
self::$cachedOverlays[$key] = self::$pageSelect->getPageOverlay($pageInput, $languageUid);
}
return self::$cachedOverlays[$key];
}
/**
* Wrapper for \TYPO3\CMS\Frontend\Page\PageRepository::getMenu()
* Caution: different signature
*
* @param integer $pageUid
* @param array $excludePages
* @param string $where
* @param boolean $showHiddenInMenu
* @param boolean $checkShortcuts
* @param array $allowedDoktypeList
* @return array
*/
public function getMenu($pageUid = NULL, $excludePages = array(), $where = '', $showHiddenInMenu = FALSE, $checkShortcuts = FALSE, $allowedDoktypeList = array()) {
if (NULL === $pageUid) {
$pageUid = $GLOBALS['TSFE']->id;
}
$addWhere = self::$pageSelect->enableFields('pages', 0);
if (0 < count($allowedDoktypeList)) {
$addWhere .= ' AND doktype IN (' . implode(',', $allowedDoktypeList) . ')';
} else {
$addWhere .= ' AND doktype != ' . PageRepository::DOKTYPE_SYSFOLDER;
}
if (0 < count($excludePages)) {
$addWhere .= ' AND uid NOT IN (' . implode(',', $excludePages) . ')';
}
if (FALSE === (boolean) $showHiddenInMenu) {
$addWhere .= ' AND nav_hide=0';
}
if ('' !== $where) {
$addWhere = $where . ' ' . $addWhere;
}
$key = md5(intval($showHiddenInMenu) . $pageUid . $addWhere . intval($checkShortcuts));
if (FALSE === isset(self::$cachedMenus[$key])) {
self::$cachedMenus[$key] = self::$pageSelect->getMenu($pageUid, '*', 'sorting', $addWhere, $checkShortcuts);
}
return self::$cachedMenus[$key];
}
/**
* Wrapper for \TYPO3\CMS\Frontend\Page\PageRepository::getRootLine()
*
* @param integer $pageUid
* @param string $MP
* @param boolean $reverse
* @return array
*/
public function getRootLine($pageUid = NULL, $MP = NULL, $reverse = FALSE) {
if (NULL === $pageUid) {
$pageUid = $GLOBALS['TSFE']->id;
}
if (NULL === $MP) {
$MP = GeneralUtility::_GP('MP');
if (TRUE === empty($MP)) {
$MP = '';
}
}
$key = md5($pageUid . $MP . (string) $reverse);
if (FALSE === isset(self::$cachedRootLines[$key])) {
$rootLine = self::$pageSelect->getRootLine($pageUid, $MP);
if (TRUE === $reverse) {
$rootLine = array_reverse($rootLine);
}
self::$cachedRootLines[$key] = $rootLine;
}
return self::$cachedRootLines[$key];
}
/**
* Checks if a page for a specific language should be hidden
*
* @param integer $pageUid
* @param integer $languageUid
* @param boolean $normalWhenNoLanguage
* @return boolean
*/
public function hidePageForLanguageUid($pageUid = 0, $languageUid = -1, $normalWhenNoLanguage = TRUE) {
if (0 === $pageUid) {
$pageUid = $GLOBALS['TSFE']->id;
}
if (-1 === $languageUid) {
$languageUid = $GLOBALS['TSFE']->sys_language_uid;
}
$page = $this->getPage($pageUid);
$l18nCfg = TRUE === isset($page['l18n_cfg']) ? $page['l18n_cfg'] : 0;
$hideIfNotTranslated = (boolean) GeneralUtility::hideIfNotTranslated($l18nCfg);
$hideIfDefaultLanguage = (boolean) GeneralUtility::hideIfDefaultLanguage($l18nCfg);
$pageOverlay = 0 !== $languageUid ? $this->getPageOverlay($pageUid, $languageUid) : array();
$translationAvailable = 0 !== count($pageOverlay);
return
(TRUE === $hideIfNotTranslated && 0 !== $languageUid && FALSE === $translationAvailable) ||
(TRUE === $hideIfDefaultLanguage && (0 === $languageUid || FALSE === $translationAvailable)) ||
(FALSE === $normalWhenNoLanguage && 0 !== $languageUid && FALSE === $translationAvailable);
}
}

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);
}
}
}
}

View File

@@ -0,0 +1,46 @@
<?php
namespace FluidTYPO3\Vhs\Utility;
/*
* 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\ExtensionManagementUtility;
/**
* Core Utility
*
* Utility to get core informations.
*
* @author Daniel Dorndorf <dorndorf@dreipunktnull.com>
* @package Vhs
* @subpackage Utility
*/
class CoreUtility {
/**
* Returns the flag icons path depending on the current core version
*
* @return string
*/
public static function getLanguageFlagIconPath() {
if (TRUE === version_compare(TYPO3_version, '7.1', '<')) {
return ExtensionManagementUtility::extPath('t3skin') . 'images/flags/';
}
return ExtensionManagementUtility::extPath('core') . 'Resources/Public/Icons/Flags/';
}
/**
* Returns the current core minor version
*
* @return string
* @throws \TYPO3\CMS\Core\Package\Exception
*/
public static function getCurrentCoreVersion() {
return substr(ExtensionManagementUtility::getExtensionVersion('core'), 0, 3);
}
}

View File

@@ -0,0 +1,69 @@
<?php
namespace FluidTYPO3\Vhs\Utility;
/*
* 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;
/**
* Frontend Simulation Utility
*
* Utility to simulate frontend enviroment in backend enviroment.
*
* @author Xaver Maierhofer <xaver.maierhofer@xwissen.info>
* @package Vhs
* @subpackage Utility
*/
class FrontendSimulationUtility {
/**
* Sets the global variables $GLOBALS['TSFE']->csConvObj and $GLOBALS['TSFE']->renderCharset in Backend mode
* This somewhat hacky work around is currently needed because the conv_case() and convCaseFirst() functions of tslib_cObj rely on those variables to be set
*
* @return mixed
*/
public static function simulateFrontendEnvironment() {
if ('BE' !== TYPO3_MODE) {
return;
}
$tsfeBackup = isset($GLOBALS['TSFE']) ? $GLOBALS['TSFE'] : NULL;
$GLOBALS['TSFE'] = new \stdClass();
// preparing csConvObj
if (FALSE === is_object($GLOBALS['TSFE']->csConvObj)) {
if (TRUE === is_object($GLOBALS['LANG'])) {
$GLOBALS['TSFE']->csConvObj = $GLOBALS['LANG']->csConvObj;
} else {
$GLOBALS['TSFE']->csConvObj = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Charset\\CharsetConverter');
}
}
// preparing renderCharset
if (FALSE === is_object($GLOBALS['TSFE']->renderCharset)) {
if (TRUE === is_object($GLOBALS['LANG'])) {
$GLOBALS['TSFE']->renderCharset = $GLOBALS['LANG']->charSet;
} else {
$GLOBALS['TSFE']->renderCharset = 'utf-8';
}
}
return $tsfeBackup;
}
/**
* Resets $GLOBALS['TSFE'] if it was previously changed by simulateFrontendEnvironment()
*
* @param mixed $tsfeBackup
* @return void
* @see simulateFrontendEnvironment()
*/
public static function resetFrontendEnvironment($tsfeBackup) {
if ('BE' !== TYPO3_MODE) {
return;
}
$GLOBALS['TSFE'] = $tsfeBackup;
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace FluidTYPO3\Vhs\Utility;
/*
* 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\Resource\File;
/**
* ViewHelper Utility
*
* Contains helper methods used in resources ViewHelpers
*
* @author Danilo Bürger <danilo.buerger@hmspl.de>, Heimspiel GmbH
* @package Vhs
* @subpackage Utility
*/
class ResourceUtility {
/**
* Fixes a bug in TYPO3 6.2.0 that the properties metadata is not overlayed on localization.
*
* @param File $file
* @return array
*/
public static function getFileArray(File $file) {
$properties = $file->getProperties();
$stat = $file->getStorage()->getFileInfo($file);
$array = $file->toArray();
foreach ($properties as $key => $value) {
$array[$key] = $value;
}
foreach ($stat as $key => $value) {
$array[$key] = $value;
}
return $array;
}
}

View File

@@ -0,0 +1,76 @@
<?php
namespace FluidTYPO3\Vhs\View;
/*
* 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\Extbase\Mvc\Controller\ControllerContext;
use TYPO3\CMS\Fluid\Compatibility\TemplateParserBuilder;
use TYPO3\CMS\Fluid\Core\Rendering\RenderingContext;
use TYPO3\CMS\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3\CMS\Fluid\View\TemplateView;
/**
* Uncache Template View
*
* @author Danilo Bürger <danilo.buerger@hmspl.de>, Heimspiel GmbH
* @package Vhs
* @subpackage View
*/
class UncacheTemplateView extends TemplateView {
/**
* @param string $postUserFunc
* @param array $conf
* @param string $content
* @return string
*/
public function callUserFunction($postUserFunc, $conf, $content) {
$partial = $conf['partial'];
$section = $conf['section'];
$arguments = TRUE === is_array($conf['arguments']) ? $conf['arguments'] : array();
/** @var \TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext $controllerContext */
$controllerContext = $conf['controllerContext'];
if (TRUE === empty($partial)) {
return '';
}
/** @var RenderingContext $renderingContext */
$renderingContext = $this->objectManager->get('TYPO3\\CMS\\Fluid\\Core\\Rendering\\RenderingContext');
$this->prepareContextsForUncachedRendering($renderingContext, $controllerContext);
return $this->renderPartialUncached($renderingContext, $partial, $section, $arguments);
}
/**
* @param \TYPO3\CMS\Fluid\Core\Rendering\RenderingContextInterface $renderingContext
* @param \TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext $controllerContext
* @return void
*/
protected function prepareContextsForUncachedRendering(RenderingContextInterface $renderingContext, ControllerContext $controllerContext) {
$renderingContext->setControllerContext($controllerContext);
$this->setRenderingContext($renderingContext);
$this->templateParser = TemplateParserBuilder::build();
$this->templateCompiler = $this->objectManager->get('TYPO3\\CMS\\Fluid\\Core\\Compiler\\TemplateCompiler');
$cacheManager = isset($GLOBALS['typo3CacheManager']) ? $GLOBALS['typo3CacheManager'] : GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Cache\\CacheManager');
$this->templateCompiler->setTemplateCache($cacheManager->getCache('fluid_template'));
}
/**
* @param \TYPO3\CMS\Fluid\Core\Rendering\RenderingContextInterface $renderingContext
* @param string $partial
* @param string $section
* @param array $arguments
* @return string
*/
protected function renderPartialUncached(RenderingContextInterface $renderingContext, $partial, $section = NULL, $arguments = array()) {
array_push($this->renderingStack, array('type' => self::RENDERING_TEMPLATE, 'parsedTemplate' => NULL, 'renderingContext' => $renderingContext));
$rendered = $this->renderPartial($partial, $section, $arguments);
array_pop($this->renderingStack);
return $rendered;
}
}

View File

@@ -0,0 +1,473 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Asset;
/*
* 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\AssetService;
use FluidTYPO3\Vhs\Traits\ArrayConsumingViewHelperTrait;
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
use TYPO3\CMS\Extbase\Utility\DebuggerUtility;
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper;
/**
* Base class for ViewHelpers capable of registering assets
* which will be included when rendering the page.
*
* Note: building of all Assets takes place in the class
* FluidTYPO3\Vhs\Service\AssetService with two reasons:
*
* - A "buildAll" method should never be possible to call
* from any Asset ViewHelper; it should only be possible
* from a single class.
* - The method but must be public and non-static and thus
* cannot be hidden from access by subclasses if placed
* in this class.
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers\Asset
*/
abstract class AbstractAssetViewHelper extends AbstractViewHelper implements AssetInterface {
use ArrayConsumingViewHelperTrait;
/**
* @var ConfigurationManagerInterface
*/
protected $configurationManager;
/**
* @var AssetService
*/
protected $assetService;
/**
* @var array
*/
private static $settingsCache = NULL;
/**
* @var array
*/
private $assetSettingsCache;
/**
* @var array
*/
protected $localSettings;
/**
* @var string
*/
protected $content;
/**
* @var ObjectManagerInterface
*/
protected $objectManager;
/**
* @var \TYPO3\CMS\Fluid\Core\ViewHelper\TagBuilder
*/
protected $tagBuilder;
/**
* Example types: raw, meta, css, js.
*
* If a LESS stylesheet is being compiled the "type"
* would be "css" because this will group the compiled
* LESS stylesheet with other CSS - allowing it to be
* merged with other CSS.
*
* @var string
*/
protected $type = 'raw';
/**
* @param ConfigurationManagerInterface $configurationManager
* @return void
*/
public function injectConfigurationManager(ConfigurationManagerInterface $configurationManager) {
$this->configurationManager = $configurationManager;
}
/**
* @param AssetService $assetService
* @return void
*/
public function injectAssetService(AssetService $assetService) {
$this->assetService = $assetService;
}
/**
* @param ObjectManagerInterface $objectManager
* @return void
*/
public function injectObjectManager(ObjectManagerInterface $objectManager) {
$this->objectManager = $objectManager;
$this->tagBuilder = $this->objectManager->get('TYPO3\\CMS\\Fluid\\Core\\ViewHelper\\TagBuilder');
}
/**
* @return void
*/
public function initializeArguments() {
$this->registerArgument('content', 'string', 'Content to insert in header/footer', FALSE, NULL);
$this->registerArgument('path', 'string', 'If not using tag content, specify path to file here', FALSE, NULL);
$this->registerArgument('external', 'boolean', 'If TRUE and standalone, includes the file as raw URL. If TRUE and not standalone then downloads the file and merges it when building Assets', FALSE, FALSE);
$this->registerArgument('name', 'string', 'Optional name of the content. If multiple occurrences of the same name happens, behavior is defined by argument "overwrite"');
$this->registerArgument('overwrite', 'boolean', 'If set to FALSE and a relocated string with "name" already exists, does not overwrite the existing relocated string. Default behavior is to overwrite.', FALSE, TRUE);
$this->registerArgument('dependencies', 'string', 'CSV list of other named assets upon which this asset depends. When included, this asset will always load after its dependencies');
$this->registerArgument('group', 'string', 'Optional name of a logical group (created dynamically just by using the name) to which this particular asset belongs.', FALSE, 'fluid');
$this->registerArgument('debug', 'boolean', 'If TRUE, outputs information about this ViewHelper when the tag is used. Two master debug switches exist in TypoScript; see documentation about Page / Asset ViewHelper');
$this->registerArgument('standalone', 'boolean', 'If TRUE, excludes this Asset from any concatenation which may be applied');
$this->registerArgument('rewrite', 'boolean', 'If FALSE, this Asset will be included as is without any processing of contained urls', FALSE, TRUE);
$this->registerArgument('fluid', 'boolean', 'If TRUE, renders this (standalone or external) Asset as if it were a Fluid template, passing along values of the "arguments" attribute or every available template variable if "arguments" not specified', FALSE, FALSE);
$this->registerArgument('variables', 'mixed', 'An optional array of arguments which you use inside the Asset, be it standalone or inline. Use this argument to ensure your Asset filenames are only reused when all variables used in the Asset are the same', FALSE, FALSE);
$this->registerArgument('allowMoveToFooter', 'boolean', 'DEPRECATED. Use movable instead.', FALSE, TRUE);
$this->registerArgument('movable', 'boolean', 'If TRUE, allows this Asset to be included in the document footer rather than the header. Should never be allowed for CSS.', FALSE, TRUE);
$this->registerArgument('trim', 'boolean', 'DEPRECATED. Trim is no longer supported. Setting this to TRUE doesn\'t do anything.', FALSE, FALSE);
$this->registerArgument('namedChunks', 'boolean', 'If FALSE, hides the comment containing the name of each of Assets which is merged in a merged file. Disable to avoid a bit more output at the cost of transparency', FALSE, FALSE);
}
/**
* @return string
*/
public function __toString() {
return $this->build();
}
/**
* Render method
*
* @return void
*/
public function render() {
$this->finalize();
}
/**
* Build this asset. Override this method in the specific
* implementation of an Asset in order to:
*
* - if necessary compile the Asset (LESS, SASS, CoffeeScript etc)
* - make a final rendering decision based on arguments
*
* Note that within this function the ViewHelper and TemplateVariable
* Containers are not dependable, you cannot use the ControllerContext
* and RenderingContext and you should therefore also never call
* renderChildren from within this function. Anything else goes; CLI
* commands to build, caching implementations - you name it.
*
* @return mixed
*/
public function build() {
if (FALSE === isset($this->arguments['path']) || TRUE === empty($this->arguments['path'])) {
return $this->getContent();
}
if (TRUE === isset($this->arguments['external']) && TRUE === (boolean) $this->arguments['external']) {
$path = $this->arguments['path'];
} else {
$path = GeneralUtility::getFileAbsFileName($this->arguments['path']);
}
$content = file_get_contents($path);
return $content;
}
/**
* Saves this Asset or perhaps discards it if overriding is
* disabled and an identically named Asset already exists.
*
* Performed from every Asset's render() for it to work.
*
* @return void
*/
protected function finalize() {
$this->assetSettingsCache = NULL;
$this->localSettings = NULL;
if (FALSE === isset($GLOBALS['VhsAssets'])) {
$GLOBALS['VhsAssets'] = array();
}
$name = $this->getName();
$overwrite = $this->getOverwrite();
$slotFree = FALSE === isset($GLOBALS['VhsAssets'][$name]);
if (FALSE === ($overwrite || $slotFree)) {
return;
}
$this->content = $this->getContent();
$this->tagBuilder->setContent($this->content);
$this->debug();
$GLOBALS['VhsAssets'][$name] = clone $this;
}
/**
* @return mixed
*/
protected function debug() {
$settings = $this->getSettings();
$debugOutputEnabled = $this->assertDebugEnabled();
$useDebugUtility = FALSE === isset($settings['useDebugUtility']) || (isset($settings['useDebugUtility']) && $settings['useDebugUtility'] > 0);
$debugInformation = $this->getDebugInformation();
if (TRUE === $debugOutputEnabled) {
if (TRUE === $useDebugUtility) {
DebuggerUtility::var_dump($debugInformation);
return '';
} else {
return var_export($debugInformation, TRUE);
}
}
}
/**
* @return array
*/
public function getDependencies() {
$assetSettings = $this->getAssetSettings();
if (TRUE === isset($assetSettings['dependencies'])) {
return GeneralUtility::trimExplode(',', $assetSettings['dependencies'], TRUE);
}
return array();
}
/**
* @return boolean
*/
protected function getOverwrite() {
$assetSettings = $this->getAssetSettings();
return (boolean) (TRUE === isset($assetSettings['overwrite']) && $assetSettings['overwrite'] > 0);
}
/**
* @return string
*/
public function getType() {
return $this->type;
}
/**
* @return string
*/
public function getName() {
$assetSettings = $this->getAssetSettings();
if (TRUE === isset($assetSettings['name']) && FALSE === empty($assetSettings['name'])) {
$name = $assetSettings['name'];
} else {
$name = md5(serialize($assetSettings));
}
return $name;
}
/**
* @return string
*/
public function getGroup() {
$assetSettings = $this->getAssetSettings();
return $assetSettings['group'];
}
/**
* @return string
*/
protected function getContent() {
$assetSettings = $this->getAssetSettings();
if (TRUE === isset($assetSettings['content']) && FALSE === empty($assetSettings['content'])) {
$content = $assetSettings['content'];
} else {
$content = $this->renderChildren();
}
return $content;
}
/**
* @return string
*/
protected function getTagWithContent() {
return $this->tagBuilder->render();
}
/**
* @return array
*/
public function getVariables() {
$assetSettings = $this->getAssetSettings();
if (TRUE === (isset($assetSettings['variables']) && is_array($assetSettings['variables']))) {
return $assetSettings['variables'];
}
return array();
}
/**
* Returns the settings used by this particular Asset
* during inclusion. Public access allows later inspection
* of the TypoScript values which were applied to the Asset.
*
* @return array
*/
public function getSettings() {
if (NULL === self::$settingsCache) {
$allTypoScript = $this->configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FULL_TYPOSCRIPT);
$settingsExist = isset($allTypoScript['plugin.']['tx_vhs.']['settings.']);
if (FALSE === $settingsExist) {
// no settings exist, but don't allow a NULL value. This prevents cache clobbering.
self::$settingsCache = array();
} else {
self::$settingsCache = GeneralUtility::removeDotsFromTS($allTypoScript['plugin.']['tx_vhs.']['settings.']);
}
}
$settings = self::$settingsCache;
if (TRUE === is_array($this->localSettings)) {
if (TRUE === method_exists('TYPO3\\CMS\\Core\\Utility\\ArrayUtility', 'mergeRecursiveWithOverrule')) {
ArrayUtility::mergeRecursiveWithOverrule($settings, $this->localSettings);
} else {
$settings = GeneralUtility::array_merge_recursive_overrule($settings, $this->localSettings);
}
}
return $settings;
}
/**
* @param array|\ArrayAccess $settings
*/
public function setSettings($settings) {
if (TRUE === is_array($settings) || TRUE === $settings instanceof \ArrayAccess) {
$this->localSettings = $settings;
}
}
/**
* @return array
*/
public function getAssetSettings() {
if (TRUE === is_array($this->assetSettingsCache)) {
return $this->assetSettingsCache;
}
// Note: name and group are taken directly from arguments; if they are changed through
// TypoScript the changed values will be returned from this function.
$name = $this->arguments['name'];
$groupName = $this->arguments['group'];
$settings = $this->getSettings();
$assetSettings = $this->arguments;
$assetSettings['type'] = $this->getType();
//TODO: Remove with deprecated argument
if (TRUE === $this->hasArgument('allowMoveToFooter')) {
$assetSettings['movable'] = $this->arguments['allowMoveToFooter'];
unset($assetSettings['allowMoveToFooter']);
}
if (TRUE === isset($settings['asset']) && TRUE === is_array($settings['asset'])) {
$assetSettings = $this->mergeArrays($assetSettings, $settings['asset']);
}
if (TRUE === (isset($settings['assetGroup'][$groupName]) && is_array($settings['assetGroup'][$groupName]))) {
$assetSettings = $this->mergeArrays($assetSettings, $settings['assetGroup'][$groupName]);
}
if (TRUE === (isset($settings['asset'][$name]) && is_array($settings['asset'][$name]))) {
$assetSettings = $this->mergeArrays($assetSettings, $settings['asset'][$name]);
}
if (FALSE === empty($assetSettings['path']) && FALSE === (boolean) $assetSettings['external']) {
$assetSettings['path'] = GeneralUtility::getFileAbsFileName($assetSettings['path']);
}
$assetSettings['name'] = $name;
$this->assetSettingsCache = $assetSettings;
return $assetSettings;
}
/**
* Allows public access to debug this particular Asset
* instance later, when including the Asset in the page.
*
* @return array
*/
public function getDebugInformation() {
return array(
'class' => get_class($this),
'settings' => $this->getAssetSettings()
);
}
/**
* Returns TRUE of settings specify that the source of this
* Asset should be rendered as if it were a Fluid template,
* using variables from the "arguments" attribute
*
* @return boolean
*/
public function assertFluidEnabled() {
$settings = $this->getAssetSettings();
if (TRUE === (isset($settings['fluid']) && $settings['fluid'] > 0)) {
return TRUE;
}
return FALSE;
}
/**
* Returns TRUE if settings specify that the name of each Asset
* should be placed above the built content when placed in merged
* Asset cache files.
*
* @return boolean
*/
public function assertAddNameCommentWithChunk() {
$settings = $this->getAssetSettings();
if (TRUE === (isset($settings['namedChunks']) && 0 < $settings['namedChunks']) || FALSE === isset($settings['namedChunks'])) {
return TRUE;
}
return FALSE;
}
/**
* Returns TRUE if the current Asset should be debugged as commanded
* by settings in TypoScript an/ord ViewHelper attributes.
*
* @return boolean
*/
public function assertDebugEnabled() {
$settings = $this->getSettings();
if (TRUE === (isset($settings['debug']) && $settings['debug'] > 0)) {
return TRUE;
}
$settings = $this->getAssetSettings();
if (TRUE === (isset($settings['asset']['debug']) && $settings['asset']['debug'] > 0)) {
return TRUE;
}
return FALSE;
}
/**
* @return boolean
*/
public function assertAllowedInFooter() {
$settings = $this->getAssetSettings();
if (TRUE === (isset($settings['movable']) && $settings['movable'] < 1)) {
return FALSE;
}
return TRUE;
}
/**
* @return boolean
*/
public function assertHasBeenRemoved() {
$groupName = $this->arguments['group'];
$settings = $this->getSettings();
$dependencies = $this->getDependencies();
array_push($dependencies, $this->getName());
foreach ($dependencies as $name) {
if (TRUE === isset($settings['asset'][$name]['remove']) && $settings['asset'][$name]['remove'] > 0) {
return TRUE;
}
}
if (TRUE === isset($settings['assetGroup'][$groupName]['remove']) && $settings['assetGroup'][$groupName]['remove'] > 0) {
return TRUE;
}
return FALSE;
}
}

View File

@@ -0,0 +1,123 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Asset;
/*
* 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.
*/
/**
* Basic interface which must be implemented by every
* possible Asset type.
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers\Asset
*/
interface AssetInterface {
/**
* Render method
*
* @return void
*/
public function render();
/**
* Build this asset. Override this method in the specific
* implementation of an Asset in order to:
*
* - if necessary compile the Asset (LESS, SASS, CoffeeScript etc)
* - make a final rendering decision based on arguments
*
* Note that within this function the ViewHelper and TemplateVariable
* Containers are not dependable, you cannot use the ControllerContext
* and RenderingContext and you should therefore also never call
* renderChildren from within this function. Anything else goes; CLI
* commands to build, caching implementations - you name it.
*
* @return mixed
*/
public function build();
/**
* @return array
*/
public function getDependencies();
/**
* @return string
*/
public function getType();
/**
* @return string
*/
public function getName();
/**
* @return array
*/
public function getVariables();
/**
* Returns the settings used by this particular Asset
* during inclusion. Public access allows later inspection
* of the TypoScript values which were applied to the Asset.
*
* @return array
*/
public function getSettings();
/**
* @return array
*/
public function getAssetSettings();
/**
* Allows public access to debug this particular Asset
* instance later, when including the Asset in the page.
*
* @return array
*/
public function getDebugInformation();
/**
* Returns TRUE if settings specify that the source of this
* Asset should be rendered as if it were a Fluid template,
* using variables from the "arguments" attribute
*
* @return boolean
*/
public function assertFluidEnabled();
/**
* Returns TRUE if settings specify that the name of each Asset
* should be placed above the built content when placed in merged
* Asset cache files.
*
* @return boolean
*/
public function assertAddNameCommentWithChunk();
/**
* Returns TRUE if the current Asset should be debugged as commanded
* by settings in TypoScript and/or ViewHelper attributes.
*
* @return boolean
*/
public function assertDebugEnabled();
/**
* @return boolean
*/
public function assertAllowedInFooter();
/**
* @return boolean
*/
public function assertHasBeenRemoved();
}

View File

@@ -0,0 +1,97 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Asset;
/*
* 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;
/**
* ### Asset DNS Prefetching ViewHelper
*
* Enables the special `<link rel="dns-prefetch" />` tag
* which instructs the browser to start prefetching DNS
* records for every domain listed in the `domains` attribute
* of this ViewHelper. Prefetching starts as soon as the browser
* becomes aware of the tag - to optimise even further, you may
* wish to control the output buffer's size to deliver your site
* HTML in chunks, the first chunk being the one containing this
* ViewHelper.
*
* Note that the web server daemon may send headers which prevent
* this prefetching and that these headers can be added in many
* ways. If prefetching does not work, you will need to inspect
* the HTTP headers returned from the actual environment. Or you
* may prefer to simply add `force="TRUE"` to this tag - but
* beware that this will affect the entire document's behaviour,
* not just for this particular set of domain prefetches. Once
* force-enabled this setting cannot be disabled (unless done so
* by manually adding an additional meta header tag as examplified
* by the `build()` method.
*
* ### Example usage:
*
* <v:asset.prefetch domains="fedext.net,ajax.google.com" />
*
* See: https://developer.mozilla.org/en-US/docs/Controlling_DNS_prefetching
*
* @package Vhs
* @subpackage ViewHelpers\Asset
*/
class PrefetchViewHelper extends AbstractAssetViewHelper {
/**
* @var string
*/
protected $type = 'link';
/**
* @return void
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerArgument('domains', 'mixed', 'Domain DNS names to prefetch. By default will add all sys_domain record DNS names', TRUE);
$this->registerArgument('protocol', 'string', 'Optional value of protocol as inserted in the resulting HREF value. If you experience problems with a non-protocol link, try enforcing http/https here', FALSE, NULL);
$this->registerArgument('protocolSeparator', 'string', 'If you do not enforce a particular protocol and wish to remove the double slashes from the hostname (your browser may not understand this!), set this attribute to an empty value (not-zero)', FALSE, '//');
$this->registerArgument('force', 'boolean', 'If TRUE, adds an additional meta header tag which forces prefetching to be enabled even if otherwise requested by the http daemon', FALSE, FALSE);
}
/**
* @return void
*/
public function render() {
$this->arguments['standalone'] = TRUE;
$this->arguments['allowMoveToFooter'] = FALSE;
$this->tagBuilder->forceClosingTag(FALSE);
$this->tagBuilder->addAttribute('rel', 'dns-prefetch');
$this->tagBuilder->addAttribute('href', '');
$this->tagBuilder->setTagName('link');
$this->finalize();
}
/**
* @return string
*/
public function build() {
$domains = $this->arguments['domains'];
if (FALSE === is_array($domains)) {
$domains = GeneralUtility::trimExplode(',', $domains, TRUE);
}
$headerCode = '';
if (TRUE === (boolean) $this->arguments['force']) {
$headerCode .= '<meta http-equiv="x-dns-prefetch-control" content="off">' . LF;
}
foreach ($domains as $domain) {
$this->tagBuilder->removeAttribute('href');
$this->tagBuilder->addAttribute('href', $this->arguments['protocol'] . $this->arguments['protocolSeparator'] . $domain);
$headerCode .= $this->tagBuilder->render() . LF;
}
return $headerCode;
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Asset;
/*
* 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.
*/
/**
* ### Basic Script ViewHelper
*
* Allows inserting a `<script>` Asset. Settings specify
* where to insert the Asset and how to treat it.
*
* @package Vhs
* @subpackage ViewHelpers\Asset
*/
class ScriptViewHelper extends AbstractAssetViewHelper {
/**
* @var string
*/
protected $type = 'js';
}

View File

@@ -0,0 +1,35 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Asset;
/*
* 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.
*/
/**
* ### Basic Style ViewHelper
*
* Allows inserting a `<link>` or `<style>` Asset. Settings
* specify where to insert the Asset and how to treat it.
*
* @package Vhs
* @subpackage ViewHelpers\Asset
*/
class StyleViewHelper extends AbstractAssetViewHelper {
/**
* @return void
*/
public function initializeArguments() {
parent::initializeArguments();
$this->overrideArgument('allowMoveToFooter', 'boolean', 'If TRUE, allows this Asset to be included in the document footer rather than the header. Should never be allowed for CSS.', FALSE, FALSE);
}
/**
* @var string
*/
protected $type = 'css';
}

View File

@@ -0,0 +1,63 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers;
/*
* 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;
/**
* ### Basic Asset ViewHelper
*
* Places the contents of the asset (the tag body) directly
* in the additional header content of the page. This most
* basic possible version of an Asset has only the core
* features shared by every Asset type:
*
* - a "name" attribute which is required, identifying the Asset
* by a lowerCamelCase or lowercase_underscored value, your
* preference (but lowerCamelCase recommended for consistency).
* - a "dependencies" attribute with a CSV list of other named
* Assets upon which the current Asset depends. When used, this
* Asset will be included after every asset listed as dependency.
* - a "group" attribute which is optional and is used ty further
* identify the Asset as belonging to a particular group which
* can be suppressed or manipulated through TypoScript. For
* example, the default value is "fluid" and if TypoScript is
* used to exclude the group "fluid" then any Asset in that
* group will simply not be loaded.
* - an "overwrite" attribute which if enabled causes any existing
* asset with the same name to be overwritten with the current
* Asset instead. If rendered in a loop only the last instance
* is actually used (this allows Assets in Partials which are
* rendered in an f:for loop).
* - a "debug" property which enables output of the information
* used by the current Asset, with an option to force debug
* mode through TypoScript.
* - additional properties which affect how the Asset is processed.
* For a full list see the argument descriptions; the same
* settings can be applied through TypoScript per-Asset, globally
* or per-Asset-group.
*
* > Note: there are no static TypoScript templates for VHS but
* > you will find a complete list in the README.md file in the
* > root of the extension folder.
*
* @package Vhs
* @subpackage ViewHelpers
*/
class AssetViewHelper extends AbstractAssetViewHelper {
/**
* @return void
*/
public function initializeArguments() {
parent::initializeArguments();
$this->overrideArgument('standalone', 'boolean', 'If TRUE, excludes this Asset from any concatenation which may be applied', FALSE, TRUE);
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers;
/*
* 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;
/**
* ### Call ViewHelper
*
* Calls a method on an existing object. Usable as inline or tag.
*
* ### Examples
*
* <!-- inline, useful as argument, for example in f:for -->
* {object -> v:call(method: 'toArray')}
* <!-- tag, useful to quickly output simple values -->
* <v:call object="{object}" method="unconventionalGetter" />
* <v:call method="unconventionalGetter">{object}</v:call>
* <!-- arguments for the method -->
* <v:call object="{object}" method="doSomethingWithArguments" arguments="{0: 'foo', 1: 'bar'}" />
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers
*/
class CallViewHelper extends AbstractViewHelper {
/**
* @param string $method
* @param object $object
* @param array $arguments
* @throws \RuntimeException
* @return mixed
*/
public function render($method, $object = NULL, array $arguments = array()) {
if (NULL === $object) {
$object = $this->renderChildren();
if (FALSE === is_object($object)) {
throw new \RuntimeException('Using v:call requires an object either as "object" attribute, tag content or inline argument', 1356849652);
}
}
if (FALSE === method_exists($object, $method)) {
throw new \RuntimeException('Method "' . $method . '" does not exist on object of type ' . get_class($object), 1356834755);
}
return call_user_func_array(array($object, $method), $arguments);
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers;
/*
* 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;
/**
* ### Case for SwitchViewHelper
*
* Used inside `v:switch` to trigger on specific values.
*
* ### Example
*
* <v:switch value="{variable}">
* <v:case case="someValue" break="TRUE">
* <!-- do whatever, if {variable} == 'someValue' -->
* </v:case>
* <v:case case="default">
* <!-- the case "default" is a reserved keyword which acts as the default case. -->
* </v:case>
* </v:switch>
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers
*/
class CaseViewHelper extends AbstractViewHelper {
/**
* Initialize
*
* @return void
*/
public function initializeArguments() {
$this->registerArgument('case', 'string', 'Value which triggers this case - reserved name "default" used for default case', TRUE);
$this->registerArgument('break', 'boolean', 'If TRUE, breaks switch on encountering this case', FALSE, FALSE);
}
/**
* Renders the case and returns array of content and break-boolean
*
* @return array
*/
public function render() {
$matchesCase = (boolean) ($this->viewHelperVariableContainer->get('FluidTYPO3\\Vhs\\ViewHelpers\\SwitchViewHelper', 'switchCaseValue') == $this->arguments['case']);
$mustContinue = $this->viewHelperVariableContainer->get('FluidTYPO3\\Vhs\\ViewHelpers\\SwitchViewHelper', 'switchContinueUntilBreak');
$isDefault = (boolean) ('default' === $this->arguments['case']);
if (TRUE === $matchesCase || TRUE == $mustContinue || TRUE === $isDefault) {
if (TRUE === $this->arguments['break']) {
$this->viewHelperVariableContainer->addOrUpdate('FluidTYPO3\\Vhs\\ViewHelpers\\SwitchViewHelper', 'switchBreakRequested', TRUE);
} else {
$this->viewHelperVariableContainer->addOrUpdate('FluidTYPO3\\Vhs\\ViewHelpers\\SwitchViewHelper', 'switchContinueUntilBreak', TRUE);
}
return $this->renderChildren();
}
return NULL;
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Condition\Context;
/*
* 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\AbstractConditionViewHelper;
use FluidTYPO3\Vhs\Traits\ConditionViewHelperTrait;
/**
* ### Condition: Is context Backend?
*
* A condition ViewHelper which renders the `then` child if
* current context being rendered is BE.
*
* ### Examples
*
* <!-- simple usage, content becomes then-child -->
* <v:condition.context.isBackend>
* Hooray for BE contexts!
* </v:condition.context.isBackend>
* <!-- extended use combined with f:then and f:else -->
* <v:condition.context.isBackend>
* <f:then>
* Hooray for BE contexts!
* </f:then>
* <f:else>
* Maybe FE, maybe CLI.
* </f:else>
* </v:condition.context.isBackend>
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers\If\Context
*/
class IsBackendViewHelper extends AbstractConditionViewHelper {
use ConditionViewHelperTrait;
/**
* This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality.
*
* @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 ('BE' === TYPO3_MODE);
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Condition\Context;
/*
* 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\AbstractConditionViewHelper;
use FluidTYPO3\Vhs\Traits\ConditionViewHelperTrait;
/**
* ### Condition: Is context CLI?
*
* A condition ViewHelper which renders the `then` child if
* current context being rendered is CLI.
*
* ### Examples
*
* <!-- simple usage, content becomes then-child -->
* <v:condition.context.isCli>
* Hooray for CLI contexts!
* </v:condition.context.isCli>
* <!-- extended use combined with f:then and f:else -->
* <v:condition.context.isCli>
* <f:then>
* Hooray for CLI contexts!
* </f:then>
* <f:else>
* Maybe BE, maybe FE.
* </f:else>
* </v:condition.context.isCli>
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers\Condition\Context
*/
class IsCliViewHelper extends AbstractConditionViewHelper {
use ConditionViewHelperTrait;
/**
* This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality.
*
* @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 defined('TYPO3_climode');
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Condition\Context;
/*
* 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\AbstractConditionViewHelper;
use FluidTYPO3\Vhs\Traits\ConditionViewHelperTrait;
/**
* ### Context: IsDevelopment
*
* Returns true if current root application context is development otherwise false.
* If no application context has been set, then the default context is production.
*
* #### Note about how to set the application context
*
* The context TYPO3 CMS runs in is specified through the environment variable TYPO3_CONTEXT.
* It can be set by .htaccess or in the server configuration
*
* See: http://docs.typo3.org/typo3cms/CoreApiReference/ApiOverview/Bootstrapping/Index.html#bootstrapping-context
*
* @author Benjamin Beck <beck@beckdigitalemedien.de>
* @package Vhs
* @subpackage ViewHelpers\Condition\Context
*/
class IsDevelopmentViewHelper extends AbstractConditionViewHelper {
use ConditionViewHelperTrait;
/**
* This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality.
*
* @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 GeneralUtility::getApplicationContext()->isDevelopment();
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Condition\Context;
/*
* 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\AbstractConditionViewHelper;
use FluidTYPO3\Vhs\Traits\ConditionViewHelperTrait;
/**
* ### Condition: Is context Frontend?
*
* A condition ViewHelper which renders the `then` child if
* current context being rendered is FE.
*
* ### Examples
*
* <!-- simple usage, content becomes then-child -->
* <v:condition.context.isFrontend>
* Hooray for BE contexts!
* </v:condition.context.isFrontend>
* <!-- extended use combined with f:then and f:else -->
* <v:condition.context.isFrontend>
* <f:then>
* Hooray for BE contexts!
* </f:then>
* <f:else>
* Maybe BE, maybe CLI.
* </f:else>
* </v:condition.context.isFrontend>
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers\If\Context
*/
class IsFrontendViewHelper extends AbstractConditionViewHelper {
use ConditionViewHelperTrait;
/**
* This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality.
*
* @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 ('FE' === TYPO3_MODE);
}
}

View File

@@ -0,0 +1,46 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Condition\Context;
/*
* 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\AbstractConditionViewHelper;
use FluidTYPO3\Vhs\Traits\ConditionViewHelperTrait;
/**
* ### Context: IsProduction
*
* Returns true if current root application context is production otherwise false.
* If no application context has been set, then this is the default context.
*
* #### Note about how to set the application context
*
* The context TYPO3 CMS runs in is specified through the environment variable TYPO3_CONTEXT.
* It can be set by .htaccess or in the server configuration
*
* See: http://docs.typo3.org/typo3cms/CoreApiReference/ApiOverview/Bootstrapping/Index.html#bootstrapping-context
*
* @author Benjamin Beck <beck@beckdigitalemedien.de>
* @package Vhs
* @subpackage ViewHelpers\Condition\Context
*/
class IsProductionViewHelper extends AbstractConditionViewHelper {
use ConditionViewHelperTrait;
/**
* This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality.
*
* @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 GeneralUtility::getApplicationContext()->isProduction();
}
}

View File

@@ -0,0 +1,46 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Condition\Context;
/*
* 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\AbstractConditionViewHelper;
use FluidTYPO3\Vhs\Traits\ConditionViewHelperTrait;
/**
* ### Context: IsProduction
*
* Returns true if current root application context is testing otherwise false.
* If no application context has been set, then the default context is production.
*
* #### Note about how to set the application context
*
* The context TYPO3 CMS runs in is specified through the environment variable TYPO3_CONTEXT.
* It can be set by .htaccess or in the server configuration
*
* See: http://docs.typo3.org/typo3cms/CoreApiReference/ApiOverview/Bootstrapping/Index.html#bootstrapping-context
*
* @author Benjamin Beck <beck@beckdigitalemedien.de>
* @package Vhs
* @subpackage ViewHelpers\Condition\Context
*/
class IsTestingViewHelper extends AbstractConditionViewHelper {
use ConditionViewHelperTrait;
/**
* This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality.
*
* @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 GeneralUtility::getApplicationContext()->isTesting();
}
}

View File

@@ -0,0 +1,125 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Condition\Form;
/*
* 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\Extbase\DomainObject\DomainObjectInterface;
use TYPO3\CMS\Extbase\Reflection\ReflectionService;
use TYPO3\CMS\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractConditionViewHelper;
use TYPO3\CMS\Fluid\Core\ViewHelper\ViewHelperVariableContainer;
use FluidTYPO3\Vhs\Traits\ConditionViewHelperTrait;
/**
* ### Form: Field Has Validator?
*
* Takes a property (dotted path supported) and renders the
* then-child if the property at the given path has any
* @validate annotation.
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers\If\Form
*/
class HasValidatorViewHelper extends AbstractConditionViewHelper {
use ConditionViewHelperTrait;
/**
* @var string
*/
const ALTERNATE_FORM_VIEWHELPER_CLASSNAME = 'TYPO3\\CMS\\Fluid\\ViewHelpers\\FormViewHelper';
/**
* @var ReflectionService
*/
static protected $staticReflectionService;
/**
* Render
*
* Renders the then-child if the property at $property of the
* object at $object (or the associated form object if $object
* is not specified) uses a certain @validate validator.
*
* @param string $property The property name, dotted path supported, to determine required
* @param string $validatorName The class name of the Validator that indicates the property is required
* @param DomainObjectInterface $object Optional object - if not specified, grabs the associated form object
* @return string
*/
public function render($property, $validatorName = NULL, DomainObjectInterface $object = NULL) {
return static::renderStatic(
$this->arguments,
$this->buildRenderChildrenClosure(),
$this->renderingContext
);
}
/**
* Default implementation for use in compiled templates
*
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return mixed
*/
static public function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext) {
if (self::$staticReflectionService === NULL) {
$objectManager = GeneralUtility::makeInstance('TYPO3\CMS\Extbase\Object\ObjectManager');
self::$staticReflectionService = $objectManager->get('TYPO3\CMS\Extbase\Reflection\ReflectionService');
}
$property = $arguments['property'];
$validatorName = isset($arguments['validatorName']) ? $arguments['validatorName'] : NULL;
$object = isset($arguments['object']) ? $arguments['object'] : NULL;
if (NULL === $object) {
$object = self::getFormObject($renderingContext->getViewHelperVariableContainer());
}
$className = get_class($object);
if (FALSE !== strpos($property, '.')) {
$pathSegments = explode('.', $property);
foreach ($pathSegments as $property) {
if (TRUE === ctype_digit($property)) {
continue;
}
$annotations = self::$staticReflectionService->getPropertyTagValues($className, $property, 'var');
$possibleClassName = array_pop($annotations);
if (FALSE !== strpos($possibleClassName, '<')) {
$className = array_pop(explode('<', trim($possibleClassName, '>')));
} elseif (TRUE === class_exists($possibleClassName)) {
$className = $possibleClassName;
}
}
}
$annotations = self::$staticReflectionService->getPropertyTagValues($className, $property, 'validate');
$hasEvaluated = TRUE;
if (0 < count($annotations) && (NULL === $validatorName || TRUE === in_array($validatorName, $annotations))) {
return static::renderStaticThenChild($arguments, $hasEvaluated);
}
return static::renderStaticElseChild($arguments, $hasEvaluated);
}
/**
* @param ViewHelperVariableContainer $viewHelperVariableContainer
* @param string $formClassName
* @return DomainObjectInterface|NULL
*/
static protected function getFormObject($viewHelperVariableContainer, $formClassName = 'Tx_Fluid_ViewHelpers_FormViewHelper') {
if (TRUE === $viewHelperVariableContainer->exists($formClassName, 'formObject')) {
return $viewHelperVariableContainer->get($formClassName, 'formObject');
}
if (self::ALTERNATE_FORM_VIEWHELPER_CLASSNAME !== $formClassName) {
return self::getFormObject($viewHelperVariableContainer, self::ALTERNATE_FORM_VIEWHELPER_CLASSNAME);
}
return NULL;
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Condition\Form;
/*
* 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\Extbase\DomainObject\DomainObjectInterface;
use TYPO3\CMS\Fluid\Core\Rendering\RenderingContextInterface;
/**
* ### Is Field Required ViewHelper (condition)
*
* Takes a property (dotted path supported) and renders the
* then-child if the property at the given path has an
* @validate NotEmpty annotation
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers\Condition\Form
*/
class IsRequiredViewHelper extends HasValidatorViewHelper {
/**
* Default implementation for use in compiled templates
*
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return mixed
*/
static public function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext) {
$arguments['validatorName'] = 'NotEmpty';
return parent::renderStatic($arguments, $renderChildrenClosure, $renderingContext);
}
}

View File

@@ -0,0 +1,177 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Condition\Iterator;
/*
* 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\Extbase\DomainObject\AbstractDomainObject;
use TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface;
use TYPO3\CMS\Extbase\Persistence\Generic\LazyObjectStorage;
use TYPO3\CMS\Extbase\Persistence\Generic\QueryResult;
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractConditionViewHelper;
use FluidTYPO3\Vhs\Traits\ConditionViewHelperTrait;
/**
* Condition ViewHelper. Renders the then-child if Iterator/array
* haystack contains needle value.
*
* ### Example:
*
* {v:condition.iterator.contains(needle: 'foo', haystack: {0: 'foo'}, then: 'yes', else: 'no')}
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers\Condition\Iterator
*/
class ContainsViewHelper extends AbstractConditionViewHelper {
use ConditionViewHelperTrait;
/**
* Initialize arguments
*/
public function initializeArguments() {
$this->registerArgument('needle', 'mixed', 'Needle to search for in haystack', TRUE);
$this->registerArgument('haystack', 'mixed', 'Haystack in which to look for needle', TRUE);
$this->registerArgument('considerKeys', 'boolean', 'Tell whether to consider keys in the search assuming haystack is an array.', FALSE, FALSE);
}
/**
* This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality.
*
* @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 FALSE !== self::assertHaystackHasNeedle($arguments['haystack'], $arguments['needle'], $arguments);;
}
/**
* @param integer $index
* @param array $arguments
* @return mixed
*/
static protected function getNeedleAtIndex($index, $arguments) {
if (0 > $index) {
return NULL;
}
$haystack = $arguments['haystack'];
$asArray = array();
if (TRUE === is_array($haystack)) {
$asArray = $haystack;
} elseif (TRUE === $haystack instanceof LazyObjectStorage) {
/** @var $haystack LazyObjectStorage */
$asArray = $haystack->toArray();
} elseif (TRUE === $haystack instanceof ObjectStorage) {
/** @var $haystack ObjectStorage */
$asArray = $haystack->toArray();
} elseif (TRUE === $haystack instanceof QueryResult) {
/** @var $haystack QueryResult */
$asArray = $haystack->toArray();
} elseif (TRUE === is_string($haystack)) {
$asArray = str_split($haystack);
}
return (TRUE === isset($asArray[$index]) ? $asArray[$index] : FALSE);
}
/**
* @param mixed $haystack
* @param mixed $needle
* @param array $arguments
* @return boolean|integer
*/
static protected function assertHaystackHasNeedle($haystack, $needle, $arguments) {
if (TRUE === is_array($haystack)) {
return self::assertHaystackIsArrayAndHasNeedle($haystack, $needle, $arguments);
} elseif ($haystack instanceof LazyObjectStorage) {
return self::assertHaystackIsObjectStorageAndHasNeedle($haystack, $needle);
} elseif ($haystack instanceof ObjectStorage) {
return self::assertHaystackIsObjectStorageAndHasNeedle($haystack, $needle);
} elseif ($haystack instanceof QueryResult) {
return self::assertHaystackIsQueryResultAndHasNeedle($haystack, $needle);
} elseif (TRUE === is_string($haystack)) {
return strpos($haystack, $needle);
}
return FALSE;
}
/**
* @param mixed $haystack
* @param mixed $needle
* @return boolean|integer
*/
static protected function assertHaystackIsQueryResultAndHasNeedle($haystack, $needle) {
if (TRUE === $needle instanceof DomainObjectInterface) {
/** @var $needle DomainObjectInterface */
$needle = $needle->getUid();
}
foreach ($haystack as $index => $candidate) {
/** @var $candidate DomainObjectInterface */
if ((integer) $candidate->getUid() === (integer) $needle) {
return $index;
}
}
return FALSE;
}
/**
* @param mixed $haystack
* @param mixed $needle
* @return boolean|integer
*/
static protected function assertHaystackIsObjectStorageAndHasNeedle($haystack, $needle) {
$index = 0;
/** @var $candidate DomainObjectInterface */
if (TRUE === $needle instanceof AbstractDomainObject) {
$needle = $needle->getUid();
}
foreach ($haystack as $candidate) {
if ((integer) $candidate->getUid() === (integer) $needle) {
return $index;
}
$index++;
}
return FALSE;
}
/**
* @param mixed $haystack
* @param mixed $needle
* @param array $arguments
* @return boolean|integer
*/
static protected function assertHaystackIsArrayAndHasNeedle($haystack, $needle, $arguments) {
if (FALSE === $needle instanceof DomainObjectInterface) {
if (TRUE === (boolean) $arguments['considerKeys']) {
$result = (boolean) (FALSE !== array_search($needle, $haystack) || TRUE === isset($haystack[$needle]));
} else {
$result = array_search($needle, $haystack);
}
return $result;
} else {
/** @var $needle DomainObjectInterface */
foreach ($haystack as $index => $straw) {
/** @var $straw DomainObjectInterface */
if ((integer) $straw->getUid() === (integer) $needle->getUid()) {
return $index;
}
}
}
return FALSE;
}
/**
* @param mixed $haystack
* @param mixed $needle
* @return boolean|integer
*/
static protected function assertHaystackIsStringAndHasNeedle($haystack, $needle) {
return strpos($haystack, $needle);
}
}

View File

@@ -0,0 +1,81 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Condition\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\AbstractConditionViewHelper;
use FluidTYPO3\Vhs\Traits\ConditionViewHelperTrait;
/**
* ### Condition: Page has subpages
*
* A condition ViewHelper which renders the `then` child if
* current page or page with provided UID has subpages. By default
* disabled subpages are considered non existent which can be overridden
* by setting $includeHidden to TRUE. To include pages that are hidden
* in menus set $showHiddenInMenu to TRUE.
*
* @author Björn Fromme <fromme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Condition\Page
*/
class HasSubpagesViewHelper extends AbstractConditionViewHelper {
use ConditionViewHelperTrait;
/**
* @var PageSelectService
*/
static protected $pageSelect;
/**
* @param PageSelectService $pageSelect
* @return void
*/
static public function setPageSelectService(PageSelectService $pageSelect) {
self::$pageSelect = $pageSelect;
}
/**
* Initialize arguments
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerArgument('pageUid', 'integer', 'value to check', FALSE, NULL);
$this->registerArgument('includeHidden', 'boolean', 'include hidden pages', FALSE, FALSE);
$this->registerArgument('showHiddenInMenu', 'boolean', 'include pages hidden in menu', FALSE, FALSE);
}
/**
* This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality.
*
* @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) {
$pageUid = $arguments['pageUid'];
$includeHidden = $arguments['includeHidden'];
$showHiddenInMenu = $arguments['showHiddenInMenu'];
if (NULL === $pageUid || TRUE === empty($pageUid) || 0 === intval($pageUid)) {
$pageUid = $GLOBALS['TSFE']->id;
}
if (self::$pageSelect === NULL) {
$objectManager = GeneralUtility::makeInstance('TYPO3\CMS\Extbase\Object\ObjectManager');
self::$pageSelect = $objectManager->get('FluidTYPO3\Vhs\Service\PageSelectService');
}
$menu = self::$pageSelect->getMenu($pageUid, array(), '', $showHiddenInMenu);
$pageHasSubPages = (0 < count($menu));
return TRUE === $pageHasSubPages;
}
}

View File

@@ -0,0 +1,62 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Condition\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\AbstractConditionViewHelper;
use TYPO3\CMS\Frontend\Page\PageRepository;
use FluidTYPO3\Vhs\Traits\ConditionViewHelperTrait;
/**
* ### Condition: Page is child page
*
* Condition ViewHelper which renders the `then` child if current
* page or page with provided UID is a child of some other page in
* the page tree. If $respectSiteRoot is set to TRUE root pages are
* never considered child pages even if they are.
*
* @author Björn Fromme <fromme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Condition\Page
*/
class IsChildPageViewHelper extends AbstractConditionViewHelper {
use ConditionViewHelperTrait;
/**
* Initialize arguments
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerArgument('pageUid', 'integer', 'value to check', FALSE, NULL);
$this->registerArgument('respectSiteRoot', 'boolean', 'value to check', FALSE, FALSE);
}
/**
* This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality.
*
* @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) {
$pageUid = $arguments['pageUid'];
$respectSiteRoot = $arguments['respectSiteRoot'];
if (NULL === $pageUid || TRUE === empty($pageUid) || 0 === intval($pageUid)) {
$pageUid = $GLOBALS['TSFE']->id;
}
$pageSelect = new PageRepository();
$page = $pageSelect->getPage($pageUid);
if (TRUE === (boolean) $respectSiteRoot && TRUE === isset($page['is_siteroot']) && TRUE === (boolean) $page['is_siteroot']) {
return FALSE;
}
return TRUE === isset($page['pid']) && 0 < $page['pid'];
}
}

View File

@@ -0,0 +1,68 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Condition\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\AbstractConditionViewHelper;
use FluidTYPO3\Vhs\Traits\ConditionViewHelperTrait;
/**
* ### Condition: Is current language
*
* A condition ViewHelper which renders the `then` child if
* current language matches the provided language uid or language
* title. When using language titles like 'de' it is required to
* provide a default title to distinguish between the standard
* and a non existing language.
*
* @author Björn Fromme <fromme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Condition\Page
*/
class IsLanguageViewHelper extends AbstractConditionViewHelper {
use ConditionViewHelperTrait;
/**
* Initialize arguments
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerArgument('language', 'string', 'language to check', TRUE);
$this->registerArgument('defaultTitle', 'string', 'title of the default language', FALSE, 'en');
}
/**
* This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality.
*
* @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) {
$language = $arguments['language'];
$defaultTitle = $arguments['defaultTitle'];
$currentLanguageUid = $GLOBALS['TSFE']->sys_language_uid;
if (TRUE === is_numeric($language)) {
$languageUid = intval($language);
} else {
$row = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('uid', 'sys_language', "title='" . $language . "'");
if (FALSE !== $row) {
$languageUid = intval($row['uid']);
} else {
if ((string) $language === $defaultTitle) {
$languageUid = $currentLanguageUid;
} else {
$languageUid = -1;
}
}
}
return $languageUid === $currentLanguageUid;
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Condition\String;
/*
* 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\AbstractConditionViewHelper;
use FluidTYPO3\Vhs\Traits\ConditionViewHelperTrait;
/**
* ### Condition: String contains substring
*
* Condition ViewHelper which renders the `then` child if provided
* string $haystack contains provided string $needle.
*
* @author Björn Fromme <fromme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Condition\String
*/
class ContainsViewHelper extends AbstractConditionViewHelper {
use ConditionViewHelperTrait;
/**
* Initialize arguments
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerArgument('haystack', 'string', 'haystack', TRUE);
$this->registerArgument('needle', 'string', 'need', TRUE);
}
/**
* This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality.
*
* @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 FALSE !== strpos($arguments['haystack'], $arguments['needle']);
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Condition\String;
/*
* 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\AbstractConditionViewHelper;
use FluidTYPO3\Vhs\Traits\ConditionViewHelperTrait;
/**
* ### Condition: String is lowercase
*
* Condition ViewHelper which renders the `then` child if provided
* string is lowercase. By default only the first letter is tested.
* To test the full string set $fullString to TRUE.
*
* @author Björn Fromme <fromme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Condition\String
*/
class IsLowercaseViewHelper extends AbstractConditionViewHelper {
use ConditionViewHelperTrait;
/**
* Initialize arguments
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerArgument('string', 'string', 'string to check', TRUE);
$this->registerArgument('fullString', 'string', 'need', FALSE, FALSE);
}
/**
* This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality.
*
* @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) {
if (TRUE === $arguments['fullString']) {
$result = ctype_lower($arguments['string']);
} else {
$result = ctype_lower(substr($arguments['string'], 0, 1));
}
return TRUE === $result;
}
}

View File

@@ -0,0 +1,46 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Condition\String;
/*
* 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\AbstractConditionViewHelper;
use FluidTYPO3\Vhs\Traits\ConditionViewHelperTrait;
/**
* ### Condition: Value is numeric
*
* Condition ViewHelper which renders the `then` child if provided
* value is numeric.
*
* @author Björn Fromme <fromme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Condition\String
*/
class IsNumericViewHelper extends AbstractConditionViewHelper {
use ConditionViewHelperTrait;
/**
* Initialize arguments
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerArgument('value', 'mixed', 'value to check', TRUE);
}
/**
* This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality.
*
* @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 TRUE === ctype_digit($arguments['value']);
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Condition\String;
/*
* 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\AbstractConditionViewHelper;
use FluidTYPO3\Vhs\Traits\ConditionViewHelperTrait;
/**
* ### Condition: String is lowercase
*
* Condition ViewHelper which renders the `then` child if provided
* string is uppercase. By default only the first letter is tested.
* To test the full string set $fullString to TRUE.
*
* @author Björn Fromme <fromme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Condition\String
*/
class IsUppercaseViewHelper extends AbstractConditionViewHelper {
use ConditionViewHelperTrait;
/**
* Initialize arguments
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerArgument('string', 'string', 'string to check', TRUE);
$this->registerArgument('fullString', 'string', 'need', FALSE, FALSE);
}
/**
* This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality.
*
* @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) {
if (TRUE === $arguments['fullString']) {
$result = ctype_upper($arguments['string']);
} else {
$result = ctype_upper(substr($arguments['string'], 0, 1));
}
return TRUE === $result;
}
}

View File

@@ -0,0 +1,104 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Condition\String;
/*
* 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\TemplateVariableViewHelperTrait;
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractConditionViewHelper;
use FluidTYPO3\Vhs\Traits\ConditionViewHelperTrait;
/**
* ### Condition: String matches regular expression
*
* Condition ViewHelper which renders the `then` child if provided
* string matches provided regular expression. $matches array containing
* the results can be made available by providing a template variable
* name with argument $as.
*
* @author Björn Fromme <fromme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Condition\String
*/
class PregViewHelper extends AbstractConditionViewHelper {
use TemplateVariableViewHelperTrait;
use ConditionViewHelperTrait;
/**
* Initialize arguments
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerArgument('pattern', 'string', 'regex pattern to match string against', TRUE);
$this->registerArgument('string', 'string', 'string to match with the regex pattern', TRUE);
$this->registerArgument('global', 'boolean', 'match global', FALSE, FALSE);
}
/**
* This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality.
*
* @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) {
$matches = array();
if (TRUE === (boolean) $arguments['global']) {
preg_match_all($arguments['pattern'], $arguments['string'], $matches, PREG_SET_ORDER);
} else {
preg_match($arguments['pattern'], $arguments['string'], $matches);
}
return 0 < count($matches);
}
/**
* 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)) {
$content = $this->renderThenChild();
} else {
$content = $this->renderElseChild();
}
return $this->renderChildrenWithVariableOrReturnInput($content);
}
/**
* 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;
$content = '';
if (static::evaluateCondition($arguments)) {
$result = static::renderStaticThenChild($arguments, $hasEvaluated);
if ($hasEvaluated) {
$content = $result;
}
} else {
$result = static::renderStaticElseChild($arguments, $hasEvaluated);
if ($hasEvaluated) {
$content = $result;
}
}
return self::renderChildrenWithVariableOrReturnInputStatic($content, $arguments['as'], $renderingContext, $renderChildrenClosure);
}
}

View File

@@ -0,0 +1,46 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Condition\Type;
/*
* 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\AbstractConditionViewHelper;
use FluidTYPO3\Vhs\Traits\ConditionViewHelperTrait;
/**
* ### Condition: Type of value is array
*
* Condition ViewHelper which renders the `then` child if type of
* provided value is array.
*
* @author Björn Fromme <fromme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Condition\Type
*/
class IsArrayViewHelper extends AbstractConditionViewHelper {
use ConditionViewHelperTrait;
/**
* Initialize arguments
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerArgument('value', 'string', 'value to check', TRUE);
}
/**
* This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality.
*
* @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['value']) && (TRUE === is_array($arguments['value'])));
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Condition\Type;
/*
* 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\AbstractConditionViewHelper;
use FluidTYPO3\Vhs\Traits\ConditionViewHelperTrait;
/**
* ### Condition: Type of value is a boolean
*
* Condition ViewHelper which renders the `then` child if type of
* provided value is a boolean.
*
* @package Vhs
* @subpackage ViewHelpers\Condition\Type
*/
class IsBooleanViewHelper extends AbstractConditionViewHelper {
use ConditionViewHelperTrait;
/**
* Initialize arguments
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerArgument('value', 'mixed', 'value to check', TRUE);
}
/**
* This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality.
*
* @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 TRUE === is_bool($arguments['value']);
}
}

View File

@@ -0,0 +1,49 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Condition\Type;
/*
* 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\Extbase\DomainObject\AbstractDomainObject;
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractConditionViewHelper;
use FluidTYPO3\Vhs\Traits\ConditionViewHelperTrait;
/**
* ### Condition: Value is a domain object
*
* Condition ViewHelper which renders the `then` child if provided
* value is a domain object, i.e. it inherits from extbase's base
* class
*
* @author Björn Fromme <fromme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Condition\Type
*/
class IsDomainObjectViewHelper extends AbstractConditionViewHelper {
use ConditionViewHelperTrait;
/**
* Initialize arguments
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerArgument('value', 'mixed', 'value to check', TRUE);
$this->registerArgument('fullString', 'string', 'need', FALSE, FALSE);
}
/**
* This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality.
*
* @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 TRUE === $arguments['value'] instanceof AbstractDomainObject;
}
}

View File

@@ -0,0 +1,46 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Condition\Type;
/*
* 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\AbstractConditionViewHelper;
use FluidTYPO3\Vhs\Traits\ConditionViewHelperTrait;
/**
* ### Condition: Type of value is float
*
* Condition ViewHelper which renders the `then` child if type of
* provided value is float.
*
* @author Björn Fromme <fromme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Condition\Type
*/
class IsFloatViewHelper extends AbstractConditionViewHelper {
use ConditionViewHelperTrait;
/**
* Initialize arguments
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerArgument('value', 'mixed', 'value to check', TRUE);
}
/**
* This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality.
*
* @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 TRUE === is_float($arguments['value']);
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Condition\Type;
/*
* 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\AbstractConditionViewHelper;
use FluidTYPO3\Vhs\Traits\ConditionViewHelperTrait;
/**
* ### Condition: Value is an instance of a class
*
* Condition ViewHelper which renders the `then` child if provided
* value is an instance of provided class name.
*
* @author Björn Fromme <fromme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Condition\Type
*/
class IsInstanceOfViewHelper extends AbstractConditionViewHelper {
use ConditionViewHelperTrait;
/**
* Initialize arguments
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerArgument('value', 'mixed', 'value to check', TRUE);
$this->registerArgument('class', 'mixed', 'className to check against', TRUE);
}
/**
* This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality.
*
* @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 TRUE === $arguments['value'] instanceof $arguments['class'];
}
}

View File

@@ -0,0 +1,46 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Condition\Type;
/*
* 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\AbstractConditionViewHelper;
use FluidTYPO3\Vhs\Traits\ConditionViewHelperTrait;
/**
* ### Condition: Type of value is integer
*
* Condition ViewHelper which renders the `then` child if type of
* provided value is integer.
*
* @author Björn Fromme <fromme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Condition\Type
*/
class IsIntegerViewHelper extends AbstractConditionViewHelper {
use ConditionViewHelperTrait;
/**
* Initialize arguments
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerArgument('value', 'mixed', 'value to check', TRUE);
}
/**
* This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality.
*
* @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 TRUE === is_integer($arguments['value']);
}
}

View File

@@ -0,0 +1,46 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Condition\Type;
/*
* 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\AbstractConditionViewHelper;
use FluidTYPO3\Vhs\Traits\ConditionViewHelperTrait;
/**
* ### Condition: Value is an object
*
* Condition ViewHelper which renders the `then` child if provided
* value is an object.
*
* @author Björn Fromme <fromme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Type\Type
*/
class IsObjectViewHelper extends AbstractConditionViewHelper {
use ConditionViewHelperTrait;
/**
* Initialize arguments
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerArgument('value', 'mixed', 'value to check', TRUE);
}
/**
* This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality.
*
* @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 TRUE === is_object($arguments['value']);
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Condition\Type;
/*
* 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\Extbase\Persistence\QueryResultInterface;
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractConditionViewHelper;
use FluidTYPO3\Vhs\Traits\ConditionViewHelperTrait;
/**
* ### Condition: Value is a query result
*
* Condition ViewHelper which renders the `then` child if provided
* value is an extbase query result
*
* @author Björn Fromme <fromme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Condition\Type
*/
class IsQueryResultViewHelper extends AbstractConditionViewHelper {
use ConditionViewHelperTrait;
/**
* Initialize arguments
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerArgument('value', 'mixed', 'value to check', TRUE);
}
/**
* This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality.
*
* @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 TRUE === $arguments['value'] instanceof QueryResultInterface;
}
}

View File

@@ -0,0 +1,46 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Condition\Type;
/*
* 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\AbstractConditionViewHelper;
use FluidTYPO3\Vhs\Traits\ConditionViewHelperTrait;
/**
* ### Condition: Type of value is string
*
* Condition ViewHelper which renders the `then` child if type of
* provided value is string.
*
* @author Björn Fromme <fromme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Condition\Type
*/
class IsStringViewHelper extends AbstractConditionViewHelper {
use ConditionViewHelperTrait;
/**
* Initialize arguments
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerArgument('value', 'mixed', 'value to check', TRUE);
}
/**
* This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality.
*
* @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 TRUE === is_string($arguments['value']);
}
}

View File

@@ -0,0 +1,46 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Condition\Type;
/*
* 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\AbstractConditionViewHelper;
use FluidTYPO3\Vhs\Traits\ConditionViewHelperTrait;
/**
* ### Condition: Value implements interface Traversable
*
* Condition ViewHelper which renders the `then` child if provided
* value implements interface Traversable
*
* @author Björn Fromme <fromme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Condition\Type
*/
class IsTraversableViewHelper extends AbstractConditionViewHelper {
use ConditionViewHelperTrait;
/**
* Initialize arguments
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerArgument('value', 'mixed', 'value to check', TRUE);
}
/**
* This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality.
*
* @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 TRUE === $arguments['value'] instanceof \Traversable;
}
}

View File

@@ -0,0 +1,46 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Condition\Variable;
/*
* 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\AbstractConditionViewHelper;
use FluidTYPO3\Vhs\Traits\ConditionViewHelperTrait;
/**
* ### Condition: Value is NULL
*
* Condition ViewHelper which renders the `then` child if provided
* value is NULL.
*
* @author Björn Fromme <fromme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\If\Var
*/
class IsNullViewHelper extends AbstractConditionViewHelper {
use ConditionViewHelperTrait;
/**
* Initialize arguments
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerArgument('value', 'string', 'value to check', TRUE);
}
/**
* This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality.
*
* @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 NULL === $arguments['value'];
}
}

View File

@@ -0,0 +1,88 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Condition\Variable;
/*
* 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\Rendering\RenderingContextInterface;
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractConditionViewHelper;
use FluidTYPO3\Vhs\Traits\ConditionViewHelperTrait;
/**
* ### Variable: Isset
*
* Renders the `then` child if the variable name given in
* the `name` argument exists in the template. The value
* can be zero, NULL or an empty string - but the ViewHelper
* will still return TRUE if the variable exists.
*
* Combines well with dynamic variable names:
*
* <!-- if {variableContainingVariableName} is "foo" this checks existence of {foo} -->
* <v:condition.variable.isset name="{variableContainingVariableName}">...</v:condition.variable.isset>
* <!-- if {suffix} is "Name" this checks existence of "variableName" -->
* <v:condition.variable.isset name="variable{suffix}">...</v:condition.variable.isset>
* <!-- outputs value of {foo} if {bar} is defined -->
* {foo -> v:condition.variable.isset(name: bar)}
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers\Condition\Variable
*/
class IssetViewHelper extends AbstractConditionViewHelper {
use ConditionViewHelperTrait;
/**
* Initialize arguments
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerArgument('name', 'string', 'name of the variable', TRUE);
}
/**
* Render
*
* @return string
*/
public function render() {
return static::renderStatic(
$this->arguments,
$this->buildRenderChildrenClosure(),
$this->renderingContext
);
}
/**
* Default implementation for use in compiled templates
*
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param \TYPO3\CMS\Fluid\Core\Rendering\RenderingContextInterface $renderingContext
* @return mixed
*/
static public function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext) {
$hasEvaluated = TRUE;
if (TRUE === $renderingContext->getTemplateVariableContainer()->exists($arguments['name'])) {
$result = static::renderStaticThenChild($arguments, $hasEvaluated);
if ($hasEvaluated) {
return $result;
}
return $renderChildrenClosure();
} else {
$result = static::renderStaticElseChild($arguments, $hasEvaluated);
if ($hasEvaluated) {
return $result;
}
}
return '';
}
}

View File

@@ -0,0 +1,199 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Content;
/*
* 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\SlideViewHelperTrait;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper;
/**
* ### Base class: Content ViewHelpers
*
* @author Claus Due <claus@namelesscoder.net>
* @author Dominique Feyer, <dfeyer@ttree.ch>
* @author Daniel Schöne, <daniel@schoene.it>
* @author Björn Fromme, <fromme@dreipunktnull.com>, dreipunktnull
* @author Danilo Bürger <danilo.buerger@hmspl.de>, Heimspiel GmbH
* @package Vhs
* @subpackage ViewHelpers\Content
*/
abstract class AbstractContentViewHelper extends AbstractViewHelper {
use SlideViewHelperTrait;
/**
* @var \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
*/
protected $contentObject;
/**
* @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
*/
protected $configurationManager;
/**
* @param \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager
* @return void
*/
public function injectConfigurationManager(ConfigurationManagerInterface $configurationManager) {
$this->configurationManager = $configurationManager;
$this->contentObject = $configurationManager->getContentObject();
}
/**
* Initialize
*/
public function initializeArguments() {
$this->registerArgument('column', 'integer', 'Name of the column to render', FALSE, 0);
$this->registerArgument('order', 'string', 'Optional sort field of content elements - RAND() supported. Note that when sliding is enabled, the sorting will be applied to records on a per-page basis and not to the total set of collected records.', FALSE, 'sorting');
$this->registerArgument('sortDirection', 'string', 'Optional sort direction of content elements', FALSE, 'ASC');
$this->registerArgument('pageUid', 'integer', 'If set, selects only content from this page UID', FALSE, 0);
$this->registerArgument('contentUids', 'array', 'If used, replaces all conditions with an "uid IN (1,2,3)" style condition using the UID values from this array');
$this->registerArgument('sectionIndexOnly', 'boolean', 'If TRUE, only renders/gets content that is marked as "include in section index"', FALSE, FALSE);
$this->registerArgument('loadRegister', 'array', 'List of LOAD_REGISTER variable');
$this->registerArgument('render', 'boolean', 'Optional returning variable as original table rows', FALSE, TRUE);
$this->registerArgument('hideUntranslated', 'boolean', 'If FALSE, will NOT include elements which have NOT been translated, if current language is NOT the default language. Default is to show untranslated elements but never display the original if there is a translated version', FALSE, FALSE);
$this->registerSlideArguments();
}
/**
* Get content records based on column and pid
*
* @return array
*/
protected function getContentRecords() {
$limit = $this->arguments['limit'];
$pageUid = $this->getPageUid();
$contentRecords = $this->getSlideRecords($pageUid, $limit);
if (TRUE === (boolean) $this->arguments['render']) {
$contentRecords = $this->getRenderedRecords($contentRecords);
}
return $contentRecords;
}
/**
* @param integer $pageUid
* @param integer $limit
* @return array[]
*/
protected function getSlideRecordsFromPage($pageUid, $limit) {
$column = (integer) $this->arguments['column'];
$order = $this->arguments['order'];
if (FALSE === empty($order)) {
$sortDirection = strtoupper(trim($this->arguments['sortDirection']));
if ('ASC' !== $sortDirection && 'DESC' !== $sortDirection) {
$sortDirection = 'ASC';
}
$order = $order . ' ' . $sortDirection;
}
$contentUids = $this->arguments['contentUids'];
if (TRUE === is_array($contentUids)) {
$conditions = 'uid IN (' . implode(',', $contentUids) . ')';
} else {
$hideUntranslated = (boolean) $this->arguments['hideUntranslated'];
$currentLanguage = $GLOBALS['TSFE']->sys_language_content;
$languageCondition = '(sys_language_uid IN (-1,' . $currentLanguage . ')';
if (0 < $currentLanguage) {
if (TRUE === $hideUntranslated) {
$languageCondition .= ' AND l18n_parent > 0';
}
$nestedQuery = $GLOBALS['TYPO3_DB']->SELECTquery('l18n_parent', 'tt_content', 'sys_language_uid = ' .
$currentLanguage . $GLOBALS['TSFE']->cObj->enableFields('tt_content'));
$languageCondition .= ' AND uid NOT IN (' . $nestedQuery . ')';
}
$languageCondition .= ')';
$conditions = "pid = '" . (integer) $pageUid . "' AND colPos = '" . (integer) $column . "'" .
$GLOBALS['TSFE']->cObj->enableFields('tt_content') . ' AND ' . $languageCondition;
}
if (TRUE === (boolean) $this->arguments['sectionIndexOnly']) {
$conditions .= ' AND sectionIndex = 1';
}
$rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('*', 'tt_content', $conditions, '', $order, $limit);
return $rows;
}
/**
* Gets the configured, or the current page UID if
* none is configured in arguments and no content_from_pid
* value exists in the current page record's attributes.
*
* @return integer
*/
protected function getPageUid() {
$pageUid = (integer) $this->arguments['pageUid'];
if (1 > $pageUid) {
$pageUid = (integer) $GLOBALS['TSFE']->page['content_from_pid'];
}
if (1 > $pageUid) {
$pageUid = (integer) $GLOBALS['TSFE']->id;
}
return $pageUid;
}
/**
* This function renders an array of tt_content record into an array of rendered content
* it returns a list of elements rendered by typoscript RECORD function
*
* @param array $rows database rows of records (each item is a tt_content table record)
* @return array
*/
protected function getRenderedRecords(array $rows) {
if (FALSE === empty($this->arguments['loadRegister'])) {
$this->contentObject->cObjGetSingle('LOAD_REGISTER', $this->arguments['loadRegister']);
}
$elements = array();
foreach ($rows as $row) {
array_push($elements, $this->renderRecord($row));
}
if (FALSE === empty($this->arguments['loadRegister'])) {
$this->contentObject->cObjGetSingle('RESTORE_REGISTER', '');
}
return $elements;
}
/**
* This function renders a raw tt_content record into the corresponding
* element by typoscript RENDER function. We keep track of already
* rendered records to avoid rendering the same record twice inside the
* same nested stack of content elements.
*
* @param array $row
* @return string|NULL
*/
protected function renderRecord(array $row) {
if (0 < $GLOBALS['TSFE']->recordRegister['tt_content:' . $row['uid']]) {
return NULL;
}
$conf = array(
'tables' => 'tt_content',
'source' => $row['uid'],
'dontCheckPid' => 1
);
$parent = $GLOBALS['TSFE']->currentRecord;
// If the currentRecord is set, we register, that this record has invoked this function.
// It's should not be allowed to do this again then!!
if (FALSE === empty($parent)) {
++$GLOBALS['TSFE']->recordRegister[$parent];
}
$html = $GLOBALS['TSFE']->cObj->cObjGetSingle('RECORDS', $conf);
$GLOBALS['TSFE']->currentRecord = $parent;
if (FALSE === empty($parent)) {
--$GLOBALS['TSFE']->recordRegister[$parent];
}
return $html;
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Content;
/*
* 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.
*/
/**
* ViewHelper used to render content elements in Fluid page templates
*
* @author Claus Due <claus@namelesscoder.net>
* @author Dominique Feyer, <dfeyer@ttree.ch>
* @author Daniel Schöne, <daniel@schoene.it>
* @author Björn Fromme, <fromme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Content
*/
class GetViewHelper extends AbstractContentViewHelper {
/**
* @return void
*/
public function initializeArguments() {
parent::initializeArguments();
$this->overrideArgument('render', 'boolean', 'Optional returning variable as original table rows', FALSE, FALSE);
}
/**
* Render method
*
* @return mixed
*/
public function render() {
if ('BE' === TYPO3_MODE) {
return '';
}
$contentRecords = $this->getContentRecords();
return $contentRecords;
}
}

View File

@@ -0,0 +1,92 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Content;
/*
* 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\TemplateVariableViewHelperTrait;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper;
/**
* ViewHelper to access data of the current content element record
*
* @author Benjamin Rau <rau@codearts.at>
* @package Vhs
* @subpackage ViewHelpers\Content
*/
class InfoViewHelper extends AbstractViewHelper {
use TemplateVariableViewHelperTrait;
/**
* @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
*/
protected $configurationManager;
/**
* @param \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface An instance of the Configuration Manager
* @return void
*/
public function injectConfigurationManager(ConfigurationManagerInterface $configurationManager) {
$this->configurationManager = $configurationManager;
}
/**
* @return void
*/
public function initializeArguments() {
$this->registerAsArgument();
$this->registerArgument('contentUid', 'integer', 'If specified, this UID will be used to fetch content element data instead of using the current content element.', FALSE, 0);
$this->registerArgument('field', 'string', 'If specified, only this field will be returned/assigned instead of the complete content element record.', FALSE, NULL);
}
/**
* @return mixed
* @throws \Exception
*/
public function render() {
$contentUid = intval($this->arguments['contentUid']);
if (0 === $contentUid) {
$cObj = $this->configurationManager->getContentObject();
$record = $cObj->data;
}
$field = $this->arguments['field'];
if (FALSE === isset($record) && 0 !== $contentUid) {
if (NULL !== $field && TRUE === isset($GLOBALS['TCA']['tt_content']['columns'][$field])) {
$selectFields = $field;
} else {
$selectFields = '*';
}
$record = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow($selectFields, 'tt_content', sprintf('uid=%d', $contentUid));
// Add the page overlay
$languageUid = intval($GLOBALS['TSFE']->sys_language_uid);
if (0 !== $languageUid && $GLOBALS['TSFE']->sys_language_contentOL) {
$record = $GLOBALS['TSFE']->sys_page->getRecordOverlay('tt_content', $record, $GLOBALS['TSFE']->sys_language_content, $GLOBALS['TSFE']->sys_language_contentOL);
}
}
if (FALSE === $record && FALSE === isset($record)) {
throw new \Exception(sprintf('Either record with uid %d or field %s do not exist.', $contentUid, $selectFields), 1358679983);
}
// Check if single field or whole record should be returned
$content = NULL;
if (NULL === $field) {
$content = $record;
} elseif (TRUE === isset($record[$field])) {
$content = $record[$field];
}
return $this->renderChildrenWithVariableOrReturnInput($content);
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Content\Random;
/*
* 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\Content\Random\RenderViewHelper;
/**
* ViewHelper for fetching a random content element in Fluid page templates
*
* @author Björn Fromme, <fromme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Content\Random
*/
class GetViewHelper extends RenderViewHelper {
/**
* Initialize ViewHelper arguments
*/
public function initializeArguments() {
parent::initializeArguments();
$this->overrideArgument('render', 'boolean', 'Optional returning variable as original table rows', FALSE, FALSE);
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Content\Random;
/*
* 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\Content\AbstractContentViewHelper;
/**
* ViewHelper for rendering a random content element in Fluid page templates
*
* @author Björn Fromme, <fromme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Content\Random
*/
class RenderViewHelper extends AbstractContentViewHelper {
/**
* Initialize ViewHelper arguments
*/
public function initializeArguments() {
parent::initializeArguments();
$this->overrideArgument('limit', 'integer', 'Optional limit to the number of content elements to render', FALSE, 1);
}
/**
* Render method
*
* @return mixed
*/
public function render() {
if ('BE' === TYPO3_MODE) {
return '';
}
// Remove limit for getContentRecords()
$limit = $this->arguments['limit'];
$this->arguments['limit'] = NULL;
// Just using getContentRecords with a limit of 1 would not support
// using slideCollect as collecting would stop as soon as one record
// was found. As a potential optimization, $render could be overrided
// so all the content records that end up unused do not get rendered.
$contentRecords = $this->getContentRecords();
if (FALSE === empty($contentRecords)) {
shuffle($contentRecords);
$contentRecords = array_slice($contentRecords, 0, $limit);
if (TRUE === (boolean) $this->arguments['render']) {
$contentRecords = implode(LF, $contentRecords);
}
}
return $contentRecords;
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Content;
/*
* 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\TemplateVariableViewHelperTrait;
/**
* ViewHelper used to render content elements in Fluid page templates
*
* @author Claus Due <claus@namelesscoder.net>
* @author Dominique Feyer, <dfeyer@ttree.ch>
* @author Daniel Schöne, <daniel@schoene.it>
* @author Björn Fromme, <fromme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Content
*/
class RenderViewHelper extends AbstractContentViewHelper {
use TemplateVariableViewHelperTrait;
/**
* @return void
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerAsArgument();
}
/**
* Render method
*
* @return string
*/
public function render() {
if ('BE' === TYPO3_MODE) {
return '';
}
$content = $this->getContentRecords();
if (FALSE === $this->hasArgument('as')) {
$content = implode(LF, $content);
}
return $this->renderChildrenWithVariableOrReturnInput($content);
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Content\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.
*/
/**
* @author Danilo Bürger <danilo.buerger@hmspl.de>, Heimspiel GmbH
* @package Vhs
* @subpackage ViewHelpers\Content\Resources
*/
class FalViewHelper extends \FluidTYPO3\Vhs\ViewHelpers\Resource\Record\FalViewHelper {
const defaultTable = 'tt_content';
const defaultField = 'image';
/**
* @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,46 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Content;
/*
* 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\Content
*/
class ResourcesViewHelper extends RecordViewHelper {
const defaultTable = 'tt_content';
const defaultField = 'image';
/**
* @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,42 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Context;
/*
* 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;
/**
* ### Context: Get
*
* Returns the current application context which may include possible sub-contexts.
* The application context can be 'Production', 'Development' or 'Testing'.
* Additionally each context can be extended with custom sub-contexts like: 'Production/Staging' or ' Production/Staging/Server1'.
* If no application context has been set by the configuration, then the default context is 'Production'.
*
* #### Note about how to set the application context
*
* The context TYPO3 CMS runs in is specified through the environment variable TYPO3_CONTEXT.
* It can be set by .htaccess or in the server configuration
*
* See: http://docs.typo3.org/typo3cms/CoreApiReference/ApiOverview/Bootstrapping/Index.html#bootstrapping-context
*
* @author Benjamin Beck <beck@beckdigitalemedien.de>
* @package Vhs
* @subpackage ViewHelpers\Context
*/
class GetViewHelper extends AbstractViewHelper {
/**
* @return string
*/
public function render () {
return (string) GeneralUtility::getApplicationContext();
}
}

View File

@@ -0,0 +1,168 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers;
/*
* 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\Extbase\Reflection\Exception\PropertyNotAccessibleException;
use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
use TYPO3\CMS\Extbase\Utility\DebuggerUtility;
use TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\ObjectAccessorNode;
use TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\ViewHelperNode;
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3\CMS\Fluid\Core\ViewHelper\Facets\ChildNodeAccessInterface;
/**
* ### ViewHelper Debug ViewHelper (sic)
*
* Debugs instances of other ViewHelpers and language
* structures. Use in conjunction with other ViewHelpers
* to inspect their current and possible arguments and
* render their documentation:
*
* <v:debug><f:format.html>{variable}</f:format.html></v:debug>
*
* Or the same expression in inline syntax:
*
* {variable -> f:format.html() -> v:debug()}
*
* Can also be used to inspect `ObjectAccessor` instances
* (e.g. variables you try to access) and rather than just
* dumping the entire contents of the variable as is done
* by `<f:debug />`, this ViewHelper makes a very simple
* dump with a warning if the variable is not defined. If
* an object is encountered (for example a domain object)
* this ViewHelper will not dump the object but instead
* will scan it for accessible properties (e.g. properties
* which have a getter method!) and only present those
* properties which can be accessed, along with the type
* of variable that property currently contains:
*
* {domainObject -> v:debug()}
*
* Assuming that `{domainObject}` is an instance of an
* object which has two methods: `getUid()` and `getTitle()`,
* debugging that instance will render something like this
* in plain text:
*
* Path: {domainObject}
* Value type: object
* Accessible properties on {domainObject}:
* {form.uid} (integer)
* {form.title} (string)
*
* The class itself can contain any number of protected
* properties, but only those which have a getter method
* can be accessed by Fluid and as therefore we only dump
* those properties which you **can in fact access**.
*
* @package Vhs
* @subpackage ViewHelpers
*/
class DebugViewHelper extends AbstractViewHelper implements ChildNodeAccessInterface {
/**
* @var ViewHelperNode[]
*/
protected $childViewHelperNodes = array();
/**
* @var ObjectAccessorNode[]
*/
protected $childObjectAccessorNodes = array();
/**
* With this flag, you can disable the escaping interceptor inside this ViewHelper.
* THIS MIGHT CHANGE WITHOUT NOTICE, NO PUBLIC API!
* @var boolean
*/
protected $escapingInterceptorEnabled = FALSE;
/**
* @return string
*/
public function render() {
$nodes = array();
foreach ($this->childViewHelperNodes as $viewHelperNode) {
$viewHelper = $viewHelperNode->getUninitializedViewHelper();
$arguments = $viewHelper->prepareArguments();
$givenArguments = $viewHelperNode->getArguments();
$viewHelperReflection = new \ReflectionClass($viewHelper);
$viewHelperDescription = $viewHelperReflection->getDocComment();
$viewHelperDescription = htmlentities($viewHelperDescription);
$viewHelperDescription = '[CLASS DOC]' . LF . $viewHelperDescription . LF;
$renderMethodDescription = $viewHelperReflection->getMethod('render')->getDocComment();
$renderMethodDescription = htmlentities($renderMethodDescription);
$renderMethodDescription = implode(LF, array_map('trim', explode(LF, $renderMethodDescription)));
$renderMethodDescription = '[RENDER METHOD DOC]' . LF . $renderMethodDescription . LF;
$argumentDefinitions = array();
foreach ($arguments as $argument) {
$name = $argument->getName();
$argumentDefinitions[$name] = ObjectAccess::getGettableProperties($argument);
}
$sections = array(
$viewHelperDescription,
DebuggerUtility::var_dump($argumentDefinitions, '[ARGUMENTS]', 4, TRUE, FALSE, TRUE),
DebuggerUtility::var_dump($givenArguments, '[CURRENT ARGUMENTS]', 4, TRUE, FALSE, TRUE),
$renderMethodDescription
);
array_push($nodes, implode(LF, $sections));
}
if (0 < count($this->childObjectAccessorNodes)) {
array_push($nodes, '[VARIABLE ACCESSORS]');
$templateVariables = $this->templateVariableContainer->getAll();
foreach ($this->childObjectAccessorNodes as $objectAccessorNode) {
$path = $objectAccessorNode->getObjectPath();
$segments = explode('.', $path);
try {
$value = ObjectAccess::getProperty($templateVariables, array_shift($segments));
foreach ($segments as $segment) {
$value = ObjectAccess::getProperty($value, $segment);
}
$type = gettype($value);
} catch (PropertyNotAccessibleException $error) {
$value = NULL;
$type = 'UNDEFINED/INACCESSIBLE';
}
$sections = array(
'Path: {' . $path . '}',
'Value type: ' . $type,
);
if (TRUE === is_object($value)) {
$sections[] = 'Accessible properties on {' . $path . '}:';
$gettable = ObjectAccess::getGettablePropertyNames($value);
unset($gettable[0]);
foreach ($gettable as $gettableProperty) {
$sections[] = ' {' . $path . '.' . $gettableProperty . '} (' . gettype(ObjectAccess::getProperty($value, $gettableProperty)) . ')';
}
} elseif (NULL !== $value) {
$sections[] = DebuggerUtility::var_dump($value, 'Dump of variable "' . $path . '"', 4, TRUE, FALSE, TRUE);
}
array_push($nodes, implode(LF, $sections));
}
}
return '<pre>' . implode(LF . LF, $nodes) . '</pre>';
}
/**
* Sets the direct child nodes of the current syntax tree node.
*
* @param \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\AbstractNode[] $childNodes
* @return void
*/
public function setChildNodes(array $childNodes) {
foreach ($childNodes as $childNode) {
if (TRUE === $childNode instanceof ViewHelperNode) {
array_push($this->childViewHelperNodes, $childNode);
}
if (TRUE === $childNode instanceof ObjectAccessorNode) {
array_push($this->childObjectAccessorNodes, $childNode);
}
}
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Extension;
/*
* 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;
/**
* Base class: Extension ViewHelpers
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers\Extension
*/
abstract class AbstractExtensionViewHelper extends AbstractViewHelper {
/**
* @return void
*/
public function initializeArguments() {
$this->registerArgument('extensionName', 'string', 'Name, in UpperCamelCase, of the extension to be checked', FALSE, NULL, TRUE);
}
/**
* @return string
*/
protected function getExtensionKey() {
$extensionName = $this->getExtensionName();
return GeneralUtility::camelCaseToLowerCaseUnderscored($extensionName);
}
/**
* @throws \RuntimeException
* @return mixed
*/
protected function getExtensionName() {
if (TRUE === isset($this->arguments['extensionName']) && FALSE === empty($this->arguments['extensionName'])) {
return $this->arguments['extensionName'];
}
$request = $this->controllerContext->getRequest();
$extensionName = $request->getControllerExtensionName();
if (TRUE === empty($extensionName)) {
throw new \RuntimeException('Unable to read extension name from ControllerContext and value not manually specified', 1364167519);
}
return $extensionName;
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Extension;
/*
* 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\ExtensionManagementUtility;
/**
* ### Extension: Icon ViewHelper
*
* Outputs the icon of the extension key. Supports both
* extension key and extension name arguments.
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers\Extension
*/
class IconViewHelper extends AbstractExtensionViewHelper {
/**
* Render method
*
* @return string
*/
public function render() {
$extensionKey = $this->getExtensionKey();
return ExtensionManagementUtility::extPath($extensionKey, 'ext_icon.gif');
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Extension;
/*
* 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\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractConditionViewHelper;
use FluidTYPO3\Vhs\Traits\ConditionViewHelperTrait;
/**
* ### Extension: Loaded (Condition) ViewHelper
*
* Condition to check if an extension is loaded.
*
* ### Example:
*
* {v:extension.loaded(extensionName: 'news', then: 'yes', else: 'no')}
*
* <v:extension.loaded extensionName="news">
* ...
* </v:extension.loaded>
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers\Extension
*/
class LoadedViewHelper extends AbstractConditionViewHelper {
use ConditionViewHelperTrait;
/**
* Initialize arguments
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerArgument('extensionName', 'string', 'Name of extension that must be loaded in order to evaluate as TRUE, UpperCamelCase', TRUE);
}
/**
* This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality.
*
* @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) {
$extensionName = $arguments['extensionName'];
$extensionKey = GeneralUtility::camelCaseToLowerCaseUnderscored($extensionName);
$isLoaded = ExtensionManagementUtility::isLoaded($extensionKey);
return TRUE === $isLoaded;
}
}

View File

@@ -0,0 +1,43 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Extension\Path;
/*
* 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\Extension\AbstractExtensionViewHelper;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
/**
* ### Path: Absolute Extension Folder Path
*
* Returns the absolute path to an extension folder.
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers\Extension\Path
*/
class AbsoluteViewHelper extends AbstractExtensionViewHelper {
/**
* @return void
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerArgument('path', 'string', 'Optional path to append, second argument when calling \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath');
}
/**
* Render method
*
* @return string
*/
public function render() {
$extensionKey = $this->getExtensionKey();
return ExtensionManagementUtility::extPath($extensionKey, TRUE === isset($this->arguments['path']) ? $this->arguments['path'] : NULL);
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Extension\Path;
/*
* 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\Extension\AbstractExtensionViewHelper;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
/**
* ### Path: Relative Extension Folder Path
*
* Returns the relative path to an Extension folder.
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers\Extension\Path
*/
class RelativeViewHelper extends AbstractExtensionViewHelper {
/**
* Render method
*
* @return string
*/
public function render() {
$extensionKey = $this->getExtensionKey();
return ExtensionManagementUtility::extRelPath($extensionKey);
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Extension\Path;
/*
* 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\Extension\AbstractExtensionViewHelper;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
/**
* ### Path: Relative Extension Resource Path
*
* Site Relative path to Extension Resources/Public folder.
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers\Extension\Path
*/
class ResourcesViewHelper extends AbstractExtensionViewHelper {
/**
* @return void
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerArgument('path', 'string', 'Optional path to append after output of \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extRelPath');
}
/**
* Render method
*
* @return string
*/
public function render() {
$extensionKey = $this->getExtensionKey();
$path = TRUE === empty($this->arguments['path']) ? '' : $this->arguments['path'];
return ExtensionManagementUtility::extRelPath($extensionKey) . 'Resources/Public/' . $path;
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Extension\Path;
/*
* 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\Extension\AbstractExtensionViewHelper;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
/**
* ### Path: Relative Extension Folder Path
*
* Returns the site relative path to an extension folder.
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers\Extension\Path
*/
class SiteRelativeViewHelper extends AbstractExtensionViewHelper {
/**
* Render method
*
* @return string
*/
public function render() {
$extensionKey = $this->getExtensionKey();
return ExtensionManagementUtility::siteRelPath($extensionKey);
}
}

View File

@@ -0,0 +1,99 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Form;
/*
* 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\Extbase\Persistence\PersistenceManagerInterface;
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper;
/**
* Form Field Name View Helper
*
* This viewhelper returns the properly prefixed name of the given
* form field and generates the corresponding HMAC to allow posting
* of dynamically added fields.
*
* @author Björn Fromme <fromme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Form
*/
class FieldNameViewHelper extends AbstractViewHelper {
/**
* @var PersistenceManagerInterface
*/
protected $persistenceManager;
/**
* @param PersistenceManagerInterface $persistenceManager
*/
public function injectPersistenceManager(PersistenceManagerInterface $persistenceManager) {
$this->persistenceManager = $persistenceManager;
}
/**
* @return void
* @api
*/
public function initializeArguments() {
$this->registerArgument('name', 'string', 'Name of the form field to generate the HMAC for.');
$this->registerArgument('property', 'string', 'Name of object property. If used in conjunction with <f:form object="...">, "name" argument will be ignored.');
}
/**
* @return string
*/
public function render() {
if (TRUE === $this->isObjectAccessorMode()) {
$formObjectName = $this->viewHelperVariableContainer->get('TYPO3\\CMS\\Fluid\\ViewHelpers\\FormViewHelper', 'formObjectName');
if (FALSE === empty($formObjectName)) {
$propertySegments = explode('.', $this->arguments['property']);
$propertyPath = '';
foreach ($propertySegments as $segment) {
$propertyPath .= '[' . $segment . ']';
}
$name = $formObjectName . $propertyPath;
} else {
$name = $this->arguments['property'];
}
} else {
$name = $this->arguments['name'];
}
if (NULL === $name || '' === $name) {
return '';
}
if (FALSE === $this->viewHelperVariableContainer->exists('TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper', 'fieldNamePrefix')) {
return $name;
}
$fieldNamePrefix = (string) $this->viewHelperVariableContainer->get('TYPO3\\CMS\\Fluid\\ViewHelpers\\FormViewHelper', 'fieldNamePrefix');
if ('' === $fieldNamePrefix) {
return $name;
}
$fieldNameSegments = explode('[', $name, 2);
$name = $fieldNamePrefix . '[' . $fieldNameSegments[0] . ']';
if (1 < count($fieldNameSegments)) {
$name .= '[' . $fieldNameSegments[1];
}
if (TRUE === $this->viewHelperVariableContainer->exists('TYPO3\\CMS\\Fluid\\ViewHelpers\\FormViewHelper', 'formFieldNames')) {
$formFieldNames = $this->viewHelperVariableContainer->get('TYPO3\\CMS\\Fluid\\ViewHelpers\\FormViewHelper', 'formFieldNames');
} else {
$formFieldNames = array();
}
$formFieldNames[] = $name;
$this->viewHelperVariableContainer->addOrUpdate('TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper', 'formFieldNames', $formFieldNames);
return $name;
}
/**
* @return boolean
*/
protected function isObjectAccessorMode() {
return (boolean) (TRUE === $this->hasArgument('property') && TRUE === $this->viewHelperVariableContainer->exists('TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper', 'formObjectName'));
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Form\Select;
/*
* 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\AbstractTagBasedViewHelper;
/**
* Optgroup ViewHelper to use under vhs:form.select
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers\Form\Select
*/
class OptgroupViewHelper extends AbstractTagBasedViewHelper {
/**
* @var string
*/
protected $tagName = 'optgroup';
/**
* Initialize
* @return void
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerUniversalTagAttributes();
$this->registerTagAttribute('label', 'string', 'Label for this option group');
}
/**
* @return string
*/
public function render() {
$this->tag->setContent($this->renderChildren());
return $this->tag->render();
}
}

View File

@@ -0,0 +1,77 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Form\Select;
/*
* 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\ViewHelpers\Form\AbstractFormFieldViewHelper;
/**
* Option ViewHelper to use under vhs:form.select
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers\Form\Select
*/
class OptionViewHelper extends AbstractFormFieldViewHelper {
/**
* @var string
*/
protected $tagName = 'option';
/**
* Initialize
*
* @return void
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerUniversalTagAttributes();
$this->registerArgument('selected', 'boolean', 'Set to TRUE to mark field as selected; otherwise detected from field value');
}
/**
* @throws \RuntimeException
* @return string
*/
public function render() {
if (!$this->viewHelperVariableContainer->exists('FluidTYPO3\Vhs\ViewHelpers\Form\SelectViewHelper', 'options')) {
throw new \RuntimeException(
'Options can only be added inside select tags, optionally inside optgroup tag(s) inside the select tag',
1313937196
);
}
if (TRUE === (boolean) $this->arguments['selected']) {
$selected = 'selected';
} else if (TRUE === $this->viewHelperVariableContainer->exists('FluidTYPO3\\Vhs\\ViewHelpers\\Form\\SelectViewHelper', 'value')) {
$value = $this->viewHelperVariableContainer->get('FluidTYPO3\\Vhs\\ViewHelpers\\Form\\SelectViewHelper', 'value');
if (FALSE === is_object($this->arguments['value']) && FALSE === is_array($this->arguments['value'])) {
if (TRUE === is_array($value)) {
$selected = TRUE === in_array($this->arguments['value'], $value) ? 'selected' : '';
} else {
$selected = (string) $this->arguments['value'] == (string) $value ? 'selected' : '';
}
}
}
$tagContent = $this->renderChildren();
$options = $this->viewHelperVariableContainer->get('FluidTYPO3\\Vhs\\ViewHelpers\\Form\\SelectViewHelper', 'options');
$options[$tagContent] = $this->arguments['value'];
$this->viewHelperVariableContainer->addOrUpdate('FluidTYPO3\\Vhs\\ViewHelpers\\Form\\SelectViewHelper', 'options', $options);
if (FALSE === empty($selected)) {
$this->tag->addAttribute('selected', 'selected');
} else {
$this->tag->removeAttribute('selected');
}
$this->tag->setContent($tagContent);
if (TRUE === isset($this->arguments['value'])) {
$this->tag->addAttribute('value', $this->arguments['value']);
}
return $this->tag->render();
}
}

View File

@@ -0,0 +1,246 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Form;
/*
* 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\Extbase\Reflection\ObjectAccess;
use TYPO3\CMS\Fluid\Core\ViewHelper\Exception;
use TYPO3\CMS\Fluid\ViewHelpers\Form\AbstractFormFieldViewHelper;
/**
* Select ViewHelper (with support for Optgroup and Option subnodes)
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers\Form
*/
class SelectViewHelper extends AbstractFormFieldViewHelper {
/**
* @var string
*/
protected $tagName = 'select';
/**
* @var mixed
*/
protected $selectedValue = NULL;
/**
* Initialize arguments.
*
* @return void
* @api
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerUniversalTagAttributes();
$this->registerTagAttribute('size', 'string', 'Size of input field');
$this->registerTagAttribute('disabled', 'string', 'Specifies that the input element should be disabled when the page loads');
$this->registerArgument('multiple', 'boolean', 'if set, multiple select field', FALSE, FALSE);
$this->registerArgument('options', 'array', 'Associative array with internal IDs as key, and the values are displayed in the select box');
$this->registerArgument('optionValueField', 'string', 'If specified, will call the appropriate getter on each object to determine the value.');
$this->registerArgument('optionLabelField', 'string', 'If specified, will call the appropriate getter on each object to determine the label.');
$this->registerArgument('sortByOptionLabel', 'boolean', 'If true, List will be sorted by label.', FALSE, FALSE);
$this->registerArgument('selectAllByDefault', 'boolean', 'If specified options are selected if none was set before.', FALSE, FALSE);
$this->registerArgument('errorClass', 'string', 'CSS class to set if there are errors for this view helper', FALSE, 'f3-form-error');
}
/**
* Render the tag.
*
* @return string rendered tag.
* @api
*/
public function render() {
$name = $this->getName();
if (TRUE === (boolean) $this->arguments['multiple']) {
$name .= '[]';
}
$this->tag->addAttribute('name', $name);
if (TRUE === isset($this->arguments['options']) && FALSE === empty($this->arguments['options'])) {
$options = $this->getOptions();
if (TRUE === empty($options)) {
$options = array('' => '');
}
$this->tag->setContent($this->renderOptionTags($options));
} else {
$this->viewHelperVariableContainer->add('FluidTYPO3\Vhs\ViewHelpers\Form\SelectViewHelper', 'options', array());
$this->viewHelperVariableContainer->add('FluidTYPO3\Vhs\ViewHelpers\Form\SelectViewHelper', 'value', $this->getValue());
$tagContent = $this->renderChildren();
$options = $this->viewHelperVariableContainer->get('FluidTYPO3\\Vhs\\ViewHelpers\\Form\\SelectViewHelper', 'options');
$this->tag->setContent($tagContent);
$this->viewHelperVariableContainer->remove('FluidTYPO3\\Vhs\\ViewHelpers\\Form\\SelectViewHelper', 'options');
if (TRUE === $this->viewHelperVariableContainer->exists('FluidTYPO3\\Vhs\\ViewHelpers\\Form\\SelectViewHelper', 'value')) {
$this->viewHelperVariableContainer->remove('FluidTYPO3\\Vhs\\ViewHelpers\\Form\\SelectViewHelper', 'value');
}
}
$this->setErrorClassAttribute();
$content = '';
// register field name for token generation.
// in case it is a multi-select, we need to register the field name
// as often as there are elements in the box
if (TRUE === (boolean) $this->arguments['multiple']) {
$content .= $this->renderHiddenFieldForEmptyValue();
$length = count($options);
for ($i = 0; $i < $length; $i++) {
$this->registerFieldNameForFormTokenGeneration($name);
}
$this->tag->addAttribute('multiple', 'multiple');
} else {
$this->registerFieldNameForFormTokenGeneration($name);
$this->tag->removeAttribute('multiple');
}
$content .= $this->tag->render();
return $content;
}
/**
* Render the option tags.
*
* @param array $options the options for the form.
* @return string rendered tags.
*/
protected function renderOptionTags($options) {
$output = '';
foreach ($options as $value => $label) {
$isSelected = $this->isSelected($value);
$output .= $this->renderOptionTag($value, $label, $isSelected) . chr(10);
}
return $output;
}
/**
* Render the option tags.
*
* @throws Exception
* @return array
*/
protected function getOptions() {
if (FALSE === is_array($this->arguments['options']) && FALSE === $this->arguments['options'] instanceof \Traversable) {
return array();
}
$options = array();
$optionsArgument = $this->arguments['options'];
foreach ($optionsArgument as $key => $value) {
if (TRUE === is_object($value)) {
if (TRUE === isset($this->arguments['optionValueField']) && FALSE === empty($this->arguments['optionValueField'])) {
$key = ObjectAccess::getProperty($value, $this->arguments['optionValueField']);
if (TRUE === is_object($key)) {
if (TRUE === method_exists($key, '__toString')) {
$key = (string) $key;
} else {
throw new Exception('Identifying value for object of class "' . get_class($value) . '" was an object.', 1247827428);
}
}
} elseif (NULL !== $this->persistenceManager->getBackend()->getIdentifierByObject($value)) {
$key = $this->persistenceManager->getBackend()->getIdentifierByObject($value);
} elseif (TRUE === method_exists($value, '__toString')) {
$key = (string) $value;
} else {
throw new Exception('No identifying value for object of class "' . get_class($value) . '" found.', 1247826696);
}
if (TRUE === isset($this->arguments['optionLabelField']) && FALSE === empty($this->arguments['optionLabelField'])) {
$value = ObjectAccess::getProperty($value, $this->arguments['optionLabelField']);
if (TRUE === is_object($value)) {
if (TRUE === method_exists($value, '__toString')) {
$value = (string) $value;
} else {
throw new Exception('Label value for object of class "' . get_class($value) . '" was an object without a __toString() method.', 1247827553);
}
}
} elseif (TRUE === method_exists($value, '__toString')) {
$value = (string) $value;
} elseif (NULL !== $this->persistenceManager->getBackend()->getIdentifierByObject($value)) {
$value = $this->persistenceManager->getBackend()->getIdentifierByObject($value);
}
}
$options[$key] = $value;
}
if (TRUE === isset($this->arguments['sortByOptionLabel']) && FALSE === empty($this->arguments['sortByOptionLabel'])) {
asort($options);
}
return $options;
}
/**
* Render the option tags.
*
* @param mixed $value Value to check for
* @return boolean TRUE if the value should be marked a s selected; FALSE otherwise
*/
protected function isSelected($value) {
$selectedValue = $this->getSelectedValue();
if ($value === $selectedValue || (string) $value === $selectedValue) {
return TRUE;
}
if (TRUE === isset($this->arguments['multiple']) && FALSE === empty($this->arguments['multiple'])) {
if (NULL === $selectedValue && TRUE === (boolean) $this->arguments['selectAllByDefault']) {
return TRUE;
} elseif (TRUE === is_array($selectedValue) && TRUE === in_array($value, $selectedValue)) {
return TRUE;
}
}
return FALSE;
}
/**
* Retrieves the selected value(s)
*
* @return mixed value string or an array of strings
*/
protected function getSelectedValue() {
$value = $this->getValue();
if (FALSE === isset($this->arguments['optionValueField']) || TRUE === empty($this->arguments['optionValueField'])) {
return $value;
}
if (FALSE === is_array($value) && FALSE === $value instanceof \Iterator) {
if (TRUE === is_object($value)) {
return ObjectAccess::getProperty($value, $this->arguments['optionValueField']);
} else {
return $value;
}
}
$selectedValues = array();
foreach ($value as $selectedValueElement) {
if (TRUE === is_object($selectedValueElement)) {
$selectedValues[] = ObjectAccess::getProperty($selectedValueElement, $this->arguments['optionValueField']);
} else {
$selectedValues[] = $selectedValueElement;
}
}
return $selectedValues;
}
/**
* Render one option tag
*
* @param string $value value attribute of the option tag (will be escaped)
* @param string $label content of the option tag (will be escaped)
* @param boolean $isSelected specifies wheter or not to add selected attribute
* @return string the rendered option tag
*/
protected function renderOptionTag($value, $label, $isSelected) {
$output = '<option value="' . htmlspecialchars($value) . '"';
if (TRUE === (boolean) $isSelected) {
$output .= ' selected="selected"';
}
$output .= '>' . htmlspecialchars($label) . '</option>';
return $output;
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Format;
/*
* 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;
/**
* ### Format: Append string content
*
* Appends a string after another string. Although this task is very
* easily done in standard Fluid - i.e. {subject}{add} - this
* ViewHelper makes advanced chained inline processing possible:
*
* <!-- useful when needing to chain string processing. Remove all "foo" and "bar"
* then add a text containing both "foo" and "bar", then format as HTML -->
* {text -> v:format.eliminate(strings: 'foo,bar')
* -> v:format.append(add: ' - my foo and bar are the only ones in this text.')
* -> f:format.html()}
* <!-- NOTE: you do not have to break the lines; done here only for presentation purposes -->
*
* Makes no sense used as tag based ViewHelper:
*
* <!-- DO NOT USE - depicts COUNTERPRODUCTIVE usage! -->
* <v:format.append add="{f:translate(key: 're')}">{subject}</v:format.append>
* <!-- ... which is the exact same as ... -->
* <f:translate key="re" />{subject} <!-- OR --> {f:translate(key: 're')}{subject}
*
* In other words: use this only when you do not have the option of
* simply using {subject}{add}, i.e. in complex inline statements used
* as attribute values on other ViewHelpers (where tag usage is undesirable).
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers\Format
*/
class AppendViewHelper extends AbstractViewHelper {
/**
* @param string $add
* @param string $subject
* @return string
*/
public function render($add, $subject = NULL) {
if (NULL === $subject) {
$subject = $this->renderChildren();
}
return $subject . $add;
}
}

View File

@@ -0,0 +1,81 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Format;
/*
* 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\Utility\FrontendSimulationUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper;
/**
* Case Formatting ViewHelper
*
* Formats string case according to provided arguments
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers\Format
*/
class CaseViewHelper extends AbstractViewHelper {
const CASE_UPPER = 'upper';
const CASE_LOWER = 'lower';
const CASE_UCWORDS = 'ucwords';
const CASE_UCFIRST = 'ucfirst';
const CASE_LCFIRST = 'lcfirst';
const CASE_CAMELCASE = 'CamelCase';
const CASE_LOWERCAMELCASE = 'lowerCamelCase';
const CASE_UNDERSCORED = 'lowercase_underscored';
/**
* @param string $string
* @param string $case
* @return string
*/
public function render($string = NULL, $case = NULL) {
if (NULL === $string) {
$string = $this->renderChildren();
}
if ('BE' === TYPO3_MODE) {
$tsfeBackup = FrontendSimulationUtility::simulateFrontendEnvironment();
}
switch ($case) {
case self::CASE_LOWER:
$string = $GLOBALS['TSFE']->csConvObj->conv_case($GLOBALS['TSFE']->renderCharset, $string, 'toLower');
break;
case self::CASE_UPPER:
$string = $GLOBALS['TSFE']->csConvObj->conv_case($GLOBALS['TSFE']->renderCharset, $string, 'toUpper');
break;
case self::CASE_UCWORDS:
$string = ucwords($string);
break;
case self::CASE_UCFIRST:
$string = $GLOBALS['TSFE']->csConvObj->convCaseFirst($GLOBALS['TSFE']->renderCharset, $string, 'toUpper');
break;
case self::CASE_LCFIRST:
$string = $GLOBALS['TSFE']->csConvObj->convCaseFirst($GLOBALS['TSFE']->renderCharset, $string, 'toLower');
break;
case self::CASE_CAMELCASE:
$string = GeneralUtility::underscoredToUpperCamelCase($string);
break;
case self::CASE_LOWERCAMELCASE:
$string = GeneralUtility::underscoredToLowerCamelCase($string);
break;
case self::CASE_UNDERSCORED:
$string = GeneralUtility::camelCaseToLowerCaseUnderscored($string);
break;
default:
break;
}
if ('BE' === TYPO3_MODE) {
FrontendSimulationUtility::resetFrontendEnvironment($tsfeBackup);
}
return $string;
}
}

View File

@@ -0,0 +1,203 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Format;
/*
* 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;
use TYPO3\CMS\Fluid\Core\ViewHelper\Exception;
/**
* ### Date range calculation/formatting ViewHelper
*
* Uses DateTime and DateInterval operations to calculate a range
* between two DateTimes.
*
* #### Usages
*
* - As formatter, the ViewHelper can output a string value such as
* "2013-04-30 - 2013-05-30" where you can configure both the start
* and end date (or their common) formats as well as the "glue"
* which binds the two dates together.
* - As interval calculator, the ViewHelper can be used with a special
* "intervalFormat" which is a string used in the constructor method
* for the DateInterval class - for example, "P3M" to add three months.
* Used this way, you can specify the start date (or rely on the
* default "now" DateTime) and specify the "intervalFormat" to add
* your desired duration to your starting date and use that as end
* date. Without the "return" attribute, this mode simply outputs
* the formatted dates with interval deciding the end date.
* - When used with the "return" attribute you can specify which type
* of data to return:
* - if "return" is "DateTime", a single DateTime instance is returned
* (which is the end date). Use this with a start date to return the
* DateTime corresponding to "intervalFormat" into the future/past.
* - if "return" is a string such as "w", "d", "h" etc. the corresponding
* counter value (weeks, days, hours etc.) is returned.
* - if "return" is an array of counter IDs, for example Array("w", "d"),
* the corresponding counters from the range are returned as an array.
*
* #### Note about LLL support and array consumers
*
* When used with the "return" attribute and when this attribute is an
* array, the output becomes suitable for consumption by f:translate, v:l
* or f:format.sprintf for example - as the "arguments" attribute:
*
* <f:translate key="myDateDisplay"
* arguments="{v:format.dateRange(intervalFormat: 'P3W', return: {0: 'w', 1: 'd'})}"
* />
*
* Which if "myDateDisplay" is a string such as "Deadline: %d week(s) and
* %d day(s)" would output a result such as "Deadline: 4 week(s) and 2 day(s)".
*
* > Tip: the values returned by this ViewHelper in both array and single
* > value return modes, are also nicely consumable by the "math" suite
* > of ViewHelpers, for example `v:math.division` would be able to divide
* > number of days by two, three etc. to further divide the date range.
*
* @author Björn Fromme <fromme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Format
*/
class DateRangeViewHelper extends AbstractViewHelper {
/**
* @var boolean
*/
protected $escapingInterceptorEnabled = FALSE;
/**
* @return void
*/
public function initializeArguments() {
$this->registerArgument('start', 'mixed', 'Start date which can be a DateTime object or a string consumable by DateTime constructor', FALSE, 'now');
$this->registerArgument('end', 'mixed', 'End date which can be a DateTime object or a string consumable by DateTime constructor', FALSE, NULL);
$this->registerArgument('intervalFormat', 'string', 'Interval format consumable by DateInterval', FALSE, NULL);
$this->registerArgument('format', 'string', 'Date format to apply to both start and end date', FALSE, 'Y-m-d');
$this->registerArgument('startFormat', 'string', 'Date format to apply to start date', FALSE, NULL);
$this->registerArgument('endFormat', 'string', 'Date format to apply to end date', FALSE, NULL);
$this->registerArgument('glue', 'string', 'Glue string to concatenate dates with', FALSE, '-');
$this->registerArgument('spaceGlue', 'boolean', 'If TRUE glue string is surrounded with whitespace', FALSE, TRUE);
$this->registerArgument('return', 'mixed', 'Return type; can be exactly "DateTime" to return a DateTime instance, a string like "w" ' .
'or "d" to return weeks, days between the two dates - or an array of w, d, etc. strings to return the corresponding range count values as an array.', FALSE, NULL);
}
/**
* Render method
*
* @throws Exception
* @return mixed
*/
public function render() {
if (TRUE === isset($this->arguments['start']) && FALSE === empty($this->arguments['start'])) {
$start = $this->arguments['start'];
} else {
$start = 'now';
}
$startDateTime = $this->enforceDateTime($start);
if (TRUE === isset($this->arguments['end']) && FALSE === empty($this->arguments['end'])) {
$endDateTime = $this->enforceDateTime($this->arguments['end']);
} else {
$endDateTime = NULL;
}
if (TRUE === isset($this->arguments['intervalFormat']) && FALSE === empty($this->arguments['intervalFormat'])) {
$intervalFormat = $this->arguments['intervalFormat'];
}
if (NULL === $intervalFormat && NULL === $endDateTime) {
throw new Exception('Either end or intervalFormat has to be provided.', 1369573110);
}
if (TRUE === isset($intervalFormat) && NULL !== $intervalFormat) {
try {
$interval = new \DateInterval($intervalFormat);
} catch (\Exception $exception) {
throw new Exception('"' . $intervalFormat . '" could not be parsed by \DateInterval constructor.', 1369573111);
}
} else {
$interval = $endDateTime->diff($startDateTime);
}
if (NULL !== $interval && NULL === $endDateTime) {
$endDateTime = new \DateTime();
$endDateTime->add($endDateTime->diff($startDateTime));
$endDateTime->add($interval);
}
$return = $this->arguments['return'];
if (NULL === $return) {
$spaceGlue = (boolean) $this->arguments['spaceGlue'];
$glue = strval($this->arguments['glue']);
$startFormat = $this->arguments['format'];
$endFormat = $this->arguments['format'];
if (NULL !== $this->arguments['startFormat'] && FALSE === empty($this->arguments['startFormat'])) {
$startFormat = $this->arguments['startFormat'];
}
if (NULL !== $this->arguments['endFormat'] && FALSE === empty($this->arguments['endFormat'])) {
$endFormat = $this->arguments['endFormat'];
}
$output = $this->formatDate($startDateTime, $startFormat);
$output .= TRUE === $spaceGlue ? ' ' : '';
$output .= $glue;
$output .= TRUE === $spaceGlue ? ' ' : '';
$output .= $this->formatDate($endDateTime, $endFormat);
} elseif ('DateTime' === $return) {
$output = $endDateTime;
} elseif (TRUE === is_string($return)) {
if (FALSE === strpos($return, '%')) {
$return = '%' . $return;
}
$output = $interval->format($return);
} elseif (TRUE === is_array($return)) {
$output = array();
foreach ($return as $format) {
if (FALSE === strpos($format, '%')) {
$format = '%' . $format;
}
array_push($output, $interval->format($format));
}
}
return $output;
}
/**
* @param mixed $date
* @return \DateTime
* @throws Exception
*/
protected function enforceDateTime($date) {
if (FALSE === $date instanceof \DateTime) {
try {
if (TRUE === is_integer($date)) {
$date = new \DateTime('@' . $date);
} else {
$date = new \DateTime($date);
}
$date->setTimezone(new \DateTimeZone(date_default_timezone_get()));
} catch (\Exception $exception) {
throw new Exception('"' . $date . '" could not be parsed by \DateTime constructor.', 1369573112);
}
}
return $date;
}
/**
* @param \DateTime $date
* @param string $format
* @return string
*/
protected function formatDate($date, $format = 'Y-m-d') {
if (FALSE !== strpos($format, '%')) {
return strftime($format, $date->format('U'));
} else {
return $date->format($format);
}
}
}

View File

@@ -0,0 +1,208 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Format;
/*
* 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;
/**
* Character/string/whitespace elimination ViewHelper
*
* There is no example - each argument describes how it should be
* used and arguments can be used individually or in any combination.
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers\Format
*/
class EliminateViewHelper extends AbstractViewHelper {
/**
* Initialize arguments
*
* @return void
*/
public function initializeArguments() {
$this->registerArgument('caseSensitive', 'boolean', 'Wether or not to perform case sensitive replacement', FALSE, TRUE);
$this->registerArgument('characters', 'mixed', "Characters to remove. Array or string, i.e. {0: 'a', 1: 'b', 2: 'c'} or 'abc' to remove all occurrences of a, b and c");
$this->registerArgument('strings', 'mixed', "Strings to remove. Array or CSV, i.e. {0: 'foo', 1: 'bar'} or 'foo,bar' to remove all occorrences of foo and bar. If your strings overlap then place the longest match first");
$this->registerArgument('whitespace', 'boolean', 'Eliminate ALL whitespace characters', FALSE, FALSE);
$this->registerArgument('whitespaceBetweenHtmlTags', 'boolean', 'Eliminate ALL whitespace characters between HTML tags', FALSE, FALSE);
$this->registerArgument('tabs', 'boolean', 'Eliminate only tab whitespaces', FALSE, FALSE);
$this->registerArgument('unixBreaks', 'boolean', 'Eliminate only UNIX line breaks', FALSE, FALSE);
$this->registerArgument('windowsBreaks', 'boolean', 'Eliminates only Windows carriage returns', FALSE, FALSE);
$this->registerArgument('digits', 'boolean', 'Eliminates all number characters (but not the dividers between floats converted to strings)', FALSE, FALSE);
$this->registerArgument('letters', 'boolean', 'Eliminates all letters (non-numbers, non-whitespace, non-syntactical)', FALSE, FALSE);
$this->registerArgument('nonAscii', 'boolean', 'Eliminates any ASCII char', FALSE, FALSE);
}
/**
* @param string $content
* @return string
*/
public function render($content = NULL) {
if (NULL === $content) {
$content = $this->renderChildren();
}
if (TRUE === isset($this->arguments['characters'])) {
$content = $this->eliminateCharacters($content, $this->arguments['characters']);
}
if (TRUE === isset($this->arguments['strings'])) {
$content = $this->eliminateStrings($content, $this->arguments['strings']);
}
if (TRUE === $this->arguments['whitespace']) {
$content = $this->eliminateWhitespace($content);
}
if (TRUE === $this->arguments['whitespaceBetweenHtmlTags']) {
$content = $this->eliminateWhitespaceBetweenHtmlTags($content);
}
if (TRUE === $this->arguments['tabs']) {
$content = $this->eliminateTabs($content);
}
if (TRUE === $this->arguments['unixBreaks']) {
$content = $this->eliminateUnixBreaks($content);
}
if (TRUE === $this->arguments['windowsBreaks']) {
$content = $this->eliminateWindowsCarriageReturns($content);
}
if (TRUE === $this->arguments['digits']) {
$content = $this->eliminateDigits($content);
}
if (TRUE === $this->arguments['letters']) {
$content = $this->eliminateLetters($content);
}
if (TRUE === $this->arguments['nonAscii']) {
$content = $this->eliminateNonAscii($content);
}
return $content;
}
/**
* @param string $content
* @param mixed $characters
* @return string
*/
protected function eliminateCharacters($content, $characters) {
$caseSensitive = (boolean) $this->arguments['caseSensitive'];
if (TRUE === is_array($characters)) {
$subjects = $characters;
} else {
$subjects = str_split($characters);
}
foreach ($subjects as $subject) {
if (TRUE === $caseSensitive) {
$content = str_replace($subject, '', $content);
} else {
$content = str_ireplace($subject, '', $content);
}
}
return $content;
}
/**
* @param string $content
* @param mixed $strings
* @return string
*/
protected function eliminateStrings($content, $strings) {
$caseSensitive = (boolean) $this->arguments['caseSensitive'];
if (TRUE === is_array($strings)) {
$subjects = $strings;
} else {
$subjects = explode(',', $strings);
}
foreach ($subjects as $subject) {
if (TRUE === $caseSensitive) {
$content = str_replace($subject, '', $content);
} else {
$content = str_ireplace($subject, '', $content);
}
}
return $content;
}
/**
* @param string $content
* @return string
*/
protected function eliminateWhitespace($content) {
$content = preg_replace('/\s+/', '', $content);
return $content;
}
/**
* @param string $content
* @return string
*/
protected function eliminateWhitespaceBetweenHtmlTags($content) {
$content = trim(preg_replace('/>\s+</', '><', $content));
return $content;
}
/**
* @param string $content
* @return string
*/
protected function eliminateTabs($content) {
$content = str_replace("\t", '', $content);
return $content;
}
/**
* @param string $content
* @return string
*/
protected function eliminateUnixBreaks($content) {
$content = str_replace("\n", '', $content);
return $content;
}
/**
* @param string $content
* @return string
*/
protected function eliminateWindowsCarriageReturns($content) {
$content = str_replace("\r", '', $content);
return $content;
}
/**
* @param string $content
* @return string
*/
protected function eliminateDigits($content) {
$content = preg_replace('#[0-9]#', '', $content);
return $content;
}
/**
* @param string $content
* @return string
*/
protected function eliminateLetters($content) {
$caseSensitive = (boolean) $this->arguments['caseSensitive'];
if (TRUE === $caseSensitive) {
$content = preg_replace('#[a-z]#', '', $content);
} else {
$content = preg_replace('/[a-z]/i', '', $content);
}
return $content;
}
/**
* @param string $content
* @return string
*/
protected function eliminateNonAscii($content) {
$caseSensitive = (boolean) $this->arguments['caseSensitive'];
$caseSensitiveIndicator = TRUE === $caseSensitive ? 'i' : '';
$content = preg_replace('/[^(\x20-\x7F)]*/' . $caseSensitiveIndicator, '', $content);
return $content;
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Format;
/*
* 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;
/**
* Hides output from browser, but still renders tag content
* which means any ViewHelper inside the tag content still
* gets processed.
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers\Format
*/
class HideViewHelper extends AbstractViewHelper {
/**
* Initialize
*
* @return void
*/
public function initializeArguments() {
$this->registerArgument('disabled', 'boolean', 'If TRUE, renders content - use to quickly enable/disable Fluid code', FALSE, FALSE);
}
/**
* @return string
*/
public function render() {
if (TRUE === (boolean) $this->arguments['disabled']) {
return $this->renderChildren();
} else {
$this->renderChildren();
}
return NULL;
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Format\Json;
/*
* 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;
use TYPO3\CMS\Fluid\Core\ViewHelper\Exception;
/**
* Converts the JSON encoded argument into a PHP variable
*
* @author Björn Fromme <fromme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Format\Json
*/
class DecodeViewHelper extends AbstractViewHelper {
/**
* @param string $json
* @throws Exception
* @return mixed
*/
public function render($json = NULL) {
if (NULL === $json) {
$json = $this->renderChildren();
if (TRUE === empty($json)) {
return NULL;
}
}
$value = json_decode($json, TRUE);
if (JSON_ERROR_NONE !== json_last_error()) {
throw new Exception('The provided argument is invalid JSON.', 1358440054);
}
return $value;
}
}

View File

@@ -0,0 +1,191 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Format\Json;
/*
* 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\Extbase\DomainObject\DomainObjectInterface;
use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3\CMS\Fluid\Core\ViewHelper\Exception;
/**
* ### JSON Encoding ViewHelper
*
* Returns a string containing the JSON representation of the argument.
* The argument may be any of the following types:
*
* - arrays, associative and traditional
* - DomainObjects
* - arrays containing DomainObjects
* - ObjectStorage containing DomainObjects
* - standard types (string, integer, boolean, float, NULL)
* - DateTime including ones found as property values on DomainObjects
*
* Recursion protection is enabled for DomainObjects with the option to
* add a special marker (any variable type above also supported here)
* which is inserted where an object which would cause recursion would
* be placed.
*
* Be specially careful when you JSON encode DomainObjects which have
* recursive relations to itself using either 1:n or m:n - in this case
* the one member of the converted relation will be whichever value you
* specified as "recursionMarker" - or the default value, NULL. When
* using the output of such conversion in JavaScript please make sure you
* check the type before assuming that every member of a converted 1:n
* or m:n recursive relation is in fact a JavaScript. Not doing so may
* result in fatal JavaScript errors in the client browser.
*
* @author Claus Due <claus@namelesscoder.net>
* @author Björn Fromme <fromme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Format\Json
*/
class EncodeViewHelper extends AbstractViewHelper {
/**
* @var array
*/
protected $encounteredClasses = array();
/**
* @param mixed $value Array or Traversable
* @param boolean $useTraversableKeys If TRUE, preserves keys from Traversables converted to arrays. Not recommended for ObjectStorages!
* @param boolean $preventRecursion If FALSE, allows recursion to occur which could potentially be fatal to the output unless managed
* @param mixed $recursionMarker Any value - string, integer, boolean, object or NULL - inserted instead of recursive instances of objects
* @param string $dateTimeFormat A date() format for converting DateTime values to JSON-compatible values. NULL means JS UNIXTIME (time()*1000)
* @return string
*/
public function render($value = NULL, $useTraversableKeys = FALSE, $preventRecursion = TRUE, $recursionMarker = NULL, $dateTimeFormat = NULL) {
if (NULL === $value) {
$value = $this->renderChildren();
if (TRUE === empty($value)) {
return '{}';
}
}
$json = $this->encodeValue($value, $useTraversableKeys, $preventRecursion, $recursionMarker, $dateTimeFormat);
return $json;
}
/**
* @param string $value
* @param boolean $useTraversableKeys
* @param boolean $preventRecursion
* @param string $recursionMarker
* @param string $dateTimeFormat
* @throws Exception
* @return mixed
*/
protected function encodeValue($value, $useTraversableKeys, $preventRecursion, $recursionMarker, $dateTimeFormat) {
if (TRUE === $value instanceof \Traversable) {
// Note: also converts Extbase ObjectStorage to \YourVendor\Extname\Domain\Model\ObjectType[] which are later each converted
$value = iterator_to_array($value, $useTraversableKeys);
} elseif (TRUE === $value instanceof DomainObjectInterface) {
// Convert to associative array,
$value = $this->recursiveDomainObjectToArray($value, $preventRecursion, $recursionMarker);
} elseif (TRUE === $value instanceof \DateTime) {
$value = $this->dateTimeToUnixtimeMiliseconds($value, $dateTimeFormat);
}
// process output of initial conversion, catching any specially supported object types such as DomainObject and DateTime
if (TRUE === is_array($value)) {
$value = $this->recursiveArrayOfDomainObjectsToArray($value, $preventRecursion, $recursionMarker);
$value = $this->recursiveDateTimeToUnixtimeMiliseconds($value, $dateTimeFormat);
};
$json = json_encode($value, JSON_HEX_AMP | JSON_HEX_QUOT | JSON_HEX_TAG);
if (JSON_ERROR_NONE !== json_last_error()) {
throw new Exception('The provided argument cannot be converted into JSON.', 1358440181);
}
return $json;
}
/**
* Converts any encountered DateTime instances to UNIXTIME timestamps
* which are then multiplied by 1000 to create a JavaScript appropriate
* time stamp - ready to be loaded into a Date object client-side.
*
* Works on already converted DomainObjects which are at this point just
* associative arrays of values - which might be DateTime instances.
*
* @param array $array
* @param string $dateTimeFormat
* @return array
*/
protected function recursiveDateTimeToUnixtimeMiliseconds(array $array, $dateTimeFormat) {
foreach ($array as $key => $possibleDateTime) {
if (TRUE === $possibleDateTime instanceof \DateTime) {
$array[$key] = $this->dateTimeToUnixtimeMiliseconds($possibleDateTime, $dateTimeFormat);
} elseif (TRUE === is_array($possibleDateTime)) {
$array[$key] = $this->recursiveDateTimeToUnixtimeMiliseconds($array[$key], $dateTimeFormat);
}
}
return $array;
}
/**
* Formats a single DateTime instance to whichever value is demanded by
* the format specified in $dateTimeFormat (DateTime::format syntax).
* Default format is NULL a JS UNIXTIME (time()*1000) is produced.
*
* @param \DateTime $dateTime
* @param string $dateTimeFormat
* @return integer
*/
protected function dateTimeToUnixtimeMiliseconds(\DateTime $dateTime, $dateTimeFormat) {
if (NULL === $dateTimeFormat) {
return intval($dateTime->format('U')) * 1000;
}
return $dateTime->format($dateTimeFormat);
}
/**
* Convert an array of possible DomainObject instances. The argument
* $possibleDomainObjects could also an associative array representation
* of another DomainObject - which means each value could potentially
* be another DomainObject, an ObjectStorage of DomainObjects or a simple
* value type. The type is checked and another recursive call is used to
* convert any nested objects.
*
* @param Tx_Extbase_DomainObject_DomainObjectInterface[] $domainObjects
* @param boolean $preventRecursion
* @param mixed $recursionMarker
* @return array
*/
protected function recursiveArrayOfDomainObjectsToArray(array $domainObjects, $preventRecursion, $recursionMarker) {
foreach ($domainObjects as $key => $possibleDomainObject) {
if (TRUE === $possibleDomainObject instanceof DomainObjectInterface) {
$domainObjects[$key] = $this->recursiveDomainObjectToArray($possibleDomainObject, $preventRecursion, $recursionMarker);
} elseif (TRUE === $possibleDomainObject instanceof \Traversable) {
$traversableAsArray = iterator_to_array($possibleDomainObject);
$domainObjects[$key] = $this->recursiveArrayOfDomainObjectsToArray($traversableAsArray, $preventRecursion, $recursionMarker);
}
}
return $domainObjects;
}
/**
* Convert a single DomainObject instance first to an array, then pass
* that array through recursive DomainObject detection. This will convert
* any 1:1, 1:n, n:1 and m:n relations.
*
* @param DomainObjectInterface $domainObject
* @param boolean $preventRecursion
* @param mixed $recursionMarker
* @return array
*/
protected function recursiveDomainObjectToArray(DomainObjectInterface $domainObject, $preventRecursion, $recursionMarker) {
$hash = spl_object_hash($domainObject);
if (TRUE === $preventRecursion && TRUE === in_array($hash, $this->encounteredClasses)) {
return $recursionMarker;
}
$converted = ObjectAccess::getGettableProperties($domainObject);
array_push($this->encounteredClasses, $hash);
$converted = $this->recursiveArrayOfDomainObjectsToArray($converted, $preventRecursion, $recursionMarker);
return $converted;
}
}

View File

@@ -0,0 +1,137 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Format;
/*
* 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\Cache\Frontend\StringFrontend;
use TYPO3\CMS\Core\Utility\CommandUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3\CMS\Fluid\Core\ViewHelper\Exception;
/**
* Markdown Transformation ViewHelper
*
* Requires an installed "markdown" utility, the specific
* implementation is less important since Markdown has no
* configuration options. However, the utility or shell
* scipt must:
*
* - accept input from STDIN
* - output to STDOUT
* - place errors in STDERR
* - be executable according to `open_basedir` and others
* - exist within (one or more of) TYPO3's configured executable paths
*
* In other words, *NIX standard behavior must be used.
*
* See: http://daringfireball.net/projects/markdown/
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers\Format
*/
class MarkdownViewHelper extends AbstractViewHelper {
/**
* @var boolean
*/
protected $escapingInterceptorEnabled = FALSE;
/**
* @var string
*/
protected $markdownExecutablePath;
/**
* @var StringFrontend
*/
protected $cache;
/**
* @return void
*/
public function initialize() {
$cacheManager = isset($GLOBALS['typo3CacheManager']) ? $GLOBALS['typo3CacheManager'] : GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Cache\\CacheManager');
$this->cache = $cacheManager->getCache('vhs_markdown');
}
/**
* @param string $text
* @param boolean $trim
* @param boolean $htmlentities
* @throws Exception
* @return string
*/
public function render($text = NULL, $trim = TRUE, $htmlentities = FALSE) {
if (NULL === $text) {
$text = $this->renderChildren();
}
if (NULL === $text) {
return NULL;
}
$cacheIdentifier = sha1($text);
if (TRUE === $this->cache->has($cacheIdentifier)) {
return $this->cache->get($cacheIdentifier);
}
$this->markdownExecutablePath = CommandUtility::getCommand('markdown');
if (FALSE === is_executable($this->markdownExecutablePath)) {
throw new Exception('Use of Markdown requires the "markdown" shell utility to be installed ' .
'and accessible; this binary could not be found in any of your configured paths available to this script', 1350511561);
}
if (TRUE === (boolean) $trim) {
$text = trim($text);
}
if (TRUE === (boolean) $htmlentities) {
$text = htmlentities($text);
}
$transformed = $this->transform($text);
$this->cache->set($cacheIdentifier, $transformed);
return $transformed;
}
/**
* @param string $text
* @throws Exception
* @return string
*/
public function transform($text) {
$descriptorspec = array(
0 => array('pipe', 'r'),
1 => array('pipe', 'w'),
2 => array('pipe', 'a')
);
$process = proc_open($this->markdownExecutablePath, $descriptorspec, $pipes, NULL, $GLOBALS['_ENV']);
stream_set_blocking($pipes[0], 1);
stream_set_blocking($pipes[1], 1);
stream_set_blocking($pipes[2], 1);
fwrite($pipes[0], $text);
fclose($pipes[0]);
$transformed = stream_get_contents($pipes[1]);
fclose($pipes[1]);
$errors = stream_get_contents($pipes[2]);
fclose($pipes[2]);
$exitCode = proc_close($process);
if ('' !== trim($errors)) {
throw new Exception('There was an error while executing ' . $this->markdownExecutablePath . '. The return code was ' .
$exitCode . ' and the message reads: ' . $errors, 1350514144);
}
return $transformed;
}
}

View File

@@ -0,0 +1,71 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Format\Placeholder;
/*
* 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\AbstractTagBasedViewHelper;
/**
* Placeholder Image ViewHelper
*
* Inserts a placeholder image from http://placehold.it/
*
* @author Claus Due
* @package Vhs
* @subpackage ViewHelpers\Format\Placeholder
*/
class ImageViewHelper extends AbstractTagBasedViewHelper {
/**
* @var string
*/
protected $tagName = 'img';
/**
* Initialize
*
* @return void
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerUniversalTagAttributes();
$this->registerArgument('width', 'integer', 'Width of rendered placeholder image', FALSE, 640);
$this->registerArgument('height', 'integer', 'Height of rendered placeholder image', FALSE, FALSE);
$this->registerArgument('backgroundColor', 'string', 'Background color', FALSE, '333333');
$this->registerArgument('textColor', 'string', 'Text color', FALSE, 'FFFFFF');
}
/**
* @param string $text
* @return string
*/
public function render($text = NULL) {
if (NULL === $text) {
$text = $this->renderChildren();
}
$height = $this->arguments['height'] != $this->arguments['width'] ? $this->arguments['height'] : NULL;
$addHeight = FALSE === empty($height) ? 'x' . $height : NULL;
$url = array(
'http://placehold.it',
$this->arguments['width'] . $addHeight,
$this->arguments['backgroundColor'],
$this->arguments['textColor'],
);
if (FALSE === empty($text)) {
array_push($url, '&text=' . urlencode($text));
}
$imageUrl = implode('/', $url);
$this->tag->forceClosingTag(FALSE);
$this->tag->addAttribute('src', $imageUrl);
$this->tag->addAttribute('alt', $imageUrl);
$this->tag->addAttribute('width', $this->arguments['width']);
$this->tag->addAttribute('height', FALSE === empty($height) ? $height : $this->arguments['width']);
return $this->tag->render();
}
}

View File

@@ -0,0 +1,169 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Format\Placeholder;
/*
* 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\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
/**
* Lipsum ViewHelper
*
* Renders Lorem Ipsum text according to provided arguments.
*
* @author Claus Due
* @package Vhs
* @subpackage ViewHelpers\Format\Placeholder
*/
class LipsumViewHelper extends AbstractViewHelper {
/**
* @var string
*/
protected $lipsum;
/**
* @var ContentObjectRenderer
*/
protected $contentObject;
/**
* @var ConfigurationManagerInterface
*/
protected $configurationManager;
/**
* @return void
*/
public function initialize() {
$this->lipsum = $this->getDefaultLoremIpsum();
}
/**
* @param ConfigurationManagerInterface $configurationManager
* @return void
*/
public function injectConfigurationManager(ConfigurationManagerInterface $configurationManager) {
$this->configurationManager = $configurationManager;
$this->contentObject = $this->configurationManager->getContentObject();
}
/**
* Initialize arguments
*/
public function initializeArguments() {
$this->registerArgument('paragraphs', 'integer', 'Number of paragraphs to output');
$this->registerArgument('wordsPerParagraph', 'integer', 'Number of words per paragraph');
$this->registerArgument('skew', 'integer', 'Amount in number of words to vary the number of words per paragraph');
$this->registerArgument('html', 'boolean', 'If TRUE, renders output as HTML paragraph tags in the same way an RTE would');
$this->registerArgument('parseFuncTSPath', 'string', 'If you want another parseFunc for HTML processing, enter the TS path here');
}
/**
* Renders Lorem Ipsum paragraphs. If $lipsum is provided it
* will be used as source text. If not provided as an argument
* or as inline argument, $lipsum is fetched from TypoScript settings.
*
* @param string $lipsum String of paragraphs file path or EXT:myext/path/to/file
* @return string
*/
public function render($lipsum = NULL) {
if (strlen($lipsum) === 0) {
$lipsum = $this->lipsum;
}
if ((strlen($lipsum) < 255 && !preg_match('/[^a-z0-9_\.\:\/]/i', $lipsum)) || 0 === strpos($lipsum, 'EXT:')) {
// argument is most likely a file reference.
$sourceFile = GeneralUtility::getFileAbsFileName($lipsum);
if (file_exists($sourceFile) === TRUE) {
$lipsum = file_get_contents($sourceFile);
} else {
GeneralUtility::sysLog('Vhs LipsumViewHelper was asked to load Lorem Ipsum from a file which does not exist. ' .
'The file was: ' . $sourceFile, 'vhs', GeneralUtility::SYSLOG_SEVERITY_WARNING);
$lipsum = $this->lipsum;
}
}
$lipsum = preg_replace('/[\\r\\n]{1,}/i', "\n", $lipsum);
$paragraphs = explode("\n", $lipsum);
$paragraphs = array_slice($paragraphs, 0, intval($this->arguments['paragraphs']));
foreach ($paragraphs as $index => $paragraph) {
$length = $this->arguments['wordsPerParagraph'] + rand(0 - intval($this->arguments['skew']), intval($this->arguments['skew']));
$words = explode(' ', $paragraph);
$paragraphs[$index] = implode(' ', array_slice($words, 0, $length));
}
$lipsum = implode("\n", $paragraphs);
if ((boolean) $this->arguments['html'] === TRUE) {
$tsParserPath = (FALSE === empty($this->arguments['parseFuncTSPath']) ? '< ' . $this->arguments['parseFuncTSPath'] : NULL);
$lipsum = $this->contentObject->parseFunc($lipsum, array(), $tsParserPath);
}
return $lipsum;
}
/**
* Get the default Lorem Ipsum. The compressed block (which is
* of course cleaned thoroughly to avoid any injection) contains
* 20 full paragraphs of Lorem Ipsum in standard latin. No bells
* and whistles there.
*
* @return string
*/
protected function getDefaultLoremIpsum() {
// Note: this MAY look suspicious but it really is just a whole lot of Lipsum
// in a compressed state. Just to make sure that you trust the block, we run
// strip_tags and htmlentities on the string before it is returned. This is not
// done on custom Lipsum - but it is done here at no risk, since we know the
// Lipsum to contain zero HTML and zero special characters, 100% ASCII. Source
// of the Lipsum text is http://www.lipsum.com set at 20 paragraphs, compressed
// through a small shell script.
$lipsum = 'eJy1WsuO7MYN3c9X6AOE+YGsDDsBDNhGAuNmX6PW9FSgR1tSzfeHr0Oyeu4iiOHFxe3plupBHh4esuqX/ZjXoT7Otg63fdmP4azXUNb5Godp3855uuarHUO51Uc9p7rdh3
mp1+vw96uWdVgKvT9fw+f8Uae2lKG06dqP1+E3+vFWp4uG5ecHeYY/PPazzcfMg9/b/Dr8Pt+Ga14f7RzO8qjzNsx3enir5zLMrZ7rfhsavVyOSo/st7oPa7muer4O//wo
57ws9HXdhm3+o83Dox3tHIcyXHWb6q1t17BWneTkidpBuxiKjF+HtixlnfbjMR/Dg0aat2s+eRhZwh80+zCfl75eV3rqVul7Xi3ZiXcz8r5u9U6jnXWlKejfvix1qle70a
J4iNfhJx6o+37dFxq4zthy5eXxBK8vL2w5Ms6trrQcMt/joxzzdZRhPvZz+KxXmfHw8O/6WVba/9tS6IVreCfz1zcx5b2qkX7eho8y0b83evM8yURLueZizuHdYZBsjRut
i8d5q2/zdmurOvT42LeJ7TtvtNttnsgIc32nJ/iDrndUH+gjYbS13LcysifLNNG8ZdOvhoUQRo+bUVdaYnkdfvl/UfnrfrxVcbVZthxToy2ytd/aQsMV8rGN/DjqWgU8br
f9mOqwNFkSrbot11Gn+QzUtre61DL82I4y/234vZ0P2n1lu2KFDJmBYkDCgKz/ftDyKtkWlqH5jnYdbbUYGAkmZFx+bS2Ei1Zu6uxRIfg+HwaFo570/q3VoSyVcHrxZ4qD
Y6cxC9AtL+kO92MrGmsnhUchA/0wb3PZxHc6qwVbDZz5pmUg39anWvg3hkmy4tjhxt8NYLSRXt3mrRAceZBh4xcI6D8e5QRHuJEUAbI+NdJrZ+THfpEpaM/yMiGGQH4xPC
aakMwjwUvIG2WmmFf90S/kVbxIYUFAUjO0bRoYkTtb7LNQwJ6wtFtOd1hpi7HmReDKMyZb2Bp9JHMzY7MW4koa+9GWz0oeGs1dFgtwrhhKKMajayBPSLQI7c1tWOqdaPd1
+FfTzRO16Wp0LHn7c1/a9aBfBD/GR5jbQ70dHKDbvvVUaCsrFGSE+yez0Zi6IH2ZeIZdUroU0Y0mJpj2lVh9J4zT+2HF+1E+K0G/Iz+JmPe53SujV5Ee3G6EpMQLoCKz/D
NP7AnqfV4yqmUNUztO2L2kkdgYnHHIZDsjMtHNsr/RThlI9XM+jvI0HYz6Tnaj+ei5lYL2O4G47oIySXc8nawHrMt0qVQvoMykfhFij+Atw7dErIFDsySi6OWlW1/BG/rf
yKSE7XMY/C95oxtQn+N0KwxMMUKMyz+ctE/Q6Tbbh+C5dzL0ycAx8qJtnTTf6/Dtsly31bcPXyabKIKYUdfzA6CFhLiQJY+d2cATgM2gVK9yheaHixGa7gLhIwkjT9oKIY
gTzfERE+5jsK/uTEzcxYKuLS+teNDQs7w5SQN/taVZ3aSkOmqCuRxu5vLRJJb447Mtj3YRIIyAxhRRZhfN231qUVL6E/mdVQgFxQf55iC+v2wzIWJAIuBQ1Y0kDMo8EWrO
CFxZGbtcQkujDGGLdC/POEOZOBNalp9GB1PxbXO+A1tKOFre/LU08pSvb9U/hVVIoiSlJKko5zPzlz8BGvSdYCyKDV0bheokRHLuN/Z/lsoP8kRlsws3ewJ9niRU7evwj0
bLCQaVNyVWxH8BBQa40L9iIkYXNhHRzaFsHktZxHyl8sbUl+k3BLKWCG5GWDvQv9bxOQVoiCeRMN8gO5A4bKVsOtmWy5aUhDRCYLfI/9B4gF5kMg4gcaMND1LxiIp8DhKA
ET0c71LNWI72jMf6i0QKiwhNvOwHdXpkxfQ01qaP0Zso2FTrKPsFA2mAQf9yvVH4FbZULDm59VuS86bkKM9xLrEyEIlKImFxemIDBdTI6xPFOY3V4Z5iMsVtuNGMY6NR6W
QY0cVr0rAcyHbiuRSWKYIU0lG5WQXKaTjByJOAIRMBp0D6qZPHsT6QnCjl1cpeJAEUj3dFqTOdL2V0bCqCbLAfLDlb2qZNjcluU0QnvzWCz0RscpRix/Ikr0fIQvjctu4a
Cy5jWhSMjaARdqzV4ezPsfMWjaw8lMkmuCGKGJPn0KQGG+USTMQGJpYrM+U0XSsrPI0VZOrAO9ja6hFYVnKwei7Aq64zFlFYOFuy9zX+1EalSXVmtiSxbZJKTZzydnCCmD
QZ25SlljwqvNsVCPbNCPkwXY0557lT3Vq0/PFZUUuGLmsyGOd9lpuWGMzEueNivzBpApFXKnokiMZUxAWZCZdLeSaWLSbRwdMZ0x4b4Sob4+UF9UozM3PdJTwDFWpORcGp
HMepcgwa1QIVfyEcEria1rZdAwASEc4kz48idxKYtTUB2q6bGo1GU5gWpU+LBU+yQs3GPBKgQXFcG0au4hkt3sSULhGM67D+0RbCAYDFWOaDGiM/Ow1oERYsBcPYkqR3Fj
JNpnxKwsVrUGaOpxYObR/Z4z/tvHZoGa9lNGq0cwEllZsotNXRBVCEpAVHX9GjJD6jPei6f9TUZJIlxaEYneLU2iuWW6NYFqSZnxo4GTGroiqSOBI+ixbuKEKlKExB0kVL
pVyzitks4e2RQ+rWBQc5rtMYygMGSjhDq0PWnZpLJCHsjh1JbX3bRklo6tuN3K3x3pgXtyzGjDIji3Vd0KNcOY1Bckh9aBTK5bjjWusxJT8oY4uYS8WYlUR5msg4BaSiMd
kyI0kXNxV6WggG3iSMPNtYGxEDe7kwivkknBzW5uexr4i7rpZO4UaKEkJCUNesusNSoDuMrSa9snIBhy6YordqnlHFC6WAXOjpyaUbh1Hk03J5YgSp1ltq7qHtrn2Zuccb
96LVqVLOaCqCKJLedxpTSM/kRN8DqmJTrYu/1w601EcOlkaHEhXbBmEELS2hGX5EDKKMD/YydGlaqGThg2OMozFWZhYEi/J8zpVWoo3Kiqh+MI64VCr2aDZob0SOJe6gQI
l9PScIOqO46jFte9RMBHu6todbBSbsADhck07fyeCguH3pqhG1IRcphhzwhEQox9AVaES3pHCByhE1i6I6O/PTTzySRFHTRtNW22Vdw8KdZhUWD1ImyCqNiiwauSLQbUgS
EvXsAgIsXPRHN1NgxroU+waR4/kn+xIix7hRChm8wECRDNOdDxnPJinuvGd6Dqcy3p2TeDAsBT27PeIr01smxYobxatd0b/BIVaheQIOzMvarThWKxIEUAX6MRk693kN32
k1NTST7XniABe0nAPkXOzG4c8G4STp2V0GI6u6zJHAuYyXPFicxrR4K34ONEvVg4U7B7qyi5M8y5XJLw4WfYUTl9rRBvdSxwUX+V4lja4bI2kVIJrTXPzUmhEqtlGlh9BS
hCdhEkuy3uEVneGUSo19TO8oMTta0rxhmHS6oP3Cy/VtRLRQnARfFiCChFGXYl1kNJK462WN6e/Ec4p+i1MBHdsKvRtfc1YhdnrlfaWXl5zQrY8eOrToSWOL+DXwY4NkfM
sr6LdBdKL70C1bLa8VPmCKpO3VxOijM72YXlRBAwGuKYe3LCbttkjBEK1J9Z+NFy5LZb1gxvj2a0erKgGi7ML7lggkMMcwDk3t50b75oAtUbdFecgCFR11z0P1lspmAbbV
jfIno+7lJeVWa5hK89YAq1klwaPkTu2aam7zG2ztyOcz0BzSPz8dlUat5RW8lv4QCtaw0MLQTk7UvugzbKgoEYDP6AbM6hZL1kkU3Yk2uKTg5u2sLIF+msER9T485LIRtB
25ObrCdw1ipV3YBz4KZiJnSVQTcek5xpck/3WbOdpyn9MUtmQ8bYR651drIul8cIKnD0Kp3qGyUaBgtXWtnKCHWTSkZYz+UPHmx9XaGw7MYM+6nSniwRoXFScyoVPjLA79
kjhN8K9C8Cu0sjWggkZNs64unytOwwMUG9USwp89BeihRoX8ZvdETojyEvv8ThGYzgJOnIIJvjz76XegTMuF6O+g9MCvWs9wI1cjwQsQdicOwxhMqTqwMZTCVLIFOUj32c
+V2tV3Ir2cNrZJ6ZhTBGsQFvlIqA2fRCH0SS4SqMoWq8NSAubiFtvhmjFBYYFISY7uJJvh3vSZULGmrxzL7EP0eqWJ7gWAWTOEZ16XOqihzZJ2QhtWaFMsjPacVwhmOpfC
fq9HcpewmizOkrOG1p84Tsu2yWfHzZUm9JidfaY0BgYxOzTrKKQmudBvAEU5zft63zpJM6pl2W3Jh7DaqJlBuBaVn7Rl7A6SN3fXdBdDigOuxLdJa03RH5KkEjxz0wAdDa
VYJKU44U/9VbtkoSIlKT1XqnrxCHHfUQSS1ZfzCHOr6RFbTwkaAkE0uxsiC1E54Gn86faOVVAe0ZaqncLs+ElgCB3oPGQlv0hjrdfjlMPukngu1KtK/TUevzfR6R39mGsr
w//zJYV0JmKy+R5nex12FPqMEdiWPnM9YhYV5glOZgGEtVlHypulSnk1t2dQz0UuMSkYjEtW8sascCnZzkryJNG5X+dNSfWDNTbmK8l/JWxVU7xE2bzNgosB1dSpSwdQhW
xQyF57QiZeNxsP50nYUxx+zH6agEAxRHnEqsH8BqIBVNokKRmb2lT1YdZNpwNiMFNvzg3al2OAoUHoN6MsueG0z5Ui2vbeIuLjKpFPT9TenUd9OVDB9QUKqe4cqL/xFGdi
eoLJ4Ep5rXVoFu0culfUkx6M5TL9+9eRSN6k7qIfMQASX3oQXVGMtmPSfLjuBzGGeNjszJMznOLW8FUMbkptifPaZV13sSQyF0qOdN9Rk+hobN164Wdc6m4V8RrHTGxXQp
N3EXiZfUJDMIQpga2uYYtOVQwtd838NhauhXzLF9AYQu16hr9u1C42SO8/kznuFkuu8wtkKoFbuoDxm3Cvn8OMziHcxkfZKgc+egBghffP+bZb9GrsmjORQPza31VR4fKl
BugvORmsyOJaRIQ8yH3I1EG2Y/+/6jqtrg4/xnazRv4v3i04aA==';
$uncompressed = gzuncompress(base64_decode($lipsum));
$safe = htmlentities(strip_tags($uncompressed));
return $safe;
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Format;
/*
* 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;
/**
* Processes output as plaintext. Will trim whitespace off
* each line that is provided, making display in a <pre>
* work correctly indented even if the source is not.
*
* Expects that you use f:format.htmlentities or similar
* if you do not want HTML to be displayed as HTML, or
* simply want it stripped out.
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers\Format
*/
class PlaintextViewHelper extends AbstractViewHelper {
/**
* Trims content, then trims each line of content
*
* @param string $content
* @return string
*/
public function render($content = NULL) {
if (NULL === $content) {
$content = $this->renderChildren();
}
$content = trim($content);
$lines = explode("\n", $content);
$lines = array_map('trim', $lines);
return implode(LF, $lines);
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Format;
/*
* 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;
/**
* ### Format: Prepend string content
*
* Prepends one string on another. Although this task is very
* easily done in standard Fluid - i.e. {add}{subject} - this
* ViewHelper makes advanced chained inline processing possible:
*
* <!-- Adds 1H to DateTime, formats using timestamp input which requires prepended @ -->
* {dateTime.timestamp
* -> v:math.sum(b: 3600)
* -> v:format.prepend(add: '@')
* -> v:format.date(format: 'Y-m-d H:i')}
* <!-- You don't have to break the syntax into lines; done here for display only -->
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers\Format
*/
class PrependViewHelper extends AbstractViewHelper {
/**
* @param string $add
* @param string $subject
* @return string
*/
public function render($add, $subject = NULL) {
if (NULL === $subject) {
$subject = $this->renderChildren();
}
return $add . $subject;
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Format;
/*
* 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;
/**
* Formats or gets detected substrings by regular expression.
* Returns matches if `return="TRUE"`, otherwise replaces any
* occurrences of `$pattern`.
* simply returns
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers\Format
*/
class RegularExpressionViewHelper extends AbstractViewHelper {
/**
* @param string $pattern The regular expression pattern to search for
* @param string $replacement The desired value to insert instead of detected matches
* @param string $subject The subject in which to perform replacements/detection; taken from tag content if not specified.
* @param boolean $return
* @return mixed
*/
public function render($pattern, $replacement, $subject = NULL, $return = FALSE) {
if (NULL === $subject) {
$subject = $this->renderChildren();
}
if (TRUE === $return) {
$returnValue = array();
preg_match($pattern, $subject, $returnValue);
} else {
$returnValue = preg_replace($pattern, $replacement, $subject);
}
return $returnValue;
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Format;
/*
* 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;
/**
* Removes XSS from $string
*
* Class RemoveXssViewHelper
* @package Vhs
* @subpackage ViewHelpers\Format
*/
class RemoveXssViewHelper extends AbstractViewHelper {
/**
* Removes XSS from string
*
* @param string $string
* @return string
*/
public function render($string = NULL) {
if (NULL === $string) {
$string = $this->renderChildren();
}
return GeneralUtility::removeXSS($string);
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Format;
/*
* 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;
/**
* Replaces $substring in $content with $replacement.
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers\Format
*/
class ReplaceViewHelper extends AbstractViewHelper {
/**
* @param string $substring
* @param string $content
* @param string $replacement
* @param integer $count
* @param boolean $caseSensitve
* @return string
*/
public function render($substring, $content = NULL, $replacement = '', $count = NULL, $caseSensitive = TRUE) {
if (NULL === $content) {
$content = $this->renderChildren();
}
$function = (TRUE === $caseSensitive ? 'str_replace' : 'str_ireplace');
return str_replace($substring, $replacement, $content, $count);
}
}

View File

@@ -0,0 +1,144 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Format;
/*
* 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;
/**
* URL text segment sanitizer. Sanitizes the content into a
* valid URL segment value which is usable in an URL without
* further processing. For example, the text "I am Mr. Brown,
* how are you?" becomes "i-am-mr-brown-how-are-you". Special
* characters like diacritics or umlauts are transliterated.
* The built-in character map can be overriden or extended by
* providing an associative array of custom mappings.
*
* Also useful when creating anchor link names, for example
* for news entries in your custom EXT:news list template, in
* which case each news item's title would become an anchor:
*
* <a name="{newsItem.title -> v:format.url.sanitizeString()}"></a>
*
* And links would look much like the detail view links:
*
* /news/#this-is-a-newsitem-title
*
* When used with list views it has the added benefit of not
* breaking if the item referenced is removed, it can be read
* by Javascript (for example to dynamically expand the news
* item being referenced). The sanitized urls are also ideal
* to use for AJAX based detail views - and in almot all cases
* the sanitized string will be 100% identical to the one used
* by Realurl when translating using table lookups.
*
* @author Claus Due <claus@namelesscoder.net>
* @author Björn Fromme <fromme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Format
*/
class SanitizeStringViewHelper extends AbstractViewHelper {
/**
* Basic character map
*
* @var array
*/
protected $characterMap = array(
'¹' => 1, '²' => 2, '³' => 3, '°' => 0, '€' => 'eur', 'æ' => 'ae', 'ǽ' => 'ae', 'À' => 'A', 'Á' => 'A', 'Â' => 'A',
'Ã' => 'A', 'Å' => 'AA', 'Ǻ' => 'A', 'Ă' => 'A', 'Ǎ' => 'A', 'Æ' => 'AE', 'Ǽ' => 'AE', 'à' => 'a', 'á' => 'a',
'â' => 'a', 'ã' => 'a', 'å' => 'aa', 'ǻ' => 'a', 'ă' => 'a', 'ǎ' => 'a', 'ª' => 'a', '@' => 'at', 'Ĉ' => 'C',
'Ċ' => 'C', 'ĉ' => 'c', 'ċ' => 'c', '©' => 'c', 'Ð' => 'Dj', 'Đ' => 'D', 'ð' => 'dj', 'đ' => 'd', 'È' => 'E',
'É' => 'E', 'Ê' => 'E', 'Ë' => 'E', 'Ĕ' => 'E', 'Ė' => 'E', 'è' => 'e', 'é' => 'e', 'ê' => 'e', 'ë' => 'e',
'ĕ' => 'e', 'ė' => 'e', 'ƒ' => 'f', 'Ĝ' => 'G', 'Ġ' => 'G', 'ĝ' => 'g', 'ġ' => 'g', 'Ĥ' => 'H', 'Ħ' => 'H',
'ĥ' => 'h', 'ħ' => 'h', 'Ì' => 'I', 'Í' => 'I', 'Î' => 'I', 'Ï' => 'I', 'Ĩ' => 'I', 'Ĭ' => 'I', 'Ǐ' => 'I',
'Į' => 'I', 'IJ' => 'IJ', 'ì' => 'i', 'í' => 'i', 'î' => 'i', 'ï' => 'i', 'ĩ' => 'i', 'ĭ' => 'i', 'ǐ' => 'i',
'į' => 'i', 'ij' => 'ij', 'Ĵ' => 'J', 'ĵ' => 'j', 'Ĺ' => 'L', 'Ľ' => 'L', 'Ŀ' => 'L', 'ĺ' => 'l', 'ľ' => 'l',
'ŀ' => 'l', 'Ñ' => 'N', 'ñ' => 'n', 'ʼn' => 'n', 'Ò' => 'O', 'Ô' => 'O', 'Õ' => 'O', 'Ō' => 'O', 'Ŏ' => 'O',
'Ǒ' => 'O', 'Ő' => 'O', 'Ơ' => 'O', 'Ø' => 'OE', 'Ǿ' => 'O', 'Œ' => 'OE', 'ò' => 'o', 'ô' => 'o', 'õ' => 'o',
'ō' => 'o', 'ŏ' => 'o', 'ǒ' => 'o', 'ő' => 'o', 'ơ' => 'o', 'ø' => 'oe', 'ǿ' => 'o', 'º' => 'o', 'œ' => 'oe',
'Ŕ' => 'R', 'Ŗ' => 'R', 'ŕ' => 'r', 'ŗ' => 'r', 'Ŝ' => 'S', 'Ș' => 'S', 'ŝ' => 's', 'ș' => 's', 'ſ' => 's',
'Ţ' => 'T', 'Ț' => 'T', 'Ŧ' => 'T', 'Þ' => 'TH', 'ţ' => 't', 'ț' => 't', 'ŧ' => 't', 'þ' => 'th', 'Ù' => 'U',
'Ú' => 'U', 'Û' => 'U', 'Ũ' => 'U', 'Ŭ' => 'U', 'Ű' => 'U', 'Ų' => 'U', 'Ư' => 'U', 'Ǔ' => 'U', 'Ǖ' => 'U',
'Ǘ' => 'U', 'Ǚ' => 'U', 'Ǜ' => 'U', 'ù' => 'u', 'ú' => 'u', 'û' => 'u', 'ũ' => 'u', 'ŭ' => 'u', 'ű' => 'u',
'ų' => 'u', 'ư' => 'u', 'ǔ' => 'u', 'ǖ' => 'u', 'ǘ' => 'u', 'ǚ' => 'u', 'ǜ' => 'u', 'Ŵ' => 'W', 'ŵ' => 'w',
'Ý' => 'Y', 'Ÿ' => 'Y', 'Ŷ' => 'Y', 'ý' => 'y', 'ÿ' => 'y', 'ŷ' => 'y', 'Ъ' => '', 'Ь' => '', 'А' => 'A',
'Б' => 'B', 'Ц' => 'C', 'Ч' => 'Ch', 'Д' => 'D', 'Е' => 'E', 'Ё' => 'E', 'Э' => 'E', 'Ф' => 'F', 'Г' => 'G',
'Х' => 'H', 'И' => 'I', 'Й' => 'J', 'Я' => 'Ja', 'Ю' => 'Ju', 'К' => 'K', 'Л' => 'L', 'М' => 'M', 'Н' => 'N',
'О' => 'O', 'П' => 'P', 'Р' => 'R', 'С' => 'S', 'Ш' => 'Sh', 'Щ' => 'Shch', 'Т' => 'T', 'У' => 'U', 'В' => 'V',
'Ы' => 'Y', 'З' => 'Z', 'Ж' => 'Zh', 'ъ' => '', 'ь' => '', 'а' => 'a', 'б' => 'b', 'ц' => 'c', 'ч' => 'ch',
'д' => 'd', 'е' => 'e', 'ё' => 'e', 'э' => 'e', 'ф' => 'f', 'г' => 'g', 'х' => 'h', 'и' => 'i', 'й' => 'j',
'я' => 'ja', 'ю' => 'ju', 'к' => 'k', 'л' => 'l', 'м' => 'm', 'н' => 'n', 'о' => 'o', 'п' => 'p', 'р' => 'r',
'с' => 's', 'ш' => 'sh', 'щ' => 'shch', 'т' => 't', 'у' => 'u', 'в' => 'v', 'ы' => 'y', 'з' => 'z', 'ж' => 'zh',
'Ä' => 'AE', 'Ö' => 'OE', 'Ü' => 'UE', 'ß' => 'ss', 'ä' => 'ae', 'ö' => 'oe', 'ü' => 'ue', 'Ç' => 'C', 'Ğ' => 'G',
'İ' => 'I', 'Ş' => 'S', 'ç' => 'c', 'ğ' => 'g', 'ı' => 'i', 'ş' => 's', 'Ā' => 'A', 'Ē' => 'E', 'Ģ' => 'G',
'Ī' => 'I', 'Ķ' => 'K', 'Ļ' => 'L', 'Ņ' => 'N', 'Ū' => 'U', 'ā' => 'a', 'ē' => 'e', 'ģ' => 'g', 'ī' => 'i',
'ķ' => 'k', 'ļ' => 'l', 'ņ' => 'n', 'ū' => 'u', 'Ґ' => 'G', 'І' => 'I', 'Ї' => 'Ji', 'Є' => 'Ye', 'ґ' => 'g',
'і' => 'i', 'ї' => 'ji', 'є' => 'ye', 'Č' => 'C', 'Ď' => 'D', 'Ě' => 'E', 'Ň' => 'N', 'Ř' => 'R', 'Š' => 'S',
'Ť' => 'T', 'Ů' => 'U', 'Ž' => 'Z', 'č' => 'c', 'ď' => 'd', 'ě' => 'e', 'ň' => 'n', 'ř' => 'r', 'š' => 's',
'ť' => 't', 'ů' => 'u', 'ž' => 'z', 'Ą' => 'A', 'Ć' => 'C', 'Ę' => 'E', 'Ł' => 'L', 'Ń' => 'N', 'Ó' => 'O',
'Ś' => 'S', 'Ź' => 'Z', 'Ż' => 'Z', 'ą' => 'a', 'ć' => 'c', 'ę' => 'e', 'ł' => 'l', 'ń' => 'n', 'ó' => 'o',
'ś' => 's', 'ź' => 'z', 'ż' => 'z', 'Α' => 'A', 'Β' => 'B', 'Γ' => 'G', 'Δ' => 'D', 'Ε' => 'E', 'Ζ' => 'Z',
'Η' => 'E', 'Θ' => 'Th', 'Ι' => 'I', 'Κ' => 'K', 'Λ' => 'L', 'Μ' => 'M', 'Ν' => 'N', 'Ξ' => 'X', 'Ο' => 'O',
'Π' => 'P', 'Ρ' => 'R', 'Σ' => 'S', 'Τ' => 'T', 'Υ' => 'Y', 'Φ' => 'Ph', 'Χ' => 'Ch', 'Ψ' => 'Ps', 'Ω' => 'O',
'Ϊ' => 'I', 'Ϋ' => 'Y', 'ά' => 'a', 'έ' => 'e', 'ή' => 'e', 'ί' => 'i', 'ΰ' => 'Y', 'α' => 'a', 'β' => 'b',
'γ' => 'g', 'δ' => 'd', 'ε' => 'e', 'ζ' => 'z', 'η' => 'e', 'θ' => 'th', 'ι' => 'i', 'κ' => 'k', 'λ' => 'l',
'μ' => 'm', 'ν' => 'n', 'ξ' => 'x', 'ο' => 'o', 'π' => 'p', 'ρ' => 'r', 'ς' => 's', 'σ' => 's', 'τ' => 't',
'υ' => 'y', 'φ' => 'ph', 'χ' => 'ch', 'ψ' => 'ps', 'ω' => 'o', 'ϊ' => 'i', 'ϋ' => 'y', 'ό' => 'o', 'ύ' => 'y',
'ώ' => 'o', 'ϐ' => 'b', 'ϑ' => 'th', 'ϒ' => 'Y', 'أ' => 'a', 'ب' => 'b', 'ت' => 't', 'ث' => 'th', 'ج' => 'g',
'ح' => 'h', 'خ' => 'kh', 'د' => 'd', 'ذ' => 'th', 'ر' => 'r', 'ز' => 'z', 'س' => 's', 'ش' => 'sh', 'ص' => 's',
'ض' => 'd', 'ط' => 't', 'ظ' => 'th', 'ع' => 'aa', 'غ' => 'gh', 'ف' => 'f', 'ق' => 'k', 'ك' => 'k', 'ل' => 'l',
'م' => 'm', 'ن' => 'n', 'ه' => 'h', 'و' => 'o', 'ي' => 'y', 'ạ' => 'a', 'ả' => 'a', 'ầ' => 'a', 'ấ' => 'a',
'ậ' => 'a', 'ẩ' => 'a', 'ẫ' => 'a', 'ằ' => 'a', 'ắ' => 'a', 'ặ' => 'a', 'ẳ' => 'a', 'ẵ' => 'a', 'ẹ' => 'e',
'ẻ' => 'e', 'ẽ' => 'e', 'ề' => 'e', 'ế' => 'e', 'ệ' => 'e', 'ể' => 'e', 'ễ' => 'e', 'ị' => 'i', 'ỉ' => 'i',
'ọ' => 'o', 'ỏ' => 'o', 'ồ' => 'o', 'ố' => 'o', 'ộ' => 'o', 'ổ' => 'o', 'ỗ' => 'o', 'ờ' => 'o', 'ớ' => 'o',
'ợ' => 'o', 'ở' => 'o', 'ỡ' => 'o', 'ụ' => 'u', 'ủ' => 'u', 'ừ' => 'u', 'ứ' => 'u', 'ự' => 'u', 'ử' => 'u',
'ữ' => 'u', 'ỳ' => 'y', 'ỵ' => 'y', 'ỷ' => 'y', 'ỹ' => 'y', 'Ạ' => 'A', 'Ả' => 'A', 'Ầ' => 'A', 'Ấ' => 'A',
'Ậ' => 'A', 'Ẩ' => 'A', 'Ẫ' => 'A', 'Ằ' => 'A', 'Ắ' => 'A', 'Ặ' => 'A', 'Ẳ' => 'A', 'Ẵ' => 'A', 'Ẹ' => 'E',
'Ẻ' => 'E', 'Ẽ' => 'E', 'Ề' => 'E', 'Ế' => 'E', 'Ệ' => 'E', 'Ể' => 'E', 'Ễ' => 'E', 'Ị' => 'I', 'Ỉ' => 'I',
'Ọ' => 'O', 'Ỏ' => 'O', 'Ồ' => 'O', 'Ố' => 'O', 'Ộ' => 'O', 'Ổ' => 'O', 'Ỗ' => 'O', 'Ờ' => 'O', 'Ớ' => 'O',
'Ợ' => 'O', 'Ở' => 'O', 'Ỡ' => 'O', 'Ụ' => 'U', 'Ủ' => 'U', 'Ừ' => 'U', 'Ứ' => 'U', 'Ự' => 'U', 'Ử' => 'U',
'Ữ' => 'U', 'Ỳ' => 'Y', 'Ỵ' => 'Y', 'Ỷ' => 'Y', 'Ỹ' => 'Y',
);
/**
* Initialize
*
* @return void
*/
public function initializeArguments() {
$this->registerArgument('string', 'string', 'The string to sanitize.', FALSE);
$this->registerArgument('customMap', 'array', 'Associative array of additional characters to replace or use to override built-in mappings.', FALSE);
}
/**
* @return string
*/
public function render() {
$string = $this->arguments['string'];
if (NULL === $string) {
$string = $this->renderChildren();
if (NULL === $string) {
return NULL;
}
}
$characterMap = $this->characterMap;
$customMap = $this->arguments['customMap'];
if (TRUE === is_array($customMap) && 0 < count($customMap)) {
$characterMap = array_merge($characterMap, $customMap);
}
$specialCharsSearch = array_keys($characterMap);
$specialCharsReplace = array_values($characterMap);
$string = str_replace($specialCharsSearch, $specialCharsReplace, $string);
$string = strtolower($string);
$pattern = '/([^a-z0-9\-]){1,}/';
$string = preg_replace($pattern, '-', $string);
return trim($string, '-');
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Format;
/*
* 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;
/**
* Gets a substring from a string or string-compatible value
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers\Format
*/
class SubstringViewHelper extends AbstractViewHelper {
/**
* Substrings a string or string-compatible value
*
* @param string $content Content string to substring
* @param integer $start Positive or negative offset
* @param integer $length Positive or negative length
* @return string
*/
public function render($content = NULL, $start = 0, $length = NULL) {
if (NULL === $content) {
$content = $this->renderChildren();
}
if (NULL !== $length) {
return substr($content, $start, $length);
}
return substr($content, $start);
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Format;
/*
* 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;
/**
* Tidy-processes a string (HTML source), applying proper
* indentation.
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers\Format
*/
class TidyViewHelper extends AbstractViewHelper {
/**
* @var boolean
*/
protected $hasTidy = FALSE;
/**
* @return void
*/
public function initialize() {
$this->hasTidy = class_exists('tidy');
}
/**
* Trims content, then trims each line of content
*
* @param string $content
* @param string $encoding
* @throws \RuntimeException
* @return string
*/
public function render($content = NULL, $encoding = 'utf8') {
if (NULL === $content) {
$content = $this->renderChildren();
}
if (TRUE === $this->hasTidy) {
$tidy = tidy_parse_string($content, array(), $encoding);
$tidy->cleanRepair();
return (string) $tidy;
}
throw new \RuntimeException('TidyViewHelper requires the PHP extension "tidy" which is not installed or not loaded.', 1352059753);
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Format;
/*
* 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;
/**
* Trims $content by stripping off $characters (string list
* of individual chars to strip off, default is all whitespaces).
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers\Format
*/
class TrimViewHelper extends AbstractViewHelper {
/**
* Trims content by stripping off $characters
*
* @param string $content
* @param string $characters
* @return string
*/
public function render($content = NULL, $characters = NULL) {
if (NULL === $content) {
$content = $this->renderChildren();
}
if (FALSE === empty($characters)) {
$content = trim($content, $characters);
} else {
$content = trim($content);
}
return $content;
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Format\Url;
/*
* 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;
/**
* Urldecodes the provided string
*
* @author Björn Fromme <fromme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Format
*/
class DecodeViewHelper extends AbstractViewHelper {
/**
* @param string $content
* @return string
*/
public function render($content = NULL) {
if (NULL === $content) {
$content = $this->renderChildren();
}
return rawurldecode($content);
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Format\Url;
/*
* 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;
/**
* Urlencodes the provided string
*
* @author Björn Fromme <fromme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Format
*/
class EncodeViewHelper extends AbstractViewHelper {
/**
* @param string $content
* @return string
*/
public function render($content = NULL) {
if (NULL === $content) {
$content = $this->renderChildren();
}
return rawurlencode($content);
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Format\Url;
/*
* 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\Format\SanitizeStringViewHelper as RelocatedSanitizeStringViewHelper;
/**
* @author Claus Due <claus@namelesscoder.net>
* @author Björn Fromme <fromme@dreipunktnull.com>, dreipunktnull
* @package Vhs
* @subpackage ViewHelpers\Format\Url
* @deprecated Use FluidTYPO3\Vhs\ViewHelpers\Format\SanitizeStringViewHelper instead
*/
class SanitizeStringViewHelper extends RelocatedSanitizeStringViewHelper {
}

View File

@@ -0,0 +1,68 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Format;
/*
* 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;
/**
* ### Wordwrap: Wrap a string at provided character count
*
* Wraps a string to $limit characters and at $break character
* while maintaining complete words. Concatenates the resulting
* strings with $glue. Code is heavily inspired
* by Codeigniter's word_wrap helper.
*
* @author Björn Fromme <fromme@dreipunktnull.com>
* @package Vhs
* @subpackage ViewHelpers\Format
*/
class WordWrapViewHelper extends AbstractViewHelper {
public function initializeArguments() {
$this->registerArgument('subject', 'string', 'Text to wrap', FALSE);
$this->registerArgument('limit', 'integer', 'Maximum length of resulting parts after wrapping', FALSE, 80);
$this->registerArgument('break', 'string', 'Character to wrap text at', FALSE, PHP_EOL);
$this->registerArgument('glue', 'string', 'Character to concatenate parts with after wrapping', FALSE, PHP_EOL);
}
/**
* @return string
*/
public function render() {
$subject = $this->arguments['subject'];
if (TRUE === empty($subject)) {
$subject = $this->renderChildren();
}
$limit = (integer) $this->arguments['limit'];
$break = $this->arguments['break'];
$glue = $this->arguments['glue'];
$subject = preg_replace('/ +/', ' ', $subject);
$subject = str_replace(array("\r\n", "\r"), PHP_EOL, $subject);
$subject = wordwrap($subject, $limit, $break, FALSE);
$output = '';
foreach (explode($break, $subject) as $line) {
if (mb_strlen($line) <= $limit) {
$output .= $line . $glue;
continue;
}
$temp = '';
while (mb_strlen($line) > $limit) {
$temp .= mb_substr($line, 0, $limit - 1);
$line = mb_substr($line, $limit - 1);
}
if (FALSE === empty($temp)) {
$output .= $temp . $glue . $line . $glue;
} else {
$output .= $line . $glue;
}
}
return $output;
}
}

View File

@@ -0,0 +1,173 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers;
/*
* 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\Parser\SyntaxTree\BooleanNode;
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractConditionViewHelper;
use FluidTYPO3\Vhs\Traits\ConditionViewHelperTrait;
/**
* @author Danilo Bürger <danilo.buerger@hmspl.de>, Heimspiel GmbH
* @package Vhs
* @subpackage ViewHelpers
*/
class IfViewHelper extends AbstractConditionViewHelper {
use ConditionViewHelperTrait;
const OPERATOR_IS_EQUAL = '==';
const OPERATOR_IS_NOT_EQUAL = '!=';
const OPERATOR_IS_GREATER_OR_EQUAL = '>=';
const OPERATOR_IS_SMALLER_OR_EQUAL = '<=';
const OPERATOR_IS_SMALLER = '<';
const OPERATOR_IS_GREATER = '>';
const OPERATOR_LOGICAL_AND = 'AND';
const OPERATOR_LOGICAL_OR = 'OR';
const OPERATOR_BOOLEAN_AND = '&&';
const OPERATOR_BOOLEAN_OR = '||';
/**
* @var array
*/
static protected $comparisonOperators = array(
self::OPERATOR_IS_EQUAL => self::OPERATOR_IS_EQUAL,
self::OPERATOR_IS_NOT_EQUAL => self::OPERATOR_IS_NOT_EQUAL,
self::OPERATOR_IS_GREATER_OR_EQUAL => self::OPERATOR_IS_GREATER_OR_EQUAL,
self::OPERATOR_IS_SMALLER_OR_EQUAL => self::OPERATOR_IS_SMALLER_OR_EQUAL,
self::OPERATOR_IS_SMALLER => self::OPERATOR_IS_SMALLER,
self::OPERATOR_IS_GREATER => self::OPERATOR_IS_GREATER
);
/**
* @var array
*/
static protected $logicalOperators = array(
self::OPERATOR_LOGICAL_AND => self::OPERATOR_LOGICAL_AND,
self::OPERATOR_LOGICAL_OR => self::OPERATOR_LOGICAL_OR,
self::OPERATOR_BOOLEAN_AND => self::OPERATOR_LOGICAL_AND,
self::OPERATOR_BOOLEAN_OR => self::OPERATOR_LOGICAL_OR
);
/**
* Lower value means less precedence
*
* @var array
*/
static protected $operatorPrecedence = array(
self::OPERATOR_LOGICAL_OR => 0,
self::OPERATOR_LOGICAL_AND => 1
);
/**
* Initialize
*
* @return void
* @api
*/
public function initializeArguments() {
self::registerArgument('stack', 'array', 'The stack to be evaluated', TRUE);
}
/**
* This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality.
*
* @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 TRUE === self::evaluateStack($arguments['stack']);
}
/**
* @throws \RuntimeException
* @param array $stack
* @return boolean
*/
static protected function evaluateStack(array $stack) {
$stackCount = count($stack);
if (0 === $stackCount) {
return FALSE;
} elseif (1 === $stackCount) {
return BooleanNode::convertToBoolean(reset($stack));
} elseif (3 === $stackCount) {
list ($leftSide, $operator, $rightSide) = array_values($stack);
if (TRUE === is_string($operator) && TRUE === isset(self::$comparisonOperators[$operator])) {
$operator = self::$comparisonOperators[$operator];
return BooleanNode::evaluateComparator($operator, $leftSide, $rightSide);
}
}
$operator = FALSE;
$operatorPrecedence = PHP_INT_MAX;
$operatorIndex = FALSE;
foreach ($stack as $index => $element) {
if (TRUE === is_string($element) && TRUE === isset(self::$logicalOperators[$element])) {
$currentOperator = self::$logicalOperators[$element];
$currentOperatorPrecedence = self::$operatorPrecedence[$currentOperator];
if ($currentOperatorPrecedence <= $operatorPrecedence) {
$operator = $currentOperator;
$operatorPrecedence = $currentOperatorPrecedence;
$operatorIndex = $index;
}
}
}
if (FALSE === $operator) {
throw new \RuntimeException('The stack was not comparable and did not include any logical operators.', 1385071197);
}
$operatorIndex = array_search($operatorIndex, array_keys($stack));
if (0 === $operatorIndex || $operatorIndex + 1 >= $stackCount) {
throw new \RuntimeException('The stack may not contain a logical operator at the first or last element.', 1385072228);
}
$leftSide = array_slice($stack, 0, $operatorIndex);
$rightSide = array_slice($stack, $operatorIndex + 1);
return self::evaluateLogicalOperator($leftSide, $operator, $rightSide);
}
/**
* @throws \RuntimeException
* @param array $leftSide
* @param string $operator
* @param array $rightSide
* @return boolean
*/
static protected function evaluateLogicalOperator(array $leftSide, $operator, array $rightSide) {
$leftCondition = self::evaluateStack(self::prepareSideForEvaluation($leftSide));
$rightCondition = self::evaluateStack(self::prepareSideForEvaluation($rightSide));
if (self::OPERATOR_LOGICAL_AND === $operator) {
return $leftCondition && $rightCondition;
} elseif (self::OPERATOR_LOGICAL_OR === $operator) {
return $leftCondition || $rightCondition;
}
throw new \RuntimeException('The stack could not be evaluated (internal error).', 1385072357);
}
/**
* @param array $side
* @return array
*/
static protected function prepareSideForEvaluation(array $side) {
if (1 === count($side)) {
$element = reset($side);
if (TRUE === is_array($element)) {
return $element;
}
}
return $side;
}
}

View File

@@ -0,0 +1,79 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Iterator;
/*
* 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;
/**
* Abstract class with basic functionality for loop view helpers.
*
* @author Danilo Bürger <danilo.buerger@hmspl.de>, Heimspiel GmbH
* @package Vhs
* @subpackage ViewHelpers\Iterator
*/
abstract class AbstractLoopViewHelper extends AbstractViewHelper {
/**
* Initialize
*
* @return void
*/
public function initializeArguments() {
$this->registerArgument('iteration', 'string', 'Variable name to insert result into, suppresses output', FALSE, NULL);
}
/**
* @param integer $i
* @param integer $from
* @param integer $to
* @param integer $step
* @param string $iterationArgument
* @return string
*/
protected function renderIteration($i, $from, $to, $step, $iterationArgument) {
if (FALSE === empty($iterationArgument)) {
$cycle = intval(($i - $from) / $step) + 1;
$iteration = array(
'index' => $i,
'cycle' => $cycle,
'isOdd' => (0 === $cycle % 2 ? FALSE : TRUE),
'isEven' => (0 === $cycle % 2 ? TRUE : FALSE),
'isFirst' => ($i === $from ? TRUE : FALSE),
'isLast' => $this->isLast($i, $from, $to, $step)
);
$this->templateVariableContainer->add($iterationArgument, $iteration);
$content = $this->renderChildren();
$this->templateVariableContainer->remove($iterationArgument);
} else {
$content = $this->renderChildren();
}
return $content;
}
/**
* @param integer $i
* @param integer $from
* @param integer $to
* @param integer $step
* @return boolean
*/
protected function isLast($i, $from, $to, $step) {
if ($from === $to) {
$isLast = TRUE;
} elseif ($from < $to) {
$isLast = ($i + $step > $to);
} else {
$isLast = ($i + $step < $to);
}
return $isLast;
}
}

View File

@@ -0,0 +1,67 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Iterator;
/*
* 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\Traits\TemplateVariableViewHelperTrait;
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper;
/**
* Creates chunks from an input Array/Traversable with option to allocate items to a fixed number of chunks
*
* @author Benjamin Rau <rau@codearts.at>, codearts
* @package Vhs
* @subpackage ViewHelpers\Iterator
*/
class ChunkViewHelper extends AbstractViewHelper {
use TemplateVariableViewHelperTrait;
use ArrayConsumingViewHelperTrait;
/**
* @return void
*/
public function initializeArguments() {
$this->registerAsArgument();
$this->registerArgument('subject', 'mixed', 'The subject Traversable/Array instance to shift', FALSE, NULL);
}
/**
* Render method
*
* @param integer $count The count of items per chunk or if fixed number of chunks
* @param boolean $fixed Whether to allocate items to a fixed number of chunks or not
* @param boolean $preserveKeys If set to true, the original array keys will be preserved in the chunks
* @throws \Exception
* @return array
*/
public function render($count, $fixed = FALSE, $preserveKeys = FALSE) {
$subject = $this->getArgumentFromArgumentsOrTagContentAndConvertToArray('subject');
$output = array();
if (0 >= $count) {
return $output;
}
if (TRUE === (boolean) $fixed) {
$subjectSize = count($subject);
if (0 < $subjectSize) {
$chunkSize = ceil($subjectSize / $count);
$output = array_chunk($subject, $chunkSize, $preserveKeys);
}
// Fill the resulting array with empty items to get the desired element count
$elementCount = count($output);
if ($elementCount < $count) {
$output += array_fill($elementCount, $count - $elementCount, NULL);
}
} else {
$output = array_chunk($subject, $count, $preserveKeys);
}
return $this->renderChildrenWithVariableOrReturnInput($output);
}
}

View File

@@ -0,0 +1,78 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Iterator;
/*
* 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\BasicViewHelperTrait;
use FluidTYPO3\Vhs\Traits\TemplateVariableViewHelperTrait;
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper;
/**
* Explode ViewHelper
*
* Explodes a string by $glue
*
* @author Claus Due <claus@namelesscoder.net>
* @package Vhs
* @subpackage ViewHelpers\Iterator
*/
class ExplodeViewHelper extends AbstractViewHelper {
use BasicViewHelperTrait;
use TemplateVariableViewHelperTrait;
/**
* @var string
*/
protected $method = 'explode';
/**
* Initialize
*
* @return void
*/
public function initializeArguments() {
$this->registerAsArgument();
$this->registerArgument('content', 'string', 'String to be exploded by glue', FALSE, NULL);
$this->registerArgument('glue', 'string', 'String used as glue in the string to be exploded. Use glue value of "constant:NAMEOFCONSTANT" (fx "constant:LF" for linefeed as glue)', FALSE, ',');
}
/**
* Render method
*
* @return mixed
*/
public function render() {
$content = $this->getArgumentFromArgumentsOrTagContent('content');
$glue = $this->resolveGlue();
$output = call_user_func_array($this->method, array($glue, $content));
return $this->renderChildrenWithVariableOrReturnInput($output);
}
/**
* Detects the proper glue string to use for implode/explode operation
*
* @return string
*/
protected function resolveGlue() {
$glue = $this->arguments['glue'];
if (FALSE !== strpos($glue, ':') && 1 < strlen($glue)) {
// glue contains a special type identifier, resolve the actual glue
list ($type, $value) = explode(':', $glue);
switch ($type) {
case 'constant':
$glue = constant($value);
break;
default:
$glue = $value;
}
}
return $glue;
}
}

View File

@@ -0,0 +1,178 @@
<?php
namespace FluidTYPO3\Vhs\ViewHelpers\Iterator;
/*
* 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\Extbase\Reflection\ObjectAccess;
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper;
/**
* ### Iterator / Extract VieWHelper
*
* Loop through the iterator and extract a key, optionally join the
* results if more than one value is found.
*
* #### Extract values from an array by key
*
* The extbase version of indexed_search returns an array of the
* previous search, which cannot easily be shown in the input field
* of the result page. This can be solved.
*
* #### Input from extbase version of indexed_search">
*
* array(
* 0 => array(
* 'sword' => 'firstWord',
* 'oper' => 'AND'
* ),
* 1 => array(
* 'sword' => 'secondWord',
* 'oper' => 'AND'
* ),
* 3 => array(
* 'sword' => 'thirdWord',
* 'oper' => 'AND'
* )
* )
*
* Show the previous search words in the search form of the
* result page:
*
* #### Example
* <f:form.textfield name="search[sword]"
* value="{v:iterator.extract(key:'sword', content: searchWords) -> v:iterator.implode(glue: ' ')}"
* class="tx-indexedsearch-searchbox-sword" />
*
* #### Get the names of several users
*
* Provided we have a bunch of FrontendUsers and we need to show
* their firstname combined into a string:
*
* <h2>Welcome
* <v:iterator.implode glue=", "><v:iterator.extract key="firstname" content="frontendUsers" /></v:iterator.implode>
* <!-- alternative: -->
* {frontendUsers -> v:iterator.extract(key: 'firstname') -> v:iterator.implode(glue: ', ')}
* </h2>
*
* #### Output
*
* <h2>Welcome Peter, Paul, Marry</h2>
*
* #### Complex example
*
* {anArray->v:iterator.extract(path: 'childProperty.secondNestedChildObject')->v:iterator.sort(direction: 'DESC', sortBy: 'propertyOnSecondChild')->v:iterator.slice(length: 10)->v:iterator.extract(key: 'uid')}
*
* #### Single return value
*
* Outputs the "uid" value of the first record in variable $someRecords without caring if there are more than
* one records. Always extracts the first value and then stops. Equivalent of chaning -> v:iterator.first().
* {someRecords -> v:iterator.extract(key: 'uid', single: TRUE)}
*
* @author Andreas Lappe <nd@kaeufli.ch>
* @package Vhs
* @subpackage ViewHelpers\Iterator
*/
class ExtractViewHelper extends AbstractViewHelper {
/**
* @param string $key The name of the key from which you wish to extract the value
* @param mixed $content The array or Iterator that contains either the value or arrays of values
* @param boolean $recursive If TRUE, attempts to extract the key from deep nested arrays
* @param boolean $single If TRUE, returns only one value - always the first one - instead of an array of values
* @return array
*/
public function render($key, $content = NULL, $recursive = TRUE, $single = FALSE) {
if (NULL === $content) {
$content = $this->renderChildren();
}
try {
// extraction from Iterators could potentially use a getter method which throws
// exceptions - although this would be bad practice. Catch the exception here
// and turn it into a WARNING log message so that output does not break.
if (TRUE === (boolean) $recursive) {
$result = $this->recursivelyExtractKey($content, $key);
} else {
$result = $this->extractByKey($content, $key);
}
} catch (\Exception $error) {
GeneralUtility::sysLog($error->getMessage(), 'vhs', GeneralUtility::SYSLOG_SEVERITY_WARNING);
$result = array();
}
if (TRUE === (boolean) $single) {
return reset($result);
}
return $result;
}
/**
* Extract by key
*
* @param \Traversable $iterator
* @param string $key
* @return mixed NULL or whatever we found at $key
* @throws \Exception
*/
public function extractByKey($iterator, $key) {
if (FALSE === is_array($iterator) && FALSE === $iterator instanceof \Traversable) {
throw new \Exception('Traversable object or array expected but received ' . gettype($iterator), 1361532490);
}
$result = ObjectAccess::getPropertyPath($iterator, $key);
return $result;
}
/**
* Recursively extract the key
*
* @param \Traversable $iterator
* @param string $key
* @return string
* @throws \Exception
*/
public function recursivelyExtractKey($iterator, $key) {
$content = array();
foreach ($iterator as $v) {
// Lets see if we find something directly:
$result = ObjectAccess::getPropertyPath($v, $key);
if (NULL !== $result) {
$content[] = $result;
} elseif (TRUE === is_array($v) || TRUE === $v instanceof \Traversable) {
$content[] = $this->recursivelyExtractKey($v, $key);
}
}
$content = $this->flattenArray($content);
return $content;
}
/**
* Flatten the result structure, to iterate it cleanly in fluid
*
* @param array $content
* @param array $flattened
* @return array
*/
public function flattenArray(array $content, $flattened = NULL) {
foreach ($content as $sub) {
if (TRUE === is_array($sub)) {
$flattened = $this->flattenArray($sub, $flattened);
} else {
$flattened[] = $sub;
}
}
return $flattened;
}
}

Some files were not shown because too many files have changed in this diff Show More