first commit

This commit is contained in:
salam committed 2026-10-19 08:18:39 +02:00
commit 1218dd1517
12716 files changed
+989233

No files matched your search

@@ -0,0 +1,74 @@
<?php
namespace TYPO3\CMS\Fluid\Core\Cache;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\Cache\Exception\InvalidDataException;
use TYPO3\CMS\Core\Cache\Frontend\PhpFrontend;
use TYPO3Fluid\Fluid\Core\Cache\FluidCacheInterface;
use TYPO3Fluid\Fluid\Core\Cache\FluidCacheWarmerInterface;
use TYPO3Fluid\Fluid\Core\Cache\StandardCacheWarmer;
/**
* Class FluidTemplateCache
*
* Connector class that enables the TYPO3 cache called "fluid_template" to be operated with the
* interface appropriate for the Fluid engine.
*
* @internal
*/
class FluidTemplateCache extends PhpFrontend implements FluidCacheInterface
{
/**
* @param null $name
*/
public function flush($name = null)
{
parent::flush();
}
/**
* @param string $entryIdentifier
* @return mixed
*/
public function get($entryIdentifier)
{
return $this->requireOnce($entryIdentifier);
}
/**
* @param string $entryIdentifier
* @param string $sourceCode
* @param array $tags
* @param int $lifetime
* @throws InvalidDataException
*/
public function set($entryIdentifier, $sourceCode, array $tags = [], $lifetime = null)
{
if (strpos($sourceCode, '<?php') === 0) {
// Remove opening PHP tag; it is added by the cache backend to which
// we delegate and would be duplicated if not removed.
$sourceCode = substr($sourceCode, 6);
}
parent::set($entryIdentifier, $sourceCode, $tags, time() + 86400);
}
/**
* @return FluidCacheWarmerInterface
*/
public function getCacheWarmer()
{
return new StandardCacheWarmer();
}
}
@@ -0,0 +1,159 @@
<?php
namespace TYPO3\CMS\Fluid\Core\Rendering;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\Cache\CacheManager;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Fluid\Core\Cache\FluidTemplateCache;
use TYPO3\CMS\Fluid\Core\Parser\InterceptorInterface;
use TYPO3\CMS\Fluid\Core\ViewHelper\ViewHelperResolver;
use TYPO3\CMS\Fluid\View\TemplatePaths;
use TYPO3Fluid\Fluid\Core\Compiler\TemplateCompiler;
use TYPO3Fluid\Fluid\Core\Parser\Configuration;
use TYPO3Fluid\Fluid\Core\Parser\TemplateParser;
use TYPO3Fluid\Fluid\Core\Variables\StandardVariableProvider;
use TYPO3Fluid\Fluid\Core\ViewHelper\ViewHelperInvoker;
use TYPO3Fluid\Fluid\Core\ViewHelper\ViewHelperVariableContainer;
use TYPO3Fluid\Fluid\View\ViewInterface;
/**
* Class RenderingContext
*/
class RenderingContext extends \TYPO3Fluid\Fluid\Core\Rendering\RenderingContext
{
/**
* Controller context being passed to the ViewHelper
*
* @var \TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext
*/
protected $controllerContext;
/**
* @param \TYPO3Fluid\Fluid\Core\ViewHelper\ViewHelperVariableContainer $viewHelperVariableContainer
*/
public function injectViewHelperVariableContainer(\TYPO3Fluid\Fluid\Core\ViewHelper\ViewHelperVariableContainer $viewHelperVariableContainer)
{
$this->viewHelperVariableContainer = $viewHelperVariableContainer;
}
/**
* @param ViewInterface $view
*/
public function __construct(ViewInterface $view = null)
{
if ($view !== null) {
// Note: if $view is received here this indicates internal framework instancing
// and it is safe to call the parent constructor. Custom, non-view-providing
// usages will only perform the initialisation below (which is sufficient mind you!)
parent::__construct($view);
} else {
// Reproduced partial initialisation from parent::__construct; minus the custom
// implementations we attach below.
$this->setTemplateParser(new TemplateParser());
$this->setTemplateCompiler(new TemplateCompiler());
$this->setViewHelperInvoker(new ViewHelperInvoker());
$this->setViewHelperVariableContainer(new ViewHelperVariableContainer());
$this->setVariableProvider(new StandardVariableProvider());
}
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
$this->setTemplateProcessors(array_map([$objectManager, 'get'], $GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['preProcessors']));
$this->setExpressionNodeTypes($GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['expressionNodeTypes']);
$this->setTemplatePaths($objectManager->get(TemplatePaths::class));
$this->setViewHelperResolver($objectManager->get(ViewHelperResolver::class));
/** @var FluidTemplateCache $cache */
$cache = $objectManager->get(CacheManager::class)->getCache('fluid_template');
if (is_a($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['fluid_template']['frontend'], FluidTemplateCache::class, true)) {
$this->setCache($cache);
}
}
/**
* Build parser configuration
*
* @return Configuration
* @throws \InvalidArgumentException if a class not implementing InterceptorInterface was registered
*/
public function buildParserConfiguration()
{
$parserConfiguration = parent::buildParserConfiguration();
if (!empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['interceptors'])) {
foreach ($GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['interceptors'] as $className) {
$interceptor = GeneralUtility::makeInstance($className);
if (!$interceptor instanceof InterceptorInterface) {
throw new \InvalidArgumentException('Interceptor "' . $className . '" needs to implement ' . InterceptorInterface::class . '.', 1462869795);
}
$parserConfiguration->addInterceptor($interceptor);
}
}
return $parserConfiguration;
}
/**
* Get the controller context which will be passed to the ViewHelper
*
* @return \TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext The controller context to set
*/
public function getControllerContext()
{
return $this->controllerContext;
}
/**
* @param string $action
*/
public function setControllerAction($action)
{
$dotPosition = strpos($action, '.');
if ($dotPosition !== false) {
$action = substr($action, 0, $dotPosition);
}
parent::setControllerAction($action);
$this->controllerContext->getRequest()->setControllerActionName(lcfirst($action));
}
/**
* @param string $controllerName
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\InvalidControllerNameException
*/
public function setControllerName($controllerName)
{
parent::setControllerName($controllerName);
$this->controllerContext->getRequest()->setControllerName($controllerName);
}
/**
* Set the controller context which will be passed to the ViewHelper
*
* @param \TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext $controllerContext The controller context to set
*/
public function setControllerContext(\TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext $controllerContext)
{
$request = $controllerContext->getRequest();
$this->controllerContext = $controllerContext;
$this->setControllerAction($request->getControllerActionName());
// Check if Request is using a sub-package key; in which case we translate this
// for our RenderingContext as an emulated plain old sub-namespace controller.
$controllerName = $request->getControllerName();
if ($request->getControllerSubpackageKey() && !strpos($controllerName, '\\')) {
$this->setControllerName($request->getControllerSubpackageKey() . '\\' . $controllerName);
} else {
$this->setControllerName($controllerName);
}
}
}
@@ -0,0 +1,95 @@
<?php
namespace TYPO3\CMS\Fluid\Core\ViewHelper;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
/**
* Class ViewHelperResolver
*
* Class whose purpose is dedicated to resolving classes which
* can be used as ViewHelpers and ExpressionNodes in Fluid.
*
* This CMS-specific version of the ViewHelperResolver works
* almost exactly like the one from Fluid itself, with the main
* differences being that this one supports a legacy mode flag
* which when toggled on makes the Fluid parser behave exactly
* like it did in the legacy CMS Fluid package.
*
* In addition to modifying the behavior or the parser when
* legacy mode is requested, this ViewHelperResolver is also
* made capable of "mixing" two different ViewHelper namespaces
* to effectively create aliases for the Fluid core ViewHelpers
* to be loaded in the TYPO3\CMS\ViewHelpers scope as well.
*
* Default ViewHelper namespaces are read TYPO3 configuration at:
*
* $GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['namespaces']
*
* Extending this array allows third party ViewHelper providers
* to automatically add or extend namespaces which then become
* available in every Fluid template file without having to
* register the namespace.
*
* @internal This is a helper class which is not considered part of TYPO3's Public API.
*/
class ViewHelperResolver extends \TYPO3Fluid\Fluid\Core\ViewHelper\ViewHelperResolver
{
/**
* ViewHelperResolver constructor
*
* Loads namespaces defined in global TYPO3 configuration. Overlays `f:`
* with `f:debug:` when Fluid debugging is enabled in the admin panel,
* causing debugging-specific ViewHelpers to be resolved in that case.
*/
public function __construct()
{
$this->namespaces = $GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['namespaces'];
if (TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_FE && $this->getBackendUser() instanceof BackendUserAuthentication) {
$configuration = $this->getBackendUser()->uc['AdminPanel'];
if (isset($configuration['preview_showFluidDebug']) && $configuration['preview_showFluidDebug']) {
$this->namespaces['f'][] = 'TYPO3\\CMS\\Fluid\\ViewHelpers\\Debug';
}
}
}
/**
* @param string $viewHelperClassName
* @return \TYPO3Fluid\Fluid\Core\ViewHelper\ViewHelperInterface
*/
public function createViewHelperInstanceFromClassName($viewHelperClassName)
{
return $this->getObjectManager()->get($viewHelperClassName);
}
/**
* @return ObjectManagerInterface
*/
protected function getObjectManager()
{
return GeneralUtility::makeInstance(ObjectManager::class);
}
/**
* @return BackendUserAuthentication
*/
protected function getBackendUser()
{
return $GLOBALS['BE_USER'];
}
}
@@ -0,0 +1,74 @@
<?php
namespace TYPO3\CMS\Fluid\Core\Widget;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Fluid\View\TemplatePaths;
/**
* This is the base class for all widget controllers.
* It is basically an ActionController and additionally has $this->widgetConfiguration set to the
* Configuration of the current Widget.
*/
abstract class AbstractWidgetController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionController implements \TYPO3\CMS\Core\SingletonInterface
{
/**
* @var array
*/
protected $supportedRequestTypes = [\TYPO3\CMS\Fluid\Core\Widget\WidgetRequest::class];
/**
* Configuration for this widget.
*
* @var array
*/
protected $widgetConfiguration;
/**
* Handles a request. The result output is returned by altering the given response.
*
* @param \TYPO3\CMS\Extbase\Mvc\RequestInterface $request The request object
* @param \TYPO3\CMS\Extbase\Mvc\ResponseInterface $response The response, modified by this handler
*/
public function processRequest(\TYPO3\CMS\Extbase\Mvc\RequestInterface $request, \TYPO3\CMS\Extbase\Mvc\ResponseInterface $response)
{
$this->widgetConfiguration = $request->getWidgetContext()->getWidgetConfiguration();
parent::processRequest($request, $response);
}
/**
* Allows the widget template root path to be overridden via the framework configuration,
* e.g. plugin.tx_extension.view.widget.<WidgetViewHelperClassName>.templateRootPath
*
* @param \TYPO3\CMS\Extbase\Mvc\View\ViewInterface $view
*/
protected function setViewConfiguration(\TYPO3\CMS\Extbase\Mvc\View\ViewInterface $view)
{
$extbaseFrameworkConfiguration = $this->configurationManager->getConfiguration(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
$widgetViewHelperClassName = $this->request->getWidgetContext()->getWidgetViewHelperClassName();
$templatePaths = new TemplatePaths($this->controllerContext->getRequest()->getControllerExtensionKey());
$parentConfiguration = $view->getTemplatePaths()->toArray();
$rootConfiguration = $templatePaths->toArray();
$pluginConfiguration = $extbaseFrameworkConfiguration['view']['widget'][$widgetViewHelperClassName] ?? [];
if (isset($pluginConfiguration['templateRootPath']) && !isset($pluginConfiguration['templateRootPaths'])) {
$pluginConfiguration['templateRootPaths'][10] = $pluginConfiguration['templateRootPath'];
}
$widgetViewConfiguration = array_merge_recursive(
(array)$rootConfiguration,
(array)$parentConfiguration,
(array)$pluginConfiguration
);
$view->getTemplatePaths()->fillFromConfigurationArray($widgetViewConfiguration);
}
}
@@ -0,0 +1,277 @@
<?php
namespace TYPO3\CMS\Fluid\Core\Widget;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3Fluid\Fluid\Component\ComponentInterface;
use TYPO3Fluid\Fluid\Core\Compiler\TemplateCompiler;
use TYPO3Fluid\Fluid\Core\Parser\SyntaxTree\ViewHelperNode;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
/**
*/
abstract class AbstractWidgetViewHelper extends AbstractViewHelper
{
/**
* The Controller associated to this widget.
* This needs to be filled by the individual subclass by an inject method.
*
* @var \TYPO3\CMS\Fluid\Core\Widget\AbstractWidgetController
*/
protected $controller;
/**
* If set to TRUE, it is an AJAX widget.
*
* @var bool
*/
protected $ajaxWidget = false;
/**
* @var \TYPO3\CMS\Fluid\Core\Widget\AjaxWidgetContextHolder
*/
private $ajaxWidgetContextHolder;
/**
* @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
*/
protected $objectManager;
/**
* @var \TYPO3\CMS\Extbase\Service\ExtensionService
*/
protected $extensionService;
/**
* @var bool
*/
protected $escapeOutput = false;
/**
* @var \TYPO3\CMS\Fluid\Core\Widget\WidgetContext
*/
private $widgetContext;
/**
* @param \TYPO3\CMS\Fluid\Core\Widget\AjaxWidgetContextHolder $ajaxWidgetContextHolder
* @internal
*/
public function injectAjaxWidgetContextHolder(\TYPO3\CMS\Fluid\Core\Widget\AjaxWidgetContextHolder $ajaxWidgetContextHolder)
{
$this->ajaxWidgetContextHolder = $ajaxWidgetContextHolder;
}
/**
* @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
* @internal
*/
public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager)
{
$this->objectManager = $objectManager;
$this->widgetContext = $this->objectManager->get(\TYPO3\CMS\Fluid\Core\Widget\WidgetContext::class);
}
/**
* @param \TYPO3\CMS\Extbase\Service\ExtensionService $extensionService
* @internal
*/
public function injectExtensionService(\TYPO3\CMS\Extbase\Service\ExtensionService $extensionService)
{
$this->extensionService = $extensionService;
}
/**
* Initialize arguments.
* @internal
*/
public function initializeArguments()
{
$this->registerArgument(
'customWidgetId',
'string',
'extend the widget identifier with a custom widget id',
false,
null
);
}
/**
* Initialize the arguments of the ViewHelper, and call the render() method of the ViewHelper.
*
* @return string the rendered ViewHelper.
* @internal
*/
public function initializeArgumentsAndRender()
{
$this->validateArguments();
$this->initialize();
$this->initializeWidgetContext();
return $this->callRenderMethod();
}
/**
* Initialize the arguments of the ViewHelper, and call the render() method of the ViewHelper.
*
* @param RenderingContextInterface $renderingContext
* @return string the rendered ViewHelper.
* @internal
*/
public function evaluate(RenderingContextInterface $renderingContext)
{
$this->renderingContext = $renderingContext;
$this->getArguments()->setRenderingContext($renderingContext);
$this->initializeWidgetContext();
return $this->callRenderMethod();
}
/**
* Stores the syntax tree child nodes in the Widget Context, so they can be
* rendered with <f:widget.renderChildren> lateron.
*
* @param RenderingContextInterface $renderingContext
* @return ComponentInterface
* @internal
*/
public function onClose(RenderingContextInterface $renderingContext): ComponentInterface
{
$node = parent::onClose($renderingContext);
$rootNode = $this->objectManager->get(\TYPO3Fluid\Fluid\Core\Parser\SyntaxTree\RootNode::class);
$rootNode->setChildren($this->getChildren());
$this->widgetContext->setViewHelperChildNodes($rootNode, $renderingContext);
return $node;
}
/**
* Initialize the Widget Context, before the Render method is called.
*/
private function initializeWidgetContext()
{
$this->widgetContext->setWidgetConfiguration($this->getWidgetConfiguration());
$this->initializeWidgetIdentifier();
$this->widgetContext->setControllerObjectName(get_class($this->controller));
$vendorName = $this->renderingContext->getControllerContext()->getRequest()->getControllerVendorName();
$extensionName = $this->renderingContext->getControllerContext()->getRequest()->getControllerExtensionName();
$pluginName = $this->renderingContext->getControllerContext()->getRequest()->getPluginName();
$this->widgetContext->setParentVendorName($vendorName);
$this->widgetContext->setParentExtensionName($extensionName);
$this->widgetContext->setParentPluginName($pluginName);
$pluginNamespace = $this->extensionService->getPluginNamespace($extensionName, $pluginName);
$this->widgetContext->setParentPluginNamespace($pluginNamespace);
$this->widgetContext->setWidgetViewHelperClassName(static::class);
if ($this->ajaxWidget === true) {
$this->ajaxWidgetContextHolder->store($this->widgetContext);
}
}
/**
* Stores the syntax tree child nodes in the Widget Context, so they can be
* rendered with <f:widget.renderChildren> lateron.
*
* @param array $childNodes The SyntaxTree Child nodes of this ViewHelper.
* @internal
*/
public function setChildNodes(array $childNodes)
{
$rootNode = $this->objectManager->get(\TYPO3Fluid\Fluid\Core\Parser\SyntaxTree\RootNode::class);
foreach ($childNodes as $childNode) {
$rootNode->addChildNode($childNode);
}
$this->widgetContext->setViewHelperChildNodes($rootNode, $this->renderingContext);
}
/**
* Generate the configuration for this widget. Override to adjust.
*
* @return array
*/
protected function getWidgetConfiguration()
{
return $this->arguments;
}
/**
* Initiate a sub request to $this->controller. Make sure to fill $this->controller
* via Dependency Injection.
*
* @return \TYPO3\CMS\Extbase\Mvc\ResponseInterface the response of this request.
* @throws \TYPO3\CMS\Fluid\Core\Widget\Exception\MissingControllerException
*/
protected function initiateSubRequest()
{
if (!isset($this->controller) || !$this->controller instanceof \TYPO3\CMS\Fluid\Core\Widget\AbstractWidgetController) {
throw new \TYPO3\CMS\Fluid\Core\Widget\Exception\MissingControllerException(
'initiateSubRequest() can not be called if there is no valid controller extending ' .
'TYPO3\\CMS\\Fluid\\Core\\Widget\\AbstractWidgetController' .
' Got "' . ($this->controller ? get_class($this->controller) : gettype($this->controller)) .
'" in class "' . static::class . '".',
1289422564
);
}
$subRequest = $this->objectManager->get(\TYPO3\CMS\Fluid\Core\Widget\WidgetRequest::class);
$subRequest->setWidgetContext($this->widgetContext);
$this->passArgumentsToSubRequest($subRequest);
$subResponse = $this->objectManager->get(\TYPO3\CMS\Extbase\Mvc\Web\Response::class);
$this->controller->processRequest($subRequest, $subResponse);
return $subResponse;
}
/**
* Pass the arguments of the widget to the subrequest.
*
* @param \TYPO3\CMS\Fluid\Core\Widget\WidgetRequest $subRequest
*/
private function passArgumentsToSubRequest(\TYPO3\CMS\Fluid\Core\Widget\WidgetRequest $subRequest)
{
$arguments = $this->renderingContext->getControllerContext()->getRequest()->getArguments();
$widgetIdentifier = $this->widgetContext->getWidgetIdentifier();
if (isset($arguments[$widgetIdentifier])) {
if (isset($arguments[$widgetIdentifier]['action'])) {
$subRequest->setControllerActionName($arguments[$widgetIdentifier]['action']);
unset($arguments[$widgetIdentifier]['action']);
}
$subRequest->setArguments($arguments[$widgetIdentifier]);
}
}
/**
* The widget identifier is unique on the current page, and is used
* in the URI as a namespace for the widget's arguments.
*
* @return string the widget identifier for this widget
* @todo clean up, and make it somehow more routing compatible.
*/
private function initializeWidgetIdentifier()
{
$viewHelperVariableContainer = $this->renderingContext->getViewHelperVariableContainer();
$widgetCounter = $viewHelperVariableContainer->get(\TYPO3\CMS\Fluid\Core\Widget\AbstractWidgetViewHelper::class, 'nextWidgetNumber', 0);
$widgetIdentifier = '@widget_' . ((isset($this->arguments['customWidgetId']) && $this->arguments['customWidgetId'] !== null) ? $this->arguments['customWidgetId'] . '_' : '') . $widgetCounter;
$viewHelperVariableContainer->addOrUpdate(\TYPO3\CMS\Fluid\Core\Widget\AbstractWidgetViewHelper::class, 'nextWidgetNumber', $widgetCounter + 1);
$this->widgetContext->setWidgetIdentifier($widgetIdentifier);
}
/**
* @param string $argumentsName
* @param string $closureName
* @param string $initializationPhpCode
* @param ViewHelperNode $node
* @param TemplateCompiler $compiler
* @return string
*/
public function compile($argumentsName, $closureName, &$initializationPhpCode, ViewHelperNode $node, TemplateCompiler $compiler)
{
$compiler->disable();
return '\'\'';
}
}
@@ -0,0 +1,133 @@
<?php
namespace TYPO3\CMS\Fluid\Core\Widget;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Security\Cryptography\HashService;
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
/**
* This object stores the WidgetContext for the currently active widgets
* of the current user, to make sure the WidgetContext is available in
* Widget AJAX requests.
*
* @internal This class is only used internally by the widget framework.
*/
class AjaxWidgetContextHolder implements \TYPO3\CMS\Core\SingletonInterface
{
/**
* An array $ajaxWidgetIdentifier => $widgetContext
* which stores the widget context.
*
* @var WidgetContext[]
*/
protected $widgetContexts = [];
/**
* @var string
*/
protected $widgetContextsStorageKey = 'TYPO3\\CMS\\Fluid\\Core\\Widget\\AjaxWidgetContextHolder_widgetContexts';
/**
* @var HashService
*/
protected $hashService;
/**
* Constructor
*/
public function __construct()
{
$this->hashService = GeneralUtility::makeInstance(HashService::class);
$this->loadWidgetContexts();
}
/**
* Loads the windget contexts from the TYPO3 user session
*/
protected function loadWidgetContexts()
{
if (isset($GLOBALS['TSFE']) && $GLOBALS['TSFE'] instanceof TypoScriptFrontendController) {
$this->widgetContexts = $this->unserializeWithHmac($GLOBALS['TSFE']->fe_user->getKey('ses', $this->widgetContextsStorageKey));
} else {
$this->widgetContexts = isset($GLOBALS['BE_USER']->uc[$this->widgetContextsStorageKey]) ? $this->unserializeWithHmac($GLOBALS['BE_USER']->uc[$this->widgetContextsStorageKey]) : [];
$GLOBALS['BE_USER']->writeUC();
}
}
/**
* Get the widget context for the given $ajaxWidgetId.
*
* @param string $ajaxWidgetId
* @return WidgetContext
*/
public function get($ajaxWidgetId)
{
if (!isset($this->widgetContexts[$ajaxWidgetId])) {
throw new \TYPO3\CMS\Fluid\Core\Widget\Exception\WidgetContextNotFoundException('No widget context was found for the Ajax Widget Identifier "' . $ajaxWidgetId . '". This only happens if AJAX URIs are called without including the widget on a page.', 1284793775);
}
return $this->widgetContexts[$ajaxWidgetId];
}
/**
* Stores the WidgetContext inside the Context, and sets the
* AjaxWidgetIdentifier inside the Widget Context correctly.
*
* @param WidgetContext $widgetContext
*/
public function store(WidgetContext $widgetContext)
{
$ajaxWidgetId = md5(uniqid(mt_rand(), true));
$widgetContext->setAjaxWidgetIdentifier($ajaxWidgetId);
$this->widgetContexts[$ajaxWidgetId] = $widgetContext;
$this->storeWidgetContexts();
}
/**
* Persists the widget contexts in the TYPO3 user session
*/
protected function storeWidgetContexts()
{
if (isset($GLOBALS['TSFE']) && $GLOBALS['TSFE'] instanceof TypoScriptFrontendController) {
$GLOBALS['TSFE']->fe_user->setKey('ses', $this->widgetContextsStorageKey, $this->serializeWithHmac($this->widgetContexts));
$GLOBALS['TSFE']->fe_user->storeSessionData();
} else {
$GLOBALS['BE_USER']->uc[$this->widgetContextsStorageKey] = $this->serializeWithHmac($this->widgetContexts);
$GLOBALS['BE_USER']->writeUc();
}
}
protected function serializeWithHmac(array $widgetContexts): string
{
$data = serialize($widgetContexts);
return $this->hashService->appendHmac($data);
}
protected function unserializeWithHmac(?string $data): array
{
if ($data === null || $data === '') {
return [];
}
try {
$data = $this->hashService->validateAndStripHmac($data);
// widget contexts literally can contain everything, that why using
// HMAC-signed `unserialize()` is the only option here unless Extbase
// structures have been refactored further
$widgetContexts = unserialize($data);
} finally {
return is_array($widgetContexts ?? null) ? $widgetContexts : [];
}
}
}
@@ -0,0 +1,104 @@
<?php
namespace TYPO3\CMS\Fluid\Core\Widget;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* This is the bootstrap for Ajax Widget responses
*
* @internal This class is only used internally by the widget framework.
*/
class Bootstrap
{
/**
* Back reference to the parent content object
* This has to be public as it is set directly from TYPO3
*
* @var \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
*/
public $cObj;
/**
* @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
*/
protected $objectManager;
/**
* @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
*/
protected $configurationManager;
/**
* @param string $content The content
* @param array $configuration The TS configuration array
* @return string $content The processed content
*/
public function run($content, $configuration)
{
$this->objectManager = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\ObjectManager::class);
$this->initializeConfiguration($configuration);
$this->configureObjectManager(true);
$ajaxWidgetContextHolder = $this->objectManager->get(\TYPO3\CMS\Fluid\Core\Widget\AjaxWidgetContextHolder::class);
$widgetIdentifier = \TYPO3\CMS\Core\Utility\GeneralUtility::_GET('fluid-widget-id');
$widgetContext = $ajaxWidgetContextHolder->get($widgetIdentifier);
$configuration['vendorName'] = $widgetContext->getParentVendorName();
$configuration['extensionName'] = $widgetContext->getParentExtensionName();
$configuration['pluginName'] = $widgetContext->getParentPluginName();
$extbaseBootstrap = $this->objectManager->get(\TYPO3\CMS\Extbase\Core\Bootstrap::class);
$extbaseBootstrap->cObj = $this->cObj;
return $extbaseBootstrap->run($content, $configuration);
}
/**
* Initializes the Object framework.
*
* @param array $configuration
* @see initialize()
*/
public function initializeConfiguration($configuration)
{
$this->configurationManager = $this->objectManager->get(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::class);
/** @var \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer $contentObject */
$contentObject = $this->cObj ?? \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::class);
$this->configurationManager->setContentObject($contentObject);
$this->configurationManager->setConfiguration($configuration);
}
/**
* Configures the object manager object configuration from
* config.tx_extbase.objects
*
* @param $isInternalCall bool Set to true by Bootstrap, not by extensions
* @see initialize()
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
*/
public function configureObjectManager(bool $isInternalCall = false)
{
if (!$isInternalCall) {
trigger_error(self::class . '->configureObjectManager() will be removed in TYPO3 v10.0.', E_USER_DEPRECATED);
}
$typoScriptSetup = $this->configurationManager->getConfiguration(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FULL_TYPOSCRIPT);
if (!is_array($typoScriptSetup['config.']['tx_extbase.']['objects.'])) {
return;
}
trigger_error('Overriding object implementations via TypoScript settings config.tx_extbase.objects and plugin.tx_%plugin%.objects will be removed in TYPO3 v10.0.', E_USER_DEPRECATED);
$objectContainer = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\Container\Container::class);
foreach ($typoScriptSetup['config.']['tx_extbase.']['objects.'] as $classNameWithDot => $classConfiguration) {
if (isset($classConfiguration['className'])) {
$originalClassName = rtrim($classNameWithDot, '.');
$objectContainer->registerImplementation($originalClassName, $classConfiguration['className']);
}
}
}
}
@@ -0,0 +1,22 @@
<?php
namespace TYPO3\CMS\Fluid\Core\Widget;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* A Fluid Widget exception.
*/
class Exception extends \TYPO3Fluid\Fluid\Core\Exception
{
}
@@ -0,0 +1,24 @@
<?php
namespace TYPO3\CMS\Fluid\Core\Widget\Exception;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* An exception if the controller is missing.
*
* @internal
*/
class MissingControllerException extends \TYPO3\CMS\Fluid\Core\Widget\Exception
{
}
@@ -0,0 +1,24 @@
<?php
namespace TYPO3\CMS\Fluid\Core\Widget\Exception;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* An exception if no widget context could be found inside the AjaxWidgetContextHolder.
*
* @internal
*/
class RenderingContextNotFoundException extends \TYPO3\CMS\Fluid\Core\Widget\Exception
{
}
@@ -0,0 +1,24 @@
<?php
namespace TYPO3\CMS\Fluid\Core\Widget\Exception;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* An exception if no widget context could be found inside the AjaxWidgetContextHolder.
*
* @internal
*/
class WidgetContextNotFoundException extends \TYPO3\CMS\Fluid\Core\Widget\Exception
{
}
@@ -0,0 +1,24 @@
<?php
namespace TYPO3\CMS\Fluid\Core\Widget\Exception;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* An exception if no widget Request could be found inside <f:renderChildren>.
*
* @internal
*/
class WidgetRequestNotFoundException extends \TYPO3\CMS\Fluid\Core\Widget\Exception
{
}
@@ -0,0 +1,298 @@
<?php
namespace TYPO3\CMS\Fluid\Core\Widget;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* The WidgetContext stores all information a widget needs to know about the
* environment.
*
* The WidgetContext can be fetched from the current WidgetRequest, and is thus
* available throughout the whole sub-request of the widget. It is used internally
* by various ViewHelpers (like <f:widget.link>, <f:widget.uri>, <f:widget.renderChildren>),
* to get knowledge over the current widget's configuration.
*
* @internal It is a purely internal class which should not be used outside of Fluid.
*/
class WidgetContext
{
/**
* Uniquely identifies a Widget Instance on a certain page.
*
* @var string
*/
protected $widgetIdentifier;
/**
* Per-User unique identifier of the widget, if it is an AJAX widget.
*
* @var string
*/
protected $ajaxWidgetIdentifier;
/**
* User-supplied widget configuration, available inside the widget
* controller as $this->widgetConfiguration.
*
* @var array
*/
protected $widgetConfiguration;
/**
* The fully qualified object name of the Controller which this widget uses.
*
* @var string
*/
protected $controllerObjectName;
/**
* The child nodes of the Widget ViewHelper.
* Only available inside non-AJAX requests.
*
* @var \TYPO3Fluid\Fluid\Core\Parser\SyntaxTree\RootNode
*/
protected $viewHelperChildNodes;
/**
* The rendering context of the ViewHelperChildNodes.
* Only available inside non-AJAX requests.
* @todo rename to something more meaningful.
*
* @var \TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface
*/
protected $viewHelperChildNodeRenderingContext;
/**
* @var string
*/
protected $parentPluginNamespace;
/**
* @var string
*/
protected $parentVendorName;
/**
* @var string
*/
protected $parentExtensionName;
/**
* @var string
*/
protected $parentPluginName;
/**
* @var string
*/
protected $widgetViewHelperClassName;
/**
* @return string
*/
public function getWidgetIdentifier()
{
return $this->widgetIdentifier;
}
/**
* @param string $widgetIdentifier
*/
public function setWidgetIdentifier($widgetIdentifier)
{
$this->widgetIdentifier = $widgetIdentifier;
}
/**
* @return string
*/
public function getAjaxWidgetIdentifier()
{
return $this->ajaxWidgetIdentifier;
}
/**
* @param string $ajaxWidgetIdentifier
*/
public function setAjaxWidgetIdentifier($ajaxWidgetIdentifier)
{
$this->ajaxWidgetIdentifier = $ajaxWidgetIdentifier;
}
/**
* Sets the URI namespace of the plugin that contains the widget
*
* @param string $parentPluginNamespace
*/
public function setParentPluginNamespace($parentPluginNamespace)
{
$this->parentPluginNamespace = $parentPluginNamespace;
}
/**
* Returns the URI namespace of the plugin that contains the widget
*
* @return string
*/
public function getParentPluginNamespace()
{
return $this->parentPluginNamespace;
}
/**
* Sets the Extension name of the plugin that contains the widget
*
* @param string $parentExtensionName
*/
public function setParentExtensionName($parentExtensionName)
{
$this->parentExtensionName = $parentExtensionName;
}
/**
* Returns the Extension name of the plugin that contains the widget
*
* @return string
*/
public function getParentExtensionName()
{
return $this->parentExtensionName;
}
/**
* Sets the Vendor name of the plugin that contains the widget
*
* @param string $parentVendorName
*/
public function setParentVendorName($parentVendorName)
{
$this->parentVendorName = $parentVendorName;
}
/**
* Returns the Vendor name of the plugin that contains the widget
*
* @return string
*/
public function getParentVendorName()
{
return $this->parentVendorName;
}
/**
* Sets the name of the plugin that contains the widget
*
* @param string $parentPluginName
*/
public function setParentPluginName($parentPluginName)
{
$this->parentPluginName = $parentPluginName;
}
/**
* Returns the name of the plugin that contains the widget
*
* @return string
*/
public function getParentPluginName()
{
return $this->parentPluginName;
}
/**
* Sets the fully qualified class name of the view helper this context belongs to
*
* @param string $widgetViewHelperClassName
*/
public function setWidgetViewHelperClassName($widgetViewHelperClassName)
{
$this->widgetViewHelperClassName = $widgetViewHelperClassName;
}
/**
* Returns the fully qualified class name of the view helper this context belongs to
*
* @return string
*/
public function getWidgetViewHelperClassName()
{
return $this->widgetViewHelperClassName;
}
/**
* @return array
*/
public function getWidgetConfiguration()
{
return $this->widgetConfiguration;
}
/**
* @param array $widgetConfiguration
*/
public function setWidgetConfiguration($widgetConfiguration)
{
$this->widgetConfiguration = $widgetConfiguration;
}
/**
* @return string
*/
public function getControllerObjectName()
{
return $this->controllerObjectName;
}
/**
* @param string $controllerObjectName
*/
public function setControllerObjectName($controllerObjectName)
{
$this->controllerObjectName = $controllerObjectName;
}
/**
* @param \TYPO3Fluid\Fluid\Core\Parser\SyntaxTree\RootNode $viewHelperChildNodes
* @param \TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface $viewHelperChildNodeRenderingContext
*/
public function setViewHelperChildNodes(\TYPO3Fluid\Fluid\Core\Parser\SyntaxTree\RootNode $viewHelperChildNodes, \TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface $viewHelperChildNodeRenderingContext)
{
$this->viewHelperChildNodes = $viewHelperChildNodes;
$this->viewHelperChildNodeRenderingContext = $viewHelperChildNodeRenderingContext;
}
/**
* @return \TYPO3Fluid\Fluid\Core\Parser\SyntaxTree\RootNode
*/
public function getViewHelperChildNodes()
{
return $this->viewHelperChildNodes;
}
/**
* @return \TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface
*/
public function getViewHelperChildNodeRenderingContext()
{
return $this->viewHelperChildNodeRenderingContext;
}
/**
* @return array
*/
public function __sleep()
{
return ['widgetIdentifier', 'ajaxWidgetIdentifier', 'widgetConfiguration', 'controllerObjectName', 'parentPluginNamespace', 'parentVendorName', 'parentExtensionName', 'parentPluginName', 'widgetViewHelperClassName'];
}
}
@@ -0,0 +1,54 @@
<?php
namespace TYPO3\CMS\Fluid\Core\Widget;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Represents a widget request.
* @internal It is a purely internal class which should not be used outside of Fluid.
*/
class WidgetRequest extends \TYPO3\CMS\Extbase\Mvc\Web\Request
{
/**
* @var \TYPO3\CMS\Fluid\Core\Widget\WidgetContext
*/
protected $widgetContext;
/**
* @return \TYPO3\CMS\Fluid\Core\Widget\WidgetContext
*/
public function getWidgetContext()
{
return $this->widgetContext;
}
/**
* @param \TYPO3\CMS\Fluid\Core\Widget\WidgetContext $widgetContext
*/
public function setWidgetContext(\TYPO3\CMS\Fluid\Core\Widget\WidgetContext $widgetContext)
{
$this->widgetContext = $widgetContext;
$this->setControllerObjectName($widgetContext->getControllerObjectName());
}
/**
* Returns the unique URI namespace for this widget in the format pluginNamespace[widgetIdentifier]
*
* @return string
*/
public function getArgumentPrefix()
{
return $this->widgetContext->getParentPluginNamespace() . '[' . $this->widgetContext->getWidgetIdentifier() . ']';
}
}
@@ -0,0 +1,71 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Fluid\Core\Widget;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\RequestInterface;
use TYPO3\CMS\Extbase\Mvc\Web\RequestBuilder;
/**
* Builds the WidgetRequest if an AJAX widget is called.
*/
class WidgetRequestBuilder extends RequestBuilder
{
/**
* @var AjaxWidgetContextHolder
*/
private $ajaxWidgetContextHolder;
/**
* @param AjaxWidgetContextHolder $ajaxWidgetContextHolder
*/
public function injectAjaxWidgetContextHolder(AjaxWidgetContextHolder $ajaxWidgetContextHolder)
{
$this->ajaxWidgetContextHolder = $ajaxWidgetContextHolder;
}
/**
* Builds a widget request object from the raw HTTP information
*
* @return RequestInterface The widget request as an object
*/
public function build(): RequestInterface
{
$request = $this->objectManager->get(WidgetRequest::class);
$request->setRequestUri(GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL'));
$request->setBaseUri(GeneralUtility::getIndpEnv('TYPO3_SITE_URL'));
$request->setMethod($_SERVER['REQUEST_METHOD'] ?? null);
if (strtolower($_SERVER['REQUEST_METHOD']) === 'post') {
$request->setArguments(GeneralUtility::_POST());
} else {
$request->setArguments(GeneralUtility::_GET());
}
$rawGetArguments = GeneralUtility::_GET();
if (isset($rawGetArguments['action'])) {
$request->setControllerActionName($rawGetArguments['action']);
}
if (!isset($rawGetArguments['fluid-widget-id'])) {
// Low level test, WidgetRequestHandler returns false in canHandleRequest () if this is not set
throw new \InvalidArgumentException(
'No Fluid Widget ID was given.',
1521190675
);
}
$widgetContext = $this->ajaxWidgetContextHolder->get($rawGetArguments['fluid-widget-id']);
$request->setWidgetContext($widgetContext);
return $request;
}
}
@@ -0,0 +1,104 @@
<?php
namespace TYPO3\CMS\Fluid\Core\Widget;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Widget request handler, which handles the request if
* f3-fluid-widget-id is found.
*
* This Request Handler gets the WidgetRequestBuilder injected.
* @internal It is a purely internal class which should not be used outside of Fluid.
*/
class WidgetRequestHandler extends \TYPO3\CMS\Extbase\Mvc\Web\AbstractRequestHandler
{
/**
* @var \TYPO3\CMS\Fluid\Core\Widget\AjaxWidgetContextHolder
*/
protected $ajaxWidgetContextHolder;
/**
* @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
*/
protected $configurationManager;
/**
* @var \TYPO3\CMS\Fluid\Core\Widget\WidgetRequestBuilder
*/
protected $requestBuilder;
/**
* @param \TYPO3\CMS\Fluid\Core\Widget\AjaxWidgetContextHolder $ajaxWidgetContextHolder
*/
public function injectAjaxWidgetContextHolder(\TYPO3\CMS\Fluid\Core\Widget\AjaxWidgetContextHolder $ajaxWidgetContextHolder)
{
$this->ajaxWidgetContextHolder = $ajaxWidgetContextHolder;
}
/**
* @param \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager
*/
public function injectConfigurationManager(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager)
{
$this->configurationManager = $configurationManager;
}
/**
* @param \TYPO3\CMS\Extbase\Mvc\Web\RequestBuilder $requestBuilder
*/
public function injectRequestBuilder(\TYPO3\CMS\Extbase\Mvc\Web\RequestBuilder $requestBuilder)
{
// This method intentionally left blank
}
/**
* @param \TYPO3\CMS\Fluid\Core\Widget\WidgetRequestBuilder $requestBuilder
*/
public function injectWidgetRequestBuilder(\TYPO3\CMS\Fluid\Core\Widget\WidgetRequestBuilder $requestBuilder)
{
$this->requestBuilder = $requestBuilder;
}
/**
* Handles the web request. The response will automatically be sent to the client.
*
* @return \TYPO3\CMS\Extbase\Mvc\Web\Response
*/
public function handleRequest()
{
$request = $this->requestBuilder->build();
$response = $this->objectManager->get(\TYPO3\CMS\Extbase\Mvc\Web\Response::class);
$this->dispatcher->dispatch($request, $response);
return $response;
}
/**
* @return bool TRUE if it is an AJAX widget request
*/
public function canHandleRequest()
{
$rawGetArguments = \TYPO3\CMS\Core\Utility\GeneralUtility::_GET();
return isset($rawGetArguments['fluid-widget-id']);
}
/**
* This request handler has a higher priority than the default request handler.
*
* @return int
*/
public function getPriority()
{
return 200;
}
}
@@ -0,0 +1,169 @@
<?php
namespace TYPO3\CMS\Fluid\View;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Fluid\Core\Rendering\RenderingContext;
use TYPO3\CMS\Fluid\View\Exception\InvalidTemplateResourceException;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\View\TemplateView;
/**
* Abstract Fluid Template View.
*
* Contains the fundamental methods which any Fluid based template view needs.
*/
abstract class AbstractTemplateView extends TemplateView implements \TYPO3\CMS\Extbase\Mvc\View\ViewInterface
{
/**
* @var \TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext
*/
protected $controllerContext;
/**
* Initializes this view.
*/
public function initializeView()
{
}
/**
* @param RenderingContextInterface $context
* @internal
*/
public function __construct(RenderingContextInterface $context = null)
{
if (!$context) {
$context = GeneralUtility::makeInstance(ObjectManager::class)->get(RenderingContext::class, $this);
}
parent::__construct($context);
}
/**
* Tells if the view implementation can render the view for the given context.
*
* By default we assume that the view implementation can handle all kinds of
* contexts. Override this method if that is not the case.
*
* @param \TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext $controllerContext Controller context which is available inside the view
* @return bool TRUE if the view has something useful to display, otherwise FALSE
*/
public function canRender(\TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext $controllerContext)
{
return true;
}
/**
* Sets the current controller context
*
* @param \TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext $controllerContext
* @internal
*/
public function setControllerContext(\TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext $controllerContext)
{
$request = $controllerContext->getRequest();
$this->controllerContext = $controllerContext;
$this->baseRenderingContext->getTemplatePaths()->fillDefaultsByPackageName($request->getControllerExtensionKey());
$this->baseRenderingContext->getTemplatePaths()->setFormat($request->getFormat());
if ($this->baseRenderingContext instanceof RenderingContext) {
$this->baseRenderingContext->setControllerContext($controllerContext);
}
}
/**
* @param string $templateName
* @internal
*/
public function setTemplate($templateName)
{
$this->baseRenderingContext->setControllerAction($templateName);
}
/**
* Sets the path and name of of the template file. Effectively overrides the
* dynamic resolving of a template file.
*
* @param string $templatePathAndFilename Template file path
*/
public function setTemplatePathAndFilename($templatePathAndFilename)
{
$this->baseRenderingContext->getTemplatePaths()->setTemplatePathAndFilename($templatePathAndFilename);
}
/**
* Set the root path(s) to the templates.
* If set, overrides the one determined from $this->templateRootPathPattern
*
* @param string[] $templateRootPaths Root path(s) to the templates. If set, overrides the one determined from $this->templateRootPathPattern
*/
public function setTemplateRootPaths(array $templateRootPaths)
{
$this->baseRenderingContext->getTemplatePaths()->setTemplateRootPaths($templateRootPaths);
}
/**
* Resolves the template root to be used inside other paths.
*
* @return string[] Path(s) to template root directory
*/
public function getTemplateRootPaths()
{
return $this->baseRenderingContext->getTemplatePaths()->getTemplateRootPaths();
}
/**
* Set the root path(s) to the partials.
* If set, overrides the one determined from $this->partialRootPathPattern
*
* @param string[] $partialRootPaths Root paths to the partials. If set, overrides the one determined from $this->partialRootPathPattern
*/
public function setPartialRootPaths(array $partialRootPaths)
{
$this->baseRenderingContext->getTemplatePaths()->setPartialRootPaths($partialRootPaths);
}
/**
* Returns the absolute path to the folder that contains Fluid partial files
*
* @return string[] Fluid partial root paths
* @throws InvalidTemplateResourceException
*/
public function getPartialRootPaths()
{
return $this->baseRenderingContext->getTemplatePaths()->getPartialRootPaths();
}
/**
* Resolves the layout root to be used inside other paths.
*
* @return string[] Fluid layout root paths
* @throws InvalidTemplateResourceException
*/
public function getLayoutRootPaths()
{
return $this->baseRenderingContext->getTemplatePaths()->getLayoutRootPaths();
}
/**
* Set the root path(s) to the layouts.
* If set, overrides the one determined from $this->layoutRootPathPattern
*
* @param string[] $layoutRootPaths Root path to the layouts. If set, overrides the one determined from $this->layoutRootPathPattern
*/
public function setLayoutRootPaths(array $layoutRootPaths)
{
$this->baseRenderingContext->getTemplatePaths()->setLayoutRootPaths($layoutRootPaths);
}
}
@@ -0,0 +1,165 @@
<?php
namespace TYPO3\CMS\Fluid\View;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext;
use TYPO3\CMS\Extbase\Mvc\Web\Request as WebRequest;
use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Fluid\Core\Rendering\RenderingContext;
use TYPO3\CMS\Fluid\View\Exception\InvalidTemplateResourceException;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
/**
* A standalone template view.
* Should be used as view if you want to use Fluid without Extbase extensions
*/
class StandaloneView extends AbstractTemplateView
{
/**
* @var ObjectManager|null
*/
protected $objectManager;
/**
* Constructor
*
* @param ContentObjectRenderer $contentObject The current cObject. If NULL a new instance will be created
* @throws \InvalidArgumentException
* @throws \UnexpectedValueException
*/
public function __construct(ContentObjectRenderer $contentObject = null)
{
$this->objectManager = GeneralUtility::makeInstance(ObjectManager::class);
/** @var ConfigurationManagerInterface $configurationManager */
$configurationManager = $this->objectManager->get(ConfigurationManagerInterface::class);
if ($contentObject === null) {
/** @var ContentObjectRenderer $contentObject */
$contentObject = GeneralUtility::makeInstance(ContentObjectRenderer::class);
}
$configurationManager->setContentObject($contentObject);
/** @var WebRequest $request */
$request = $this->objectManager->get(WebRequest::class);
$request->setRequestUri(GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL'));
$request->setBaseUri(GeneralUtility::getIndpEnv('TYPO3_SITE_URL'));
/** @var UriBuilder $uriBuilder */
$uriBuilder = $this->objectManager->get(UriBuilder::class);
$uriBuilder->setRequest($request);
/** @var ControllerContext $controllerContext */
$controllerContext = $this->objectManager->get(ControllerContext::class);
$controllerContext->setRequest($request);
$controllerContext->setUriBuilder($uriBuilder);
$renderingContext = $this->objectManager->get(RenderingContext::class, $this);
$renderingContext->setControllerContext($controllerContext);
parent::__construct($renderingContext);
}
/**
* Sets the format of the current request (default format is "html")
*
* @param string $format
* @throws \RuntimeException
*/
public function setFormat($format)
{
if ($this->baseRenderingContext instanceof RenderingContext) {
$this->baseRenderingContext->getControllerContext()->getRequest()->setFormat($format);
$this->baseRenderingContext->getTemplatePaths()->setFormat($format);
} else {
throw new \RuntimeException('The rendering context must be of type ' . RenderingContext::class, 1482251886);
}
}
/**
* Returns the format of the current request (defaults is "html")
*
* @return string $format
* @throws \RuntimeException
*/
public function getFormat()
{
if ($this->baseRenderingContext instanceof RenderingContext) {
return $this->baseRenderingContext->getControllerContext()->getRequest()->getFormat();
}
throw new \RuntimeException('The rendering context must be of type ' . RenderingContext::class, 1482251887);
}
/**
* Returns the current request object
*
* @return WebRequest
* @throws \RuntimeException
* @internal
*/
public function getRequest()
{
if ($this->baseRenderingContext instanceof RenderingContext) {
return $this->baseRenderingContext->getControllerContext()->getRequest();
}
throw new \RuntimeException('The rendering context must be of type ' . RenderingContext::class, 1482251888);
}
/**
* Returns the absolute path to a Fluid template file if it was specified with setTemplatePathAndFilename() before.
* If the template filename was never specified, Fluid attempts to resolve the file based on controller and action.
*
* NB: If TemplatePaths was previously told to use the specific template path and filename it will short-circuit
* and return that template path and filename directly, instead of attempting to resolve it.
*
* @return string Fluid template path
*/
public function getTemplatePathAndFilename()
{
$templatePaths = $this->baseRenderingContext->getTemplatePaths();
return $templatePaths->resolveTemplateFileForControllerAndActionAndFormat(
$this->baseRenderingContext->getControllerName(),
$this->baseRenderingContext->getControllerAction(),
$templatePaths->getFormat()
);
}
/**
* Sets the Fluid template source
* You can use setTemplatePathAndFilename() alternatively if you only want to specify the template path
*
* @param string $templateSource Fluid template source code
*/
public function setTemplateSource($templateSource)
{
$this->baseRenderingContext->getTemplatePaths()->setTemplateSource($templateSource);
}
/**
* Checks whether a template can be resolved for the current request
*
* @return bool
*/
public function hasTemplate()
{
try {
$this->baseRenderingContext->getTemplatePaths()->getTemplateSource(
$this->baseRenderingContext->getControllerName(),
$this->baseRenderingContext->getControllerAction()
);
return true;
} catch (InvalidTemplateResourceException $e) {
return false;
}
}
}
@@ -0,0 +1,223 @@
<?php
namespace TYPO3\CMS\Fluid\View;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Extbase\Object\ObjectManager;
/**
* Class TemplatePaths
*
* Custom implementation for template paths resolving, one which differs from the base
* implementation in that it is capable of resolving template paths based on TypoScript
* configuration when given a package name, and is aware of the Frontend/Backend contexts of TYPO3.
*
* @internal This is for internal Fluid use only.
*/
class TemplatePaths extends \TYPO3Fluid\Fluid\View\TemplatePaths
{
/**
* @var string
*/
protected $templateSource;
/**
* @var string
*/
protected $templatePathAndFilename;
/**
* @param string $extensionKey
* @return string|null
*/
protected function getExtensionPrivateResourcesPath($extensionKey)
{
$extensionKey = trim($extensionKey);
if ($extensionKey && ExtensionManagementUtility::isLoaded($extensionKey)) {
return ExtensionManagementUtility::extPath($extensionKey) . 'Resources/Private/';
}
return null;
}
/**
* @return ConfigurationManagerInterface
*/
protected function getConfigurationManager()
{
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
$configurationManager = $objectManager->get(ConfigurationManagerInterface::class);
return $configurationManager;
}
/**
* @param string $extensionKey
* @return array
*/
protected function getContextSpecificViewConfiguration($extensionKey)
{
if (empty($extensionKey)) {
return [];
}
$resources = $this->getExtensionPrivateResourcesPath($extensionKey);
$paths = [
self::CONFIG_TEMPLATEROOTPATHS => [$resources . 'Templates/'],
self::CONFIG_PARTIALROOTPATHS => [$resources . 'Partials/'],
self::CONFIG_LAYOUTROOTPATHS => [$resources . 'Layouts/']
];
$configuredPaths = [];
if (!empty($this->templateRootPaths) || !empty($this->partialRootPaths) || !empty($this->layoutRootPaths)) {
// The view was configured already
$configuredPaths = [
self::CONFIG_TEMPLATEROOTPATHS => $this->templateRootPaths,
self::CONFIG_PARTIALROOTPATHS => $this->partialRootPaths,
self::CONFIG_LAYOUTROOTPATHS => $this->layoutRootPaths,
];
} else {
$typoScript = (array)$this->getConfigurationManager()->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FULL_TYPOSCRIPT);
$signature = str_replace('_', '', $extensionKey);
if ($this->isBackendMode() && isset($typoScript['module.']['tx_' . $signature . '.']['view.'])) {
$configuredPaths = (array)$typoScript['module.']['tx_' . $signature . '.']['view.'];
$configuredPaths = GeneralUtility::removeDotsFromTS($configuredPaths);
} elseif ($this->isFrontendMode() && isset($typoScript['plugin.']['tx_' . $signature . '.']['view.'])) {
$configuredPaths = (array)$typoScript['plugin.']['tx_' . $signature . '.']['view.'];
$configuredPaths = GeneralUtility::removeDotsFromTS($configuredPaths);
}
}
if (empty($configuredPaths)) {
return $paths;
}
foreach ($paths as $name => $defaultPaths) {
if (!empty($configuredPaths[$name])) {
$paths[$name] = array_merge($defaultPaths, ArrayUtility::sortArrayWithIntegerKeys((array)$configuredPaths[$name]));
}
}
return $paths;
}
/**
* Fills the path arrays with defaults, by package name.
* Reads those defaults from TypoScript if possible and
* if not defined, uses fallback paths by convention.
*
* @param string $packageName
*/
public function fillDefaultsByPackageName($packageName)
{
$this->fillFromConfigurationArray($this->getContextSpecificViewConfiguration($packageName));
}
/**
* Overridden setter with enforced sorting behavior
*
* @param array $templateRootPaths
*/
public function setTemplateRootPaths(array $templateRootPaths)
{
parent::setTemplateRootPaths(
ArrayUtility::sortArrayWithIntegerKeys($templateRootPaths)
);
}
/**
* Overridden setter with enforced sorting behavior
*
* @param array $layoutRootPaths
*/
public function setLayoutRootPaths(array $layoutRootPaths)
{
parent::setLayoutRootPaths(
ArrayUtility::sortArrayWithIntegerKeys($layoutRootPaths)
);
}
/**
* Overridden setter with enforced sorting behavior
*
* @param array $partialRootPaths
*/
public function setPartialRootPaths(array $partialRootPaths)
{
parent::setPartialRootPaths(
ArrayUtility::sortArrayWithIntegerKeys($partialRootPaths)
);
}
/**
* Public API for currently protected method. Can be dropped when switching to
* Fluid 1.1.0 or above.
*
* @param string $partialName
* @return string
*/
public function getPartialPathAndFilename($partialName)
{
return parent::getPartialPathAndFilename($partialName);
}
/**
* Get absolute path to template file
*
* @return string Returns the absolute path to a Fluid template file
*/
public function getTemplatePathAndFilename()
{
return $this->templatePathAndFilename;
}
/**
* Guarantees that $reference is turned into a
* correct, absolute path. The input can be a
* relative path or a FILE: or EXT: reference
* but cannot be a FAL resource identifier.
*
* @param mixed $reference
* @return string
*/
protected function ensureAbsolutePath($reference)
{
if (!is_array($reference)) {
return PathUtility::isAbsolutePath($reference) ? $reference : GeneralUtility::getFileAbsFileName($reference);
}
foreach ($reference as &$subValue) {
$subValue = $this->ensureAbsolutePath($subValue);
}
return $reference;
}
/**
* @return bool
*/
protected function isBackendMode()
{
return TYPO3_MODE === 'BE';
}
/**
* @return bool
*/
protected function isFrontendMode()
{
return TYPO3_MODE === 'FE';
}
}
@@ -0,0 +1,53 @@
<?php
namespace TYPO3\CMS\Fluid\View;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext;
use TYPO3Fluid\Fluid\View\Exception\InvalidTemplateResourceException;
/**
* The main template view. Should be used as view if you want Fluid Templating
*/
class TemplateView extends AbstractTemplateView
{
/**
* Sets the path and name of the layout file. Overrides the dynamic resolving of the layout file.
*
* @param string $layoutPathAndFilename Path and filename of the layout file
*/
public function setLayoutPathAndFilename($layoutPathAndFilename)
{
$this->baseRenderingContext->getTemplatePaths()->setLayoutPathAndFilename($layoutPathAndFilename);
}
/**
* Checks whether a template can be resolved for the current request context.
*
* @param ControllerContext $controllerContext Controller context which is available inside the view
* @return bool
*/
public function canRender(ControllerContext $controllerContext)
{
try {
$request = $controllerContext->getRequest();
$this->setControllerContext($controllerContext);
$this->baseRenderingContext->getTemplatePaths()->setFormat($request->getFormat());
$this->baseRenderingContext->getTemplatePaths()->getTemplateSource($request->getControllerName(), $request->getControllerActionName());
return true;
} catch (InvalidTemplateResourceException $e) {
return false;
}
}
}
@@ -0,0 +1,66 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* ViewHelper which creates a :html:`<base href="..."></base>` tag.
*
* The Base URI is taken from the current request.
*
* Examples
* ========
*
* Example::
*
* <f:base />
*
* Output::
*
* <base href="http://yourdomain.tld/" />
*
* Depending on your domain.
*/
class BaseViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* As this ViewHelper renders HTML, the output must not be escaped.
*
* @var bool
*/
protected $escapeOutput = false;
/**
* Render the "Base" tag by outputting $request->getBaseUri()
*
* Note: renders as <base></base>, because IE6 will else refuse to display
* the page...
*
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
*
* @return string "base"-Tag.
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
return '<base href="' . htmlspecialchars($renderingContext->getControllerContext()->getRequest()->getBaseUri()) . '" />';
}
}
@@ -0,0 +1,67 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Be;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Backend\Template\DocumentTemplate;
use TYPO3\CMS\Core\Page\PageRenderer;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
/**
* The abstract base class for all backend ViewHelpers
* Note: backend ViewHelpers are still experimental!
*/
abstract class AbstractBackendViewHelper extends AbstractViewHelper
{
/**
* Gets instance of template if exists or create a new one.
* Saves instance in viewHelperVariableContainer
*
* @return DocumentTemplate $doc
*/
public function getDocInstance()
{
$viewHelperVariableContainer = $this->renderingContext->getViewHelperVariableContainer();
if ($viewHelperVariableContainer->exists(self::class, 'DocumentTemplate')) {
$doc = $viewHelperVariableContainer->get(self::class, 'DocumentTemplate');
} else {
/** @var DocumentTemplate $doc */
$doc = GeneralUtility::makeInstance(DocumentTemplate::class);
$viewHelperVariableContainer->add(self::class, 'DocumentTemplate', $doc);
}
return $doc;
}
/**
* Gets instance of PageRenderer if exists or create a new one.
* Saves instance in viewHelperVariableContainer
*
* @return PageRenderer
*/
public function getPageRenderer()
{
$viewHelperVariableContainer = $this->renderingContext->getViewHelperVariableContainer();
if ($viewHelperVariableContainer->exists(self::class, 'PageRenderer')) {
$pageRenderer = $viewHelperVariableContainer->get(self::class, 'PageRenderer');
} else {
/** @var DocumentTemplate $doc */
$pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
$viewHelperVariableContainer->add(self::class, 'PageRenderer', $pageRenderer);
}
return $pageRenderer;
}
}
@@ -0,0 +1,115 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Be\Buttons;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Fluid\ViewHelpers\Be\AbstractBackendViewHelper;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
/**
* ViewHelper which returns CSH (context sensitive help) button with icon.
*
* .. note::
* The CSH button will only work, if the current BE user has the "Context
* Sensitive Help mode" set to something else than "Display no help
* information" in the Users settings.
*
* .. note::
* This ViewHelper is experimental!
*
* Examples
* ========
*
* Default::
*
* <f:be.buttons.csh />
*
* CSH button as known from the TYPO3 backend.
*
* Full configuration::
*
* <f:be.buttons.csh table="xMOD_csh_corebe" field="someCshKey" />
*
* CSH button as known from the TYPO3 backend with some custom settings.
*
* Full configuration with content::
*
* <f:be.buttons.csh table="xMOD_csh_corebe" field="someCshKey">
* some text to link
* </f:be.buttons.csh>
*
* A link with text "some text to link" to link the help.
*/
class CshViewHelper extends AbstractBackendViewHelper
{
/**
* As this ViewHelper renders HTML, the output must not be escaped.
*
* @var bool
*/
protected $escapeOutput = false;
/**
* Initialize arguments.
*
* @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('table', 'string', 'Table name (\'_MOD_\'+module name). If not set, the current module name will be used');
$this->registerArgument('field', 'string', 'Field name (CSH locallang main key)', false, '');
$this->registerArgument('wrap', 'string', 'Markup to wrap around the CSH, split by "|"', false, '');
}
/**
* Render context sensitive help (CSH) for the given table
*
* @return string the rendered CSH icon
*/
public function render()
{
return static::renderStatic(
$this->arguments,
$this->buildRenderChildrenClosure(),
$this->renderingContext
);
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return string
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$table = $arguments['table'];
$field = $arguments['field'];
$wrap = $arguments['wrap'];
if ($table === null) {
$currentRequest = $renderingContext->getControllerContext()->getRequest();
$moduleName = $currentRequest->getPluginName();
$table = '_MOD_' . $moduleName;
}
$content = (string)$renderChildrenClosure();
if ($content !== '') {
return BackendUtility::wrapInHelp($table, $field, $content);
}
return '<div class="docheader-csh">' . BackendUtility::cshItem($table, $field, '', $wrap) . '</div>';
}
}
@@ -0,0 +1,113 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Be\Buttons;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Backend\Template\DocumentTemplate;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\ViewHelpers\Be\AbstractBackendViewHelper;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
/**
* ViewHelper which returns shortcut button with icon.
*
* .. note::
* This ViewHelper is experimental!
*
* Examples
* ========
*
* Default::
*
* <f:be.buttons.shortcut />
*
* Shortcut button as known from the TYPO3 backend.
* By default the current page id, module name and all module arguments will be stored.
*
* Explicitly set parameters to be stored in the shortcut::
*
* <f:be.buttons.shortcut getVars="{0: 'route', 1: 'myOwnPrefix'}" setVars="{0: 'function'}" />
*
* Shortcut button as known from the TYPO3 backend.
* This time only the specified GET parameters and SET[]-settings will be stored.
*
* .. note:
*
* Normally you won't need to set getVars & setVars parameters in Extbase modules.
*/
class ShortcutViewHelper extends AbstractBackendViewHelper
{
/**
* As this ViewHelper renders HTML, the output must not be escaped.
*
* @var bool
*/
protected $escapeOutput = false;
/**
* Initialize arguments.
*
* @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('getVars', 'array', 'List of GET variables to store. By default the current id, module and all module arguments will be stored', false, []);
$this->registerArgument('setVars', 'array', 'List of SET[] variables to store. See DocumentTemplate::makeShortcutIcon(). Normally won\'t be used by Extbase modules', false, []);
}
/**
* Renders a shortcut button as known from the TYPO3 backend
*
* @return string the rendered shortcut button
* @see \TYPO3\CMS\Backend\Template\DocumentTemplate::makeShortcutIcon()
*/
public function render()
{
return static::renderStatic(
$this->arguments,
$this->buildRenderChildrenClosure(),
$this->renderingContext
);
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return string
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$getVars = $arguments['getVars'];
$setVars = $arguments['setVars'];
$mayMakeShortcut = $GLOBALS['BE_USER']->mayMakeShortcut();
if ($mayMakeShortcut) {
$doc = GeneralUtility::makeInstance(DocumentTemplate::class);
$currentRequest = $renderingContext->getControllerContext()->getRequest();
$extensionName = $currentRequest->getControllerExtensionName();
$moduleName = $currentRequest->getPluginName();
if (count($getVars) === 0) {
$modulePrefix = strtolower('tx_' . $extensionName . '_' . $moduleName);
$getVars = ['id', 'route', $modulePrefix];
}
$getList = implode(',', $getVars);
$setList = implode(',', $setVars);
return $doc->makeShortcutIcon($getList, $setList, $moduleName);
}
return '';
}
}
@@ -0,0 +1,120 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Be;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
/**
* ViewHelper which allows you to create extbase based modules in the style of TYPO3 default modules.
*
* Examples
* ========
*
* Simple::
*
* <f:be.container>your module content</f:be.container>
*
* "your module content" wrapped with proper head & body tags.
* Default backend CSS styles and JavaScript will be included.
*
* All options::
*
* <f:be.container pageTitle="foo"
* includeCssFiles="{0: '{f:uri.resource(path:\'Css/Styles.css\')}'}"
* includeJsFiles="{0: '{f:uri.resource(path:\'JavaScript/Library1.js\')}', 1: '{f:uri.resource(path:\'JavaScript/Library2.js\')}'}"
* addJsInlineLabels="{0: 'label1', 1: 'label2'}"
* >
* your module content
* </f:be.container>
*
* "your module content" wrapped with proper head & body tags.
* Custom CSS file :file:`EXT:your_extension/Resources/Public/Css/styles.css` and
* JavaScript files :file:`EXT:your_extension/Resources/Public/JavaScript/Library1.js` and
* :file:`EXT:your_extension/Resources/Public/JavaScript/Library2.js`
* will be loaded, plus some inline labels for usage in JS code.
*/
class ContainerViewHelper extends AbstractBackendViewHelper
{
/**
* @var bool
*/
protected $escapeOutput = false;
/**
* Initialize arguments.
*
* @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('pageTitle', 'string', 'Title tag of the module. Not required by default, as BE modules are shown in a frame', false, '');
$this->registerArgument('includeCssFiles', 'array', 'List of custom CSS file to be loaded');
$this->registerArgument('includeJsFiles', 'array', 'List of custom JavaScript file to be loaded');
$this->registerArgument('addJsInlineLabels', 'array', 'Custom labels to add to JavaScript inline labels');
$this->registerArgument('includeRequireJsModules', 'array', 'List of RequireJS modules to be loaded');
}
/**
* Render start page with \TYPO3\CMS\Backend\Template\DocumentTemplate and pageTitle
*
* @return string
* @see \TYPO3\CMS\Backend\Template\DocumentTemplate
* @see \TYPO3\CMS\Core\Page\PageRenderer
*/
public function render()
{
$pageTitle = $this->arguments['pageTitle'];
$includeCssFiles = $this->arguments['includeCssFiles'];
$includeJsFiles = $this->arguments['includeJsFiles'];
$addJsInlineLabels = $this->arguments['addJsInlineLabels'];
$includeRequireJsModules = $this->arguments['includeRequireJsModules'];
$pageRenderer = $this->getPageRenderer();
$doc = $this->getDocInstance();
$doc->JScode .= GeneralUtility::wrapJS($doc->redirectUrls());
// Include custom CSS and JS files
if (is_array($includeCssFiles) && count($includeCssFiles) > 0) {
foreach ($includeCssFiles as $addCssFile) {
$pageRenderer->addCssFile($addCssFile);
}
}
if (is_array($includeJsFiles) && count($includeJsFiles) > 0) {
foreach ($includeJsFiles as $addJsFile) {
$pageRenderer->addJsFile($addJsFile);
}
}
if (is_array($includeRequireJsModules) && count($includeRequireJsModules) > 0) {
foreach ($includeRequireJsModules as $addRequireJsFile) {
$pageRenderer->loadRequireJsModule($addRequireJsFile);
}
}
// Add inline language labels
if (is_array($addJsInlineLabels) && count($addJsInlineLabels) > 0) {
$extensionKey = $this->renderingContext->getControllerContext()->getRequest()->getControllerExtensionKey();
foreach ($addJsInlineLabels as $key) {
$label = LocalizationUtility::translate($key, $extensionKey);
$pageRenderer->addInlineLanguageLabel($key, $label);
}
}
// Render the content and return it
$output = $this->renderChildren();
$output = $doc->startPage($pageTitle) . $output;
$output .= $doc->endPage();
return $output;
}
}
@@ -0,0 +1,150 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Be;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\Utility\MathUtility;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithContentArgumentAndRenderStatic;
/**
* ViewHelper for rendering a styled content infobox markup.
*
* States
* ======
*
* The Infobox provides different context sensitive states that
* can be used to provide an additional visual feedback to the
* to the user to underline the meaning of the information.
*
* Possible values are in range from ``-2`` to ``2``. Please choose a
* meaningful value from the following list.
*
* ``-2``
* Notices (Default)
* ``-1``
* Information
* ``0``
* Positive feedback
* ``1``
* Warnings
* ``2``
* Error
*
* Examples
* ========
*
* Simple::
*
* <f:be.infobox title="Message title">your box content</f:be.infobox>
*
* All options::
*
* <f:be.infobox title="Message title" message="your box content" state="-2" iconName="check" disableIcon="true" />
*/
class InfoboxViewHelper extends AbstractViewHelper
{
use CompileWithContentArgumentAndRenderStatic;
const STATE_NOTICE = -2;
const STATE_INFO = -1;
const STATE_OK = 0;
const STATE_WARNING = 1;
const STATE_ERROR = 2;
/**
* As this ViewHelper renders HTML, the output must not be escaped.
*
* @var bool
*/
protected $escapeOutput = false;
/**
* Initialize arguments.
*
* @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
*/
public function initializeArguments()
{
$this->registerArgument('message', 'string', 'The message of the info box, if NULL tag content is used');
$this->registerArgument('title', 'string', 'The title of the info box');
$this->registerArgument('state', 'int', 'The state of the box, InfoboxViewHelper::STATE_*', false, self::STATE_NOTICE);
$this->registerArgument('iconName', 'string', 'The icon name from font awesome, NULL sets default icon');
$this->registerArgument('disableIcon', 'bool', 'If set to TRUE, the icon is not rendered.', false, false);
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
*
* @return string
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$title = $arguments['title'];
$message = $renderChildrenClosure();
$state = $arguments['state'];
$isInRange = MathUtility::isIntegerInRange($state, -2, 2);
if (!$isInRange) {
$state = -2;
}
$iconName = $arguments['iconName'];
$disableIcon = $arguments['disableIcon'];
$classes = [
self::STATE_NOTICE => 'notice',
self::STATE_INFO => 'info',
self::STATE_OK => 'success',
self::STATE_WARNING => 'warning',
self::STATE_ERROR => 'danger'
];
$icons = [
self::STATE_NOTICE => 'lightbulb-o',
self::STATE_INFO => 'info',
self::STATE_OK => 'check',
self::STATE_WARNING => 'exclamation',
self::STATE_ERROR => 'times'
];
$stateClass = $classes[$state];
$icon = $icons[$state];
if ($iconName !== null) {
$icon = $iconName;
}
$iconTemplate = '';
if (!$disableIcon) {
$iconTemplate = '' .
'<div class="media-left">' .
'<span class="fa-stack fa-lg callout-icon">' .
'<i class="fa fa-circle fa-stack-2x"></i>' .
'<i class="fa fa-' . htmlspecialchars($icon) . ' fa-stack-1x"></i>' .
'</span>' .
'</div>';
}
$titleTemplate = '';
if ($title !== null) {
$titleTemplate = '<h4 class="callout-title">' . htmlspecialchars($title) . '</h4>';
}
return '<div class="callout callout-' . htmlspecialchars($stateClass) . '">' .
'<div class="media">' .
$iconTemplate .
'<div class="media-body">' .
$titleTemplate .
'<div class="callout-body">' . $message . '</div>' .
'</div>' .
'</div>' .
'</div>';
}
}
@@ -0,0 +1,117 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Be\Labels;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Fluid\ViewHelpers\Be\AbstractBackendViewHelper;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
/**
* ViewHelper which returns CSH (context sensitive help) label with icon hover.
*
* .. note::
* The CSH label will only work, if the current BE user has the "Context
* Sensitive Help mode" set to something else than "Display no help
* information" in the Users settings.
*
* .. note::
* This ViewHelper is experimental!
*
* Examples
* ========
*
* Default::
*
* <f:be.labels.csh />
*
* CSH label as known from the TYPO3 backend.
*
* Full configuration::
*
* <f:be.labels.csh table="xMOD_csh_corebe" field="someCshKey" label="lang/Resources/Private/Language/locallang/header.languages" />
*
* CSH label as known from the TYPO3 backend with some custom settings.
*/
class CshViewHelper extends AbstractBackendViewHelper
{
/**
* As this ViewHelper renders HTML, the output must not be escaped.
*
* @var bool
*/
protected $escapeOutput = false;
/**
* Returns the Language Service
* @return LanguageService
*/
protected static function getLanguageService()
{
return $GLOBALS['LANG'];
}
/**
* Initialize arguments.
*
* @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('table', 'string', 'Table name (\'_MOD_\'+module name). If not set, the current module name will be used');
$this->registerArgument('field', 'string', 'Field name (CSH locallang main key)', false, '');
$this->registerArgument('label', 'string', 'Language label which is wrapped with the CSH', false, '');
}
/**
* Render context sensitive help (CSH) for the given table
*
* @return string the rendered CSH icon
*/
public function render()
{
return static::renderStatic(
$this->arguments,
$this->buildRenderChildrenClosure(),
$this->renderingContext
);
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return string
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$table = $arguments['table'];
$field = $arguments['field'];
$label = $arguments['label'];
if ($table === null) {
$currentRequest = $renderingContext->getControllerContext()->getRequest();
$moduleName = $currentRequest->getPluginName();
$table = '_MOD_' . $moduleName;
}
if (strpos($label, 'LLL:') === 0) {
$label = self::getLanguageService()->sL($label);
}
// Double encode can be set to true, once the typo3fluid/fluid fix is released and required
$label = '<label>' . htmlspecialchars($label, ENT_QUOTES, null, false) . '</label>';
return BackendUtility::wrapInHelp($table, $field, $label);
}
}
@@ -0,0 +1,85 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Be;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Backend\Routing\UriBuilder;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
/**
* A ViewHelper for creating URIs to modules.
*
* Examples
* ========
*
* URI to the web_ts module on page 92::
*
* <f:be.link route="web_ts" parameters="{id: 92}">Go to web_ts</f:be.link>
*
* ``<a href="/typo3/index.php?route=%2module%2web_ts%2&moduleToken=b6e9c9f?id=92">Go to web_ts</a>``
*/
class LinkViewHelper extends AbstractTagBasedViewHelper
{
/**
* @var string
*/
protected $tagName = 'a';
/**
* Arguments initialization
*
* @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('route', 'string', 'The name of the route', true);
$this->registerArgument('parameters', 'array', 'An array of parameters', false, []);
$this->registerArgument('referenceType', 'string', 'The type of reference to be generated (one of the constants)', false, UriBuilder::ABSOLUTE_PATH);
$this->registerTagAttribute('name', 'string', 'Specifies the name of an anchor');
$this->registerTagAttribute(
'rel',
'string',
'Specifies the relationship between the current document and the linked document'
);
$this->registerTagAttribute(
'rev',
'string',
'Specifies the relationship between the linked document and the current document'
);
$this->registerTagAttribute('target', 'string', 'Specifies where to open the linked document');
$this->registerUniversalTagAttributes();
}
/**
* @return string Rendered link
*/
public function render()
{
$uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
$route = $this->arguments['route'];
$parameters = $this->arguments['parameters'];
$referenceType = $this->arguments['referenceType'];
$uri = $uriBuilder->buildUriFromRoute($route, $parameters, $referenceType);
$this->tag->addAttribute('href', $uri);
$this->tag->setContent($this->renderChildren());
$this->tag->forceClosingTag(true);
return $this->tag->render();
}
}
@@ -0,0 +1,75 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Be\Menus;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3Fluid\Fluid\Core\Parser\SyntaxTree\ViewHelperNode;
/**
* ViewHelper which groups options of an option tag.
*
* Example
* =======
*
* ::
*
* <f:be.menus.actionMenu>
* <f:be.menus.actionMenuItem label="Default: Welcome" controller="Default" action="index" />
* <f:be.menus.actionMenuItem label="Community: get in touch" controller="Community" action="index" />
*
* <f:be.menus.actionMenuItemGroup label="Information">
* <f:be.menus.actionMenuItem label="PHP Information" controller="Information" action="listPhpInfo" />
* <f:be.menus.actionMenuItem label="Documentation" controller="Information" action="documentation" />
* <f:be.menus.actionMenuItem label="Hooks" controller="Information" action="hooks" />
* <f:be.menus.actionMenuItem label="Signals" controller="Information" action="signals" />
* <f:be.menus.actionMenuItem label="XClasses" controller="Information" action="xclass" />
* </f:be.menus.actionMenuItemGroup>
* </f:be.menus.actionMenu>
*/
class ActionMenuItemGroupViewHelper extends ActionMenuViewHelper
{
/**
* @var string
*/
protected $tagName = 'optgroup';
/**
* Initialize arguments.
*
* @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('label', 'string', 'label', false, '');
}
/**
* @return string
*/
public function render()
{
$label = $this->arguments['label'];
$this->tag->addAttribute('label', $label);
$options = '';
foreach ($this->childNodes as $childNode) {
if ($childNode instanceof ViewHelperNode) {
$options .= $childNode->evaluate($this->renderingContext);
}
}
$this->tag->setContent($options);
return $this->tag->render();
}
}
@@ -0,0 +1,116 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Be\Menus;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
/**
* ViewHelper which returns an option tag.
* This ViewHelper only works in conjunction with :php:`\TYPO3\CMS\Fluid\ViewHelpers\Be\Menus\ActionMenuViewHelper`.
*
* .. note::
* This ViewHelper is experimental!
*
* Examples
* ========
*
* Simple::
*
* <f:be.menus.actionMenu>
* <f:be.menus.actionMenuItem label="Overview" controller="Blog" action="index" />
* <f:be.menus.actionMenuItem label="Create new Blog" controller="Blog" action="new" />
* <f:be.menus.actionMenuItem label="List Posts" controller="Post" action="index" arguments="{blog: blog}" />
* </f:be.menus.actionMenu>
*
* Selectbox with the options "Overview", "Create new Blog" and "List Posts".
*
* Localized::
*
* <f:be.menus.actionMenu>
* <f:be.menus.actionMenuItem label="{f:translate(key='overview')}" controller="Blog" action="index" />
* <f:be.menus.actionMenuItem label="{f:translate(key='create_blog')}" controller="Blog" action="new" />
* </f:be.menus.actionMenu>
*
* Localized selectbox.
*/
class ActionMenuItemViewHelper extends AbstractTagBasedViewHelper
{
/**
* @var string
*/
protected $tagName = 'option';
/**
* Initialize arguments.
*
* @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('label', 'string', 'label of the option tag', true);
$this->registerArgument('controller', 'string', 'controller to be associated with this ActionMenuItem', true);
$this->registerArgument('action', 'string', 'the action to be associated with this ActionMenuItem', true);
$this->registerArgument('arguments', 'array', 'additional controller arguments to be passed to the action when this ActionMenuItem is selected', false, []);
}
/**
* Renders an ActionMenu option tag
*
* @return string the rendered option tag
* @see \TYPO3\CMS\Fluid\ViewHelpers\Be\Menus\ActionMenuViewHelper
*/
public function render()
{
$label = $this->arguments['label'];
$controller = $this->arguments['controller'];
$action = $this->arguments['action'];
$arguments = $this->arguments['arguments'];
$uri = $this->renderingContext->getControllerContext()->getUriBuilder()->reset()->uriFor($action, $arguments, $controller);
$this->tag->addAttribute('value', $uri);
if (!$this->tag->hasAttribute('selected')) {
$this->evaluateSelectItemState($controller, $action, $arguments);
}
$this->tag->setContent(
// Double encode can be set to true, once the typo3fluid/fluid fix is released and required
htmlspecialchars($label, ENT_QUOTES, null, false)
);
return $this->tag->render();
}
protected function evaluateSelectItemState(string $controller, string $action, array $arguments): void
{
$currentRequest = $this->renderingContext->getControllerContext()->getRequest();
$flatRequestArguments = ArrayUtility::flatten(
array_merge([
'controller' => $currentRequest->getControllerName(),
'action' => $currentRequest->getControllerActionName()
], $currentRequest->getArguments())
);
$flatViewHelperArguments = ArrayUtility::flatten(
array_merge(['controller' => $controller, 'action' => $action], $arguments)
);
if (
$this->arguments['selected'] ||
array_diff($flatRequestArguments, $flatViewHelperArguments) === []
) {
$this->tag->addAttribute('selected', 'selected');
}
}
}
@@ -0,0 +1,106 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Be\Menus;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3Fluid\Fluid\Core\Compiler\TemplateCompiler;
use TYPO3Fluid\Fluid\Core\Parser\SyntaxTree\ViewHelperNode;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
/**
* ViewHelper which returns a select box, that can be used to switch between
* multiple actions and controllers and looks similar to TYPO3s funcMenu.
*
* .. note::
* This ViewHelper is experimental!
*
* Examples
* ========
*
* Simple::
*
* <f:be.menus.actionMenu>
* <f:be.menus.actionMenuItem label="Overview" controller="Blog" action="index" />
* <f:be.menus.actionMenuItem label="Create new Blog" controller="Blog" action="new" />
* <f:be.menus.actionMenuItem label="List Posts" controller="Post" action="index" arguments="{blog: blog}" />
* </f:be.menus.actionMenu>
*
* Selectbox with the options "Overview", "Create new Blog" and "List Posts".
*
* Localized::
*
* <f:be.menus.actionMenu>
* <f:be.menus.actionMenuItem label="{f:translate(key:'overview')}" controller="Blog" action="index" />
* <f:be.menus.actionMenuItem label="{f:translate(key:'create_blog')}" controller="Blog" action="new" />
* </f:be.menus.actionMenu>
*
* Localized selectbox.
*/
class ActionMenuViewHelper extends AbstractTagBasedViewHelper
{
/**
* @var string
*/
protected $tagName = 'select';
/**
* An array of \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\AbstractNode
*
* @var array
*/
protected $childNodes = [];
/**
* Initialize arguments.
*
* @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('defaultController', 'string', 'defaultController');
}
/**
* Render FunctionMenu
*
* @return string
*/
public function render()
{
$this->tag->addAttribute('onchange', 'jumpToUrl(this.options[this.selectedIndex].value, this);');
$options = '';
foreach ($this->childNodes as $childNode) {
if ($childNode instanceof ViewHelperNode) {
$options .= $childNode->evaluate($this->renderingContext);
}
}
$this->tag->setContent($options);
return '<div class="docheader-funcmenu">' . $this->tag->render() . '</div>';
}
/**
* @param string $argumentsName
* @param string $closureName
* @param string $initializationPhpCode
* @param ViewHelperNode $node
* @param TemplateCompiler $compiler
*/
public function compile($argumentsName, $closureName, &$initializationPhpCode, ViewHelperNode $node, TemplateCompiler $compiler)
{
// @TODO: replace with a true compiling method to make compilable!
$compiler->disable();
return null;
}
}
@@ -0,0 +1,97 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Be;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Imaging\Icon;
use TYPO3\CMS\Core\Imaging\IconFactory;
use TYPO3\CMS\Core\Type\Bitmask\Permission;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
/**
* ViewHelper which return page info icon as known from TYPO3 backend modules.
*
* .. note::
* This ViewHelper is experimental!
*
* Examples
* ========
*
* Default::
*
* <f:be.pageInfo />
*
* Page info icon with context menu
*/
class PageInfoViewHelper extends AbstractBackendViewHelper
{
/**
* This ViewHelper renders HTML, thus output must not be escaped
*
* @var bool
*/
protected $escapeOutput = false;
/**
* Render javascript in header
*
* @return string the rendered page info icon
* @see \TYPO3\CMS\Backend\Template\DocumentTemplate::getPageInfo() Note: can't call this method as it's protected!
*/
public function render()
{
return static::renderStatic(
[],
$this->buildRenderChildrenClosure(),
$this->renderingContext
);
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
*
* @return string
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$id = GeneralUtility::_GP('id');
$pageRecord = BackendUtility::readPageAccess($id, $GLOBALS['BE_USER']->getPagePermsClause(Permission::PAGE_SHOW));
// Add icon with context menu, etc:
/** @var IconFactory $iconFactory */
$iconFactory = GeneralUtility::makeInstance(IconFactory::class);
if ($pageRecord['uid']) {
// If there IS a real page
$altText = BackendUtility::getRecordIconAltText($pageRecord, 'pages');
$theIcon = '<span title="' . $altText . '">' . $iconFactory->getIconForRecord('pages', $pageRecord, Icon::SIZE_SMALL)->render() . '</span>';
// Make Icon:
$theIcon = BackendUtility::wrapClickMenuOnIcon($theIcon, 'pages', $pageRecord['uid']);
// Setting icon with context menu + uid
$theIcon .= ' <em>[PID: ' . $pageRecord['uid'] . ']</em>';
} else {
// On root-level of page tree
// Make Icon
$theIcon = '<span title="' . htmlspecialchars($GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename']) . '">' . $iconFactory->getIcon('apps-pagetree-page-domain', Icon::SIZE_SMALL)->render() . '</span>';
if ($GLOBALS['BE_USER']->isAdmin()) {
$theIcon = BackendUtility::wrapClickMenuOnIcon($theIcon, 'pages');
}
}
return $theIcon;
}
}
@@ -0,0 +1,92 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Be;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Type\Bitmask\Permission;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
/**
* ViewHelper which returns the current page path as known from TYPO3 backend modules.
*
* .. note::
* This ViewHelper is experimental!
*
* Examples
* ========
*
* Default::
*
* <f:be.pagePath />
*
* Current page path, prefixed with "Path:" and wrapped in a span with the class ``typo3-docheader-pagePath``.
*/
class PagePathViewHelper extends AbstractBackendViewHelper
{
/**
* This ViewHelper renders HTML, thus output must not be escaped
*
* @var bool
*/
protected $escapeOutput = false;
/**
* Renders the current page path
*
* @return string the rendered page path
* @see \TYPO3\CMS\Backend\Template\DocumentTemplate::getPagePath() Note: can't call this method as it's protected!
*/
public function render()
{
return static::renderStatic(
[],
$this->buildRenderChildrenClosure(),
$this->renderingContext
);
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
*
* @return string
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$id = GeneralUtility::_GP('id');
$pageRecord = BackendUtility::readPageAccess($id, $GLOBALS['BE_USER']->getPagePermsClause(Permission::PAGE_SHOW));
// Is this a real page
if ($pageRecord['uid']) {
$title = $pageRecord['_thePathFull'];
} else {
$title = $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'];
}
// Setting the path of the page
$pagePath = htmlspecialchars($GLOBALS['LANG']->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.path')) . ': <span class="typo3-docheader-pagePath">';
// crop the title to title limit (or 50, if not defined)
$cropLength = empty($GLOBALS['BE_USER']->uc['titleLen']) ? 50 : $GLOBALS['BE_USER']->uc['titleLen'];
$croppedTitle = GeneralUtility::fixed_lgd_cs($title, -$cropLength);
if ($croppedTitle !== $title) {
$pagePath .= '<abbr title="' . htmlspecialchars($title) . '">' . htmlspecialchars($croppedTitle) . '</abbr>';
} else {
$pagePath .= htmlspecialchars($title);
}
$pagePath .= '</span>';
return $pagePath;
}
}
@@ -0,0 +1,118 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Be;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\Page\PageRenderer;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* ViewHelper which allows you to create extbase based modules in the style of TYPO3 default modules.
*
* Examples
* ========
*
* All options::
*
* <f:be.pageRenderer pageTitle="foo"
* includeCssFiles="{0: '{f:uri.resource(path:\'Css/Styles.css\')}'}"
* includeJsFiles="{0: '{f:uri.resource(path:\'JavaScript/Library1.js\')}', 1: '{f:uri.resource(path:\'JavaScript/Library2.js\')}'}"
* addJsInlineLabels="{0: 'label1', 1: 'label2'}" />
*
* Custom CSS file :file:`EXT:your_extension/Resources/Public/Css/styles.css` and
* JavaScript files :file:`EXT:your_extension/Resources/Public/JavaScript/Library1.js` and
* :file:`EXT:your_extension/Resources/Public/JavaScript/Library2.js`
* will be loaded, plus some inline labels for usage in JS code.
*/
class PageRendererViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* Initialize arguments.
*
* @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
*/
public function initializeArguments()
{
$this->registerArgument('pageTitle', 'string', 'title tag of the module. Not required by default, as BE modules are shown in a frame', false, '');
$this->registerArgument('includeCssFiles', 'array', 'List of custom CSS file to be loaded');
$this->registerArgument('includeJsFiles', 'array', 'List of custom JavaScript file to be loaded');
$this->registerArgument('addJsInlineLabels', 'array', 'Custom labels to add to JavaScript inline labels');
$this->registerArgument('includeRequireJsModules', 'array', 'List of RequireJS modules to be loaded');
$this->registerArgument('addInlineSettings', 'array', 'Adds Javascript Inline Setting');
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$pageRenderer = static::getPageRenderer();
$pageTitle = $arguments['pageTitle'];
$includeCssFiles = $arguments['includeCssFiles'];
$includeJsFiles = $arguments['includeJsFiles'];
$addJsInlineLabels = $arguments['addJsInlineLabels'];
$includeRequireJsModules = $arguments['includeRequireJsModules'];
$addInlineSettings = $arguments['addInlineSettings'];
if ($pageTitle) {
$pageRenderer->setTitle($pageTitle);
}
// Include custom CSS and JS files
if (is_array($includeCssFiles) && count($includeCssFiles) > 0) {
foreach ($includeCssFiles as $addCssFile) {
$pageRenderer->addCssFile($addCssFile);
}
}
if (is_array($includeJsFiles) && count($includeJsFiles) > 0) {
foreach ($includeJsFiles as $addJsFile) {
$pageRenderer->addJsFile($addJsFile);
}
}
if (is_array($includeRequireJsModules) && count($includeRequireJsModules) > 0) {
foreach ($includeRequireJsModules as $addRequireJsFile) {
$pageRenderer->loadRequireJsModule($addRequireJsFile);
}
}
if (is_array($addInlineSettings) && count($addInlineSettings) > 0) {
$pageRenderer->addInlineSettingArray(null, $addInlineSettings);
}
// Add inline language labels
if (is_array($addJsInlineLabels) && count($addJsInlineLabels) > 0) {
$extensionKey = $renderingContext->getControllerContext()->getRequest()->getControllerExtensionKey();
foreach ($addJsInlineLabels as $key) {
$label = LocalizationUtility::translate($key, $extensionKey);
$pageRenderer->addInlineLanguageLabel($key, $label);
}
}
}
/**
* @return PageRenderer
*/
protected static function getPageRenderer()
{
return GeneralUtility::makeInstance(PageRenderer::class);
}
}
@@ -0,0 +1,61 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Be\Security;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractConditionViewHelper;
/**
* This ViewHelper implements an ifAuthenticated/else condition for backend
* users and backend groups.
*
* Examples
* ========
*
* Basic usage::
*
* <f:be.security.ifAuthenticated>
* This is being shown whenever a BE user is logged in
* </f:be.security.ifAuthenticated>
*
* Everything inside the :html:`<f:be.ifAuthenticated>` tag is being displayed
* if the user is authenticated with any backend user account.
*
* IfAuthenticated / then / else::
*
* <f:be.security.ifAuthenticated>
* <f:then>
* This is being shown in case you have access.
* </f:then>
* <f:else>
* This is being displayed in case you do not have access.
* </f:else>
* </f:be.security.ifAuthenticated>
*
* Everything inside the :html:`<f:then></f:then>` is displayed the backend user is logged in.
* :html:`<f:else></f:else>` is displayed if no backend user is logged in.
*/
class IfAuthenticatedViewHelper extends AbstractConditionViewHelper
{
/**
* This method decides if the condition is TRUE or FALSE. It can be overridden 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
*/
protected static function evaluateCondition($arguments = null)
{
return isset($GLOBALS['BE_USER']) && $GLOBALS['BE_USER']->user['uid'] > 0;
}
}
@@ -0,0 +1,103 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Be\Security;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractConditionViewHelper;
/**
* This ViewHelper implements an ifHasRole/else condition for backend users
* and backend groups.
*
* Role refers to backend user groups. The :html:`role` attribute can either be
* the title of a group, or the uid.
*
* Examples
* ========
*
* Basic usage::
*
* <f:be.security.ifHasRole role="Administrator">
* This is being shown in case the current BE user belongs to a BE usergroup (aka role) titled "Administrator" (case sensitive)
* </f:be.security.ifHasRole>
*
* Everything inside the :html:`<f:ifHasRole>` tag is being displayed if the
* logged in backend user belongs to the specified backend group.
*
* Using the usergroup uid as role identifier::
*
* <f:be.security.ifHasRole role="1">
* This is being shown in case the current BE user belongs to a BE usergroup (aka role) with the uid "1"
* </f:be.security.ifHasRole>
*
* Everything inside the :html:`<f:ifHasRole>` tag is being displayed if the
* logged in backend user belongs to the specified backend group.
*
* IfRole / then / else::
*
* <f:be.security.ifHasRole role="Administrator">
* <f:then>
* This is being shown in case you have the role.
* </f:then>
* <f:else>
* This is being displayed in case you do not have the role.
* </f:else>
* </f:be.security.ifHasRole>
*
* Everything inside the :html:`<f:then></f:then>` tag is displayed if the
* logged in backend user belongs to the specified backend group.
* Otherwise, everything inside the :html:`<f:else></f:else>` tag is displayed.
*/
class IfHasRoleViewHelper extends AbstractConditionViewHelper
{
/**
* Initializes the "role" argument.
* Renders <f:then> child if the current logged in BE user belongs to the specified role (aka usergroup)
* otherwise renders <f:else> child.
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('role', 'string', 'The usergroup (either the usergroup uid or its title).');
}
/**
* This method decides if the condition is TRUE or FALSE. It can be overridden 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
*/
protected static function evaluateCondition($arguments = null)
{
$role = $arguments['role'];
if (!is_array($GLOBALS['BE_USER']->userGroups)) {
return false;
}
if (is_numeric($role)) {
foreach ($GLOBALS['BE_USER']->userGroups as $userGroup) {
if ((int)$userGroup['uid'] === (int)$role) {
return true;
}
}
} else {
foreach ($GLOBALS['BE_USER']->userGroups as $userGroup) {
if ($userGroup['title'] === $role) {
return true;
}
}
}
return false;
}
}
@@ -0,0 +1,155 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Be;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Type\Bitmask\Permission;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* ViewHelper which renders a record list as known from the TYPO3 list module.
*
* .. note::
* This feature is experimental!
*
* Examples
* ========
*
* Minimal::
*
* <f:be.tableList tableName="fe_users" />
*
* List of all "Website user" records stored in the configured storage PID.
* Records will be editable, if the current backend user has got edit rights for the table ``fe_users``.
*
* Only the title column (username) will be shown.
*
* Context menu is active.
*
* Full::
*
* <f:be.tableList tableName="fe_users" fieldList="{0: 'name', 1: 'email'}"
* storagePid="1"
* levels="2"
* filter="foo"
* recordsPerPage="10"
* sortField="name"
* sortDescending="true"
* readOnly="true"
* enableClickMenu="false"
* clickTitleMode="info"
* />
*
* List of "Website user" records with a text property of ``foo`` stored on PID ``1`` and two levels down.
* Clicking on a username will open the TYPO3 info popup for the respective record
*/
class TableListViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\Be\AbstractBackendViewHelper
{
/**
* As this ViewHelper renders HTML, the output must not be escaped.
*
* @var bool
*/
protected $escapeOutput = false;
/**
* @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
*/
protected $configurationManager;
/**
* @param \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager
*/
public function injectConfigurationManager(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager)
{
$this->configurationManager = $configurationManager;
}
/**
* Initialize arguments.
*
* @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('tableName', 'string', 'name of the database table', true);
$this->registerArgument('fieldList', 'array', 'list of fields to be displayed. If empty, only the title column (configured in $TCA[$tableName][\'ctrl\'][\'title\']) is shown', false, []);
$this->registerArgument('storagePid', 'int', 'by default, records are fetched from the storage PID configured in persistence.storagePid. With this argument, the storage PID can be overwritten');
$this->registerArgument('levels', 'int', 'corresponds to the level selector of the TYPO3 list module. By default only records from the current storagePid are fetched', false, 0);
$this->registerArgument('filter', 'string', 'corresponds to the "Search String" textbox of the TYPO3 list module. If not empty, only records matching the string will be fetched', false, '');
$this->registerArgument('recordsPerPage', 'int', 'amount of records to be displayed at once. Defaults to $TCA[$tableName][\'interface\'][\'maxSingleDBListItems\'] or (if that\'s not set) to 100', false, 0);
$this->registerArgument('sortField', 'string', 'table field to sort the results by', false, '');
$this->registerArgument('sortDescending', 'bool', 'if TRUE records will be sorted in descending order', false, false);
$this->registerArgument('readOnly', 'bool', 'if TRUE, the edit icons won\'t be shown. Otherwise edit icons will be shown, if the current BE user has edit rights for the specified table!', false, false);
$this->registerArgument('enableClickMenu', 'bool', 'enables context menu', false, true);
$this->registerArgument('clickTitleMode', 'string', 'one of "edit", "show" (only pages, tt_content), "info');
}
/**
* Renders a record list as known from the TYPO3 list module
* Note: This feature is experimental!
*
* @return string the rendered record list
* @see \TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList
*/
public function render()
{
$tableName = $this->arguments['tableName'];
$fieldList = $this->arguments['fieldList'];
$storagePid = $this->arguments['storagePid'];
$levels = $this->arguments['levels'];
$filter = $this->arguments['filter'];
$recordsPerPage = $this->arguments['recordsPerPage'];
$sortField = $this->arguments['sortField'];
$sortDescending = $this->arguments['sortDescending'];
$readOnly = $this->arguments['readOnly'];
$enableClickMenu = $this->arguments['enableClickMenu'];
$clickTitleMode = $this->arguments['clickTitleMode'];
$this->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Recordlist/Recordlist');
$pageinfo = BackendUtility::readPageAccess(GeneralUtility::_GP('id'), $GLOBALS['BE_USER']->getPagePermsClause(Permission::PAGE_SHOW));
/** @var \TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList $dblist */
$dblist = GeneralUtility::makeInstance(\TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList::class);
$dblist->pageRow = $pageinfo;
if ($readOnly === false) {
$dblist->calcPerms = $GLOBALS['BE_USER']->calcPerms($pageinfo);
}
$dblist->showClipboard = false;
$dblist->disableSingleTableView = true;
$dblist->clickTitleMode = $clickTitleMode;
$dblist->clickMenuEnabled = $enableClickMenu;
if ($storagePid === null) {
$frameworkConfiguration = $this->configurationManager->getConfiguration(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
$storagePid = $frameworkConfiguration['persistence']['storagePid'];
}
$dblist->start($storagePid, $tableName, (int)GeneralUtility::_GP('pointer'), $filter, $levels, $recordsPerPage);
$dblist->allFields = true;
$dblist->dontShowClipControlPanels = true;
$dblist->displayFields = false;
$dblist->setFields = [$tableName => $fieldList];
$dblist->noControlPanels = true;
$dblist->sortField = $sortField;
$dblist->sortRev = $sortDescending;
$dblist->script = $_SERVER['REQUEST_URI'];
$dblist->generateList();
$js = 'var T3_THIS_LOCATION = ' . GeneralUtility::quoteJSvalue(rawurlencode(GeneralUtility::getIndpEnv('REQUEST_URI')));
$html = GeneralUtility::wrapJS($js) . $dblist->HTMLcode;
return $html;
}
}
@@ -0,0 +1,74 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Be;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Backend\Routing\UriBuilder;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* A ViewHelper for creating URIs to modules.
*
* Examples
* ========
*
* URI to the web_ts module on page 92::
*
* <f:be.uri route="web_ts" parameters="{id: 92}"/>
*
* ``/typo3/index.php?M=web_ts&moduleToken=b6e9c9f?id=92``
*
* Inline notation::
*
* {f:be.uri(route: 'web_ts', parameters: '{id: 92}')}
*
* ``/typo3/index.php?route=%2module%2web_ts%2&moduleToken=b6e9c9f?id=92``
*/
class UriViewHelper extends AbstractBackendViewHelper
{
/**
* Arguments initialization
*
* @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('route', 'string', 'The name of the route', true);
$this->registerArgument('parameters', 'array', 'An array of parameters', false, []);
$this->registerArgument(
'referenceType',
'string',
'The type of reference to be generated (one of the constants)',
false,
UriBuilder::ABSOLUTE_PATH
);
}
/**
* @return string Rendered link
*/
public function render()
{
/** @var UriBuilder $uriBuilder */
$uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
$route = $this->arguments['route'];
$parameters = $this->arguments['parameters'];
$referenceType = $this->arguments['referenceType'];
$uri = $uriBuilder->buildUriFromRoute($route, $parameters, $referenceType);
return (string)$uri;
}
}
@@ -0,0 +1,127 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Be\Widget\Controller;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Class PaginateController
*/
class PaginateController extends \TYPO3\CMS\Fluid\Core\Widget\AbstractWidgetController
{
/**
* @var array
*/
protected $configuration = ['itemsPerPage' => 10, 'insertAbove' => false, 'insertBelow' => true, 'recordsLabel' => ''];
/**
* @var \TYPO3\CMS\Extbase\Persistence\QueryResultInterface
*/
protected $objects;
/**
* @var int
*/
protected $currentPage = 1;
/**
* @var int
*/
protected $numberOfPages = 1;
/**
* @var int
*/
protected $offset = 0;
/**
* @var int
*/
protected $itemsPerPage = 0;
/**
* @var int
*/
protected $numberOfObjects = 0;
/**
* Initializes necessary variables for all actions.
*/
public function initializeAction()
{
$this->objects = $this->widgetConfiguration['objects'];
\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($this->configuration, $this->widgetConfiguration['configuration'], false);
$this->numberOfObjects = count($this->objects);
$itemsPerPage = (int)$this->configuration['itemsPerPage'];
$this->numberOfPages = $itemsPerPage > 0 ? ceil($this->numberOfObjects / $itemsPerPage) : 0;
}
/**
* @param int $currentPage
*/
public function indexAction($currentPage = 1)
{
// set current page
$this->currentPage = (int)$currentPage;
if ($this->currentPage < 1) {
$this->currentPage = 1;
}
if ($this->currentPage > $this->numberOfPages) {
// set $modifiedObjects to NULL if the page does not exist
$modifiedObjects = null;
} else {
// modify query
$this->itemsPerPage = (int)$this->configuration['itemsPerPage'];
$query = $this->objects->getQuery();
$query->setLimit($this->itemsPerPage);
$this->offset = $this->itemsPerPage * ($this->currentPage - 1);
if ($this->currentPage > 1) {
$query->setOffset($this->offset);
}
$modifiedObjects = $query->execute();
}
$this->view->assign('contentArguments', [
$this->widgetConfiguration['as'] => $modifiedObjects
]);
$this->view->assign('configuration', $this->configuration);
$this->view->assign('pagination', $this->buildPagination());
}
/**
* Returns an array with the keys "current", "numberOfPages", "nextPage", "previousPage", "startRecord", "endRecord"
*
* @return array
*/
protected function buildPagination()
{
$endRecord = $this->offset + $this->itemsPerPage;
if ($endRecord > $this->numberOfObjects) {
$endRecord = $this->numberOfObjects;
}
$pagination = [
'current' => $this->currentPage,
'numberOfPages' => $this->numberOfPages,
'hasLessPages' => $this->currentPage > 1,
'hasMorePages' => $this->currentPage < $this->numberOfPages,
'startRecord' => $this->offset + 1,
'endRecord' => $endRecord
];
if ($this->currentPage < $this->numberOfPages) {
$pagination['nextPage'] = $this->currentPage + 1;
}
if ($this->currentPage > 1) {
$pagination['previousPage'] = $this->currentPage - 1;
}
return $pagination;
}
}
@@ -0,0 +1,83 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Be\Widget;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
/**
* This ViewHelper renders a Pagination of objects for the TYPO3 Backend.
*
* Examples
* ========
*
* Minimum call with required arguments only::
*
* <f:be.widget.paginate objects="{blogs}" as="paginatedBlogs">
* use {paginatedBlogs} as you used {blogs} before, most certainly inside
* a <f:for> loop.
* </f:be.widget.paginate>
*
* Full example with all configuration options::
*
* <f:be.widget.paginate objects="{blogs}" as="paginatedBlogs" configuration="{itemsPerPage: 5, insertAbove: 1, insertBelow: 0, recordsLabel: 'MyRecords'}">
* use {paginatedBlogs} as you used {blogs} before, most certainly inside
* a <f:for> loop.
* </f:be.widget.paginate>
*
* The ``recordsLabel`` option can be used to replace the text in "Records 1 - 99" with a custom label.
*
* Performance characteristics
* ===========================
*
* In the above examples, it looks like ``{blogs}`` contains all Blog objects, thus
* you might wonder if all objects were fetched from the database.
* However, the blogs are NOT fetched from the database until you actually use them,
* so the paginate ViewHelper will adjust the query sent to the database and receive
* only the small subset of objects.
* So, there is no negative performance overhead in using the Be Paginate Widget.
*/
class PaginateViewHelper extends \TYPO3\CMS\Fluid\Core\Widget\AbstractWidgetViewHelper
{
/**
* @var \TYPO3\CMS\Fluid\ViewHelpers\Be\Widget\Controller\PaginateController
*/
protected $controller;
/**
* @param \TYPO3\CMS\Fluid\ViewHelpers\Be\Widget\Controller\PaginateController $controller
*/
public function injectPaginateController(\TYPO3\CMS\Fluid\ViewHelpers\Be\Widget\Controller\PaginateController $controller)
{
$this->controller = $controller;
}
/**
* Initialize arguments.
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('objects', QueryResultInterface::class, 'The QueryResult containing all objects.', true);
$this->registerArgument('as', 'string', 'as', true);
$this->registerArgument('configuration', 'array', 'configuration', false, ['itemsPerPage' => 10, 'insertAbove' => false, 'insertBelow' => true, 'maximumNumberOfLinks' => 99]);
}
/**
* @return string
*/
public function render()
{
return $this->initiateSubRequest();
}
}
@@ -0,0 +1,237 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\TimeTracker\TimeTracker;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithContentArgumentAndRenderStatic;
/**
* This ViewHelper renders CObjects from the global TypoScript configuration.
*
* .. note::
* You have to ensure proper escaping (htmlspecialchars/intval/etc.) on your own!
*
* Examples
* ========
*
* Render lib object
* -----------------
*
* ::
*
* <f:cObject typoscriptObjectPath="lib.someLibObject" />
*
* Rendered :ts:`lib.someLibObject`.
*
* Specify cObject data & current value
* ------------------------------------
*
* ::
*
* <f:cObject typoscriptObjectPath="lib.customHeader" data="{article}" currentValueKey="title" />
*
* Rendered :ts:`lib.customHeader`. Data and current value will be available in TypoScript.
*
* Inline notation
* ---------------
*
* ::
*
* {article -> f:cObject(typoscriptObjectPath: 'lib.customHeader')}
*
* Rendered :ts:`lib.customHeader`. Data will be available in TypoScript.
*
* Accessing the data in TypoScript
* --------------------------------
*
* ::
*
* lib.customHeader = COA
* lib.customHeader {
* 10 = TEXT
* 10.field = author
* 20 = TEXT
* 20.current = 1
* }
*
* When passing an object with ``{data}`` the properties of the object are accessable with :ts:`.field` in
* TypoScript. If only a single value is passed or the ``currentValueKey`` is specified :ts:`.current = 1`
* can be used in the TypoScript.
*/
class CObjectViewHelper extends AbstractViewHelper
{
use CompileWithContentArgumentAndRenderStatic;
/**
* Disable escaping of child nodes' output
*
* @var bool
*/
protected $escapeChildren = false;
/**
* Disable escaping of this node's output
*
* @var bool
*/
protected $escapeOutput = false;
/**
* @var \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController contains a backup of the current $GLOBALS['TSFE'] if used in BE mode
*/
protected static $tsfeBackup;
/**
* Initialize arguments.
*
* @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
*/
public function initializeArguments()
{
$this->registerArgument('data', 'mixed', 'the data to be used for rendering the cObject. Can be an object, array or string. If this argument is not set, child nodes will be used');
$this->registerArgument('typoscriptObjectPath', 'string', 'the TypoScript setup path of the TypoScript object to render', true);
$this->registerArgument('currentValueKey', 'string', 'currentValueKey');
$this->registerArgument('table', 'string', 'the table name associated with "data" argument. Typically tt_content or one of your custom tables. This argument should be set if rendering a FILES cObject where file references are used, or if the data argument is a database record.', false, '');
}
/**
* Renders the TypoScript object in the given TypoScript setup path.
*
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return mixed
* @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$data = $renderChildrenClosure();
$typoscriptObjectPath = $arguments['typoscriptObjectPath'];
$currentValueKey = $arguments['currentValueKey'];
$table = $arguments['table'];
$contentObjectRenderer = static::getContentObjectRenderer();
if (!isset($GLOBALS['TSFE']) || !($GLOBALS['TSFE'] instanceof TypoScriptFrontendController)) {
static::simulateFrontendEnvironment();
}
$currentValue = null;
if (is_object($data)) {
$data = \TYPO3\CMS\Extbase\Reflection\ObjectAccess::getGettableProperties($data);
} elseif (is_string($data) || is_numeric($data)) {
$currentValue = (string)$data;
$data = [$data];
}
$contentObjectRenderer->start($data, $table);
if ($currentValue !== null) {
$contentObjectRenderer->setCurrentVal($currentValue);
} elseif ($currentValueKey !== null && isset($data[$currentValueKey])) {
$contentObjectRenderer->setCurrentVal($data[$currentValueKey]);
}
$pathSegments = GeneralUtility::trimExplode('.', $typoscriptObjectPath);
$lastSegment = array_pop($pathSegments);
$setup = static::getConfigurationManager()->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FULL_TYPOSCRIPT);
foreach ($pathSegments as $segment) {
if (!array_key_exists($segment . '.', $setup)) {
throw new \TYPO3Fluid\Fluid\Core\ViewHelper\Exception(
'TypoScript object path "' . $typoscriptObjectPath . '" does not exist',
1253191023
);
}
$setup = $setup[$segment . '.'];
}
if (!isset($setup[$lastSegment])) {
throw new \TYPO3Fluid\Fluid\Core\ViewHelper\Exception(
'No Content Object definition found at TypoScript object path "' . $typoscriptObjectPath . '"',
1540246570
);
}
$content = self::renderContentObject($contentObjectRenderer, $setup, $typoscriptObjectPath, $lastSegment);
if (!isset($GLOBALS['TSFE']) || !($GLOBALS['TSFE'] instanceof TypoScriptFrontendController)) {
static::resetFrontendEnvironment();
}
return $content;
}
/**
* Renders single content object and increases time tracker stack pointer
*
* @param ContentObjectRenderer $contentObjectRenderer
* @param array $setup
* @param string $typoscriptObjectPath
* @param string $lastSegment
* @return string
*/
protected static function renderContentObject(ContentObjectRenderer $contentObjectRenderer, array $setup, string $typoscriptObjectPath, string $lastSegment): string
{
$timeTracker = GeneralUtility::makeInstance(TimeTracker::class);
if ($timeTracker->LR) {
$timeTracker->push('/f:cObject/', '<' . $typoscriptObjectPath);
}
$timeTracker->incStackPointer();
$content = $contentObjectRenderer->cObjGetSingle($setup[$lastSegment], $setup[$lastSegment . '.'] ?? [], $typoscriptObjectPath);
$timeTracker->decStackPointer();
if ($timeTracker->LR) {
$timeTracker->pull($content);
}
return $content;
}
/**
* @return ConfigurationManagerInterface
*/
protected static function getConfigurationManager()
{
return GeneralUtility::makeInstance(ObjectManager::class)->get(ConfigurationManagerInterface::class);
}
/**
* @return ContentObjectRenderer
*/
protected static function getContentObjectRenderer()
{
return GeneralUtility::makeInstance(
ContentObjectRenderer::class,
$GLOBALS['TSFE'] ?? GeneralUtility::makeInstance(TypoScriptFrontendController::class, null, 0, 0)
);
}
/**
* Sets the $TSFE->cObjectDepthCounter in Backend mode
* This somewhat hacky work around is currently needed because the cObjGetSingle() function of \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer relies on this setting
*/
protected static function simulateFrontendEnvironment()
{
static::$tsfeBackup = $GLOBALS['TSFE'] ?? null;
$GLOBALS['TSFE'] = new \stdClass();
$GLOBALS['TSFE']->cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
$GLOBALS['TSFE']->cObjectDepthCounter = 100;
}
/**
* Resets $GLOBALS['TSFE'] if it was previously changed by simulateFrontendEnvironment()
*
* @see simulateFrontendEnvironment()
*/
protected static function resetFrontendEnvironment()
{
$GLOBALS['TSFE'] = static::$tsfeBackup;
}
}
@@ -0,0 +1,136 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Debug;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Utility\PathUtility;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
/**
* Debuggable version of :ref:`f:render <typo3-fluid-render>` - performs the
* same rendering operation but wraps the output with HTML that can be
* inspected with the admin panel in frontend.
*
* Replaces ``f:render`` when the admin panel decides (see
* :php:`ViewHelperResolver` class). Also possible to use explicitly by using
* ``f:debug.render`` instead of the normal ``f:render`` statement.
*/
class RenderViewHelper extends AbstractViewHelper
{
/**
* @var bool
*/
protected $escapeOutput = false;
/**
* Initializes additional arguments available for this ViewHelper.
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('debug', 'boolean', 'If true, the admin panel shows debug information if activated,', false, true);
$this->registerArgument('section', 'string', 'Section to render - combine with partial to render section in partial', false, null);
$this->registerArgument('partial', 'string', 'Partial to render, with or without section', false, null);
$this->registerArgument('arguments', 'array', 'Array of variables to be transferred. Use {_all} for all variables', false, []);
$this->registerArgument('optional', 'boolean', 'If TRUE, considers the *section* optional. Partial never is.', false, false);
$this->registerArgument('default', 'mixed', 'Value (usually string) to be displayed if the section or partial does not exist', false, null);
$this->registerArgument('contentAs', 'string', 'If used, renders the child content and adds it as a template variable with this name for use in the partial/section', false, null);
}
/**
* Renders the content.
*
* @return string
*/
public function render()
{
$isDebug = $this->arguments['debug'];
$section = $this->arguments['section'];
$partial = $this->arguments['partial'];
$arguments = (array)$this->arguments['arguments'];
$optional = (boolean)$this->arguments['optional'];
$contentAs = $this->arguments['contentAs'];
$tagContent = $this->renderChildren();
if ($contentAs !== null) {
$arguments[$contentAs] = $tagContent;
}
$content = '';
$viewHelperVariableContainer = $this->renderingContext->getViewHelperVariableContainer();
if ($partial !== null) {
$content = $viewHelperVariableContainer->getView()->renderPartial($partial, $section, $arguments, $optional);
} elseif ($section !== null) {
$content = $viewHelperVariableContainer->getView()->renderSection($section, $arguments, $optional);
}
// Replace empty content with default value. If default is
// not set, NULL is returned and cast to a new, empty string
// outside of this ViewHelper.
if ($content === '') {
$content = $this->arguments['default'] ?? $tagContent;
}
// if debug is disabled, return content
if (!$isDebug) {
return $content;
}
$cssRules = [];
$cssRules[] = 'display: block';
$cssRules[] = 'background-color: #fff';
$cssRules[] = 'padding: 5px';
$cssRules[] = 'border: 1px solid #f00';
$cssRules[] = 'color: #000';
$cssRules[] = 'overflow: hidden';
$cssWrapper = implode(';', $cssRules);
$cssRules[] = 'font-size: 11px';
$cssRules[] = 'font-family: Monospace';
$cssTitle = implode(';', $cssRules);
$debugInfo = [];
if (isset($this->arguments['partial'])) {
$path = $this->renderingContext->getTemplatePaths()->getPartialPathAndFilename($partial);
$path = str_replace(
[
Environment::getBackendPath() . '/ext/',
Environment::getExtensionsPath() . '/',
Environment::getFrameworkBasePath() . '/'
],
'EXT:',
$path
);
$path = PathUtility::stripPathSitePrefix($path);
$debugInfo['Partial'] = 'Partial: ' . $path;
}
if (isset($this->arguments['section'])) {
$debugInfo['Section'] = 'Section: ' . htmlspecialchars($section);
}
$debugContent = sprintf(
'<strong>%s</strong>',
implode('<br />', $debugInfo)
);
return sprintf(
'<div class="t3js-debug-template" title="%s" style="%s"><span style="%s">%s</span>%s</div>',
htmlspecialchars(implode('/', array_keys($debugInfo))),
$cssTitle,
$cssWrapper,
$debugContent,
$content
);
}
}
@@ -0,0 +1,100 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Extbase\Utility\DebuggerUtility;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* This ViewHelper generates a HTML dump of the tagged variable.
*
* Examples
* ========
*
* Simple
* ------
*
* ::
*
* <f:debug>{testVariables.array}</f:debug>
*
* foobarbazfoo
*
* All Features
* ------------
*
* ::
*
* <f:debug title="My Title" maxDepth="5"
* blacklistedClassNames="{0:'Tx_BlogExample_Domain_Model_Administrator'}"
* blacklistedPropertyNames="{0:'posts'}"
* plainText="true" ansiColors="false"
* inline="true"
* >
* {blogs}
* </f:debug>
*
* [A HTML view of the var_dump]
*/
class DebugViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* This prevents double escaping as the output is encoded in DebuggerUtility::var_dump
*
* @var bool
*/
protected $escapeChildren = false;
/**
* Output of this viewhelper is already escaped
*
* @var bool
*/
protected $escapeOutput = false;
/**
* Initialize arguments.
*
* @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
*/
public function initializeArguments()
{
$this->registerArgument('title', 'string', 'optional custom title for the debug output');
$this->registerArgument('maxDepth', 'int', 'Sets the max recursion depth of the dump (defaults to 8). De- or increase the number according to your needs and memory limit.', false, 8);
$this->registerArgument('plainText', 'bool', 'If TRUE, the dump is in plain text, if FALSE the debug output is in HTML format.', false, false);
$this->registerArgument('ansiColors', 'bool', 'If TRUE, ANSI color codes is added to the plaintext output, if FALSE (default) the plaintext debug output not colored.', false, false);
$this->registerArgument('inline', 'bool', 'if TRUE, the dump is rendered at the position of the <f:debug> tag. If FALSE (default), the dump is displayed at the top of the page.', false, false);
$this->registerArgument('blacklistedClassNames', 'array', 'An array of class names (RegEx) to be filtered. Default is an array of some common class names.');
$this->registerArgument('blacklistedPropertyNames', 'array', 'An array of property names and/or array keys (RegEx) to be filtered. Default is an array of some common property names.');
}
/**
* A wrapper for \TYPO3\CMS\Extbase\Utility\DebuggerUtility::var_dump().
*
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
*
* @return string
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
return DebuggerUtility::var_dump($renderChildrenClosure(), $arguments['title'], $arguments['maxDepth'], (bool)$arguments['plainText'], (bool)$arguments['ansiColors'], (bool)$arguments['inline'], $arguments['blacklistedClassNames'], $arguments['blacklistedPropertyNames']);
}
}
@@ -0,0 +1,147 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\Messaging\FlashMessageRendererResolver;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* ViewHelper which renders the flash messages (if there are any) as an unsorted list.
*
* In case you need custom Flash Message HTML output, please write your own ViewHelper for the moment.
*
* Examples
* ========
*
* Simple
* ------
*
* ::
*
* <f:flashMessages />
*
* A list of flash messages.
*
* TYPO3 core style
* ----------------
*
* ::
*
* <f:flashMessages />
*
* Output::
*
* <div class="typo3-messages">
* <div class="alert alert-info">
* <div class="media">
* <div class="media-left">
* <span class="fa-stack fa-lg">
* <i class="fa fa-circle fa-stack-2x"></i>
* <i class="fa fa-info fa-stack-1x"></i>
* </span>
* </div>
* <div class="media-body">
* <h4 class="alert-title">Info - Title for Info message</h4>
* <p class="alert-message">Message text here.</p>
* </div>
* </div>
* </div>
* </div>
*
* Output flash messages as a description list
* -------------------------------------------
*
* ::
*
* <f:flashMessages as="flashMessages">
* <dl class="messages">
* <f:for each="{flashMessages}" as="flashMessage">
* <dt>{flashMessage.code}</dt>
* <dd>{flashMessage.message}</dd>
* </f:for>
* </dl>
* </f:flashMessages>
*
* Output::
*
* <dl class="messages">
* <dt>1013</dt>
* <dd>Some Warning Message.</dd>
* </dl>
*
* Using a specific queue
* ----------------------
*
* ::
*
* <f:flashMessages queueIdentifier="myQueue" />
*/
class FlashMessagesViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* ViewHelper outputs HTML therefore output escaping has to be disabled
*
* @var bool
*/
protected $escapeOutput = false;
/**
* Initialize arguments
*/
public function initializeArguments()
{
$this->registerArgument('queueIdentifier', 'string', 'Flash-message queue to use');
$this->registerArgument('as', 'string', 'The name of the current flashMessage variable for rendering inside');
}
/**
* Renders FlashMessages and flushes the FlashMessage queue
* Note: This disables the current page cache in order to prevent FlashMessage output
* from being cached.
*
* @see \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController::no_cache
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return mixed
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$as = $arguments['as'];
$queueIdentifier = $arguments['queueIdentifier'] ?? null;
$flashMessages = $renderingContext->getControllerContext()
->getFlashMessageQueue($queueIdentifier)->getAllMessagesAndFlush();
if ($flashMessages === null || count($flashMessages) === 0) {
return '';
}
if ($as === null) {
return GeneralUtility::makeInstance(FlashMessageRendererResolver::class)
->resolve()
->render($flashMessages);
}
$templateVariableContainer = $renderingContext->getVariableProvider();
$templateVariableContainer->add($as, $flashMessages);
$content = $renderChildrenClosure();
$templateVariableContainer->remove($as);
return $content;
}
}
@@ -0,0 +1,398 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Form;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
/**
* Abstract Form ViewHelper. Bundles functionality related to direct property access of objects in other Form ViewHelpers.
*
* If you set the "property" attribute to the name of the property to resolve from the object, this class will
* automatically set the name and value of a form element.
*/
abstract class AbstractFormFieldViewHelper extends AbstractFormViewHelper
{
/**
* @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
*/
protected $configurationManager;
/**
* @var bool
*/
protected $respectSubmittedDataValue = false;
/**
* @param \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager
*/
public function injectConfigurationManager(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager)
{
$this->configurationManager = $configurationManager;
}
/**
* Initialize arguments.
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('name', 'string', 'Name of input tag');
$this->registerArgument('value', 'mixed', 'Value of input tag');
$this->registerArgument(
'property',
'string',
'Name of Object Property. If used in conjunction with <f:form object="...">, "name" and "value" properties will be ignored.'
);
}
/**
* Getting the current configuration for respectSubmittedDataValue.
*
* @return bool
*/
public function getRespectSubmittedDataValue()
{
return $this->respectSubmittedDataValue;
}
/**
* Define respectSubmittedDataValue to enable or disable the usage of the submitted values in the viewhelper.
*
* @param bool $respectSubmittedDataValue
*/
public function setRespectSubmittedDataValue($respectSubmittedDataValue)
{
$this->respectSubmittedDataValue = $respectSubmittedDataValue;
}
/**
* Get the name of this form element.
* Either returns arguments['name'], or the correct name for Object Access.
*
* In case property is something like bla.blubb (hierarchical), then [bla][blubb] is generated.
*
* @return string Name
*/
protected function getName()
{
$name = $this->getNameWithoutPrefix();
return $this->prefixFieldName($name);
}
/**
* Shortcut for retrieving the request from the controller context
*
* @return \TYPO3\CMS\Extbase\Mvc\Request
*/
protected function getRequest()
{
return $this->renderingContext->getControllerContext()->getRequest();
}
/**
* Get the name of this form element, without prefix.
*
* @return string name
*/
protected function getNameWithoutPrefix()
{
if ($this->isObjectAccessorMode()) {
$formObjectName = $this->renderingContext->getViewHelperVariableContainer()->get(
\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class,
'formObjectName'
);
if (!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 ($this->hasArgument('value') && is_object($this->arguments['value'])) {
// @todo Use $this->persistenceManager->isNewObject() once it is implemented
if (null !== $this->persistenceManager->getIdentifierByObject($this->arguments['value'])) {
$name .= '[__identity]';
}
}
return $name;
}
/**
* Returns the current value of this Form ViewHelper and converts it to an identifier string in case it's an object
* The value is determined as follows:
* * If property mapping errors occurred and the form is re-displayed, the *last submitted* value is returned
* * Else the bound property is returned (only in objectAccessor-mode)
* * As fallback the "value" argument of this ViewHelper is used
*
* Note: This method should *not* be used for form elements that must not change the value attribute, e.g. (radio) buttons and checkboxes.
*
* @return mixed Value
*/
protected function getValueAttribute()
{
$value = null;
if ($this->respectSubmittedDataValue) {
$value = $this->getValueFromSubmittedFormData($value);
} elseif ($this->hasArgument('value')) {
$value = $this->arguments['value'];
} elseif ($this->isObjectAccessorMode()) {
$value = $this->getPropertyValue();
}
$value = $this->convertToPlainValue($value);
return $value;
}
/**
* If property mapping errors occurred and the form is re-displayed, the *last submitted* value is returned by this
* method.
*
* Note:
* This method should *not* be used for form elements that must not change the value attribute, e.g. (radio)
* buttons and checkboxes. The default behaviour is not to use this method. You need to set
* respectSubmittedDataValue to TRUE to enable the form data handling for the viewhelper.
*
* @param mixed $value
* @return mixed Value
*/
protected function getValueFromSubmittedFormData($value)
{
$submittedFormData = null;
if ($this->hasMappingErrorOccurred()) {
$submittedFormData = $this->getLastSubmittedFormData();
}
if ($submittedFormData !== null) {
$value = $submittedFormData;
} elseif ($this->hasArgument('value')) {
$value = $this->arguments['value'];
} elseif ($this->isObjectAccessorMode()) {
$value = $this->getPropertyValue();
}
return $value;
}
/**
* Converts an arbitrary value to a plain value
*
* @param mixed $value The value to convert
* @return mixed
*/
protected function convertToPlainValue($value)
{
if (is_object($value)) {
$identifier = $this->persistenceManager->getIdentifierByObject($value);
if ($identifier !== null) {
$value = $identifier;
}
}
return $value;
}
/**
* Checks if a property mapping error has occurred in the last request.
*
* @return bool TRUE if a mapping error occurred, FALSE otherwise
*/
protected function hasMappingErrorOccurred()
{
return $this->renderingContext->getControllerContext()->getRequest()->getOriginalRequest() !== null;
}
/**
* Get the form data which has last been submitted; only returns valid data in case
* a property mapping error has occurred. Check with hasMappingErrorOccurred() before!
*
* @return mixed
*/
protected function getLastSubmittedFormData()
{
$propertyPath = rtrim(preg_replace('/(\\]\\[|\\[|\\])/', '.', $this->getNameWithoutPrefix()), '.');
$value = ObjectAccess::getPropertyPath(
$this->renderingContext->getControllerContext()->getRequest()->getOriginalRequest()->getArguments(),
$propertyPath
);
return $value;
}
/**
* Add additional identity properties in case the current property is hierarchical (of the form "bla.blubb").
* Then, [bla][__identity] has to be generated as well.
*/
protected function addAdditionalIdentityPropertiesIfNeeded()
{
if (!$this->isObjectAccessorMode()) {
return;
}
$viewHelperVariableContainer = $this->renderingContext->getViewHelperVariableContainer();
if (!$viewHelperVariableContainer->exists(
\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class,
'formObject'
)
) {
return;
}
$propertySegments = explode('.', $this->arguments['property']);
// hierarchical property. If there is no "." inside (thus $propertySegments == 1), we do not need to do anything
if (count($propertySegments) < 2) {
return;
}
$formObject = $viewHelperVariableContainer->get(
\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class,
'formObject'
);
$objectName = $viewHelperVariableContainer->get(
\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class,
'formObjectName'
);
// If count == 2 -> we need to go through the for-loop exactly once
$propertySegmentsCount = count($propertySegments);
for ($i = 1; $i < $propertySegmentsCount; $i++) {
$object = ObjectAccess::getPropertyPath($formObject, implode('.', array_slice($propertySegments, 0, $i)));
$objectName .= '[' . $propertySegments[$i - 1] . ']';
$hiddenIdentityField = $this->renderHiddenIdentityField($object, $objectName);
// Add the hidden identity field to the ViewHelperVariableContainer
$additionalIdentityProperties = $viewHelperVariableContainer->get(
\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class,
'additionalIdentityProperties'
);
$additionalIdentityProperties[$objectName] = $hiddenIdentityField;
$viewHelperVariableContainer->addOrUpdate(
\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class,
'additionalIdentityProperties',
$additionalIdentityProperties
);
}
}
/**
* Get the current property of the object bound to this form.
*
* @return mixed Value
*/
protected function getPropertyValue()
{
$viewHelperVariableContainer = $this->renderingContext->getViewHelperVariableContainer();
if (!$viewHelperVariableContainer->exists(
\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class,
'formObject'
)
) {
return null;
}
$formObject = $viewHelperVariableContainer->get(
\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class,
'formObject'
);
return ObjectAccess::getPropertyPath($formObject, $this->arguments['property']);
}
/**
* Internal method which checks if we should evaluate a domain object or just output arguments['name'] and arguments['value']
*
* @return bool TRUE if we should evaluate the domain object, FALSE otherwise.
*/
protected function isObjectAccessorMode()
{
return $this->hasArgument('property') && $this->renderingContext->getViewHelperVariableContainer()->exists(
\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class,
'formObjectName'
);
}
/**
* Add an CSS class if this ViewHelper has errors
*/
protected function setErrorClassAttribute()
{
if ($this->hasArgument('class')) {
$cssClass = $this->arguments['class'] . ' ';
} else {
$cssClass = '';
}
$mappingResultsForProperty = $this->getMappingResultsForProperty();
if ($mappingResultsForProperty->hasErrors()) {
if ($this->hasArgument('errorClass')) {
$cssClass .= $this->arguments['errorClass'];
} else {
$cssClass .= 'error';
}
$this->tag->addAttribute('class', $cssClass);
}
}
/**
* Get errors for the property and form name of this ViewHelper
*
* @return \TYPO3\CMS\Extbase\Error\Result Array of errors
*/
protected function getMappingResultsForProperty()
{
if (!$this->isObjectAccessorMode()) {
return new \TYPO3\CMS\Extbase\Error\Result();
}
$originalRequestMappingResults = $this->getRequest()->getOriginalRequestMappingResults();
$formObjectName = $this->renderingContext->getViewHelperVariableContainer()->get(
\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class,
'formObjectName'
);
return $originalRequestMappingResults->forProperty($formObjectName)->forProperty($this->arguments['property']);
}
/**
* Renders a hidden field with the same name as the element, to make sure the empty value is submitted
* in case nothing is selected. This is needed for checkbox and multiple select fields
*
* @return string the hidden field.
*/
protected function renderHiddenFieldForEmptyValue()
{
$hiddenFieldNames = [];
$viewHelperVariableContainer = $this->renderingContext->getViewHelperVariableContainer();
if ($viewHelperVariableContainer->exists(
\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class,
'renderedHiddenFields'
)
) {
$hiddenFieldNames = $viewHelperVariableContainer->get(
\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class,
'renderedHiddenFields'
);
}
$fieldName = $this->getName();
if (substr($fieldName, -2) === '[]') {
$fieldName = substr($fieldName, 0, -2);
}
if (!in_array($fieldName, $hiddenFieldNames, true)) {
$hiddenFieldNames[] = $fieldName;
$viewHelperVariableContainer->addOrUpdate(
\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class,
'renderedHiddenFields',
$hiddenFieldNames
);
return '<input type="hidden" name="' . htmlspecialchars($fieldName) . '" value="" />';
}
return '';
}
}
@@ -0,0 +1,113 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Form;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
/**
* Abstract Form ViewHelper. Bundles functionality related to direct property access of objects in other Form ViewHelpers.
*
* If you set the "property" attribute to the name of the property to resolve from the object, this class will
* automatically set the name and value of a form element.
*/
abstract class AbstractFormViewHelper extends AbstractTagBasedViewHelper
{
/**
* @var \TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface
*/
protected $persistenceManager;
/**
* @param \TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface $persistenceManager
*/
public function injectPersistenceManager(\TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface $persistenceManager)
{
$this->persistenceManager = $persistenceManager;
}
/**
* Prefixes / namespaces the given name with the form field prefix
*
* @param string $fieldName field name to be prefixed
* @return string namespaced field name
*/
protected function prefixFieldName($fieldName)
{
if ($fieldName === null || $fieldName === '') {
return '';
}
$viewHelperVariableContainer = $this->renderingContext->getViewHelperVariableContainer();
if (!$viewHelperVariableContainer->exists(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'fieldNamePrefix')) {
return $fieldName;
}
$fieldNamePrefix = (string)$viewHelperVariableContainer->get(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'fieldNamePrefix');
if ($fieldNamePrefix === '') {
return $fieldName;
}
$fieldNameSegments = explode('[', $fieldName, 2);
$fieldName = $fieldNamePrefix . '[' . $fieldNameSegments[0] . ']';
if (count($fieldNameSegments) > 1) {
$fieldName .= '[' . $fieldNameSegments[1];
}
return $fieldName;
}
/**
* Renders a hidden form field containing the technical identity of the given object.
*
* @param object $object Object to create the identity field for
* @param string $name Name
* @return string A hidden field containing the Identity (UID in TYPO3 Flow, uid in Extbase) of the given object or NULL if the object is unknown to the persistence framework
* @see \TYPO3\CMS\Extbase\Mvc\Controller\Argument::setValue()
*/
protected function renderHiddenIdentityField($object, $name)
{
if ($object instanceof \TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy) {
$object = $object->_loadRealInstance();
}
if (!is_object($object)
|| !($object instanceof \TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject)
|| ($object->_isNew() && !$object->_isClone())) {
return '';
}
// Intentionally NOT using PersistenceManager::getIdentifierByObject here!!
// Using that one breaks re-submission of data in forms in case of an error.
$identifier = $object->getUid();
if ($identifier === null) {
return LF . '<!-- Object of type ' . get_class($object) . ' is without identity -->' . LF;
}
$name = $this->prefixFieldName($name) . '[__identity]';
$this->registerFieldNameForFormTokenGeneration($name);
return LF . '<input type="hidden" name="' . htmlspecialchars($name) . '" value="' . htmlspecialchars($identifier) . '" />' . LF;
}
/**
* Register a field name for inclusion in the HMAC / Form Token generation
*
* @param string $fieldName name of the field to register
*/
protected function registerFieldNameForFormTokenGeneration($fieldName)
{
$viewHelperVariableContainer = $this->renderingContext->getViewHelperVariableContainer();
if ($viewHelperVariableContainer->exists(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'formFieldNames')) {
$formFieldNames = $viewHelperVariableContainer->get(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'formFieldNames');
} else {
$formFieldNames = [];
}
$formFieldNames[] = $fieldName;
$viewHelperVariableContainer->addOrUpdate(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'formFieldNames', $formFieldNames);
}
}
@@ -0,0 +1,117 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Form;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Creates a button.
*
* Examples
* ========
*
* Defaults::
*
* <f:form.button>Send Mail</f:form.button>
*
* Output::
*
* <button type="submit" name="" value="">Send Mail</button>
*
* Disabled cancel button with some HTML5 attributes::
*
* <f:form.button type="reset" disabled="disabled"
* name="buttonName" value="buttonValue"
* formmethod="post" formnovalidate="formnovalidate"
* >
* Cancel
* </f:form.button>
*
* Output::
*
* <button disabled="disabled" formmethod="post" formnovalidate="formnovalidate" type="reset" name="myForm[buttonName]" value="buttonValue">Cancel</button>
*/
class ButtonViewHelper extends AbstractFormFieldViewHelper
{
/**
* @var string
*/
protected $tagName = 'button';
/**
* Initialize the arguments.
*
* @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerTagAttribute(
'autofocus',
'string',
'Specifies that a button should automatically get focus when the page loads'
);
$this->registerTagAttribute(
'disabled',
'string',
'Specifies that the input element should be disabled when the page loads'
);
$this->registerTagAttribute('form', 'string', 'Specifies one or more forms the button belongs to');
$this->registerTagAttribute(
'formaction',
'string',
'Specifies where to send the form-data when a form is submitted. Only for type="submit"'
);
$this->registerTagAttribute(
'formenctype',
'string',
'Specifies how form-data should be encoded before sending it to a server. Only for type="submit" (e.g. "application/x-www-form-urlencoded", "multipart/form-data" or "text/plain")'
);
$this->registerTagAttribute(
'formmethod',
'string',
'Specifies how to send the form-data (which HTTP method to use). Only for type="submit" (e.g. "get" or "post")'
);
$this->registerTagAttribute(
'formnovalidate',
'string',
'Specifies that the form-data should not be validated on submission. Only for type="submit"'
);
$this->registerTagAttribute(
'formtarget',
'string',
'Specifies where to display the response after submitting the form. Only for type="submit" (e.g. "_blank", "_self", "_parent", "_top", "framename")'
);
$this->registerUniversalTagAttributes();
$this->registerArgument('type', 'string', 'Specifies the type of button (e.g. "button", "reset" or "submit")', false, 'submit');
}
/**
* Renders the button.
*
* @return string
*/
public function render()
{
$type = $this->arguments['type'];
$name = $this->getName();
$this->registerFieldNameForFormTokenGeneration($name);
$this->tag->addAttribute('type', $type);
$this->tag->addAttribute('name', $name);
$this->tag->addAttribute('value', $this->getValueAttribute());
$this->tag->setContent($this->renderChildren());
return $this->tag->render();
}
}
@@ -0,0 +1,140 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Form;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* ViewHelper which creates a simple checkbox :html:`<input type="checkbox">`.
*
* Examples
* ========
*
* Simple one
* ----------
*
* ::
*
* <f:form.checkbox name="myCheckBox" value="someValue" />
*
* Output::
*
* <input type="checkbox" name="myCheckBox" value="someValue" />
*
* Preselect
* ---------
*
* ::
*
* <f:form.checkbox name="myCheckBox" value="someValue" checked="{object.value} == 5" />
*
* Output::
*
* <input type="checkbox" name="myCheckBox" value="someValue" checked="checked" />
*
* Depending on bound ``object`` to surrounding :ref:`f:form <typo3-fluid-form>`.
*
* Bind to object property
* -----------------------
*
* ::
*
* <f:form.checkbox property="interests" value="TYPO3" />
*
* Output::
*
* <input type="checkbox" name="user[interests][]" value="TYPO3" checked="checked" />
*
* Depending on property ``interests``.
*/
class CheckboxViewHelper extends AbstractFormFieldViewHelper
{
/**
* @var string
*/
protected $tagName = 'input';
/**
* Initialize the arguments.
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerTagAttribute(
'disabled',
'string',
'Specifies that the input element should be disabled when the page loads'
);
$this->registerArgument(
'errorClass',
'string',
'CSS class to set if there are errors for this ViewHelper',
false,
'f3-form-error'
);
$this->overrideArgument('value', 'string', 'Value of input tag. Required for checkboxes', true);
$this->registerUniversalTagAttributes();
$this->registerArgument('checked', 'bool', 'Specifies that the input element should be preselected');
$this->registerArgument('multiple', 'bool', 'Specifies whether this checkbox belongs to a multivalue (is part of a checkbox group)', false, false);
}
/**
* Renders the checkbox.
*
* @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
* @return string
*/
public function render()
{
$checked = $this->arguments['checked'];
$multiple = $this->arguments['multiple'];
$this->tag->addAttribute('type', 'checkbox');
$nameAttribute = $this->getName();
$valueAttribute = $this->getValueAttribute();
$propertyValue = null;
if ($this->hasMappingErrorOccurred()) {
$propertyValue = $this->getLastSubmittedFormData();
}
if ($checked === null && $propertyValue === null) {
$propertyValue = $this->getPropertyValue();
}
if ($propertyValue instanceof \Traversable) {
$propertyValue = iterator_to_array($propertyValue);
}
if (is_array($propertyValue)) {
$propertyValue = array_map([$this, 'convertToPlainValue'], $propertyValue);
if ($checked === null) {
$checked = in_array($valueAttribute, $propertyValue);
}
$nameAttribute .= '[]';
} elseif ($multiple === true) {
$nameAttribute .= '[]';
} elseif ($propertyValue !== null) {
$checked = (boolean)$propertyValue === (boolean)$valueAttribute;
}
$this->registerFieldNameForFormTokenGeneration($nameAttribute);
$this->tag->addAttribute('name', $nameAttribute);
$this->tag->addAttribute('value', $valueAttribute);
if ($checked === true) {
$this->tag->addAttribute('checked', 'checked');
}
$this->setErrorClassAttribute();
$hiddenField = $this->renderHiddenFieldForEmptyValue();
return $hiddenField . $this->tag->render();
}
}
@@ -0,0 +1,69 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Form;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Renders an :html:`<input type="hidden" ...>` tag.
*
* Examples
* ========
*
* Example::
*
* <f:form.hidden name="myHiddenValue" value="42" />
*
* Output::
*
* <input type="hidden" name="myHiddenValue" value="42" />
*
* You can also use the "property" attribute if you have bound an object to the form.
* See :ref:`<f:form> <typo3-fluid-form>` for more documentation.
*/
class HiddenViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\Form\AbstractFormFieldViewHelper
{
/**
* @var string
*/
protected $tagName = 'input';
/**
* Initialize the arguments.
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerUniversalTagAttributes();
}
/**
* Renders the hidden field.
*
* @return string
*/
public function render()
{
$name = $this->getName();
$this->registerFieldNameForFormTokenGeneration($name);
$this->setRespectSubmittedDataValue(true);
$this->tag->addAttribute('type', 'hidden');
$this->tag->addAttribute('name', $name);
$this->tag->addAttribute('value', $this->getValueAttribute());
$this->addAdditionalIdentityPropertiesIfNeeded();
return $this->tag->render();
}
}
@@ -0,0 +1,87 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Form;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* ViewHelper which creates a simple Password Text Box :html:`<input type="password">`.
*
* Examples
* ========
*
* Example::
*
* <f:form.password name="myPassword" />
*
* Output::
*
* <input type="password" name="myPassword" value="default value" />
*/
class PasswordViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\Form\AbstractFormFieldViewHelper
{
/**
* @var string
*/
protected $tagName = 'input';
/**
* Initialize the arguments.
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerTagAttribute(
'disabled',
'string',
'Specifies that the input element should be disabled when the page loads'
);
$this->registerTagAttribute(
'maxlength',
'int',
'The maxlength attribute of the input field (will not be validated)'
);
$this->registerTagAttribute('placeholder', 'string', 'The placeholder of the textfield');
$this->registerTagAttribute('readonly', 'string', 'The readonly attribute of the input field');
$this->registerTagAttribute('size', 'int', 'The size of the input field');
$this->registerArgument(
'errorClass',
'string',
'CSS class to set if there are errors for this ViewHelper',
false,
'f3-form-error'
);
$this->registerUniversalTagAttributes();
}
/**
* Renders the password input field.
*
* @return string
*/
public function render()
{
$name = $this->getName();
$this->registerFieldNameForFormTokenGeneration($name);
$this->setRespectSubmittedDataValue(true);
$this->tag->addAttribute('type', 'password');
$this->tag->addAttribute('name', $name);
$this->tag->addAttribute('value', $this->getValueAttribute());
$this->addAdditionalIdentityPropertiesIfNeeded();
$this->setErrorClassAttribute();
return $this->tag->render();
}
}
@@ -0,0 +1,131 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Form;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* ViewHelper which creates a simple radio button :html:`<input type="radio">`.
*
* Examples
* ========
*
* Simple
* ------
*
* ::
*
* <f:form.radio name="myRadioButton" value="someValue" />
*
* Output::
*
* <input type="radio" name="myRadioButton" value="someValue" />
*
* Preselect
* ---------
*
* ::
*
* <f:form.radio name="myRadioButton" value="someValue" checked="{object.value} == 5" />
*
* Output::
*
* <input type="radio" name="myRadioButton" value="someValue" checked="checked" />
*
* Depending on bound ``object`` to surrounding :ref:`f:form <typo3-fluid-form>`.
*
* Bind to object property
* -----------------------
*
* ::
*
* <f:form.radio property="newsletter" value="1" /> yes
* <f:form.radio property="newsletter" value="0" /> no
*
* Output::
*
* <input type="radio" name="user[newsletter]" value="1" checked="checked" /> yes
* <input type="radio" name="user[newsletter]" value="0" /> no
*
* Depending on property ``newsletter``.
*/
class RadioViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\Form\AbstractFormFieldViewHelper
{
/**
* @var string
*/
protected $tagName = 'input';
/**
* Initialize the arguments.
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument(
'errorClass',
'string',
'CSS class to set if there are errors for this ViewHelper',
false,
'f3-form-error'
);
$this->registerArgument('checked', 'bool', 'Specifies that the input element should be preselected');
$this->overrideArgument('value', 'string', 'Value of input tag. Required for radio buttons', true);
$this->registerUniversalTagAttributes();
$this->registerTagAttribute(
'disabled',
'string',
'Specifies that the input element should be disabled when the page loads'
);
}
/**
* Renders the checkbox.
*
* @return string
*/
public function render()
{
$checked = $this->arguments['checked'];
$this->tag->addAttribute('type', 'radio');
$nameAttribute = $this->getName();
$valueAttribute = $this->getValueAttribute();
$propertyValue = null;
if ($this->hasMappingErrorOccurred()) {
$propertyValue = $this->getLastSubmittedFormData();
}
if ($checked === null && $propertyValue === null) {
$propertyValue = $this->getPropertyValue();
$propertyValue = $this->convertToPlainValue($propertyValue);
}
if ($propertyValue !== null) {
// no type-safe comparison by intention
$checked = $propertyValue == $valueAttribute;
}
$this->registerFieldNameForFormTokenGeneration($nameAttribute);
$this->tag->addAttribute('name', $nameAttribute);
$this->tag->addAttribute('value', $valueAttribute);
if ($checked === true) {
$this->tag->addAttribute('checked', 'checked');
}
$this->setErrorClassAttribute();
return $this->tag->render();
}
}
@@ -0,0 +1,54 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Form\Select;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Adds custom :html:`<optgroup>` tags inside an :ref:`<f:form.select> <typo3-fluid-form-select>`,
* supports further child :ref:`<f:form.select.option> <typo3-fluid-form-select-option>` tags.
*/
class OptgroupViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\Form\AbstractFormFieldViewHelper
{
/**
* @var string
*/
protected $tagName = 'optgroup';
/**
* Initialize additional arguments available for this tag ViewHelper.
*/
public function initializeArguments()
{
$this->registerUniversalTagAttributes();
$this->registerArgument('additionalAttributes', 'array', 'Additional tag attributes. They will be added directly to the resulting HTML tag.');
$this->registerArgument('data', 'array', 'Additional data-* attributes. They will each be added with a "data-" prefix.');
$this->registerTagAttribute('label', 'string', 'Human-readable label property for the generated optgroup tag');
$this->registerTagAttribute('disabled', 'boolean', 'If true, option group is rendered as disabled', false, false);
}
/**
* @return string
*/
public function render()
{
if ($this->arguments['disabled']) {
$this->tag->addAttribute('disabled', 'disabled');
} else {
$this->tag->removeAttribute('disabled');
}
$this->tag->setContent($this->renderChildren());
return $this->tag->render();
}
}
@@ -0,0 +1,80 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Form\Select;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Fluid\ViewHelpers\Form\SelectViewHelper;
/**
* Adds custom :html:`<option>` tags inside an :ref:`<f:form.select> <typo3-fluid-form-select>`.
*/
class OptionViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\Form\AbstractFormFieldViewHelper
{
/**
* @var string
*/
protected $tagName = 'option';
/**
* Initialize additional arguments available for this tag ViewHelper.
*/
public function initializeArguments()
{
$this->registerUniversalTagAttributes();
$this->registerArgument('selected', 'boolean', 'If set, overrides automatic detection of selected state for this option.');
$this->registerArgument('additionalAttributes', 'array', 'Additional tag attributes. They will be added directly to the resulting HTML tag.');
$this->registerArgument('data', 'array', 'Additional data-* attributes. They will each be added with a "data-" prefix.');
$this->registerTagAttribute('value', 'mixed', 'Value to be inserted in HTML tag - must be convertible to string!');
}
/**
* @return string
*/
public function render()
{
if ($this->arguments['selected'] ?? $this->isValueSelected($this->arguments['value'])) {
$this->tag->addAttribute('selected', 'selected');
}
$childContent = $this->renderChildren();
$this->tag->setContent($childContent);
$value = $this->arguments['value'] ?? $childContent;
$this->tag->addAttribute('value', $value);
$parentRequestedFormTokenFieldName = $this->renderingContext->getViewHelperVariableContainer()->get(
SelectViewHelper::class,
'registerFieldNameForFormTokenGeneration'
);
if ($parentRequestedFormTokenFieldName) {
// parent (select field) has requested this option must add one more
// entry in the token generation registry for one additional potential
// value of the field. Happens when "multiple" is true on parent.
$this->registerFieldNameForFormTokenGeneration($parentRequestedFormTokenFieldName);
}
return $this->tag->render();
}
/**
* @param mixed $value
* @return bool
*/
protected function isValueSelected($value)
{
$selectedValue = $this->renderingContext->getViewHelperVariableContainer()->get(SelectViewHelper::class, 'selectedValue');
if (is_array($selectedValue)) {
return in_array($value, $selectedValue);
}
if ($selectedValue instanceof \Iterator) {
return in_array($value, iterator_to_array($selectedValue));
}
return $value == $selectedValue;
}
}
@@ -0,0 +1,357 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Form;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* This ViewHelper generates a :html:`<select>` dropdown list for the use with a form.
*
* Basic usage
* ===========
*
* The most straightforward way is to supply an associative array as the ``options`` parameter.
* The array key is used as option key, and the value is used as human-readable name.
*
* Basic usage::
*
* <f:form.select name="paymentOptions" options="{payPal: 'PayPal International Services', visa: 'VISA Card'}" />
*
* Pre select a value
* ------------------
*
* To pre select a value, set ``value`` to the option key which should be selected.
* Default value::
*
* <f:form.select name="paymentOptions" options="{payPal: 'PayPal International Services', visa: 'VISA Card'}" value="visa" />
*
* Generates a dropdown box like above, except that "VISA Card" is selected.
*
* If the select box is a multi-select box :html:`multiple="1"`, then "value" can be an array as well.
*
* Custom options and option group rendering
* -----------------------------------------
*
* Child nodes can be used to create a completely custom set of
* :html:`<option>` and :html:`<optgroup>` tags in a way compatible with the
* HMAC generation.
* To do so, leave out the ``options`` argument and use child ViewHelpers:
*
* Custom options and optgroup::
*
* <f:form.select name="myproperty">
* <f:form.select.option value="1">Option one</f:form.select.option>
* <f:form.select.option value="2">Option two</f:form.select.option>
* <f:form.select.optgroup>
* <f:form.select.option value="3">Grouped option one</f:form.select.option>
* <f:form.select.option value="4">Grouped option twi</f:form.select.option>
* </f:form.select.optgroup>
* </f:form.select>
*
* .. note::
* Do not use vanilla :html:`<option>` or :html:`<optgroup>` tags!
* They will invalidate the HMAC generation!
*
* Usage on domain objects
* -----------------------
*
* If you want to output domain objects, you can just pass them as array into the ``options`` parameter.
* To define what domain object value should be used as option key, use the ``optionValueField`` variable. Same goes for ``optionLabelField``.
* If neither is given, the Identifier (UID/uid) and the :php:`__toString()` method are tried as fallbacks.
*
* If the ``optionValueField`` variable is set, the getter named after that value is used to retrieve the option key.
* If the ``optionLabelField`` variable is set, the getter named after that value is used to retrieve the option value.
*
* If the ``prependOptionLabel`` variable is set, an option item is added in first position, bearing an empty string or -
* if provided, the value of the ``prependOptionValue`` variable as value.
*
* Domain objects::
*
* <f:form.select name="users" options="{userArray}" optionValueField="id" optionLabelField="firstName" />
*
* In the above example, the ``userArray`` is an array of "User" domain objects, with no array key specified.
*
* So, in the above example, the method :php:`$user->getId()` is called to
* retrieve the key, and :php:`$user->getFirstName()` to retrieve the displayed
* value of each entry.
*
* The ``value`` property now expects a domain object, and tests for object equivalence.
*/
class SelectViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\Form\AbstractFormFieldViewHelper
{
/**
* @var string
*/
protected $tagName = 'select';
/**
* @var mixed
*/
protected $selectedValue;
/**
* Initialize arguments.
*/
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('options', 'array', 'Associative array with internal IDs as key, and the values are displayed in the select box. Can be combined with or replaced by child f:form.select.* nodes.');
$this->registerArgument('optionsAfterContent', 'boolean', 'If true, places auto-generated option tags after those rendered in the tag content. If false, automatic options come first.', false, false);
$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 ViewHelper', false, 'f3-form-error');
$this->registerArgument('prependOptionLabel', 'string', 'If specified, will provide an option at first position with the specified label.');
$this->registerArgument('prependOptionValue', 'string', 'If specified, will provide an option at first position with the specified value.');
$this->registerArgument('multiple', 'boolean', 'If set multiple options may be selected.', false, false);
$this->registerArgument('required', 'boolean', 'If set no empty value is allowed.', false, false);
}
/**
* Render the tag.
*
* @return string rendered tag.
*/
public function render()
{
if (isset($this->arguments['required']) && $this->arguments['required']) {
$this->tag->addAttribute('required', 'required');
}
$name = $this->getName();
if (isset($this->arguments['multiple']) && $this->arguments['multiple']) {
$this->tag->addAttribute('multiple', 'multiple');
$name .= '[]';
}
$this->tag->addAttribute('name', $name);
$options = $this->getOptions();
$viewHelperVariableContainer = $this->renderingContext->getViewHelperVariableContainer();
$this->addAdditionalIdentityPropertiesIfNeeded();
$this->setErrorClassAttribute();
$content = '';
// register field name for token generation.
$this->registerFieldNameForFormTokenGeneration($name);
// in case it is a multi-select, we need to register the field name
// as often as there are elements in the box
if (isset($this->arguments['multiple']) && $this->arguments['multiple']) {
$content .= $this->renderHiddenFieldForEmptyValue();
// Register the field name additional times as required by the total number of
// options. Since we already registered it once above, we start the counter at 1
// instead of 0.
$optionsCount = count($options);
for ($i = 1; $i < $optionsCount; $i++) {
$this->registerFieldNameForFormTokenGeneration($name);
}
// save the parent field name so that any child f:form.select.option
// tag will know to call registerFieldNameForFormTokenGeneration
// this is the reason why "self::class" is used instead of static::class (no LSB)
$viewHelperVariableContainer->addOrUpdate(
self::class,
'registerFieldNameForFormTokenGeneration',
$name
);
}
$viewHelperVariableContainer->addOrUpdate(self::class, 'selectedValue', $this->getSelectedValue());
$prependContent = $this->renderPrependOptionTag();
$tagContent = $this->renderOptionTags($options);
$childContent = $this->renderChildren();
$viewHelperVariableContainer->remove(self::class, 'selectedValue');
$viewHelperVariableContainer->remove(self::class, 'registerFieldNameForFormTokenGeneration');
if (isset($this->arguments['optionsAfterContent']) && $this->arguments['optionsAfterContent']) {
$tagContent = $childContent . $tagContent;
} else {
$tagContent .= $childContent;
}
$tagContent = $prependContent . $tagContent;
$this->tag->forceClosingTag(true);
$this->tag->setContent($tagContent);
$content .= $this->tag->render();
return $content;
}
/**
* Render prepended option tag
*
* @return string rendered prepended empty option
*/
protected function renderPrependOptionTag()
{
$output = '';
if ($this->hasArgument('prependOptionLabel')) {
$value = $this->hasArgument('prependOptionValue') ? $this->arguments['prependOptionValue'] : '';
$label = $this->arguments['prependOptionLabel'];
$output .= $this->renderOptionTag($value, $label, false) . LF;
}
return $output;
}
/**
* 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) . LF;
}
return $output;
}
/**
* Render the option tags.
*
* @return array an associative array of options, key will be the value of the option tag
*/
protected function getOptions()
{
if (!is_array($this->arguments['options']) && !$this->arguments['options'] instanceof \Traversable) {
return [];
}
$options = [];
$optionsArgument = $this->arguments['options'];
foreach ($optionsArgument as $key => $value) {
if (is_object($value) || is_array($value)) {
if ($this->hasArgument('optionValueField')) {
$key = \TYPO3\CMS\Extbase\Reflection\ObjectAccess::getPropertyPath($value, $this->arguments['optionValueField']);
if (is_object($key)) {
if (method_exists($key, '__toString')) {
$key = (string)$key;
} else {
throw new \TYPO3Fluid\Fluid\Core\ViewHelper\Exception('Identifying value for object of class "' . get_class($value) . '" was an object.', 1247827428);
}
}
} elseif ($this->persistenceManager->getIdentifierByObject($value) !== null) {
// @todo use $this->persistenceManager->isNewObject() once it is implemented
$key = $this->persistenceManager->getIdentifierByObject($value);
} elseif (method_exists($value, '__toString')) {
$key = (string)$value;
} else {
throw new \TYPO3Fluid\Fluid\Core\ViewHelper\Exception('No identifying value for object of class "' . get_class($value) . '" found.', 1247826696);
}
if ($this->hasArgument('optionLabelField')) {
$value = \TYPO3\CMS\Extbase\Reflection\ObjectAccess::getPropertyPath($value, $this->arguments['optionLabelField']);
if (is_object($value)) {
if (method_exists($value, '__toString')) {
$value = (string)$value;
} else {
throw new \TYPO3Fluid\Fluid\Core\ViewHelper\Exception('Label value for object of class "' . get_class($value) . '" was an object without a __toString() method.', 1247827553);
}
}
} elseif (method_exists($value, '__toString')) {
$value = (string)$value;
} elseif ($this->persistenceManager->getIdentifierByObject($value) !== null) {
// @todo use $this->persistenceManager->isNewObject() once it is implemented
$value = $this->persistenceManager->getIdentifierByObject($value);
}
}
$options[$key] = $value;
}
if ($this->arguments['sortByOptionLabel']) {
asort($options, SORT_LOCALE_STRING);
}
return $options;
}
/**
* Render the option tags.
*
* @param mixed $value Value to check for
* @return bool 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 ($this->hasArgument('multiple')) {
if ($selectedValue === null && $this->arguments['selectAllByDefault'] === true) {
return true;
}
if (is_array($selectedValue) && 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()
{
$this->setRespectSubmittedDataValue(true);
$value = $this->getValueAttribute();
if (!is_array($value) && !$value instanceof \Traversable) {
return $this->getOptionValueScalar($value);
}
$selectedValues = [];
foreach ($value as $selectedValueElement) {
$selectedValues[] = $this->getOptionValueScalar($selectedValueElement);
}
return $selectedValues;
}
/**
* Get the option value for an object
*
* @param mixed $valueElement
* @return string
*/
protected function getOptionValueScalar($valueElement)
{
if (is_object($valueElement)) {
if ($this->hasArgument('optionValueField')) {
return \TYPO3\CMS\Extbase\Reflection\ObjectAccess::getPropertyPath($valueElement, $this->arguments['optionValueField']);
}
// @todo use $this->persistenceManager->isNewObject() once it is implemented
if ($this->persistenceManager->getIdentifierByObject($valueElement) !== null) {
return $this->persistenceManager->getIdentifierByObject($valueElement);
}
return (string)$valueElement;
}
return $valueElement;
}
/**
* 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 bool $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 ($isSelected) {
$output .= ' selected="selected"';
}
$output .= '>' . htmlspecialchars($label) . '</option>';
return $output;
}
}
@@ -0,0 +1,84 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Form;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Creates a submit button.
*
* Examples
* ========
*
* Defaults
* --------
*
* ::
*
* <f:form.submit value="Send Mail" />
*
* Output::
*
* <input type="submit" />
*
* Dummy content for template preview
* ----------------------------------
*
* ::
*
* <f:form.submit name="mySubmit" value="Send Mail"><button>dummy button</button></f:form.submit>
*
* Output::
*
* <input type="submit" name="mySubmit" value="Send Mail" />
*/
class SubmitViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\Form\AbstractFormFieldViewHelper
{
/**
* @var string
*/
protected $tagName = 'input';
/**
* Initialize the arguments.
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerTagAttribute(
'disabled',
'string',
'Specifies that the input element should be disabled when the page loads'
);
$this->registerUniversalTagAttributes();
}
/**
* Renders the submit button.
*
* @return string
*/
public function render()
{
$name = $this->getName();
$this->registerFieldNameForFormTokenGeneration($name);
$this->tag->addAttribute('type', 'submit');
$this->tag->addAttribute('value', $this->getValueAttribute());
if (!empty($name)) {
$this->tag->addAttribute('name', $name);
}
return $this->tag->render();
}
}
@@ -0,0 +1,80 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Form;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Generates an :html:`<textarea>`.
*
* The value of the text area needs to be set via the ``value`` attribute, as with all other form ViewHelpers.
*
* Examples
* ========
*
* Example::
*
* <f:form.textarea name="myTextArea" value="This is shown inside the textarea" />
*
* Output::
*
* <textarea name="myTextArea">This is shown inside the textarea</textarea>
*/
class TextareaViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\Form\AbstractFormFieldViewHelper
{
/**
* @var string
*/
protected $tagName = 'textarea';
/**
* Initialize the arguments.
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerTagAttribute('autofocus', 'string', 'Specifies that a text area should automatically get focus when the page loads');
$this->registerTagAttribute('rows', 'int', 'The number of rows of a text area');
$this->registerTagAttribute('cols', 'int', 'The number of columns of a text area');
$this->registerTagAttribute('disabled', 'string', 'Specifies that the input element should be disabled when the page loads');
$this->registerTagAttribute('placeholder', 'string', 'The placeholder of the textarea');
$this->registerArgument('errorClass', 'string', 'CSS class to set if there are errors for this ViewHelper', false, 'f3-form-error');
$this->registerTagAttribute('readonly', 'string', 'The readonly attribute of the textarea', false);
$this->registerArgument('required', 'bool', 'Specifies whether the textarea is required', false, false);
$this->registerUniversalTagAttributes();
}
/**
* Renders the textarea.
*
* @return string
*/
public function render()
{
$required = $this->arguments['required'] ?? false;
$name = $this->getName();
$this->registerFieldNameForFormTokenGeneration($name);
$this->setRespectSubmittedDataValue(true);
$this->tag->forceClosingTag(true);
$this->tag->addAttribute('name', $name);
if ($required === true) {
$this->tag->addAttribute('required', 'required');
}
$this->tag->setContent(htmlspecialchars($this->getValueAttribute()));
$this->addAdditionalIdentityPropertiesIfNeeded();
$this->setErrorClassAttribute();
return $this->tag->render();
}
}
@@ -0,0 +1,91 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Form;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* ViewHelper which creates a text field :html:`<input type="text">`.
*
* Examples
* ========
*
* Example::
*
* <f:form.textfield name="myTextBox" value="default value" />
*
* Output::
*
* <input type="text" name="myTextBox" value="default value" />
*/
class TextfieldViewHelper extends AbstractFormFieldViewHelper
{
/**
* @var string
*/
protected $tagName = 'input';
/**
* Initialize the arguments.
*
* @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerTagAttribute('autofocus', 'string', 'Specifies that an input should automatically get focus when the page loads');
$this->registerTagAttribute('disabled', 'string', 'Specifies that the input element should be disabled when the page loads');
$this->registerTagAttribute('maxlength', 'int', 'The maxlength attribute of the input field (will not be validated)');
$this->registerTagAttribute('readonly', 'string', 'The readonly attribute of the input field');
$this->registerTagAttribute('size', 'int', 'The size of the input field');
$this->registerTagAttribute('placeholder', 'string', 'The placeholder of the textfield');
$this->registerTagAttribute('pattern', 'string', 'HTML5 validation pattern');
$this->registerArgument('errorClass', 'string', 'CSS class to set if there are errors for this ViewHelper', false, 'f3-form-error');
$this->registerUniversalTagAttributes();
$this->registerArgument('required', 'bool', 'If the field is required or not', false, false);
$this->registerArgument('type', 'string', 'The field type, e.g. "text", "email", "url" etc.', false, 'text');
}
/**
* Renders the textfield.
*
* @return string
*/
public function render()
{
$required = $this->arguments['required'] ?? false;
$type = $this->arguments['type'] ?? null;
$name = $this->getName();
$this->registerFieldNameForFormTokenGeneration($name);
$this->setRespectSubmittedDataValue(true);
$this->tag->addAttribute('type', $type);
$this->tag->addAttribute('name', $name);
$value = $this->getValueAttribute();
if ($value !== null) {
$this->tag->addAttribute('value', $value);
}
if ($required !== false) {
$this->tag->addAttribute('required', 'required');
}
$this->addAdditionalIdentityPropertiesIfNeeded();
$this->setErrorClassAttribute();
return $this->tag->render();
}
}
@@ -0,0 +1,74 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Form;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* A ViewHelper which generates an :html:`<input type="file">` HTML element.
* Make sure to set ``enctype="multipart/form-data"`` on the form!
*
* Examples
* ========
*
* Example::
*
* <f:form.upload name="file" />
*
* Output::
*
* <input type="file" name="file" />
*/
class UploadViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\Form\AbstractFormFieldViewHelper
{
/**
* @var string
*/
protected $tagName = 'input';
/**
* Initialize the arguments.
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerTagAttribute('disabled', 'string', 'Specifies that the input element should be disabled when the page loads');
$this->registerTagAttribute('multiple', 'string', 'Specifies that the file input element should allow multiple selection of files');
$this->registerArgument('errorClass', 'string', 'CSS class to set if there are errors for this ViewHelper', false, 'f3-form-error');
$this->registerUniversalTagAttributes();
}
/**
* Renders the upload field.
*
* @return string
*/
public function render()
{
$name = $this->getName();
$allowedFields = ['name', 'type', 'tmp_name', 'error', 'size'];
foreach ($allowedFields as $fieldName) {
$this->registerFieldNameForFormTokenGeneration($name . '[' . $fieldName . ']');
}
$this->tag->addAttribute('type', 'file');
if (isset($this->arguments['multiple'])) {
$this->tag->addAttribute('name', $name . '[]');
} else {
$this->tag->addAttribute('name', $name);
}
$this->setErrorClassAttribute();
return $this->tag->render();
}
}
@@ -0,0 +1,111 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Form;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* Validation results ViewHelper
*
* Examples
* ========
*
* Output error messages as a list::
*
* <f:form.validationResults>
* <f:if condition="{validationResults.flattenedErrors}">
* <ul class="errors">
* <f:for each="{validationResults.flattenedErrors}" as="errors" key="propertyPath">
* <li>{propertyPath}
* <ul>
* <f:for each="{errors}" as="error">
* <li>{error.code}: {error}</li>
* </f:for>
* </ul>
* </li>
* </f:for>
* </ul>
* </f:if>
* </f:form.validationResults>
*
* Output::
*
* <ul class="errors">
* <li>1234567890: Validation errors for argument "newBlog"</li>
* </ul>
*
* Output error messages for a single property::
*
* <f:form.validationResults for="someProperty">
* <f:if condition="{validationResults.flattenedErrors}">
* <ul class="errors">
* <f:for each="{validationResults.errors}" as="error">
* <li>{error.code}: {error}</li>
* </f:for>
* </ul>
* </f:if>
* </f:form.validationResults>
*
* Output::
*
* <ul class="errors">
* <li>1234567890: Some error message</li>
* </ul>
*/
class ValidationResultsViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* As this ViewHelper renders HTML, the output must not be escaped.
*
* @var bool
*/
protected $escapeOutput = false;
/**
* Initialize arguments
*/
public function initializeArguments()
{
$this->registerArgument('for', 'string', 'The name of the error name (e.g. argument name or property name). This can also be a property path (like blog.title), and will then only display the validation errors of that property.', false, '');
$this->registerArgument('as', 'string', 'The name of the variable to store the current error', false, 'validationResults');
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return mixed
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$templateVariableContainer = $renderingContext->getVariableProvider();
$controllerContext = $renderingContext->getcontrollerContext();
$for = $arguments['for'];
$as = $arguments['as'];
$validationResults = $controllerContext->getRequest()->getOriginalRequestMappingResults();
if ($validationResults !== null && $for !== '') {
$validationResults = $validationResults->forProperty($for);
}
$templateVariableContainer->add($as, $validationResults);
$output = $renderChildrenClosure();
$templateVariableContainer->remove($as);
return $output;
}
}
@@ -0,0 +1,473 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Form ViewHelper. Generates a :html:`<form>` Tag.
*
* Basic usage
* ===========
*
* Use :html:`<f:form>` to output an HTML :html:`<form>` tag which is targeted
* at the specified action, in the current controller and package.
* It will submit the form data via a POST request. If you want to change this,
* use :html:`method="get"` as an argument.
*
* Examples
* ========
*
* A complex form with a specified encoding type
* ---------------------------------------------
*
* Form with enctype set::
*
* <f:form action=".." controller="..." package="..." enctype="multipart/form-data">...</f:form>
*
* A Form which should render a domain object
* ------------------------------------------
*
* Binding a domain object to a form::
*
* <f:form action="..." name="customer" object="{customer}">
* <f:form.hidden property="id" />
* <f:form.textbox property="name" />
* </f:form>
*
* This automatically inserts the value of ``{customer.name}`` inside the
* textbox and adjusts the name of the textbox accordingly.
*/
class FormViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\Form\AbstractFormViewHelper
{
/**
* @var string
*/
protected $tagName = 'form';
/**
* @var \TYPO3\CMS\Extbase\Security\Cryptography\HashService
*/
protected $hashService;
/**
* @var \TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfigurationService
*/
protected $mvcPropertyMappingConfigurationService;
/**
* @var \TYPO3\CMS\Extbase\Service\ExtensionService
*/
protected $extensionService;
/**
* We need the arguments of the formActionUri on requesthash calculation
* therefore we will store them in here right after calling uriBuilder
*
* @var array
*/
protected $formActionUriArguments;
/**
* @param \TYPO3\CMS\Extbase\Security\Cryptography\HashService $hashService
*/
public function injectHashService(\TYPO3\CMS\Extbase\Security\Cryptography\HashService $hashService)
{
$this->hashService = $hashService;
}
/**
* @param \TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfigurationService $mvcPropertyMappingConfigurationService
*/
public function injectMvcPropertyMappingConfigurationService(\TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfigurationService $mvcPropertyMappingConfigurationService)
{
$this->mvcPropertyMappingConfigurationService = $mvcPropertyMappingConfigurationService;
}
/**
* @param \TYPO3\CMS\Extbase\Service\ExtensionService $extensionService
*/
public function injectExtensionService(\TYPO3\CMS\Extbase\Service\ExtensionService $extensionService)
{
$this->extensionService = $extensionService;
}
/**
* Initialize arguments.
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('action', 'string', 'Target action');
$this->registerArgument('arguments', 'array', 'Arguments', false, []);
$this->registerArgument('controller', 'string', 'Target controller');
$this->registerArgument('extensionName', 'string', 'Target Extension Name (without "tx_" prefix and no underscores). If NULL the current extension name is used');
$this->registerArgument('pluginName', 'string', 'Target plugin. If empty, the current plugin name is used');
$this->registerArgument('pageUid', 'int', 'Target page uid');
$this->registerArgument('object', 'mixed', 'Object to use for the form. Use in conjunction with the "property" attribute on the sub tags');
$this->registerArgument('pageType', 'int', 'Target page type', false, 0);
$this->registerArgument('noCache', 'bool', 'set this to disable caching for the target page. You should not need this.', false, false);
$this->registerArgument('noCacheHash', 'bool', 'set this to suppress the cHash query parameter created by TypoLink. You should not need this.', false, false);
$this->registerArgument('section', 'string', 'The anchor to be added to the action URI (only active if $actionUri is not set)', false, '');
$this->registerArgument('format', 'string', 'The requested format (e.g. ".html") of the target page (only active if $actionUri is not set)', false, '');
$this->registerArgument('additionalParams', 'array', 'additional action URI query parameters that won\'t be prefixed like $arguments (overrule $arguments) (only active if $actionUri is not set)', false, []);
$this->registerArgument('absolute', 'bool', 'If set, an absolute action URI is rendered (only active if $actionUri is not set)', false, false);
$this->registerArgument('addQueryString', 'bool', 'If set, the current query parameters will be kept in the action URI (only active if $actionUri is not set)', false, false);
$this->registerArgument('argumentsToBeExcludedFromQueryString', 'array', 'arguments to be removed from the action URI. Only active if $addQueryString = TRUE and $actionUri is not set', false, []);
$this->registerArgument('addQueryStringMethod', 'string', 'Method to use when keeping query parameters (GET or POST, only active if $actionUri is not set', false, 'GET');
$this->registerArgument('fieldNamePrefix', 'string', 'Prefix that will be added to all field names within this form. If not set the prefix will be tx_yourExtension_plugin');
$this->registerArgument('actionUri', 'string', 'can be used to overwrite the "action" attribute of the form tag');
$this->registerArgument('objectName', 'string', 'name of the object that is bound to this form. If this argument is not specified, the name attribute of this form is used to determine the FormObjectName');
$this->registerArgument('hiddenFieldClassName', 'string', 'hiddenFieldClassName');
$this->registerTagAttribute('enctype', 'string', 'MIME type with which the form is submitted');
$this->registerTagAttribute('method', 'string', 'Transfer type (GET or POST)');
$this->registerTagAttribute('name', 'string', 'Name of form');
$this->registerTagAttribute('onreset', 'string', 'JavaScript: On reset of the form');
$this->registerTagAttribute('onsubmit', 'string', 'JavaScript: On submit of the form');
$this->registerTagAttribute('target', 'string', 'Target attribute of the form');
$this->registerTagAttribute('novalidate', 'bool', 'Indicate that the form is not to be validated on submit.');
$this->registerUniversalTagAttributes();
}
/**
* Render the form.
*
* @return string rendered form
*/
public function render()
{
$this->setFormActionUri();
if (isset($this->arguments['method']) && strtolower($this->arguments['method']) === 'get') {
$this->tag->addAttribute('method', 'get');
} else {
$this->tag->addAttribute('method', 'post');
}
if (isset($this->arguments['novalidate']) && $this->arguments['novalidate'] === true) {
$this->tag->addAttribute('novalidate', 'novalidate');
}
$this->addFormObjectNameToViewHelperVariableContainer();
$this->addFormObjectToViewHelperVariableContainer();
$this->addFieldNamePrefixToViewHelperVariableContainer();
$this->addFormFieldNamesToViewHelperVariableContainer();
$formContent = $this->renderChildren();
if (isset($this->arguments['hiddenFieldClassName']) && $this->arguments['hiddenFieldClassName'] !== null) {
$content = LF . '<div class="' . htmlspecialchars($this->arguments['hiddenFieldClassName']) . '">';
} else {
$content = LF . '<div>';
}
$content .= $this->renderHiddenIdentityField($this->arguments['object'] ?? null, $this->getFormObjectName());
$content .= $this->renderAdditionalIdentityFields();
$content .= $this->renderHiddenReferrerFields();
// Render the trusted list of all properties after everything else has been rendered
$content .= $this->renderTrustedPropertiesField();
$content .= LF . '</div>' . LF;
$content .= $formContent;
$this->tag->setContent($content);
$this->removeFieldNamePrefixFromViewHelperVariableContainer();
$this->removeFormObjectFromViewHelperVariableContainer();
$this->removeFormObjectNameFromViewHelperVariableContainer();
$this->removeFormFieldNamesFromViewHelperVariableContainer();
$this->removeCheckboxFieldNamesFromViewHelperVariableContainer();
return $this->tag->render();
}
/**
* Sets the "action" attribute of the form tag
*/
protected function setFormActionUri()
{
if ($this->hasArgument('actionUri')) {
$formActionUri = $this->arguments['actionUri'];
} else {
$pageUid = (isset($this->arguments['pageUid']) && (int)$this->arguments['pageUid'] > 0) ? (int)$this->arguments['pageUid'] : null;
$uriBuilder = $this->renderingContext->getControllerContext()->getUriBuilder();
$formActionUri = $uriBuilder
->reset()
->setTargetPageUid($pageUid)
->setTargetPageType($this->arguments['pageType'] ?? 0)
->setNoCache($this->arguments['noCache'] ?? false)
->setUseCacheHash(isset($this->arguments['noCacheHash']) ? !$this->arguments['noCacheHash'] : true)
->setSection($this->arguments['section'] ?? '')
->setCreateAbsoluteUri($this->arguments['absolute'] ?? false)
->setArguments(isset($this->arguments['additionalParams']) ? (array)$this->arguments['additionalParams'] : [])
->setAddQueryString($this->arguments['addQueryString'] ?? false)
->setAddQueryStringMethod($this->arguments['addQueryStringMethod'] ?? null)
->setArgumentsToBeExcludedFromQueryString(isset($this->arguments['argumentsToBeExcludedFromQueryString']) ? (array)$this->arguments['argumentsToBeExcludedFromQueryString'] : [])
->setFormat($this->arguments['format'] ?? '')
->uriFor(
$this->arguments['action'] ?? null,
$this->arguments['arguments'] ?? [],
$this->arguments['controller'] ?? null,
$this->arguments['extensionName'] ?? null,
$this->arguments['pluginName'] ?? null
);
$this->formActionUriArguments = $uriBuilder->getArguments();
}
$this->tag->addAttribute('action', $formActionUri);
}
/**
* Render additional identity fields which were registered by form elements.
* This happens if a form field is defined like property="bla.blubb" - then we might need an identity property for the sub-object "bla".
*
* @return string HTML-string for the additional identity properties
*/
protected function renderAdditionalIdentityFields()
{
$viewHelperVariableContainer = $this->renderingContext->getViewHelperVariableContainer();
if ($viewHelperVariableContainer->exists(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'additionalIdentityProperties')) {
$additionalIdentityProperties = $viewHelperVariableContainer->get(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'additionalIdentityProperties');
$output = '';
foreach ($additionalIdentityProperties as $identity) {
$output .= LF . $identity;
}
return $output;
}
return '';
}
/**
* Renders hidden form fields for referrer information about
* the current controller and action.
*
* @return string Hidden fields with referrer information
* @todo filter out referrer information that is equal to the target (e.g. same packageKey)
*/
protected function renderHiddenReferrerFields()
{
$request = $this->renderingContext->getControllerContext()->getRequest();
$extensionName = $request->getControllerExtensionName();
$vendorName = $request->getControllerVendorName();
$controllerName = $request->getControllerName();
$actionName = $request->getControllerActionName();
$actionRequest = [
'@extension' => $extensionName,
'@controller' => $controllerName,
'@action' => $actionName,
];
$result = LF;
$result .= '<input type="hidden" name="' . htmlspecialchars($this->prefixFieldName('__referrer[@extension]')) . '" value="' . htmlspecialchars($extensionName) . '" />' . LF;
if ($vendorName !== null) {
$result .= '<input type="hidden" name="' . $this->prefixFieldName('__referrer[@vendor]') . '" value="' . $vendorName . '" />' . LF;
$actionRequest['@vendor'] = $vendorName;
}
$result .= '<input type="hidden" name="' . htmlspecialchars($this->prefixFieldName('__referrer[@controller]')) . '" value="' . htmlspecialchars($controllerName) . '" />' . LF;
$result .= '<input type="hidden" name="' . htmlspecialchars($this->prefixFieldName('__referrer[@action]')) . '" value="' . htmlspecialchars($actionName) . '" />' . LF;
$result .= '<input type="hidden" name="' . htmlspecialchars($this->prefixFieldName('__referrer[arguments]')) . '" value="' . htmlspecialchars($this->hashService->appendHmac(base64_encode(serialize($request->getArguments())))) . '" />' . LF;
$result .= '<input type="hidden" name="' . htmlspecialchars($this->prefixFieldName('__referrer[@request]')) . '" value="' . htmlspecialchars($this->hashService->appendHmac(serialize($actionRequest))) . '" />' . LF;
return $result;
}
/**
* Adds the form object name to the ViewHelperVariableContainer if "objectName" argument or "name" attribute is specified.
*/
protected function addFormObjectNameToViewHelperVariableContainer()
{
$formObjectName = $this->getFormObjectName();
if ($formObjectName !== null) {
$this->renderingContext->getViewHelperVariableContainer()->add(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'formObjectName', $formObjectName);
}
}
/**
* Removes the form name from the ViewHelperVariableContainer.
*/
protected function removeFormObjectNameFromViewHelperVariableContainer()
{
$formObjectName = $this->getFormObjectName();
if ($formObjectName !== null) {
$this->renderingContext->getViewHelperVariableContainer()->remove(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'formObjectName');
}
}
/**
* Returns the name of the object that is bound to this form.
* If the "objectName" argument has been specified, this is returned. Otherwise the name attribute of this form.
* If neither objectName nor name arguments have been set, NULL is returned.
*
* @return string specified Form name or NULL if neither $objectName nor $name arguments have been specified
*/
protected function getFormObjectName()
{
$formObjectName = null;
if ($this->hasArgument('objectName')) {
$formObjectName = $this->arguments['objectName'];
} elseif ($this->hasArgument('name')) {
$formObjectName = $this->arguments['name'];
}
return $formObjectName;
}
/**
* Adds the object that is bound to this form to the ViewHelperVariableContainer if the formObject attribute is specified.
*/
protected function addFormObjectToViewHelperVariableContainer()
{
if ($this->hasArgument('object')) {
$viewHelperVariableContainer = $this->renderingContext->getViewHelperVariableContainer();
$viewHelperVariableContainer->add(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'formObject', $this->arguments['object']);
$viewHelperVariableContainer->add(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'additionalIdentityProperties', []);
}
}
/**
* Removes the form object from the ViewHelperVariableContainer.
*/
protected function removeFormObjectFromViewHelperVariableContainer()
{
if ($this->hasArgument('object')) {
$viewHelperVariableContainer = $this->renderingContext->getViewHelperVariableContainer();
$viewHelperVariableContainer->remove(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'formObject');
$viewHelperVariableContainer->remove(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'additionalIdentityProperties');
}
}
/**
* Adds the field name prefix to the ViewHelperVariableContainer
*/
protected function addFieldNamePrefixToViewHelperVariableContainer()
{
$fieldNamePrefix = $this->getFieldNamePrefix();
$this->renderingContext->getViewHelperVariableContainer()->add(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'fieldNamePrefix', $fieldNamePrefix);
}
/**
* Get the field name prefix
*
* @return string
*/
protected function getFieldNamePrefix()
{
if ($this->hasArgument('fieldNamePrefix')) {
return $this->arguments['fieldNamePrefix'];
}
return $this->getDefaultFieldNamePrefix();
}
/**
* Removes field name prefix from the ViewHelperVariableContainer
*/
protected function removeFieldNamePrefixFromViewHelperVariableContainer()
{
$this->renderingContext->getViewHelperVariableContainer()->remove(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'fieldNamePrefix');
}
/**
* Adds a container for form field names to the ViewHelperVariableContainer
*/
protected function addFormFieldNamesToViewHelperVariableContainer()
{
$this->renderingContext->getViewHelperVariableContainer()->add(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'formFieldNames', []);
}
/**
* Removes the container for form field names from the ViewHelperVariableContainer
*/
protected function removeFormFieldNamesFromViewHelperVariableContainer()
{
$viewHelperVariableContainer = $this->renderingContext->getViewHelperVariableContainer();
$viewHelperVariableContainer->remove(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'formFieldNames');
if ($viewHelperVariableContainer->exists(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'renderedHiddenFields')) {
$viewHelperVariableContainer->remove(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'renderedHiddenFields');
}
}
/**
* Render the request hash field
*
* @return string the hmac field
*/
protected function renderRequestHashField()
{
$formFieldNames = $this->viewHelperVariableContainer->get(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'formFieldNames');
$this->postProcessUriArgumentsForRequestHash($this->formActionUriArguments, $formFieldNames);
$requestHash = $this->requestHashService->generateRequestHash($formFieldNames, $this->getFieldNamePrefix());
return '<input type="hidden" name="' . $this->prefixFieldName('__hmac') . '" value="' . htmlspecialchars($requestHash) . '" />';
}
/**
* Add the URI arguments after postprocessing to the request hash as well.
* @param array $arguments
* @param array $results
* @param string $currentPrefix
* @param int $level
*/
protected function postProcessUriArgumentsForRequestHash($arguments, &$results, $currentPrefix = '', $level = 0)
{
if (count($arguments)) {
foreach ($arguments as $argumentName => $argumentValue) {
if (is_array($argumentValue)) {
$prefix = $level == 0 ? $argumentName : $currentPrefix . '[' . $argumentName . ']';
$this->postProcessUriArgumentsForRequestHash($argumentValue, $results, $prefix, $level + 1);
} else {
$results[] = $level == 0 ? $argumentName : $currentPrefix . '[' . $argumentName . ']';
}
}
}
}
/**
* Retrieves the default field name prefix for this form
*
* @return string default field name prefix
*/
protected function getDefaultFieldNamePrefix()
{
$request = $this->renderingContext->getControllerContext()->getRequest();
if ($this->hasArgument('extensionName')) {
$extensionName = $this->arguments['extensionName'];
} else {
$extensionName = $request->getControllerExtensionName();
}
if ($this->hasArgument('pluginName')) {
$pluginName = $this->arguments['pluginName'];
} else {
$pluginName = $request->getPluginName();
}
if ($extensionName !== null && $pluginName != null) {
return $this->extensionService->getPluginNamespace($extensionName, $pluginName);
}
return '';
}
/**
* Remove Checkbox field names from ViewHelper variable container, to start from scratch when a new form starts.
*/
protected function removeCheckboxFieldNamesFromViewHelperVariableContainer()
{
$viewHelperVariableContainer = $this->renderingContext->getViewHelperVariableContainer();
if ($viewHelperVariableContainer->exists(\TYPO3\CMS\Fluid\ViewHelpers\Form\CheckboxViewHelper::class, 'checkboxFieldNames')) {
$viewHelperVariableContainer->remove(\TYPO3\CMS\Fluid\ViewHelpers\Form\CheckboxViewHelper::class, 'checkboxFieldNames');
}
}
/**
* Render the request hash field
*
* @return string The hmac field
*/
protected function renderTrustedPropertiesField()
{
$formFieldNames = $this->renderingContext->getViewHelperVariableContainer()->get(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'formFieldNames');
$requestHash = $this->mvcPropertyMappingConfigurationService->generateTrustedPropertiesToken($formFieldNames, $this->getFieldNamePrefix());
return '<input type="hidden" name="' . htmlspecialchars($this->prefixFieldName('__trustedProperties')) . '" value="' . htmlspecialchars($requestHash) . '" />';
}
}
@@ -0,0 +1,42 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Format;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
/**
* This is the base class for ViewHelpers that work with encodings.
* Currently that are format.htmlentities, format.htmlentitiesDecode and format.htmlspecialchars
*/
abstract class AbstractEncodingViewHelper extends AbstractViewHelper
{
/**
* @var string
*/
protected static $defaultEncoding;
/**
* Resolve the default encoding. If none is set in Frontend or Backend, uses UTF-8.
*
* @return string the encoding
*/
protected static function resolveDefaultEncoding()
{
if (self::$defaultEncoding === null) {
self::$defaultEncoding = 'UTF-8';
}
return self::$defaultEncoding;
}
}
@@ -0,0 +1,126 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Format;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithContentArgumentAndRenderStatic;
/**
* Formats an integer with a byte count into human readable form.
*
* Examples
* ========
*
* Simple
* ------
*
* ::
*
* {fileSize -> f:format.bytes()}
*
* ``123 KB``
* Depending on the value of ``{fileSize}``.
*
* With arguments
* --------------
*
* ::
*
* {fileSize -> f:format.bytes(decimals: 2, decimalSeparator: '.', thousandsSeparator: ',')}
*
* ``1,023.00 B``
* Depending on the value of ``{fileSize}``.
*
* You may provide an own set of units, like this: ``B,KB,MB,GB,TB,PB,EB,ZB,YB``.
*
* Custom units
* ------------
*
* ::
*
* {fileSize -> f:format.bytes(units: '{f:translate(\'viewhelper.format.bytes.units\', \'fluid\')}'
*
* ``123 KB``
* Depending on the value of ``{fileSize}``.
*/
class BytesViewHelper extends AbstractViewHelper
{
use CompileWithContentArgumentAndRenderStatic;
/**
* Output is escaped already. We must not escape children, to avoid double encoding.
*
* @var bool
*/
protected $escapeChildren = false;
/**
* Initialize ViewHelper arguments
*/
public function initializeArguments()
{
$this->registerArgument('value', 'int', 'The incoming data to convert, or NULL if VH children should be used');
$this->registerArgument('decimals', 'int', 'The number of digits after the decimal point', false, 0);
$this->registerArgument('decimalSeparator', 'string', 'The decimal point character', false, '.');
$this->registerArgument('thousandsSeparator', 'string', 'The character for grouping the thousand digits', false, ',');
$this->registerArgument('units', 'string', 'comma separated list of available units, default is LocalizationUtility::translate(\'viewhelper.format.bytes.units\', \'fluid\')');
}
/**
* Render the supplied byte count as a human readable string.
*
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param \TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface $renderingContext
*
* @return string Formatted byte count
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
if ($arguments['units'] !== null) {
$units = $arguments['units'];
} else {
$units = LocalizationUtility::translate('viewhelper.format.bytes.units', 'fluid');
}
$units = GeneralUtility::trimExplode(',', $units, true);
$value = $renderChildrenClosure();
if (is_numeric($value)) {
$value = (float)$value;
}
if (!is_int($value) && !is_float($value)) {
$value = 0;
}
$bytes = max($value, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
$bytes /= pow(2, 10 * $pow);
return sprintf(
'%s %s',
number_format(
round($bytes, 4 * $arguments['decimals']),
$arguments['decimals'],
$arguments['decimalSeparator'],
$arguments['thousandsSeparator']
),
$units[$pow]
);
}
}
@@ -0,0 +1,164 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Format;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3Fluid\Fluid\Core\Exception;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* Modifies the case of an input string to upper- or lowercase or capitalization.
* The default transformation will be uppercase as in `mb_convert_case`_.
*
* Possible modes are:
*
* ``lower``
* Transforms the input string to its lowercase representation
*
* ``upper``
* Transforms the input string to its uppercase representation
*
* ``capital``
* Transforms the input string to its first letter upper-cased, i.e. capitalization
*
* ``uncapital``
* Transforms the input string to its first letter lower-cased, i.e. uncapitalization
*
* ``capitalWords``
* Not supported yet: Transforms the input string to each containing word being capitalized
*
* Note that the behavior will be the same as in the appropriate PHP function `mb_convert_case`_;
* especially regarding locale and multibyte behavior.
*
* .. _mb_convert_case: https://www.php.net/manual/function.mb-convert-case.php
*
* Examples
* ========
*
* Default
* -------
*
* ::
*
* <f:format.case>Some Text with miXed case</f:format.case>
*
* Output::
*
* SOME TEXT WITH MIXED CASE
*
* Example with given mode
* -----------------------
*
* ::
*
* <f:format.case mode="capital">someString</f:format.case>
*
* Output::
*
* SomeString
*/
class CaseViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* Directs the input string being converted to "lowercase"
*/
const CASE_LOWER = 'lower';
/**
* Directs the input string being converted to "UPPERCASE"
*/
const CASE_UPPER = 'upper';
/**
* Directs the input string being converted to "Capital case"
*/
const CASE_CAPITAL = 'capital';
/**
* Directs the input string being converted to "unCapital case"
*/
const CASE_UNCAPITAL = 'uncapital';
/**
* Directs the input string being converted to "Capital Case For Each Word"
*/
const CASE_CAPITAL_WORDS = 'capitalWords';
/**
* Output is escaped already. We must not escape children, to avoid double encoding.
*
* @var bool
*/
protected $escapeChildren = false;
/**
* Initialize ViewHelper arguments
*/
public function initializeArguments()
{
$this->registerArgument('value', 'string', 'The input value. If not given, the evaluated child nodes will be used.', false, null);
$this->registerArgument('mode', 'string', 'The case to apply, must be one of this\' CASE_* constants. Defaults to uppercase application.', false, self::CASE_UPPER);
}
/**
* Changes the case of the input string
*
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return string
* @throws Exception
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$value = $arguments['value'];
$mode = $arguments['mode'];
if ($value === null) {
$value = $renderChildrenClosure();
}
switch ($mode) {
case self::CASE_LOWER:
$output = mb_strtolower($value, 'utf-8');
break;
case self::CASE_UPPER:
$output = mb_strtoupper($value, 'utf-8');
break;
case self::CASE_CAPITAL:
$firstChar = mb_substr($value, 0, 1, 'utf-8');
$firstChar = mb_strtoupper($firstChar, 'utf-8');
$remainder = mb_substr($value, 1, null, 'utf-8');
$output = $firstChar . $remainder;
break;
case self::CASE_UNCAPITAL:
$firstChar = mb_substr($value, 0, 1, 'utf-8');
$firstChar = mb_strtolower($firstChar, 'utf-8');
$remainder = mb_substr($value, 1, null, 'utf-8');
$output = $firstChar . $remainder;
break;
case self::CASE_CAPITAL_WORDS:
$output = mb_convert_case($value, MB_CASE_TITLE, 'utf-8');
break;
default:
throw new Exception('The case mode "' . $mode . '" supplied to Fluid\'s format.case ViewHelper is not supported.', 1358349150);
}
return $output;
}
}
@@ -0,0 +1,141 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Format;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* Use this ViewHelper to crop the text between its opening and closing tags.
*
* Examples
* ========
*
* Defaults
* --------
*
* ::
*
* <f:format.crop maxCharacters="10">
* This is some very long text
* </f:format.crop>
*
* ``This is…``
*
* Custom suffix
* -------------
*
* ::
*
* <f:format.crop maxCharacters="17" append="&nbsp;[more]">
* This is some very long text
* </f:format.crop>
*
* ``This is some&nbsp;[more]``
*
* Don't respect word boundaries
* -----------------------------
*
* ::
*
* <f:format.crop maxCharacters="10" respectWordBoundaries="false">
* This is some very long text
* </f:format.crop>
*
* ``This is so…``
*
* Don't respect HTML tags
* -----------------------
*
* ::
*
* <f:format.crop maxCharacters="28" respectWordBoundaries="false" respectHtml="false">
* This is some text with <strong>HTML</strong> tags
* </f:format.crop>
*
* ``This is some text with <stro``
*
* Inline notation
* ---------------
*
* ::
*
* {someLongText -> f:format.crop(maxCharacters: 10)}
*
* ``someLongText cropped after 10 characters…``
* Depending on the value of ``{someLongText}``.
*/
class CropViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* The output may contain HTML and can not be escaped.
*
* @var bool
*/
protected $escapeOutput = false;
/**
* Initialize arguments.
*
* @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
*/
public function initializeArguments()
{
$this->registerArgument('maxCharacters', 'int', 'Place where to truncate the string', true);
$this->registerArgument('append', 'string', 'What to append, if truncation happened', false, '&hellip;');
$this->registerArgument('respectWordBoundaries', 'bool', 'If TRUE and division is in the middle of a word, the remains of that word is removed.', false, true);
$this->registerArgument('respectHtml', 'bool', 'If TRUE the cropped string will respect HTML tags and entities. Technically that means, that cropHTML() is called rather than crop()', false, true);
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
*
* @return string
* @throws \InvalidArgumentException
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$maxCharacters = $arguments['maxCharacters'];
$append = $arguments['append'];
$respectWordBoundaries = $arguments['respectWordBoundaries'];
$respectHtml = $arguments['respectHtml'];
$stringToTruncate = $renderChildrenClosure();
// Even if we are in extbase/fluid context here, we're switching to a casual class of the framework here
// that has no dependency injection and other stuff. Therefor it is ok to use makeInstance instead of
// the ObjectManager here directly for additional performance
// Additionally, it would be possible to retrieve the "current" content object via ConfigurationManager->getContentObject(),
// but both crop() and cropHTML() are "nearly" static and do not depend on current content object settings, so
// it is safe to use a fresh instance here directly.
/** @var ContentObjectRenderer $contentObject */
$contentObject = GeneralUtility::makeInstance(ContentObjectRenderer::class);
if ($respectHtml) {
$content = $contentObject->cropHTML($stringToTruncate, $maxCharacters . '|' . $append . '|' . $respectWordBoundaries);
} else {
$content = $contentObject->crop($stringToTruncate, $maxCharacters . '|' . $append . '|' . $respectWordBoundaries);
}
return $content;
}
}
@@ -0,0 +1,145 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Format;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* Formats a given float to a currency representation.
*
* Examples
* ========
*
* Defaults
* --------
*
* ::
*
* <f:format.currency>123.456</f:format.currency>
*
* Output::
*
* 123,46
*
* All parameters
* --------------
*
* ::
*
* <f:format.currency decimalSeparator="." thousandsSeparator="," decimals="2"
* currencySign="$" prependCurrency="true" separateCurrency="false"
* >
* 54321
* </f:format.currency>
*
* Output::
*
* $54,321.00
*
* Inline notation
* ---------------
*
* ::
*
* {someNumber -> f:format.currency(thousandsSeparator: ',', currencySign: 'EUR')}
*
* Output::
*
* 54,321,00 EUR
*
* Depending on the value of ``{someNumber}``.
*
* Use dash for decimals without value
* -----------------------------------
*
* ::
*
* <f:format.currency useDash="true">123.00</f:format.currency>
*
* Output::
*
* 123,-
*/
class CurrencyViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* Output is escaped already. We must not escape children, to avoid double encoding.
*
* @var bool
*/
protected $escapeChildren = false;
/**
* Initialize arguments.
*
* @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
*/
public function initializeArguments()
{
$this->registerArgument('currencySign', 'string', 'The currency sign, eg $ or €.', false, '');
$this->registerArgument('decimalSeparator', 'string', 'The separator for the decimal point.', false, ',');
$this->registerArgument('thousandsSeparator', 'string', 'The thousands separator.', false, '.');
$this->registerArgument('prependCurrency', 'bool', 'Select if the currency sign should be prepended', false, false);
$this->registerArgument('separateCurrency', 'bool', 'Separate the currency sign from the number by a single space, defaults to true due to backwards compatibility', false, true);
$this->registerArgument('decimals', 'int', 'Set decimals places.', false, 2);
$this->registerArgument('useDash', 'bool', 'Use the dash instead of decimal 00', false, false);
}
/**
* Formats a float to currency formatting
*
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
*
* @return string the formatted amount
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$currencySign = $arguments['currencySign'];
$decimalSeparator = $arguments['decimalSeparator'];
$thousandsSeparator = $arguments['thousandsSeparator'];
$prependCurrency = $arguments['prependCurrency'];
$separateCurrency = $arguments['separateCurrency'];
$decimals = $arguments['decimals'];
$useDash = $arguments['useDash'];
$floatToFormat = $renderChildrenClosure();
if (empty($floatToFormat)) {
$floatToFormat = 0.0;
} else {
$floatToFormat = (float)$floatToFormat;
}
$output = number_format($floatToFormat, $decimals, $decimalSeparator, $thousandsSeparator);
if ($useDash && $floatToFormat === floor($floatToFormat)) {
$output = explode($decimalSeparator, $output)[0] . $decimalSeparator . '—';
}
if ($currencySign !== '') {
$currencySeparator = $separateCurrency ? ' ' : '';
if ($prependCurrency === true) {
$output = $currencySign . $currencySeparator . $output;
} else {
$output = $output . $currencySeparator . $currencySign;
}
}
return $output;
}
}
@@ -0,0 +1,171 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Format;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\MathUtility;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Exception;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithContentArgumentAndRenderStatic;
/**
* Formats an object implementing :php:`\DateTimeInterface`.
*
* Examples
* ========
*
* Defaults
* --------
*
* ::
*
* <f:format.date>{dateObject}</f:format.date>
*
* ``1980-12-13``
* Depending on the current date.
*
* Custom date format
* ------------------
*
* ::
*
* <f:format.date format="H:i">{dateObject}</f:format.date>
*
* ``01:23``
* Depending on the current time.
*
* Relative date with given time
* -----------------------------
*
* ::
*
* <f:format.date format="Y" base="{dateObject}">-1 year</f:format.date>
*
* ``2016``
* Assuming dateObject is in 2017.
*
* strtotime string
* ----------------
*
* ::
*
* <f:format.date format="d.m.Y - H:i:s">+1 week 2 days 4 hours 2 seconds</f:format.date>
*
* ``13.12.1980 - 21:03:42``
* Depending on the current time, see https://www.php.net/manual/function.strtotime.php.
*
* Localized dates using strftime date format
* ------------------------------------------
*
* ::
*
* <f:format.date format="%d. %B %Y">{dateObject}</f:format.date>
*
* ``13. Dezember 1980``
* Depending on the current date and defined locale. In the example you see the 1980-12-13 in a german locale.
*
* Inline notation
* ---------------
*
* ::
*
* {f:format.date(date: dateObject)}
*
* ``1980-12-13``
* Depending on the value of ``{dateObject}``.
*
* Inline notation (2nd variant)
* -----------------------------
*
* ::
*
* {dateObject -> f:format.date()}
*
* ``1980-12-13``
* Depending on the value of ``{dateObject}``.
*/
class DateViewHelper extends AbstractViewHelper
{
use CompileWithContentArgumentAndRenderStatic;
/**
* Needed as child node's output can return a DateTime object which can't be escaped
*
* @var bool
*/
protected $escapeChildren = false;
/**
* Initialize arguments
*/
public function initializeArguments()
{
$this->registerArgument('date', 'mixed', 'Either an object implementing DateTimeInterface or a string that is accepted by DateTime constructor');
$this->registerArgument('format', 'string', 'Format String which is taken to format the Date/Time', false, '');
$this->registerArgument('base', 'mixed', 'A base time (an object implementing DateTimeInterface or a string) used if $date is a relative date specification. Defaults to current time.');
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
*
* @return string
* @throws Exception
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$format = $arguments['format'];
$base = $arguments['base'] ?? GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('date', 'timestamp');
if (is_string($base)) {
$base = trim($base);
}
if ($format === '') {
$format = $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] ?: 'Y-m-d';
}
$date = $renderChildrenClosure();
if ($date === null) {
return '';
}
if (is_string($date)) {
$date = trim($date);
}
if ($date === '') {
$date = 'now';
}
if (!$date instanceof \DateTimeInterface) {
try {
$base = $base instanceof \DateTimeInterface ? $base->format('U') : strtotime((MathUtility::canBeInterpretedAsInteger($base) ? '@' : '') . $base);
$dateTimestamp = strtotime((MathUtility::canBeInterpretedAsInteger($date) ? '@' : '') . $date, $base);
$date = new \DateTime('@' . $dateTimestamp);
$date->setTimezone(new \DateTimeZone(date_default_timezone_get()));
} catch (\Exception $exception) {
throw new Exception('"' . $date . '" could not be parsed by \DateTime constructor: ' . $exception->getMessage(), 1241722579);
}
}
if (strpos($format, '%') !== false) {
return strftime($format, $date->format('U'));
}
return $date->format($format);
}
}
@@ -0,0 +1,149 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Format;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* Renders a string by passing it to a TYPO3 `parseFunc`_.
* You can either specify a path to the TypoScript setting or set the `parseFunc`_ options directly.
* By default :ts:`lib.parseFunc_RTE` is used to parse the string.
*
* Examples
* ========
*
* Default parameters
* ------------------
*
* ::
*
* <f:format.html>foo <b>bar</b>. Some <LINK 1>link</LINK>.</f:format.html>
*
* Output::
*
* <p class="bodytext">foo <b>bar</b>. Some <a href="index.php?id=1" >link</a>.</p>
*
* Depending on TYPO3 setup.
*
* Custom parseFunc
* ----------------
*
* ::
*
* <f:format.html parseFuncTSPath="lib.parseFunc">foo <b>bar</b>. Some <LINK 1>link</LINK>.</f:format.html>
*
* Output::
*
* foo <b>bar</b>. Some <a href="index.php?id=1" >link</a>.
*
* Inline notation
* ---------------
*
* ::
*
* {someText -> f:format.html(parseFuncTSPath: 'lib.parseFunc')}
*
* Output::
*
* foo <b>bar</b>. Some <a href="index.php?id=1" >link</a>.
*
* .. _parseFunc: https://docs.typo3.org/m/typo3/reference-typoscript/master/en-us/Functions/Parsefunc.html
*/
class HtmlViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* @var \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController contains a backup of the current $GLOBALS['TSFE'] if used in BE mode
*/
protected static $tsfeBackup;
/**
* Children must not be escaped, to be able to pass {bodytext} directly to it
*
* @var bool
*/
protected $escapeChildren = false;
/**
* Plain HTML should be returned, no output escaping allowed
*
* @var bool
*/
protected $escapeOutput = false;
/**
* Initialize arguments.
*
* @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
*/
public function initializeArguments()
{
$this->registerArgument('parseFuncTSPath', 'string', 'Path to TypoScript parseFunc setup', false, 'lib.parseFunc_RTE');
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
*
* @return string the parsed string.
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$parseFuncTSPath = $arguments['parseFuncTSPath'];
if (TYPO3_MODE === 'BE') {
self::simulateFrontendEnvironment();
}
$value = $renderChildrenClosure();
$contentObject = GeneralUtility::makeInstance(ContentObjectRenderer::class);
$contentObject->start([]);
$content = $contentObject->parseFunc($value, [], '< ' . $parseFuncTSPath);
if (TYPO3_MODE === 'BE') {
self::resetFrontendEnvironment();
}
return $content;
}
/**
* Copies the specified parseFunc configuration to $GLOBALS['TSFE']->tmpl->setup in Backend mode
* This somewhat hacky work around is currently needed because the parseFunc() function of \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer relies on those variables to be set
*/
protected static function simulateFrontendEnvironment()
{
self::$tsfeBackup = $GLOBALS['TSFE'] ?? null;
$GLOBALS['TSFE'] = new \stdClass();
$GLOBALS['TSFE']->tmpl = new \stdClass();
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
$configurationManager = $objectManager->get(ConfigurationManagerInterface::class);
$GLOBALS['TSFE']->tmpl->setup = $configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FULL_TYPOSCRIPT);
}
/**
* Resets $GLOBALS['TSFE'] if it was previously changed by simulateFrontendEnvironment()
*
* @see simulateFrontendEnvironment()
*/
protected static function resetFrontendEnvironment()
{
$GLOBALS['TSFE'] = self::$tsfeBackup;
}
}
@@ -0,0 +1,103 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Format;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithContentArgumentAndRenderStatic;
/**
* Applies :php:`html_entity_decode()` to a value.
* See https://www.php.net/html_entity_decode.
*
* Examples
* ========
*
* Default notation
* ----------------
*
* ::
*
* <f:format.htmlentitiesDecode>{text}</f:format.htmlentitiesDecode>
*
* Text containing the following escaped signs: ``&amp;`` ``&quot;`` ``&#039;`` ``&lt;`` ``&gt;``, will be processed by :php:`html_entity_decode()`.
* These will result in: ``&`` ``"`` ``'`` ``<`` ``>``.
*
* Inline notation
* ---------------
*
* ::
*
* {text -> f:format.htmlentitiesDecode(encoding: 'ISO-8859-1')}
*
* Text containing the following escaped signs: ``&amp;`` ``&quot;`` ``&#039;`` ``&lt;`` ``&gt;``, will be processed by :php:`html_entity_decode()`.
* These will result in: ``&`` ``"`` ``'`` ``<`` ``>``.
*
* But encoded as ISO-8859-1.
*/
class HtmlentitiesDecodeViewHelper extends AbstractEncodingViewHelper
{
use CompileWithContentArgumentAndRenderStatic;
/**
* We accept value and children interchangeably, thus we must disable children escaping.
*
* @var bool
*/
protected $escapeChildren = false;
/**
* If we decode, we must not encode again after that.
*
* @var bool
*/
protected $escapeOutput = false;
/**
* Initialize ViewHelper arguments
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('value', 'string', 'string to format');
$this->registerArgument('keepQuotes', 'bool', 'If TRUE, single and double quotes won\'t be replaced (sets ENT_NOQUOTES flag).', false, false);
$this->registerArgument('encoding', 'string', '');
}
/**
* Converts all HTML entities to their applicable characters as needed using PHPs html_entity_decode() function.
*
* @see https://www.php.net/html_entity_decode
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
*
* @return string
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$value = $renderChildrenClosure();
$encoding = $arguments['encoding'];
$keepQuotes = $arguments['keepQuotes'];
if (!is_string($value)) {
return $value;
}
if ($encoding === null) {
$encoding = self::resolveDefaultEncoding();
}
$flags = $keepQuotes ? ENT_NOQUOTES : ENT_COMPAT;
return html_entity_decode($value, $flags, $encoding);
}
}
@@ -0,0 +1,103 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Format;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithContentArgumentAndRenderStatic;
/**
* Applies :php:`htmlentities()` escaping to a value.
* See https://www.php.net/manual/function.htmlentities.php.
*
* Examples
* ========
*
* Default notation
* ----------------
*
* ::
*
* <f:format.htmlentities>{text}</f:format.htmlentities>
*
* Text containing the following signs ``&`` ``"`` ``'`` ``<`` ``>`` will be processed by :php:`htmlentities()`.
* These will result in: ``&amp;`` ``&quot;`` ``&#039;`` ``&lt;`` ``&gt;``.
*
* Inline notation
* ---------------
*
* ::
*
* {text -> f:format.htmlentities(encoding: 'ISO-8859-1')}
*
* Text containing the following signs ``&`` ``"`` ``'`` ``<`` ``>`` will be processed by :php:`htmlentities()`.
* These will result in: ``&amp;`` ``&quot;`` ``&#039;`` ``&lt;`` ``&gt;``.
*
* But encoded as ISO-8859-1.
*/
class HtmlentitiesViewHelper extends AbstractEncodingViewHelper
{
use CompileWithContentArgumentAndRenderStatic;
/**
* Output gets encoded by this viewhelper
*
* @var bool
*/
protected $escapeOutput = false;
/**
* This prevents double encoding as the whole output gets encoded at the end
*
* @var bool
*/
protected $escapeChildren = false;
/**
* Initialize ViewHelper arguments
*/
public function initializeArguments()
{
$this->registerArgument('value', 'string', 'string to format');
$this->registerArgument('keepQuotes', 'bool', 'If TRUE, single and double quotes won\'t be replaced (sets ENT_NOQUOTES flag).', false, false);
$this->registerArgument('encoding', 'string', '');
$this->registerArgument('doubleEncode', 'bool', 'If FALSE existing html entities won\'t be encoded, the default is to convert everything.', false, true);
}
/**
* Escapes special characters with their escaped counterparts as needed using PHPs htmlentities() function.
*
* @see https://www.php.net/manual/function.htmlentities.php
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return mixed
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$value = $renderChildrenClosure();
$encoding = $arguments['encoding'];
$keepQuotes = $arguments['keepQuotes'];
$doubleEncode = $arguments['doubleEncode'];
if (!is_string($value) && !(is_object($value) && method_exists($value, '__toString'))) {
return $value;
}
if ($encoding === null) {
$encoding = self::resolveDefaultEncoding();
}
$flags = $keepQuotes ? ENT_NOQUOTES : ENT_QUOTES;
return htmlentities((string)$value, $flags, $encoding, $doubleEncode);
}
}
@@ -0,0 +1,100 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Format;
/*
* This file is part of the TYPO3 CMS project.
*
* It originated from the Neos.Form package (www.neos.io)
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithContentArgumentAndRenderStatic;
/**
* Wrapper for PHPs :php:`json_encode` function.
* See https://www.php.net/manual/function.json-encode.php.
*
* Examples
* ========
*
* Encoding a view variable
* ------------------------
*
* ::
*
* {someArray -> f:format.json()}
*
* ``["array","values"]``
* Depending on the value of ``{someArray}``.
*
* Associative array
* -----------------
*
* ::
*
* {f:format.json(value: {foo: 'bar', bar: 'baz'})}
*
* ``{"foo":"bar","bar":"baz"}``
*
* Non associative array with forced object
* ----------------------------------------
*
* ::
*
* {f:format.json(value: {0: 'bar', 1: 'baz'}, forceObject: true)}
*
* ``{"0":"bar","1":"baz"}``
*/
class JsonViewHelper extends AbstractViewHelper
{
use CompileWithContentArgumentAndRenderStatic;
/**
* @var bool
*/
protected $escapeChildren = false;
/**
* Initialize arguments
*/
public function initializeArguments()
{
$this->registerArgument('value', 'mixed', 'The incoming data to convert, or null if VH children should be used');
$this->registerArgument('forceObject', 'bool', 'Outputs an JSON object rather than an array', false, false);
}
/**
* Applies json_encode() on the specified value.
*
* Outputs content with its JSON representation. To prevent issues in HTML context, occurrences
* of greater-than or less-than characters are converted to their hexadecimal representations.
*
* If $forceObject is TRUE a JSON object is outputted even if the value is a non-associative array
* Example: array('foo', 'bar') as input will not be ["foo","bar"] but {"0":"foo","1":"bar"}
*
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @see https://www.php.net/manual/function.json-encode.php
* @return string
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$value = $renderChildrenClosure();
$options = JSON_HEX_TAG;
if ($arguments['forceObject'] !== false) {
$options = $options | JSON_FORCE_OBJECT;
}
return json_encode($value, $options);
}
}
@@ -0,0 +1,77 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Format;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithContentArgumentAndRenderStatic;
/**
* Wrapper for PHPs :php:`nl2br` function.
* See https://www.php.net/manual/function.nl2br.php.
*
* Examples
* ========
*
* Default
* -------
*
* ::
*
* <f:format.nl2br>{text_with_linebreaks}</f:format.nl2br>
*
* Text with line breaks replaced by ``<br />``
*
* Inline notation
* ---------------
*
* ::
*
* {text_with_linebreaks -> f:format.nl2br()}
*
* Text with line breaks replaced by ``<br />``
*/
class Nl2brViewHelper extends AbstractViewHelper
{
use CompileWithContentArgumentAndRenderStatic;
/**
* @var bool
*/
protected $escapeOutput = false;
/**
* Initialize arguments.
*
* @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
*/
public function initializeArguments()
{
$this->registerArgument('value', 'string', 'string to format');
}
/**
* Applies nl2br() on the specified value.
*
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param \TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface $renderingContext
* @return string
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
return nl2br($renderChildrenClosure());
}
}
@@ -0,0 +1,90 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Format;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* Formats a number with custom precision, decimal point and grouped thousands.
* See https://www.php.net/manual/function.number-format.php.
*
* Examples
* ========
*
* Defaults
* --------
*
* ::
*
* <f:format.number>423423.234</f:format.number>
*
* ``423,423.20``
*
* With all parameters
* -------------------
*
* ::
*
* <f:format.number decimals="1" decimalSeparator="," thousandsSeparator=".">
* 423423.234
* </f:format.number>
*
* ``423.423,2``
*/
class NumberViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* Output is escaped already. We must not escape children, to avoid double encoding.
*
* @var bool
*/
protected $escapeChildren = false;
/**
* Initialize arguments.
*
* @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
*/
public function initializeArguments()
{
$this->registerArgument('decimals', 'int', 'The number of digits after the decimal point', false, '2');
$this->registerArgument('decimalSeparator', 'string', 'The decimal point character', false, '.');
$this->registerArgument('thousandsSeparator', 'string', 'The character for grouping the thousand digits', false, ',');
}
/**
* Format the numeric value as a number with grouped thousands, decimal point and
* precision.
*
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
*
* @return string The formatted number
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$decimals = $arguments['decimals'];
$decimalSeparator = $arguments['decimalSeparator'];
$thousandsSeparator = $arguments['thousandsSeparator'];
$stringToFormat = $renderChildrenClosure();
return number_format((float)$stringToFormat, $decimals, $decimalSeparator, $thousandsSeparator);
}
}
@@ -0,0 +1,106 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Format;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithContentArgumentAndRenderStatic;
/**
* Formats a string using PHPs :php:`str_pad` function.
* See https://www.php.net/manual/function.str_pad.php.
*
* Examples
* ========
*
* Defaults
* --------
*
* ::
*
* <f:format.padding padLength="10">TYPO3</f:format.padding>
*
* Output::
*
* TYPO3␠␠␠␠␠
*
* ``TYPO3␠␠␠␠␠``
*
* Specify padding string
* ----------------------
*
* ::
*
* <f:format.padding padLength="10" padString="-=">TYPO3</f:format.padding>
*
* ``TYPO3-=-=-``
*
* Specify padding type
* --------------------
*
* ::
*
* <f:format.padding padLength="10" padString="-" padType="both">TYPO3</f:format.padding>
*
* ``--TYPO3---``
*/
class PaddingViewHelper extends AbstractViewHelper
{
use CompileWithContentArgumentAndRenderStatic;
/**
* Output is escaped already. We must not escape children, to avoid double encoding.
*
* @var bool
*/
protected $escapeChildren = false;
/**
* Initialize arguments.
*
* @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
*/
public function initializeArguments()
{
$this->registerArgument('value', 'string', 'string to format');
$this->registerArgument('padLength', 'int', 'Length of the resulting string. If the value of pad_length is negative or less than the length of the input string, no padding takes place.', true);
$this->registerArgument('padString', 'string', 'The padding string', false, ' ');
$this->registerArgument('padType', 'string', 'Append the padding at this site (Possible values: right,left,both. Default: right)', false, 'right');
}
/**
* Pad a string to a certain length with another string
*
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param \TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface $renderingContext
* @return string The formatted value
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$value = $renderChildrenClosure();
$padTypes = [
'left' => STR_PAD_LEFT,
'right' => STR_PAD_RIGHT,
'both' => STR_PAD_BOTH
];
$padType = $arguments['padType'];
if (!isset($padTypes[$padType])) {
$padType = 'right';
}
return str_pad($value, $arguments['padLength'], $arguments['padString'], $padTypes[$padType]);
}
}
@@ -0,0 +1,122 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Format;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithContentArgumentAndRenderStatic;
/**
* Removes tags from the given string (applying PHPs :php:`strip_tags()` function)
* See https://www.php.net/manual/function.strip-tags.php.
*
* Examples
* ========
*
* Default notation
* ----------------
*
* ::
*
* <f:format.stripTags>Some Text with <b>Tags</b> and an &Uuml;mlaut.</f:format.stripTags>
*
* Some Text with Tags and an &Uuml;mlaut. :php:`strip_tags()` applied.
*
* .. note::
* Encoded entities are not decoded.
*
* Default notation with allowedTags
* ---------------------------------
*
* ::
*
* <f:format.stripTags allowedTags="<p><span><div><script>">
* <p>paragraph</p><span>span</span><div>divider</div><iframe>iframe</iframe><script>script</script>
* </f:format.stripTags>
*
* Output::
*
* <p>paragraph</p><span>span</span><div>divider</div>iframe<script>script</script>
*
* Inline notation
* ---------------
*
* ::
*
* {text -> f:format.stripTags()}
*
* Text without tags :php:`strip_tags()` applied.
*
* Inline notation with allowedTags
* --------------------------------
*
* ::
*
* {text -> f:format.stripTags(allowedTags: "<p><span><div><script>")}
*
* Text with p, span, div and script Tags inside, all other tags are removed.
*/
class StripTagsViewHelper extends AbstractViewHelper
{
use CompileWithContentArgumentAndRenderStatic;
/**
* No output escaping as some tags may be allowed
*
* @var bool
*/
protected $escapeOutput = false;
/**
* Initialize ViewHelper arguments
*
* @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
*/
public function initializeArguments()
{
$this->registerArgument('value', 'string', 'string to format');
$this->registerArgument('allowedTags', 'string', 'Optional string of allowed tags as required by PHPs strip_tags() f
unction');
}
/**
* To ensure all tags are removed, child node's output must not be escaped
*
* @var bool
*/
protected $escapeChildren = false;
/**
* Applies strip_tags() on the specified value.
*
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param \TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface $renderingContext
* @see https://www.php.net/manual/function.strip-tags.php
* @return string
*/
public static function renderStatic(
array $arguments,
\Closure $renderChildrenClosure,
RenderingContextInterface $renderingContext
) {
$value = $renderChildrenClosure();
$allowedTags = $arguments['allowedTags'];
if (!is_string($value) && !(is_object($value) && method_exists($value, '__toString'))) {
return $value;
}
return strip_tags((string)$value, $allowedTags);
}
}
@@ -0,0 +1,90 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Format;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Exception;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithContentArgumentAndRenderStatic;
/**
* Encodes the given string according to http://www.faqs.org/rfcs/rfc3986.html
* Applying PHPs :php:`rawurlencode()` function.
* See https://www.php.net/manual/function.rawurlencode.php.
*
* .. note::
* The output is not escaped. You may have to ensure proper escaping on your own.
*
* Examples
* ========
*
* Default notation
* ----------------
*
* ::
*
* <f:format.rawurlencode>foo @+%/</f:format.rawurlencode>
*
* ``foo%20%40%2B%25%2F`` :php:`rawurlencode()` applied.
*
* Inline notation
* ---------------
*
* ::
*
* {text -> f:format.urlencode()}
*
* Url encoded text :php:`rawurlencode()` applied.
*/
class UrlencodeViewHelper extends AbstractViewHelper
{
use CompileWithContentArgumentAndRenderStatic;
/**
* Output is escaped already. We must not escape children, to avoid double encoding.
*
* @var bool
*/
protected $escapeChildren = false;
/**
* Initialize ViewHelper arguments
*
* @throws Exception
*/
public function initializeArguments()
{
$this->registerArgument('value', 'string', 'string to format');
}
/**
* Escapes special characters with their escaped counterparts as needed using PHPs rawurlencode() function.
*
* @see https://www.php.net/manual/function.rawurlencode.php
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
*
* @return mixed
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$value = $renderChildrenClosure();
if (!is_string($value) && !(is_object($value) && method_exists($value, '__toString'))) {
return $value;
}
return rawurlencode((string)$value);
}
}
@@ -0,0 +1,204 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers;
/* *
* This script is part of the TYPO3 project - inspiring people to share! *
* *
* TYPO3 is free software; you can redistribute it and/or modify it under *
* the terms of the GNU General Public License version 2 as published by *
* the Free Software Foundation. *
* *
* This script is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- *
* TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General *
* Public License for more details. *
* */
use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection;
use TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Exception;
/**
* Resizes a given image (if required) and renders the respective img tag.
*
* Examples
* ========
*
* Default
* -------
*
* ::
*
* <f:image src="EXT:myext/Resources/Public/typo3_logo.png" alt="alt text" />
*
* Output in frontend::
*
* <img alt="alt text" src="typo3conf/ext/myext/Resources/Public/typo3_logo.png" width="396" height="375" />
*
* or in backend::
*
* <img alt="alt text" src="../typo3conf/ext/viewhelpertest/Resources/Public/typo3_logo.png" width="396" height="375" />
*
* Image Object
* ------------
*
* ::
*
* <f:image image="{imageObject}" />
*
* Output::
*
* <img alt="alt set in image record" src="fileadmin/_processed_/323223424.png" width="396" height="375" />
*
* Inline notation
* ---------------
*
* ::
*
* {f:image(src: 'EXT:viewhelpertest/Resources/Public/typo3_logo.png', alt: 'alt text', minWidth: 30, maxWidth: 40)}
*
* Output::
*
* <img alt="alt text" src="../typo3temp/assets/images/f13d79a526.png" width="40" height="38" />
*
* Depending on your TYPO3s encryption key.
*
* Other resource type (e.g. PDF)
* ------------------------------
*
* ::
*
* <f:image src="fileadmin/user_upload/example.pdf" alt="foo" />
*
* If your graphics processing library is set up correctly then it will output a thumbnail of the first page of your PDF document:
* ``<img src="fileadmin/_processed_/1/2/csm_example_aabbcc112233.gif" width="200" height="284" alt="foo">``
*
* Non-existent image
* ------------------
*
* ::
*
* <f:image src="NonExistingImage.png" alt="foo" />
*
* ``Could not get image resource for "NonExistingImage.png".``
*/
class ImageViewHelper extends AbstractTagBasedViewHelper
{
/**
* @var string
*/
protected $tagName = 'img';
/**
* @var \TYPO3\CMS\Extbase\Service\ImageService
*/
protected $imageService;
/**
* @param \TYPO3\CMS\Extbase\Service\ImageService $imageService
*/
public function injectImageService(\TYPO3\CMS\Extbase\Service\ImageService $imageService)
{
$this->imageService = $imageService;
}
/**
* Initialize arguments.
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerUniversalTagAttributes();
$this->registerTagAttribute('alt', 'string', 'Specifies an alternate text for an image', false);
$this->registerTagAttribute('ismap', 'string', 'Specifies an image as a server-side image-map. Rarely used. Look at usemap instead', false);
$this->registerTagAttribute('longdesc', 'string', 'Specifies the URL to a document that contains a long description of an image', false);
$this->registerTagAttribute('usemap', 'string', 'Specifies an image as a client-side image-map', false);
$this->registerArgument('src', 'string', 'a path to a file, a combined FAL identifier or an uid (int). If $treatIdAsReference is set, the integer is considered the uid of the sys_file_reference record. If you already got a FAL object, consider using the $image parameter instead');
$this->registerArgument('treatIdAsReference', 'bool', 'given src argument is a sys_file_reference record');
$this->registerArgument('image', 'object', 'a FAL object');
$this->registerArgument('crop', 'string|bool', 'overrule cropping of image (setting to FALSE disables the cropping set in FileReference)');
$this->registerArgument('cropVariant', 'string', 'select a cropping variant, in case multiple croppings have been specified or stored in FileReference', false, 'default');
$this->registerArgument('width', 'string', 'width of the image. This can be a numeric value representing the fixed width of the image in pixels. But you can also perform simple calculations by adding "m" or "c" to the value. See imgResource.width for possible options.');
$this->registerArgument('height', 'string', 'height of the image. This can be a numeric value representing the fixed height of the image in pixels. But you can also perform simple calculations by adding "m" or "c" to the value. See imgResource.width for possible options.');
$this->registerArgument('minWidth', 'int', 'minimum width of the image');
$this->registerArgument('minHeight', 'int', 'minimum height of the image');
$this->registerArgument('maxWidth', 'int', 'maximum width of the image');
$this->registerArgument('maxHeight', 'int', 'maximum height of the image');
$this->registerArgument('absolute', 'bool', 'Force absolute URL', false, false);
}
/**
* Resizes a given image (if required) and renders the respective img tag
*
* @see https://docs.typo3.org/typo3cms/TyposcriptReference/ContentObjects/Image/
*
* @throws Exception
* @return string Rendered tag
*/
public function render()
{
if (($this->arguments['src'] === null && $this->arguments['image'] === null) || ($this->arguments['src'] !== null && $this->arguments['image'] !== null)) {
throw new Exception('You must either specify a string src or a File object.', 1382284106);
}
try {
$image = $this->imageService->getImage($this->arguments['src'], $this->arguments['image'], $this->arguments['treatIdAsReference']);
$cropString = $this->arguments['crop'];
if ($cropString === null && $image->hasProperty('crop') && $image->getProperty('crop')) {
$cropString = $image->getProperty('crop');
}
$cropVariantCollection = CropVariantCollection::create((string)$cropString);
$cropVariant = $this->arguments['cropVariant'] ?: 'default';
$cropArea = $cropVariantCollection->getCropArea($cropVariant);
$processingInstructions = [
'width' => $this->arguments['width'],
'height' => $this->arguments['height'],
'minWidth' => $this->arguments['minWidth'],
'minHeight' => $this->arguments['minHeight'],
'maxWidth' => $this->arguments['maxWidth'],
'maxHeight' => $this->arguments['maxHeight'],
'crop' => $cropArea->isEmpty() ? null : $cropArea->makeAbsoluteBasedOnFile($image),
];
$processedImage = $this->imageService->applyProcessingInstructions($image, $processingInstructions);
$imageUri = $this->imageService->getImageUri($processedImage, $this->arguments['absolute']);
if (!$this->tag->hasAttribute('data-focus-area')) {
$focusArea = $cropVariantCollection->getFocusArea($cropVariant);
if (!$focusArea->isEmpty()) {
$this->tag->addAttribute('data-focus-area', $focusArea->makeAbsoluteBasedOnFile($image));
}
}
$this->tag->addAttribute('src', $imageUri);
$this->tag->addAttribute('width', $processedImage->getProperty('width'));
$this->tag->addAttribute('height', $processedImage->getProperty('height'));
$alt = $image->getProperty('alternative');
$title = $image->getProperty('title');
// The alt-attribute is mandatory to have valid html-code, therefore add it even if it is empty
if (empty($this->arguments['alt'])) {
$this->tag->addAttribute('alt', $alt);
}
if (empty($this->arguments['title']) && $title) {
$this->tag->addAttribute('title', $title);
}
} catch (ResourceDoesNotExistException $e) {
// thrown if file does not exist
throw new Exception($e->getMessage(), 1509741911, $e);
} catch (\UnexpectedValueException $e) {
// thrown if a file has been replaced with a folder
throw new Exception($e->getMessage(), 1509741912, $e);
} catch (\RuntimeException $e) {
// RuntimeException thrown if a file is outside of a storage
throw new Exception($e->getMessage(), 1509741913, $e);
} catch (\InvalidArgumentException $e) {
// thrown if file storage does not exist
throw new Exception($e->getMessage(), 1509741914, $e);
}
return $this->tag->render();
}
}
@@ -0,0 +1,118 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Link;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
/**
* A ViewHelper for creating links to extbase actions.
*
* Examples
* ========
*
* link to the show-action of the current controller::
*
* <f:link.action action="show">action link</f:link.action>
*
* Output::
*
* <a href="index.php?id=123&tx_myextension_plugin[action]=show&tx_myextension_plugin[controller]=Standard&cHash=xyz">action link</a>
*
* Depending on the current page and your TypoScript configuration.
*/
class ActionViewHelper extends AbstractTagBasedViewHelper
{
/**
* @var string
*/
protected $tagName = 'a';
/**
* Arguments initialization
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerUniversalTagAttributes();
$this->registerTagAttribute('name', 'string', 'Specifies the name of an anchor');
$this->registerTagAttribute('rel', 'string', 'Specifies the relationship between the current document and the linked document');
$this->registerTagAttribute('rev', 'string', 'Specifies the relationship between the linked document and the current document');
$this->registerTagAttribute('target', 'string', 'Specifies where to open the linked document');
$this->registerArgument('action', 'string', 'Target action');
$this->registerArgument('controller', 'string', 'Target controller. If NULL current controllerName is used');
$this->registerArgument('extensionName', 'string', 'Target Extension Name (without "tx_" prefix and no underscores). If NULL the current extension name is used');
$this->registerArgument('pluginName', 'string', 'Target plugin. If empty, the current plugin name is used');
$this->registerArgument('pageUid', 'int', 'Target page. See TypoLink destination');
$this->registerArgument('pageType', 'int', 'Type of the target page. See typolink.parameter');
$this->registerArgument('noCache', 'bool', 'Set this to disable caching for the target page. You should not need this.');
$this->registerArgument('noCacheHash', 'bool', 'Set this to suppress the cHash query parameter created by TypoLink. You should not need this.');
$this->registerArgument('section', 'string', 'The anchor to be added to the URI');
$this->registerArgument('format', 'string', 'The requested format, e.g. ".html');
$this->registerArgument('linkAccessRestrictedPages', 'bool', 'If set, links pointing to access restricted pages will still link to the page even though the page cannot be accessed.');
$this->registerArgument('additionalParams', 'array', 'Additional query parameters that won\'t be prefixed like $arguments (overrule $arguments)');
$this->registerArgument('absolute', 'bool', 'If set, the URI of the rendered link is absolute');
$this->registerArgument('addQueryString', 'bool', 'If set, the current query parameters will be kept in the URI');
$this->registerArgument('argumentsToBeExcludedFromQueryString', 'array', 'Arguments to be removed from the URI. Only active if $addQueryString = TRUE');
$this->registerArgument('addQueryStringMethod', 'string', 'Set which parameters will be kept. Only active if $addQueryString = TRUE');
$this->registerArgument('arguments', 'array', 'Arguments for the controller action, associative array');
}
/**
* @return string Rendered link
*/
public function render()
{
$action = $this->arguments['action'];
$controller = $this->arguments['controller'];
$extensionName = $this->arguments['extensionName'];
$pluginName = $this->arguments['pluginName'];
$pageUid = (int)$this->arguments['pageUid'] ?: null;
$pageType = (int)$this->arguments['pageType'];
$noCache = (bool)$this->arguments['noCache'];
$noCacheHash = (bool)$this->arguments['noCacheHash'];
$section = (string)$this->arguments['section'];
$format = (string)$this->arguments['format'];
$linkAccessRestrictedPages = (bool)$this->arguments['linkAccessRestrictedPages'];
$additionalParams = (array)$this->arguments['additionalParams'];
$absolute = (bool)$this->arguments['absolute'];
$addQueryString = (bool)$this->arguments['addQueryString'];
$argumentsToBeExcludedFromQueryString = (array)$this->arguments['argumentsToBeExcludedFromQueryString'];
$addQueryStringMethod = $this->arguments['addQueryStringMethod'];
$parameters = $this->arguments['arguments'];
$uriBuilder = $this->renderingContext->getControllerContext()->getUriBuilder();
$uri = $uriBuilder
->reset()
->setTargetPageUid($pageUid)
->setTargetPageType($pageType)
->setNoCache($noCache)
->setUseCacheHash(!$noCacheHash)
->setSection($section)
->setFormat($format)
->setLinkAccessRestrictedPages($linkAccessRestrictedPages)
->setArguments($additionalParams)
->setCreateAbsoluteUri($absolute)
->setAddQueryString($addQueryString)
->setArgumentsToBeExcludedFromQueryString($argumentsToBeExcludedFromQueryString)
->setAddQueryStringMethod($addQueryStringMethod)
->uriFor($action, $parameters, $controller, $extensionName, $pluginName);
if ($uri === '') {
return $this->renderChildren();
}
$this->tag->addAttribute('href', $uri);
$this->tag->setContent($this->renderChildren());
$this->tag->forceClosingTag(true);
return $this->tag->render();
}
}
@@ -0,0 +1,107 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Link;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
/**
* Email link ViewHelper.
* Generates an email link incorporating TYPO3s `spamProtectEmailAddresses`_ TypoScript setting.
*
* .. _spamProtectEmailAddresses: https://docs.typo3.org/m/typo3/reference-typoscript/master/en-us/Setup/Config/Index.html#spamprotectemailaddresses
*
* Examples
* ========
*
* Basic email link
* ----------------
*
* ::
*
* <f:link.email email="foo@bar.tld" />
*
* Output::
*
* <a href="javascript:linkTo_UnCryptMailto('ocknvq,hqqBdct0vnf');">foo(at)bar.tld</a>
*
* Depending on `spamProtectEmailAddresses`_ setting.
*
* Email link with custom linktext
* -------------------------------
*
* ::
*
* <f:link.email email="foo@bar.tld">some custom content</f:link.email>
*
* Output::
*
* <a href="javascript:linkTo_UnCryptMailto('ocknvq,hqqBdct0vnf');">some custom content</a>
*
* Depending on `spamProtectEmailAddresses`_ setting.
*/
class EmailViewHelper extends AbstractTagBasedViewHelper
{
/**
* @var string
*/
protected $tagName = 'a';
/**
* Arguments initialization
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('email', 'string', 'The email address to be turned into a link', true);
$this->registerUniversalTagAttributes();
$this->registerTagAttribute('name', 'string', 'Specifies the name of an anchor');
$this->registerTagAttribute('rel', 'string', 'Specifies the relationship between the current document and the linked document');
$this->registerTagAttribute('rev', 'string', 'Specifies the relationship between the linked document and the current document');
$this->registerTagAttribute('target', 'string', 'Specifies where to open the linked document');
}
/**
* @return string Rendered email link
*/
public function render()
{
$email = $this->arguments['email'];
if ($this->isFrontendAvailable()) {
list($linkHref, $linkText) = $GLOBALS['TSFE']->cObj->getMailTo($email, '');
$escapeSpecialCharacters = !isset($GLOBALS['TSFE']->spamProtectEmailAddresses) || $GLOBALS['TSFE']->spamProtectEmailAddresses !== 'ascii';
} else {
$linkHref = 'mailto:' . $email;
$linkText = htmlspecialchars($email);
$escapeSpecialCharacters = true;
}
$tagContent = $this->renderChildren();
if ($tagContent !== null) {
$linkText = $tagContent;
}
$this->tag->setContent($linkText);
$this->tag->addAttribute('href', $linkHref, $escapeSpecialCharacters);
$this->tag->forceClosingTag(true);
return $this->tag->render();
}
/**
* @return bool
*/
protected function isFrontendAvailable()
{
return TYPO3_MODE === 'FE';
}
}
@@ -0,0 +1,87 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Link;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
/**
* A ViewHelper for creating links to external targets.
*
* Examples
* ========
*
* Default
* -------
*
* ::
*
* <f:link.external uri="http://www.typo3.org" target="_blank">external link</f:link.external>
*
* Output::
*
* <a href="http://www.typo3.org" target="_blank">external link</a>
*
* Custom default scheme
* ---------------------
*
* ::
*
* <f:link.external uri="typo3.org" defaultScheme="ftp">external ftp link</f:link.external>
*
* Output::
*
* <a href="ftp://typo3.org">external ftp link</a>
*/
class ExternalViewHelper extends AbstractTagBasedViewHelper
{
/**
* @var string
*/
protected $tagName = 'a';
/**
* Initialize arguments
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('uri', 'string', 'The URI that will be put in the href attribute of the rendered link tag', true);
$this->registerArgument('defaultScheme', 'string', 'Scheme the href attribute will be prefixed with if specified $uri does not contain a scheme already', false, 'http');
$this->registerUniversalTagAttributes();
$this->registerTagAttribute('name', 'string', 'Specifies the name of an anchor');
$this->registerTagAttribute('rel', 'string', 'Specifies the relationship between the current document and the linked document');
$this->registerTagAttribute('rev', 'string', 'Specifies the relationship between the linked document and the current document');
$this->registerTagAttribute('target', 'string', 'Specifies where to open the linked document');
}
/**
* @return string Rendered link
*/
public function render()
{
$uri = $this->arguments['uri'];
$defaultScheme = $this->arguments['defaultScheme'];
$scheme = parse_url($uri, PHP_URL_SCHEME);
if ($scheme === null && $defaultScheme !== '') {
$uri = $defaultScheme . '://' . $uri;
}
$this->tag->addAttribute('href', $uri);
$this->tag->setContent($this->renderChildren());
$this->tag->forceClosingTag(true);
return $this->tag->render();
}
}
@@ -0,0 +1,133 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Link;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
/**
* A ViewHelper for creating links to TYPO3 pages.
*
* Examples
* ========
*
* Link to the current page
* ------------------------
*
* ::
*
* <f:link.page>page link</f:link.page>
*
* Output::
*
* <a href="/page/path/name.html">page link</a>
*
* Depending on current page, routing and page path configuration.
*
* Query parameters
* ----------------
*
* ::
*
* <f:link.page pageUid="1" additionalParams="{foo: 'bar'}">page link</f:link.page>
*
* Output::
*
* <a href="/page/path/name.html?foo=bar">page link</a>
*
* Depending on current page, routing and page path configuration.
*
* Query parameters for extensions
* -------------------------------
*
* ::
*
* <f:link.page pageUid="1" additionalParams="{extension_key: {foo: 'bar'}}">page link</f:link.page>
*
* Output::
*
* <a href="/page/path/name.html?extension_key[foo]=bar">page link</a>
*
* Depending on current page, routing and page path configuration.
*/
class PageViewHelper extends AbstractTagBasedViewHelper
{
/**
* @var string
*/
protected $tagName = 'a';
/**
* Arguments initialization
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerUniversalTagAttributes();
$this->registerTagAttribute('target', 'string', 'Target of link', false);
$this->registerTagAttribute('rel', 'string', 'Specifies the relationship between the current document and the linked document', false);
$this->registerArgument('pageUid', 'int', 'Target page. See TypoLink destination');
$this->registerArgument('pageType', 'int', 'Type of the target page. See typolink.parameter');
$this->registerArgument('noCache', 'bool', 'Set this to disable caching for the target page. You should not need this.');
$this->registerArgument('noCacheHash', 'bool', 'Set this to suppress the cHash query parameter created by TypoLink. You should not need this.');
$this->registerArgument('section', 'string', 'The anchor to be added to the URI');
$this->registerArgument('linkAccessRestrictedPages', 'bool', 'If set, links pointing to access restricted pages will still link to the page even though the page cannot be accessed.');
$this->registerArgument('additionalParams', 'array', 'Additional query parameters that won\'t be prefixed like $arguments (overrule $arguments)');
$this->registerArgument('absolute', 'bool', 'If set, the URI of the rendered link is absolute');
$this->registerArgument('addQueryString', 'bool', 'If set, the current query parameters will be kept in the URI');
$this->registerArgument('argumentsToBeExcludedFromQueryString', 'array', 'Arguments to be removed from the URI. Only active if $addQueryString = TRUE');
$this->registerArgument('addQueryStringMethod', 'string', 'Set which parameters will be kept. Only active if $addQueryString = TRUE');
}
/**
* @return string Rendered page URI
*/
public function render()
{
$pageUid = isset($this->arguments['pageUid']) ? (int)$this->arguments['pageUid'] : null;
$pageType = isset($this->arguments['pageType']) ? (int)$this->arguments['pageType'] : 0;
$noCache = isset($this->arguments['noCache']) ? (bool)$this->arguments['noCache'] : false;
$noCacheHash = isset($this->arguments['noCacheHash']) ? (bool)$this->arguments['noCacheHash'] : false;
$section = isset($this->arguments['section']) ? (string)$this->arguments['section'] : '';
$linkAccessRestrictedPages = isset($this->arguments['linkAccessRestrictedPages']) ? (bool)$this->arguments['linkAccessRestrictedPages'] : false;
$additionalParams = isset($this->arguments['additionalParams']) ? (array)$this->arguments['additionalParams'] : [];
$absolute = isset($this->arguments['absolute']) ? (bool)$this->arguments['absolute'] : false;
$addQueryString = isset($this->arguments['addQueryString']) ? (bool)$this->arguments['addQueryString'] : false;
$argumentsToBeExcludedFromQueryString = isset($this->arguments['argumentsToBeExcludedFromQueryString']) ? (array)$this->arguments['argumentsToBeExcludedFromQueryString'] : [];
$addQueryStringMethod = $this->arguments['addQueryStringMethod'] ?? null;
$uriBuilder = $this->renderingContext->getControllerContext()->getUriBuilder();
$uri = $uriBuilder->reset()
->setTargetPageUid($pageUid)
->setTargetPageType($pageType)
->setNoCache($noCache)
->setUseCacheHash(!$noCacheHash)
->setSection($section)
->setLinkAccessRestrictedPages($linkAccessRestrictedPages)
->setArguments($additionalParams)
->setCreateAbsoluteUri($absolute)
->setAddQueryString($addQueryString)
->setArgumentsToBeExcludedFromQueryString($argumentsToBeExcludedFromQueryString)
->setAddQueryStringMethod($addQueryStringMethod)
->build();
if ((string)$uri !== '') {
$this->tag->addAttribute('href', $uri);
$this->tag->setContent($this->renderChildren());
$this->tag->forceClosingTag(true);
$result = $this->tag->render();
} else {
$result = $this->renderChildren();
}
return $result;
}
}
@@ -0,0 +1,208 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Link;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
use TYPO3\CMS\Frontend\Service\TypoLinkCodecService;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Exception;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* A ViewHelper to create links from fields supported by the link wizard
*
* Example
* =======
*
* ``{link}`` contains: ``t3://page?uid=2&arg1=val1#9 _blank some-css-class "Title containing Whitespace"``.
*
* Or a legacy version from older TYPO3 versions:
* ``{link}`` contains: ``9 _blank - "testtitle with whitespace" &X=y``.
*
* Minimal usage
* -------------
*
* ::
*
* <f:link.typolink parameter="{link}">
* Linktext
* </f:link.typolink>
*
* Output::
*
* <a href="/page/path/name.html?X=y" title="testtitle with whitespace" target="_blank">
* Linktext
* </a>
*
* Depending on current page, routing and page path configuration.
*
* Full parameter usage
* --------------------
*
* ::
*
* <f:link.typolink parameter="{link}" additionalParams="&u=b"
* target="_blank"
* class="ico-class" title="some title"
* additionalAttributes="{type:'button'}"
* useCacheHash="true"
* >
* Linktext
* </f:link.typolink>
*
* Output::
*
* <a href="/page/path/name.html?X=y&u=b" title="some title" target="_blank" class="ico-class" type="button">
* Linktext
* </a>
*
* Depending on routing and page path configuration.
*/
class TypolinkViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* @var bool
*/
protected $escapeOutput = false;
/**
* Initialize ViewHelper arguments
*
* @throws Exception
*/
public function initializeArguments()
{
$this->registerArgument('parameter', 'string', 'stdWrap.typolink style parameter string', true);
$this->registerArgument('target', 'string', '', false, '');
$this->registerArgument('class', 'string', '', false, '');
$this->registerArgument('title', 'string', '', false, '');
$this->registerArgument('additionalParams', 'string', '', false, '');
$this->registerArgument('additionalAttributes', 'array', '', false, []);
$this->registerArgument('useCacheHash', 'bool', '', false, false);
$this->registerArgument('addQueryString', 'bool', '', false, false);
$this->registerArgument('addQueryStringMethod', 'string', '', false, 'GET');
$this->registerArgument('addQueryStringExclude', 'string', '', false, '');
$this->registerArgument('absolute', 'bool', 'Ensure the resulting URL is an absolute URL', false, false);
}
/**
* Render
*
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return mixed|string
* @throws \InvalidArgumentException
* @throws \UnexpectedValueException
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$parameter = $arguments['parameter'] ?? '';
$target = $arguments['target'] ?? '';
$class = $arguments['class'] ?? '';
$title = $arguments['title'] ?? '';
$additionalParams = $arguments['additionalParams'] ?? '';
$additionalAttributes = $arguments['additionalAttributes'] ?? [];
$useCacheHash = $arguments['useCacheHash'] ?? false;
$addQueryString = $arguments['addQueryString'] ?? false;
$addQueryStringMethod = $arguments['addQueryStringMethod'] ?? 'GET';
$addQueryStringExclude = $arguments['addQueryStringExclude'] ?? '';
$absolute = $arguments['absolute'] ?? false;
// Merge the $parameter with other arguments
$typolinkParameter = self::createTypolinkParameterArrayFromArguments($parameter, $target, $class, $title, $additionalParams);
// array(param1 -> value1, param2 -> value2) --> param1="value1" param2="value2" for typolink.ATagParams
$extraAttributes = [];
foreach ($additionalAttributes as $attributeName => $attributeValue) {
$extraAttributes[] = $attributeName . '="' . htmlspecialchars($attributeValue) . '"';
}
$aTagParams = implode(' ', $extraAttributes);
// If no link has to be rendered, the inner content will be returned as such
$content = (string)$renderChildrenClosure();
if ($parameter) {
/** @var ContentObjectRenderer $contentObject */
$contentObject = GeneralUtility::makeInstance(ContentObjectRenderer::class);
$contentObject->start([], '');
$content = $contentObject->stdWrap(
$content,
[
'typolink.' => [
'parameter' => $typolinkParameter,
'ATagParams' => $aTagParams,
'useCacheHash' => $useCacheHash,
'addQueryString' => $addQueryString,
'addQueryString.' => [
'method' => $addQueryStringMethod,
'exclude' => $addQueryStringExclude
],
'forceAbsoluteUrl' => $absolute
]
]
);
}
return $content;
}
/**
* Transforms ViewHelper arguments to typo3link.parameters.typoscript option as array.
*
* @param string $parameter Example: 19 _blank - "testtitle \"with whitespace\"" &X=y
* @param string $target
* @param string $class
* @param string $title
* @param string $additionalParams
*
* @return string The final TypoLink string
*/
protected static function createTypolinkParameterArrayFromArguments($parameter, $target = '', $class = '', $title = '', $additionalParams = '')
{
$typoLinkCodec = GeneralUtility::makeInstance(TypoLinkCodecService::class);
$typolinkConfiguration = $typoLinkCodec->decode($parameter);
if (empty($typolinkConfiguration)) {
return $typolinkConfiguration;
}
// Override target if given in target argument
if ($target) {
$typolinkConfiguration['target'] = $target;
}
// Combine classes if given in both "parameter" string and "class" argument
if ($class) {
$classes = explode(' ', trim($typolinkConfiguration['class']) . ' ' . trim($class));
$typolinkConfiguration['class'] = implode(' ', array_unique(array_filter($classes)));
}
// Override title if given in title argument
if ($title) {
$typolinkConfiguration['title'] = $title;
}
// Combine additionalParams
if ($additionalParams) {
$typolinkConfiguration['additionalParams'] .= $additionalParams;
}
return $typoLinkCodec->encode($typolinkConfiguration);
}
}
@@ -0,0 +1,179 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection;
use TYPO3\CMS\Core\Resource\FileInterface;
use TYPO3\CMS\Core\Resource\FileReference;
use TYPO3\CMS\Core\Resource\Rendering\RendererRegistry;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Domain\Model\AbstractFileFolder;
use TYPO3\CMS\Extbase\Service\ImageService;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
/**
* Render a given media file with the correct html tag.
*
* It asks the :php:`RendererRegistry` for the correct Renderer class and if not found it falls
* back to the :php:`ImageViewHelper` as that is the "Renderer" class for images in Fluid context.
*
* Examples
* ========
*
* Image Object
* ------------
*
* ::
*
* <f:media file="{file}" width="400" height="375" />
*
* Output::
*
* <img alt="alt set in image record" src="fileadmin/_processed_/323223424.png" width="396" height="375" />
*
* MP4 Video Object
* ----------------
*
* ::
*
* <f:media file="{file}" width="400" height="375" />
*
* Output::
*
* <video width="400" height="375" controls><source src="fileadmin/user_upload/my-video.mp4" type="video/mp4"></video>
*
* MP4 Video Object with loop and autoplay option set
* --------------------------------------------------
*
* ::
*
* <f:media file="{file}" width="400" height="375" additionalConfig="{loop: '1', autoplay: '1'}" />
*
* Output::
*
* <video width="400" height="375" controls loop><source src="fileadmin/user_upload/my-video.mp4" type="video/mp4"></video>
*/
class MediaViewHelper extends AbstractTagBasedViewHelper
{
/**
* @var string
*/
protected $tagName = 'img';
/**
* Initialize arguments.
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerUniversalTagAttributes();
$this->registerTagAttribute('alt', 'string', 'Specifies an alternate text for an image', false);
$this->registerArgument('file', 'object', 'File', true);
$this->registerArgument('additionalConfig', 'array', 'This array can hold additional configuration that is passed though to the Renderer object', false, []);
$this->registerArgument('width', 'string', 'This can be a numeric value representing the fixed width of in pixels. But you can also perform simple calculations by adding "m" or "c" to the value. See imgResource.width for possible options.');
$this->registerArgument('height', 'string', 'This can be a numeric value representing the fixed height in pixels. But you can also perform simple calculations by adding "m" or "c" to the value. See imgResource.width for possible options.');
$this->registerArgument('cropVariant', 'string', 'select a cropping variant, in case multiple croppings have been specified or stored in FileReference', false, 'default');
}
/**
* Render a given media file
*
* @return string Rendered tag
* @throws \UnexpectedValueException
*/
public function render()
{
$file = $this->arguments['file'];
$additionalConfig = (array)$this->arguments['additionalConfig'];
$width = $this->arguments['width'];
$height = $this->arguments['height'];
// get Resource Object (non ExtBase version)
if (is_callable([$file, 'getOriginalResource'])) {
// We have a domain model, so we need to fetch the FAL resource object from there
$file = $file->getOriginalResource();
}
if (!($file instanceof FileInterface || $file instanceof AbstractFileFolder)) {
throw new \UnexpectedValueException('Supplied file object type ' . get_class($file) . ' must be FileInterface or AbstractFileFolder.', 1454252193);
}
$fileRenderer = RendererRegistry::getInstance()->getRenderer($file);
// Fallback to image when no renderer is found
if ($fileRenderer === null) {
return $this->renderImage($file, $width, $height);
}
$additionalConfig = array_merge_recursive($this->arguments, $additionalConfig);
return $fileRenderer->render($file, $width, $height, $additionalConfig);
}
/**
* Render img tag
*
* @param FileInterface $image
* @param string $width
* @param string $height
* @return string Rendered img tag
*/
protected function renderImage(FileInterface $image, $width, $height)
{
$cropVariant = $this->arguments['cropVariant'] ?: 'default';
$cropString = $image instanceof FileReference ? $image->getProperty('crop') : '';
$cropVariantCollection = CropVariantCollection::create((string)$cropString);
$cropArea = $cropVariantCollection->getCropArea($cropVariant);
$processingInstructions = [
'width' => $width,
'height' => $height,
'crop' => $cropArea->isEmpty() ? null : $cropArea->makeAbsoluteBasedOnFile($image),
];
$imageService = $this->getImageService();
$processedImage = $imageService->applyProcessingInstructions($image, $processingInstructions);
$imageUri = $imageService->getImageUri($processedImage);
if (!$this->tag->hasAttribute('data-focus-area')) {
$focusArea = $cropVariantCollection->getFocusArea($cropVariant);
if (!$focusArea->isEmpty()) {
$this->tag->addAttribute('data-focus-area', $focusArea->makeAbsoluteBasedOnFile($image));
}
}
$this->tag->addAttribute('src', $imageUri);
$this->tag->addAttribute('width', $processedImage->getProperty('width'));
$this->tag->addAttribute('height', $processedImage->getProperty('height'));
$alt = $image->getProperty('alternative');
$title = $image->getProperty('title');
// The alt-attribute is mandatory to have valid html-code, therefore add it even if it is empty
if (empty($this->arguments['alt'])) {
$this->tag->addAttribute('alt', $alt);
}
if (empty($this->arguments['title']) && $title) {
$this->tag->addAttribute('title', $title);
}
return $this->tag->render();
}
/**
* Return an instance of ImageService
*
* @return ImageService
*/
protected function getImageService()
{
return GeneralUtility::makeInstance(ImageService::class);
}
}
@@ -0,0 +1,129 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* Render the inner parts of a Widget.
* This ViewHelper can only be used in a template which belongs to a Widget Controller.
*
* It renders everything inside the Widget ViewHelper, and you can pass additional
* arguments.
*/
class RenderChildrenViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* As this ViewHelper might render HTML, the output must not be escaped
*
* @var bool
*/
protected $escapeOutput = false;
/**
* Initialize arguments
*/
public function initializeArguments()
{
$this->registerArgument('arguments', 'array', 'Arguments to assign as template variables', false, []);
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return mixed
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$subRenderingContext = static::getWidgetRenderingContext($renderingContext);
$widgetChildNodes = static::getWidgetChildNodes($renderingContext);
static::addArgumentsToTemplateVariableContainer($subRenderingContext, $arguments['arguments']);
$output = $widgetChildNodes->evaluate($subRenderingContext);
static::removeArgumentsFromTemplateVariableContainer($subRenderingContext, $arguments['arguments']);
return $output;
}
/**
* Get the widget rendering context, or throw an exception if it cannot be found.
*
* @param RenderingContextInterface $renderingContext
* @throws \TYPO3\CMS\Fluid\Core\Widget\Exception\RenderingContextNotFoundException
* @return \TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface
*/
protected static function getWidgetRenderingContext(RenderingContextInterface $renderingContext)
{
$subRenderingContext = static::getWidgetContext($renderingContext)->getViewHelperChildNodeRenderingContext();
if (!$subRenderingContext instanceof \TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface) {
throw new \TYPO3\CMS\Fluid\Core\Widget\Exception\RenderingContextNotFoundException('Rendering Context not found inside Widget. <f:renderChildren> has been used in an AJAX Request, but is only usable in non-ajax mode.', 1284986604);
}
return $subRenderingContext;
}
/**
* @param RenderingContextInterface $renderingContext
* @return \TYPO3Fluid\Fluid\Core\Parser\SyntaxTree\RootNode
*/
protected static function getWidgetChildNodes(RenderingContextInterface $renderingContext)
{
return static::getWidgetContext($renderingContext)->getViewHelperChildNodes();
}
/**
* @param RenderingContextInterface $renderingContext
* @throws \TYPO3\CMS\Fluid\Core\Widget\Exception\WidgetRequestNotFoundException
* @return \TYPO3\CMS\Fluid\Core\Widget\WidgetContext
*/
protected static function getWidgetContext(RenderingContextInterface $renderingContext)
{
$request = $renderingContext->getControllerContext()->getRequest();
if (!$request instanceof \TYPO3\CMS\Fluid\Core\Widget\WidgetRequest) {
throw new \TYPO3\CMS\Fluid\Core\Widget\Exception\WidgetRequestNotFoundException('The Request is not a WidgetRequest! <f:renderChildren> must be called inside a Widget Template.', 1284986120);
}
return $request->getWidgetContext();
}
/**
* Add the given arguments to the TemplateVariableContainer of the widget.
*
* @param RenderingContextInterface $renderingContext
* @param array $arguments
*/
protected static function addArgumentsToTemplateVariableContainer(RenderingContextInterface $renderingContext, array $arguments)
{
$templateVariableContainer = $renderingContext->getVariableProvider();
foreach ($arguments as $identifier => $value) {
$templateVariableContainer->add($identifier, $value);
}
}
/**
* Remove the given arguments from the TemplateVariableContainer of the widget.
*
* @param RenderingContextInterface $renderingContext
* @param array $arguments
*/
protected static function removeArgumentsFromTemplateVariableContainer(RenderingContextInterface $renderingContext, array $arguments)
{
$templateVariableContainer = $renderingContext->getVariableProvider();
foreach ($arguments as $identifier => $value) {
$templateVariableContainer->remove($identifier);
}
}
}
@@ -0,0 +1,30 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Class RenderViewHelper
* @inheritdoc
*/
class RenderViewHelper extends \TYPO3Fluid\Fluid\ViewHelpers\RenderViewHelper
{
/**
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('debug', 'boolean', 'If true, the admin panel shows debug information if activated,', false, true);
}
}
@@ -0,0 +1,110 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Fluid\ViewHelpers\Sanitize;
use TYPO3\CMS\Core\Html\SanitizerBuilderFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\HtmlSanitizer\Builder\BuilderInterface;
use TYPO3\HtmlSanitizer\Sanitizer;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Exception;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* Passes a given content through `typo3/html-sanitizer` to mitigate potential
* cross-site scripting occurrences. Given `default` build corresponds to class
* `TYPO3\CMS\Core\Html\DefaultSanitizerBuilder` declaring allowed HTML tags,
* attributes and their values.
*
* Examples
* ========
*
* Default parameters
* ------------------
*
* ::
*
* <f:sanitize.html>
* <img src="/img.png" class="image" onmouseover="alert(document.location)">
* </f:sanitize.html>
*
* Output::
*
* <img src="/img.png" class="image">
*
* Inline notation
* ---------------
*
* ::
*
* {richTextFieldContent -> f:sanitize.html(build: 'default')}
*/
class HtmlViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* @var bool
*/
protected $escapeChildren = false;
/**
* @var bool
*/
protected $escapeOutput = false;
/**
* @throws Exception
*/
public function initializeArguments()
{
$this->registerArgument(
'build',
'string',
'preset name or class-like name of sanitization builder',
false,
'default'
);
}
/**
* @param array{build: string|class-string} $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
*
* @return string the parsed string.
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$value = $renderChildrenClosure();
$build = $arguments['build'] ?? 'default';
return static::createSanitizer($build)->sanitize($value);
}
protected static function createSanitizer(string $build): Sanitizer
{
if (class_exists($build) && is_a($build, BuilderInterface::class, true)) {
$builder = GeneralUtility::makeInstance($build);
} else {
$factory = GeneralUtility::makeInstance(SanitizerBuilderFactory::class);
$builder = $factory->build($build);
}
return $builder->build();
}
}
@@ -0,0 +1,68 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Security;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractConditionViewHelper;
/**
* This ViewHelper implements an ifAuthenticated/else condition for frontend users.
*
* Examples
* ========
*
* Basic usage
* -----------
*
* ::
*
* <f:security.ifAuthenticated>
* This is being shown whenever a FE user is logged in
* </f:security.ifAuthenticated>
*
* Everything inside the :html:`<f:security.ifAuthenticated>` tag is being displayed if
* current frontend user is authenticated.
*
* IfAuthenticated / then / else
* -----------------------------
*
* ::
*
* <f:security.ifAuthenticated>
* <f:then>
* This is being shown in case you have access.
* </f:then>
* <f:else>
* This is being displayed in case you do not have access.
* </f:else>
* </f:security.ifAuthenticated>
*
* Everything inside the :html:`<f:then></f:then>` tag is displayed if frontend user is authenticated.
* Otherwise, everything inside the :html:`<f:else></f:else>` tag is displayed.
*/
class IfAuthenticatedViewHelper extends AbstractConditionViewHelper
{
/**
* This method decides if the condition is TRUE or FALSE. It can be overridden 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
*/
protected static function evaluateCondition($arguments = null)
{
return GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('frontend.user', 'id', 0) > 0;
}
}
@@ -0,0 +1,105 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Security;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Context\UserAspect;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractConditionViewHelper;
/**
* This ViewHelper implements an ifHasRole/else condition for frontend groups.
*
* Examples
* ========
*
* Basic usage
* -----------
*
* ::
*
* <f:security.ifHasRole role="Administrator">
* This is being shown in case the current FE user belongs to a FE usergroup (aka role) titled "Administrator" (case sensitive)
* </f:security.ifHasRole>
*
* Everything inside the :html:`<f:security.ifHasRole>` tag is being displayed if the
* logged in frontend user belongs to the specified frontend user group.
* Comparison is done by comparing to title of the user groups.
*
* Using the usergroup uid as role identifier
* ------------------------------------------
*
* ::
*
* <f:security.ifHasRole role="1">
* This is being shown in case the current FE user belongs to a FE usergroup (aka role) with the uid "1"
* </f:security.ifHasRole>
*
* Everything inside the :html:`<f:security.ifHasRole>` tag is being displayed if the
* logged in frontend user belongs to the specified role. Comparison is done
* using the ``uid`` of frontend user groups.
*
* IfRole / then / else
* --------------------
*
* ::
*
* <f:security.ifHasRole role="Administrator">
* <f:then>
* This is being shown in case you have the role.
* </f:then>
* <f:else>
* This is being displayed in case you do not have the role.
* </f:else>
* </f:security.ifHasRole>
*
* Everything inside the :html:`<f:then></f:then>` tag is displayed if the logged in FE user belongs to the specified role.
* Otherwise, everything inside the :html:`<f:else></f:else>` tag is displayed.
*/
class IfHasRoleViewHelper extends AbstractConditionViewHelper
{
/**
* Initializes the "role" argument.
* Renders <f:then> child if the current logged in FE user belongs to the specified role (aka usergroup)
* otherwise renders <f:else> child.
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('role', 'string', 'The usergroup (either the usergroup uid or its title).');
}
/**
* This method decides if the condition is TRUE or FALSE. It can be overridden 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
*/
protected static function evaluateCondition($arguments = null)
{
$role = $arguments['role'];
/** @var UserAspect $userAspect */
$userAspect = GeneralUtility::makeInstance(Context::class)->getAspect('frontend.user');
if (!$userAspect->isLoggedIn()) {
return false;
}
if (is_numeric($role)) {
$groupIds = $userAspect->getGroupIds();
return in_array((int)$role, $groupIds, true);
}
return in_array($role, $userAspect->getGroupNames(), true);
}
}
@@ -0,0 +1,175 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
use TYPO3Fluid\Fluid\Core\Exception;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* Translate a key from locallang. The files are loaded from the folder
* :file:`Resources/Private/Language/`.
*
* Examples
* ========
*
* Translate key
* -------------
*
* ::
*
* <f:translate key="key1" />
*
* Value of key ``key1`` in the current website language. Alternatively id can
* be used instead of key::
*
* <f:translate id="key1" />
*
* This will output the same as above. If both id and key are set, id will take precedence.
*
* Keep HTML tags
* --------------
*
* ::
*
* <f:format.raw><f:translate key="htmlKey" /></f:format.raw>
*
* Value of key ``htmlKey`` in the current website language, no :php:`htmlspecialchars()` applied.
*
* Translate key from custom locallang file
* ----------------------------------------
*
* ::
*
* <f:translate key="LLL:EXT:myext/Resources/Private/Language/locallang.xlf:key1" />
*
* Value of key ``key1`` in the current website language.
*
* Inline notation with arguments and default value
* ------------------------------------------------
*
* ::
*
* {f:translate(key: 'someKey', arguments: {0: 'dog', 1: 'fox'}, default: 'default value')}
*
* Value of key ``someKey`` in the current website language
* with the given arguments (“dog” and “fox”) assigned for the specified
* ``%s`` conversions (:php:`sprintf()`) in the language file::
*
* <trans-unit id="someKey" resname="someKey">
* <source>Some text about a %s and a %s.</source>
* </trans-unit>
*
* The output will be "Some text about a dog and a fox".
*
* If the key ``someKey`` is not found in the language file, the output is “default value”.
*
* Inline notation with extension name
* -----------------------------------
*
* ::
*
* {f:translate(key: 'someKey', extensionName: 'SomeExtensionName')}
*
* Value of key ``someKey`` in the current website language.
* The locallang file of extension "some_extension_name" will be used.
*/
class TranslateViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* Output is escaped already. We must not escape children, to avoid double encoding.
*
* @var bool
*/
protected $escapeChildren = false;
/**
* Initialize arguments.
*
* @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
*/
public function initializeArguments()
{
$this->registerArgument('key', 'string', 'Translation Key');
$this->registerArgument('id', 'string', 'Translation ID. Same as key.');
$this->registerArgument('default', 'string', 'If the given locallang key could not be found, this value is used. If this argument is not set, child nodes will be used to render the default');
$this->registerArgument('arguments', 'array', 'Arguments to be replaced in the resulting string');
$this->registerArgument('extensionName', 'string', 'UpperCamelCased extension key (for example BlogExample)');
$this->registerArgument('languageKey', 'string', 'Language key ("dk" for example) or "default" to use for this translation. If this argument is empty, we use the current language');
$this->registerArgument('alternativeLanguageKeys', 'array', 'Alternative language keys if no translation does exist');
}
/**
* Return array element by key.
*
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @throws Exception
* @return string
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$key = $arguments['key'];
$id = $arguments['id'];
$default = $arguments['default'];
$extensionName = $arguments['extensionName'];
$translateArguments = $arguments['arguments'];
// Use key if id is empty.
if ($id === null) {
$id = $key;
}
if ((string)$id === '') {
throw new Exception('An argument "key" or "id" has to be provided', 1351584844);
}
$request = $renderingContext->getControllerContext()->getRequest();
$extensionName = $extensionName ?? $request->getControllerExtensionName();
try {
$value = static::translate($id, $extensionName, $translateArguments, $arguments['languageKey'], $arguments['alternativeLanguageKeys']);
} catch (\InvalidArgumentException $e) {
$value = null;
}
if ($value === null) {
$value = $default ?? $renderChildrenClosure();
if (!empty($translateArguments)) {
$value = vsprintf($value, $translateArguments);
}
}
return $value;
}
/**
* Wrapper call to static LocalizationUtility
*
* @param string $id Translation Key
* @param string $extensionName UpperCamelCased extension key (for example BlogExample)
* @param array $arguments Arguments to be replaced in the resulting string
* @param string $languageKey Language key to use for this translation
* @param string[] $alternativeLanguageKeys Alternative language keys if no translation does exist
*
* @return string|null
*/
protected static function translate($id, $extensionName, $arguments, $languageKey, $alternativeLanguageKeys)
{
return LocalizationUtility::translate($id, $extensionName, $arguments, $languageKey, $alternativeLanguageKeys);
}
}
@@ -0,0 +1,106 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Uri;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* A ViewHelper for creating URIs to extbase actions.
*
* Examples
* ========
*
* URI to the show-action of the current controller::
*
* <f:uri.action action="show" />
*
* ``/page/path/name.html?tx_myextension_plugin[action]=show&tx_myextension_plugin[controller]=Standard&cHash=xyz``
*
* Depending on current page, routing and page path configuration.
*/
class ActionViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* Initialize arguments
*/
public function initializeArguments()
{
$this->registerArgument('action', 'string', 'Target action');
$this->registerArgument('arguments', 'array', 'Arguments', false, []);
$this->registerArgument('controller', 'string', 'Target controller. If NULL current controllerName is used');
$this->registerArgument('extensionName', 'string', 'Target Extension Name (without "tx_" prefix and no underscores). If NULL the current extension name is used');
$this->registerArgument('pluginName', 'string', 'Target plugin. If empty, the current plugin name is used');
$this->registerArgument('pageUid', 'int', 'Target page. See TypoLink destination');
$this->registerArgument('pageType', 'int', 'Type of the target page. See typolink.parameter', false, 0);
$this->registerArgument('noCache', 'bool', 'Set this to disable caching for the target page. You should not need this.', false, false);
$this->registerArgument('noCacheHash', 'bool', 'Set this to suppress the cHash query parameter created by TypoLink. You should not need this.', false, false);
$this->registerArgument('section', 'string', 'The anchor to be added to the URI', false, '');
$this->registerArgument('format', 'string', 'The requested format, e.g. ".html', false, '');
$this->registerArgument('linkAccessRestrictedPages', 'bool', 'If set, links pointing to access restricted pages will still link to the page even though the page cannot be accessed.', false, false);
$this->registerArgument('additionalParams', 'array', 'additional query parameters that won\'t be prefixed like $arguments (overrule $arguments)', false, []);
$this->registerArgument('absolute', 'bool', 'If set, an absolute URI is rendered', false, false);
$this->registerArgument('addQueryString', 'bool', 'If set, the current query parameters will be kept in the URI', false, false);
$this->registerArgument('argumentsToBeExcludedFromQueryString', 'array', 'arguments to be removed from the URI. Only active if $addQueryString = TRUE', false, []);
$this->registerArgument('addQueryStringMethod', 'string', 'Set which parameters will be kept. Only active if $addQueryString = TRUE');
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return string
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$pageUid = $arguments['pageUid'];
$pageType = $arguments['pageType'];
$noCache = $arguments['noCache'];
$noCacheHash = $arguments['noCacheHash'];
$section = $arguments['section'];
$format = $arguments['format'];
$linkAccessRestrictedPages = $arguments['linkAccessRestrictedPages'];
$additionalParams = $arguments['additionalParams'];
$absolute = $arguments['absolute'];
$addQueryString = $arguments['addQueryString'];
$argumentsToBeExcludedFromQueryString = $arguments['argumentsToBeExcludedFromQueryString'];
$addQueryStringMethod = $arguments['addQueryStringMethod'];
$action = $arguments['action'];
$controller = $arguments['controller'];
$extensionName = $arguments['extensionName'];
$pluginName = $arguments['pluginName'];
$arguments = $arguments['arguments'];
$uri = $renderingContext->getControllerContext()->getUriBuilder()
->reset()
->setTargetPageUid($pageUid)
->setTargetPageType($pageType)
->setNoCache($noCache)
->setUseCacheHash(!$noCacheHash)
->setSection($section)
->setFormat($format)
->setLinkAccessRestrictedPages($linkAccessRestrictedPages)
->setArguments($additionalParams)
->setCreateAbsoluteUri($absolute)
->setAddQueryString($addQueryString)
->setArgumentsToBeExcludedFromQueryString($argumentsToBeExcludedFromQueryString)
->setAddQueryStringMethod($addQueryStringMethod)
->uriFor($action, $arguments, $controller, $extensionName, $pluginName);
return $uri;
}
}
@@ -0,0 +1,69 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Uri;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* Email URI ViewHelper.
* Generates an email URI incorporating TYPO3s `spamProtectEmailAddresses`_ TypoScript setting.
*
* .. _spamProtectEmailAddresses: https://docs.typo3.org/m/typo3/reference-typoscript/master/en-us/Setup/Config/Index.html#spamprotectemailaddresses
*
* Example
* =======
*
* Basic email URI::
*
* <f:uri.email email="foo@bar.tld" />
*
* Output::
*
* javascript:linkTo_UnCryptMailto('ocknvq,hqqBdct0vnf');
*
* Depending on `spamProtectEmailAddresses`_ setting.
*/
class EmailViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* Initialize arguments
*/
public function initializeArguments()
{
$this->registerArgument('email', 'string', 'The email address to be turned into a URI', true);
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
*
* @return string Rendered email link
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$email = $arguments['email'];
if (TYPO3_MODE === 'FE') {
$emailParts = $GLOBALS['TSFE']->cObj->getMailTo($email, $email);
return reset($emailParts);
}
return 'mailto:' . $email;
}
}
@@ -0,0 +1,79 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Uri;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* A ViewHelper for creating URIs to external targets.
* Currently the specified URI is simply passed through.
*
* Examples
* ========
*
* Default
* -------
*
* ::
*
* <f:uri.external uri="http://www.typo3.org" />
*
* ``http://www.typo3.org``
*
* Custom default scheme
* ---------------------
*
* ::
*
* <f:uri.external uri="typo3.org" defaultScheme="ftp" />
*
* ``ftp://typo3.org``
*/
class ExternalViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* Initialize arguments
*
* @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
*/
public function initializeArguments()
{
$this->registerArgument('uri', 'string', 'target URI', true);
$this->registerArgument('defaultScheme', 'string', 'scheme the href attribute will be prefixed with if specified $uri does not contain a scheme already', false, 'http');
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
*
* @return string Rendered URI
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$uri = $arguments['uri'];
$defaultScheme = $arguments['defaultScheme'];
$scheme = parse_url($uri, PHP_URL_SCHEME);
if ($scheme === null && $defaultScheme !== '') {
$uri = $defaultScheme . '://' . $uri;
}
return $uri;
}
}
@@ -0,0 +1,177 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Uri;
/* *
* This script is part of the TYPO3 project - inspiring people to share! *
* *
* TYPO3 is free software; you can redistribute it and/or modify it under *
* the terms of the GNU General Public License version 2 as published by *
* the Free Software Foundation. *
* *
* This script is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- *
* TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General *
* Public License for more details. *
* */
use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection;
use TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Extbase\Service\ImageService;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Exception;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* Resizes a given image (if required) and returns its relative path.
*
* Examples
* ========
*
* Default
* -------
*
* ::
*
* <f:uri.image src="EXT:myext/Resources/Public/typo3_logo.png" />
*
* Results in the following output within TYPO3 frontend:
*
* ``typo3conf/ext/myext/Resources/Public/typo3_logo.png``
*
* and the following output inside TYPO3 backend:
*
* ``../typo3conf/ext/myext/Resources/Public/typo3_logo.png``
*
* Image Object
* ------------
*
* ::
*
* <f:uri.image image="{imageObject}" />
*
* Results in the following output within TYPO3 frontend:
*
* ``fileadmin/images/image.png``
*
* and the following output inside TYPO3 backend:
*
* ``fileadmin/images/image.png``
*
* Inline notation
* ---------------
*
* ::
*
* {f:uri.image(src: 'EXT:myext/Resources/Public/typo3_logo.png', minWidth: 30, maxWidth: 40)}
*
* ``typo3temp/assets/images/[b4c0e7ed5c].png``
*
* Depending on your TYPO3s encryption key.
*
* Non existing image
* ------------------
*
* ::
*
* <f:uri.image src="NonExistingImage.png" />
*
* ``Could not get image resource for "NonExistingImage.png".``
*/
class ImageViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* Initialize arguments
*/
public function initializeArguments()
{
$this->registerArgument('src', 'string', 'src');
$this->registerArgument('treatIdAsReference', 'bool', 'given src argument is a sys_file_reference record', false, false);
$this->registerArgument('image', 'object', 'image');
$this->registerArgument('crop', 'string|bool', 'overrule cropping of image (setting to FALSE disables the cropping set in FileReference)');
$this->registerArgument('cropVariant', 'string', 'select a cropping variant, in case multiple croppings have been specified or stored in FileReference', false, 'default');
$this->registerArgument('width', 'string', 'width of the image. This can be a numeric value representing the fixed width of the image in pixels. But you can also perform simple calculations by adding "m" or "c" to the value. See imgResource.width for possible options.');
$this->registerArgument('height', 'string', 'height of the image. This can be a numeric value representing the fixed height of the image in pixels. But you can also perform simple calculations by adding "m" or "c" to the value. See imgResource.width for possible options.');
$this->registerArgument('minWidth', 'int', 'minimum width of the image');
$this->registerArgument('minHeight', 'int', 'minimum height of the image');
$this->registerArgument('maxWidth', 'int', 'maximum width of the image');
$this->registerArgument('maxHeight', 'int', 'maximum height of the image');
$this->registerArgument('absolute', 'bool', 'Force absolute URL', false, false);
}
/**
* Resizes the image (if required) and returns its path. If the image was not resized, the path will be equal to $src
*
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return string
* @throws Exception
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$src = $arguments['src'];
$image = $arguments['image'];
$treatIdAsReference = $arguments['treatIdAsReference'];
$cropString = $arguments['crop'];
$absolute = $arguments['absolute'];
if (($src === null && $image === null) || ($src !== null && $image !== null)) {
throw new Exception('You must either specify a string src or a File object.', 1460976233);
}
try {
$imageService = self::getImageService();
$image = $imageService->getImage($src, $image, $treatIdAsReference);
if ($cropString === null && $image->hasProperty('crop') && $image->getProperty('crop')) {
$cropString = $image->getProperty('crop');
}
$cropVariantCollection = CropVariantCollection::create((string)$cropString);
$cropVariant = $arguments['cropVariant'] ?: 'default';
$cropArea = $cropVariantCollection->getCropArea($cropVariant);
$processingInstructions = [
'width' => $arguments['width'],
'height' => $arguments['height'],
'minWidth' => $arguments['minWidth'],
'minHeight' => $arguments['minHeight'],
'maxWidth' => $arguments['maxWidth'],
'maxHeight' => $arguments['maxHeight'],
'crop' => $cropArea->isEmpty() ? null : $cropArea->makeAbsoluteBasedOnFile($image),
];
$processedImage = $imageService->applyProcessingInstructions($image, $processingInstructions);
return $imageService->getImageUri($processedImage, $absolute);
} catch (ResourceDoesNotExistException $e) {
// thrown if file does not exist
throw new Exception($e->getMessage(), 1509741907, $e);
} catch (\UnexpectedValueException $e) {
// thrown if a file has been replaced with a folder
throw new Exception($e->getMessage(), 1509741908, $e);
} catch (\RuntimeException $e) {
// RuntimeException thrown if a file is outside of a storage
throw new Exception($e->getMessage(), 1509741909, $e);
} catch (\InvalidArgumentException $e) {
// thrown if file storage does not exist
throw new Exception($e->getMessage(), 1509741910, $e);
}
}
/**
* Return an instance of ImageService using object manager
*
* @return ImageService
*/
protected static function getImageService()
{
/** @var ObjectManager $objectManager */
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
return $objectManager->get(ImageService::class);
}
}
@@ -0,0 +1,106 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Uri;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* A ViewHelper for creating URIs to TYPO3 pages.
*
* Examples
* ========
*
* URI to the current page
* -----------------------
*
* ::
*
* <f:uri.page>page link</f:uri.page>
*
* ``/page/path/name.html``
*
* Depending on current page, routing and page path configuration.
*
* Query parameters
* ----------------
*
* ::
*
* <f:uri.page pageUid="1" additionalParams="{foo: 'bar'}" />
*
* ``/page/path/name.html?foo=bar``
*
* Depending on current page, routing and page path configuration.
*
* Query parameters for extensions
* -------------------------------
*
* ::
*
* <f:uri.page pageUid="1" additionalParams="{extension_key: {foo: 'bar'}}" />
*
* ``/page/path/name.html?extension_key[foo]=bar``
*
* Depending on current page, routing and page path configuration.
*/
class PageViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* Initialize arguments
*/
public function initializeArguments()
{
$this->registerArgument('pageUid', 'int', 'target PID');
$this->registerArgument('additionalParams', 'array', 'query parameters to be attached to the resulting URI', false, []);
$this->registerArgument('pageType', 'int', 'type of the target page. See typolink.parameter', false, 0);
$this->registerArgument('noCache', 'bool', 'set this to disable caching for the target page. You should not need this.', false, false);
$this->registerArgument('noCacheHash', 'bool', 'set this to suppress the cHash query parameter created by TypoLink. You should not need this.', false, false);
$this->registerArgument('section', 'string', 'the anchor to be added to the URI', false, '');
$this->registerArgument('linkAccessRestrictedPages', 'bool', 'If set, links pointing to access restricted pages will still link to the page even though the page cannot be accessed.', false, false);
$this->registerArgument('absolute', 'bool', 'If set, the URI of the rendered link is absolute', false, false);
$this->registerArgument('addQueryString', 'bool', 'If set, the current query parameters will be kept in the URI', false, false);
$this->registerArgument('argumentsToBeExcludedFromQueryString', 'array', 'arguments to be removed from the URI. Only active if $addQueryString = TRUE', false, []);
$this->registerArgument('addQueryStringMethod', 'string', 'Set which parameters will be kept. Only active if $addQueryString = TRUE');
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return string Rendered page URI
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$pageUid = $arguments['pageUid'];
$additionalParams = $arguments['additionalParams'];
$pageType = $arguments['pageType'];
$noCache = $arguments['noCache'];
$noCacheHash = $arguments['noCacheHash'];
$section = $arguments['section'];
$linkAccessRestrictedPages = $arguments['linkAccessRestrictedPages'];
$absolute = $arguments['absolute'];
$addQueryString = $arguments['addQueryString'];
$argumentsToBeExcludedFromQueryString = $arguments['argumentsToBeExcludedFromQueryString'];
$addQueryStringMethod = $arguments['addQueryStringMethod'];
$uriBuilder = $renderingContext->getControllerContext()->getUriBuilder();
$uri = $uriBuilder->setTargetPageUid($pageUid)->setTargetPageType($pageType)->setNoCache($noCache)->setUseCacheHash(!$noCacheHash)->setSection($section)->setLinkAccessRestrictedPages($linkAccessRestrictedPages)->setArguments($additionalParams)->setCreateAbsoluteUri($absolute)->setAddQueryString($addQueryString)->setArgumentsToBeExcludedFromQueryString($argumentsToBeExcludedFromQueryString)->setAddQueryStringMethod($addQueryStringMethod)->build();
return $uri;
}
}
@@ -0,0 +1,95 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Uri;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* A ViewHelper for creating URIs to resources.
*
* Examples
* ========
*
* Defaults
* --------
*
* ::
*
* <link href="{f:uri.resource(path:'css/stylesheet.css')}" rel="stylesheet" />
*
* Output::
*
* <link href="typo3conf/ext/example_extension/Resources/Resources/Public/css/stylesheet.css" rel="stylesheet" />
*
* Depending on current extension.
*
* With extension name
* -------------------
*
* ::
*
* <link href="{f:uri.resource(path:'css/stylesheet.css', extensionName: 'AnotherExtension')}" rel="stylesheet" />
*
* Output::
*
* <link href="typo3conf/ext/another_extension/Resources/Resources/Public/css/stylesheet.css" rel="stylesheet" />
*/
class ResourceViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* Initialize arguments
*/
public function initializeArguments()
{
$this->registerArgument('path', 'string', 'The path and filename of the resource (relative to Public resource directory of the extension).', true);
$this->registerArgument('extensionName', 'string', 'Target extension name. If not set, the current extension name will be used');
$this->registerArgument('absolute', 'bool', 'If set, an absolute URI is rendered', false, false);
}
/**
* Render the URI to the resource. The filename is used from child content.
*
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return string The URI to the resource
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$path = $arguments['path'];
$extensionName = $arguments['extensionName'];
$absolute = $arguments['absolute'];
if ($extensionName === null) {
$extensionName = $renderingContext->getControllerContext()->getRequest()->getControllerExtensionName();
}
$uri = 'EXT:' . GeneralUtility::camelCaseToLowerCaseUnderscored($extensionName) . '/Resources/Public/' . $path;
$uri = GeneralUtility::getFileAbsFileName($uri);
if ($absolute === false && $uri !== false) {
$uri = PathUtility::getAbsoluteWebPath($uri);
}
if ($absolute === true) {
$uri = PathUtility::stripPathSitePrefix($uri);
$uri = $renderingContext->getControllerContext()->getRequest()->getBaseUri() . $uri;
}
return $uri;
}
}
@@ -0,0 +1,133 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Uri;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
use TYPO3\CMS\Frontend\Service\TypoLinkCodecService;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* A ViewHelper to create uris from fields supported by the link wizard.
*
* Example
* =======
*
* ``{link}`` contains ``19 - - - &X=y``
*
* Please note that due to the nature of typolink you have to provide a full
* set of parameters.
* If you use the parameter only, then target, class and title will be discarded.
*
* Minimal usage
* -------------
*
* ::
*
* <f:uri.typolink parameter="{link}" />
*
* ``/page/path/name.html?X=y``
*
* Depending on routing and page path configuration.
*
* Full parameter usage
* --------------------
*
* ::
*
* <f:uri.typolink parameter="{link}" additionalParams="&u=b" useCacheHash="true" />
*
* ``/page/path/name.html?X=y&u=b``
*
* Depending on routing and page path configuration.
*/
class TypolinkViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* Initialize arguments
*/
public function initializeArguments()
{
$this->registerArgument('parameter', 'string', 'stdWrap.typolink style parameter string', true);
$this->registerArgument('additionalParams', 'string', 'stdWrap.typolink additionalParams', false, '');
$this->registerArgument('useCacheHash', 'bool', '', false, false);
$this->registerArgument('addQueryString', 'bool', '', false, false);
$this->registerArgument('addQueryStringMethod', 'string', '', false, 'GET');
$this->registerArgument('addQueryStringExclude', 'string', '', false, '');
$this->registerArgument('absolute', 'bool', 'Ensure the resulting URL is an absolute URL', false, false);
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
*
* @return string
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$parameter = $arguments['parameter'];
$additionalParams = $arguments['additionalParams'];
$useCacheHash = $arguments['useCacheHash'];
$addQueryString = $arguments['addQueryString'];
$addQueryStringMethod = $arguments['addQueryStringMethod'];
$addQueryStringExclude = $arguments['addQueryStringExclude'];
$absolute = $arguments['absolute'];
$content = '';
if ($parameter) {
$contentObject = GeneralUtility::makeInstance(ContentObjectRenderer::class);
$content = $contentObject->typoLink_URL(
[
'parameter' => self::createTypolinkParameterFromArguments($parameter, $additionalParams),
'useCacheHash' => $useCacheHash,
'addQueryString' => $addQueryString,
'addQueryString.' => [
'method' => $addQueryStringMethod,
'exclude' => $addQueryStringExclude
],
'forceAbsoluteUrl' => $absolute
]
);
}
return $content;
}
/**
* Transforms ViewHelper arguments to typo3link.parameters.typoscript option as array.
*
* @param string $parameter Example: 19 _blank - "testtitle with whitespace" &X=y
* @param string $additionalParameters
*
* @return string The final TypoLink string
*/
protected static function createTypolinkParameterFromArguments($parameter, $additionalParameters = '')
{
$typoLinkCodec = GeneralUtility::makeInstance(TypoLinkCodecService::class);
$typolinkConfiguration = $typoLinkCodec->decode($parameter);
// Combine additionalParams
if ($additionalParameters) {
$typolinkConfiguration['additionalParams'] .= $additionalParameters;
}
return $typoLinkCodec->encode($typolinkConfiguration);
}
}
@@ -0,0 +1,84 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Widget;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
/**
* Simple autocomplete widget.
*
* .. note::
* Make sure to include jQuery and jQuery UI in the HTML, like that::
*
* <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
* <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.4/jquery-ui.min.js"></script>
* <link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.3/themes/base/jquery-ui.css" type="text/css" media="all" />
* <link rel="stylesheet" href="http://static.jquery.com/ui/css/demo-docs-theme/ui.theme.css" type="text/css" media="all" />
*
* You can include the provided TypoScript template that includes the above snippet to the pages headerData.
*
* Examples
* ========
*
* Render lib object::
*
* <input type="text" id="name" />
* <f:widget.autocomplete for="name" objects="{posts}" searchProperty="author">
*
* Output::
*
* <input type="text" id="name" />
*
* The input field and the required JavaScript for the Ajax communication.
* See Resources/Private/Templates/ViewHelpers/Widget/Autocomplete/Index.html
*/
class AutocompleteViewHelper extends \TYPO3\CMS\Fluid\Core\Widget\AbstractWidgetViewHelper
{
/**
* @var bool
*/
protected $ajaxWidget = true;
/**
* @var \TYPO3\CMS\Fluid\ViewHelpers\Widget\Controller\AutocompleteController
*/
protected $controller;
/**
* Initialize arguments
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('objects', QueryResultInterface::class, 'Objects to auto-complete', true);
$this->registerArgument('for', 'string', 'Property to fill', true);
$this->registerArgument('searchProperty', 'string', 'Property to search within when filtering list', true);
}
/**
* @param \TYPO3\CMS\Fluid\ViewHelpers\Widget\Controller\AutocompleteController $controller
*/
public function injectAutocompleteController(\TYPO3\CMS\Fluid\ViewHelpers\Widget\Controller\AutocompleteController $controller)
{
$this->controller = $controller;
}
/**
* @return string
*/
public function render()
{
return $this->initiateSubRequest();
}
}
@@ -0,0 +1,56 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Widget\Controller;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Class AutocompleteController
*/
class AutocompleteController extends \TYPO3\CMS\Fluid\Core\Widget\AbstractWidgetController
{
/**
* Simply assigns the ID of the widget.
*/
public function indexAction()
{
$this->view->assign('id', $this->widgetConfiguration['for']);
}
/**
* @param string $term
* @return string
*/
public function autocompleteAction($term)
{
$searchProperty = $this->widgetConfiguration['searchProperty'];
$query = $this->widgetConfiguration['objects']->getQuery();
$constraint = $query->getConstraint();
if ($constraint !== null) {
$query->matching($query->logicalAnd($constraint, $query->like($searchProperty, '%' . $term . '%', false)));
} else {
$query->matching($query->like($searchProperty, '%' . $term . '%', false));
}
$results = $query->execute();
$output = [];
foreach ($results as $singleResult) {
$val = \TYPO3\CMS\Extbase\Reflection\ObjectAccess::getProperty($singleResult, $searchProperty);
$output[] = [
'id' => $val,
'label' => $val,
'value' => $val
];
}
return json_encode($output);
}
}
@@ -0,0 +1,210 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Widget\Controller;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
use TYPO3\CMS\Fluid\Core\Widget\AbstractWidgetController;
/**
* Class PaginateController
*/
class PaginateController extends AbstractWidgetController
{
/**
* @var array
*/
protected $configuration = [
'itemsPerPage' => 10,
'insertAbove' => false,
'insertBelow' => true,
'maximumNumberOfLinks' => 99,
'addQueryStringMethod' => '',
'section' => ''
];
/**
* @var QueryResultInterface|ObjectStorage|array
*/
protected $objects;
/**
* @var int
*/
protected $currentPage = 1;
/**
* @var int
*/
protected $maximumNumberOfLinks = 99;
/**
* @var int
*/
protected $numberOfPages = 1;
/**
* @var int
*/
protected $displayRangeStart;
/**
* @var int
*/
protected $displayRangeEnd;
/**
* Initializes the current information on which page the visitor is.
*/
public function initializeAction()
{
$this->objects = $this->widgetConfiguration['objects'];
ArrayUtility::mergeRecursiveWithOverrule($this->configuration, $this->widgetConfiguration['configuration'], false);
$itemsPerPage = (int)$this->configuration['itemsPerPage'];
$this->numberOfPages = $itemsPerPage > 0 ? ceil(count($this->objects) / $itemsPerPage) : 0;
$this->maximumNumberOfLinks = (int)$this->configuration['maximumNumberOfLinks'];
}
/**
* @param int $currentPage
*/
public function indexAction($currentPage = 1)
{
// set current page
$this->currentPage = (int)$currentPage;
if ($this->currentPage < 1) {
$this->currentPage = 1;
}
if ($this->currentPage > $this->numberOfPages) {
// set $modifiedObjects to NULL if the page does not exist
$modifiedObjects = null;
} else {
// modify query
$itemsPerPage = (int)$this->configuration['itemsPerPage'];
$offset = 0;
if ($this->objects instanceof QueryResultInterface) {
$offset = (int)$this->objects->getQuery()->getOffset();
}
if ($this->currentPage > 1) {
$offset = $offset + ((int)($itemsPerPage * ($this->currentPage - 1)));
}
$modifiedObjects = $this->prepareObjectsSlice($itemsPerPage, $offset);
}
$this->view->assign('contentArguments', [
$this->widgetConfiguration['as'] => $modifiedObjects
]);
$this->view->assign('configuration', $this->configuration);
$this->view->assign('pagination', $this->buildPagination());
}
/**
* If a certain number of links should be displayed, adjust before and after
* amounts accordingly.
*/
protected function calculateDisplayRange()
{
$maximumNumberOfLinks = $this->maximumNumberOfLinks;
if ($maximumNumberOfLinks > $this->numberOfPages) {
$maximumNumberOfLinks = $this->numberOfPages;
}
$delta = floor($maximumNumberOfLinks / 2);
$this->displayRangeStart = $this->currentPage - $delta;
$this->displayRangeEnd = $this->currentPage + $delta - ($maximumNumberOfLinks % 2 === 0 ? 1 : 0);
if ($this->displayRangeStart < 1) {
$this->displayRangeEnd -= $this->displayRangeStart - 1;
}
if ($this->displayRangeEnd > $this->numberOfPages) {
$this->displayRangeStart -= $this->displayRangeEnd - $this->numberOfPages;
}
$this->displayRangeStart = (int)max($this->displayRangeStart, 1);
$this->displayRangeEnd = (int)min($this->displayRangeEnd, $this->numberOfPages);
}
/**
* Returns an array with the keys "pages", "current", "numberOfPages",
* "nextPage" & "previousPage"
*
* @return array
*/
protected function buildPagination()
{
$this->calculateDisplayRange();
$pages = [];
for ($i = $this->displayRangeStart; $i <= $this->displayRangeEnd; $i++) {
$pages[] = ['number' => $i, 'isCurrent' => $i === $this->currentPage];
}
$pagination = [
'pages' => $pages,
'current' => $this->currentPage,
'numberOfPages' => $this->numberOfPages,
'displayRangeStart' => $this->displayRangeStart,
'displayRangeEnd' => $this->displayRangeEnd,
'hasLessPages' => $this->displayRangeStart > 2,
'hasMorePages' => $this->displayRangeEnd + 1 < $this->numberOfPages
];
if ($this->currentPage < $this->numberOfPages) {
$pagination['nextPage'] = $this->currentPage + 1;
}
if ($this->currentPage > 1) {
$pagination['previousPage'] = $this->currentPage - 1;
}
return $pagination;
}
/**
* @param int $itemsPerPage
* @param int $offset
*
* @return array|QueryResultInterface
* @throws \InvalidArgumentException
*/
protected function prepareObjectsSlice($itemsPerPage, $offset)
{
if ($this->objects instanceof QueryResultInterface) {
$currentRange = $offset + $itemsPerPage;
$endOfRange = min($currentRange, count($this->objects));
$query = $this->objects->getQuery();
$query->setLimit($itemsPerPage);
if ($offset > 0) {
$query->setOffset($offset);
if ($currentRange > $endOfRange) {
$newLimit = $endOfRange - $offset;
$query->setLimit($newLimit);
}
}
$modifiedObjects = $query->execute();
return $modifiedObjects;
}
if ($this->objects instanceof ObjectStorage) {
$modifiedObjects = [];
$objectArray = $this->objects->toArray();
$endOfRange = min($offset + $itemsPerPage, count($objectArray));
for ($i = $offset; $i < $endOfRange; $i++) {
$modifiedObjects[] = $objectArray[$i];
}
return $modifiedObjects;
}
if (is_array($this->objects)) {
$modifiedObjects = array_slice($this->objects, $offset, $itemsPerPage);
return $modifiedObjects;
}
throw new \InvalidArgumentException(
'The ViewHelper "' . static::class
. '" accepts as argument "QueryResultInterface", "\SplObjectStorage", "ObjectStorage" or an array. '
. 'given: ' . get_class($this->objects),
1385547291
);
}
}
@@ -0,0 +1,127 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Widget;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
/**
* A ViewHelper for creating Links to Extbase actions within widgets.
*
* Examples
* ========
*
* URI to the show-action of the current controller::
*
* <f:widget.link action="show">link</f:widget.link>
*
* Output::
*
* <a href="index.php?id=123&tx_myextension_plugin[widgetIdentifier][action]=show&tx_myextension_plugin[widgetIdentifier][controller]=Standard&cHash=xyz">link</a>
*
* Depending on current page, routing and page path configuration.
*/
class LinkViewHelper extends AbstractTagBasedViewHelper
{
/**
* @var string
*/
protected $tagName = 'a';
/**
* Initialize arguments
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerUniversalTagAttributes();
$this->registerTagAttribute('name', 'string', 'Specifies the name of an anchor');
$this->registerTagAttribute('rel', 'string', 'Specifies the relationship between the current document and the linked document');
$this->registerTagAttribute('rev', 'string', 'Specifies the relationship between the linked document and the current document');
$this->registerTagAttribute('target', 'string', 'Specifies where to open the linked document');
$this->registerArgument('useCacheHash', 'bool', 'True whether the cache hash should be appended to the URL', false, false);
$this->registerArgument('addQueryStringMethod', 'string', 'Method to be used for query string');
$this->registerArgument('action', 'string', 'Target action');
$this->registerArgument('arguments', 'array', 'Arguments', false, []);
$this->registerArgument('section', 'string', 'The anchor to be added to the URI', false, '');
$this->registerArgument('format', 'string', 'The requested format, e.g. ".html', false, '');
$this->registerArgument('ajax', 'bool', 'TRUE if the URI should be to an AJAX widget, FALSE otherwise.', false, false);
}
/**
* Render the link.
*
* @return string The rendered link
*/
public function render()
{
$ajax = $this->arguments['ajax'];
if ($ajax === true) {
$uri = $this->getAjaxUri();
} else {
$uri = $this->getWidgetUri();
}
$this->tag->addAttribute('href', $uri);
$this->tag->setContent($this->renderChildren());
return $this->tag->render();
}
/**
* Get the URI for an AJAX Request.
*
* @return string the AJAX URI
*/
protected function getAjaxUri()
{
$action = $this->arguments['action'];
$arguments = $this->arguments['arguments'];
if ($action === null) {
$action = $this->renderingContext->getControllerContext()->getRequest()->getControllerActionName();
}
$arguments['id'] = $GLOBALS['TSFE']->id;
// @todo page type should be configurable
$arguments['type'] = 7076;
$arguments['fluid-widget-id'] = $this->renderingContext->getControllerContext()->getRequest()->getWidgetContext()->getAjaxWidgetIdentifier();
$arguments['action'] = $action;
return '?' . http_build_query($arguments, null, '&');
}
/**
* Get the URI for a non-AJAX Request.
*
* @return string the Widget URI
*/
protected function getWidgetUri()
{
$uriBuilder = $this->renderingContext->getControllerContext()->getUriBuilder();
$argumentPrefix = $this->renderingContext->getControllerContext()->getRequest()->getArgumentPrefix();
$arguments = $this->hasArgument('arguments') ? $this->arguments['arguments'] : [];
if ($this->hasArgument('action')) {
$arguments['action'] = $this->arguments['action'];
}
if ($this->hasArgument('format') && $this->arguments['format'] !== '') {
$arguments['format'] = $this->arguments['format'];
}
return $uriBuilder->reset()
->setArguments([$argumentPrefix => $arguments])
->setSection($this->arguments['section'])
->setUseCacheHash($this->arguments['useCacheHash'])
->setAddQueryString(true)
->setAddQueryStringMethod($this->arguments['addQueryStringMethod'])
->setArgumentsToBeExcludedFromQueryString([$argumentPrefix, 'cHash'])
->setFormat($this->arguments['format'])
->build();
}
}
@@ -0,0 +1,97 @@
<?php
namespace TYPO3\CMS\Fluid\ViewHelpers\Widget;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
use TYPO3\CMS\Fluid\Core\Widget\AbstractWidgetViewHelper;
/**
* This ViewHelper renders a Pagination of objects.
*
* Examples
* ========
*
* Required arguments
* ------------------
*
* ::
*
* <f:widget.paginate objects="{blogs}" as="paginatedBlogs">
* use {paginatedBlogs} as you used {blogs} before, most certainly inside
* a <f:for> loop.
* </f:widget.paginate>
*
* Full configuration
* ------------------
*
* ::
*
* <f:widget.paginate objects="{blogs}" as="paginatedBlogs" configuration="{itemsPerPage: 5, insertAbove: 1, insertBelow: 0, maximumNumberOfLinks: 10}">
* use {paginatedBlogs} as you used {blogs} before, most certainly inside
* a <f:for> loop.
* </f:widget.paginate>
*
* Performance characteristics
* ===========================
*
* In the above examples, it looks like {blogs} contains all Blog objects, thus
* you might wonder if all objects were fetched from the database.
* However, the blogs are NOT fetched from the database until you actually use them,
* so the paginate ViewHelper will adjust the query sent to the database and receive
* only the small subset of objects.
* So, there is no negative performance overhead in using the Paginate Widget.
*/
class PaginateViewHelper extends AbstractWidgetViewHelper
{
/**
* @var \TYPO3\CMS\Fluid\ViewHelpers\Widget\Controller\PaginateController
*/
protected $controller;
/**
* @param \TYPO3\CMS\Fluid\ViewHelpers\Widget\Controller\PaginateController $controller
*/
public function injectPaginateController(\TYPO3\CMS\Fluid\ViewHelpers\Widget\Controller\PaginateController $controller)
{
$this->controller = $controller;
}
/**
* Initialize arguments.
*
* @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('objects', 'mixed', 'Object', true);
$this->registerArgument('as', 'string', 'as', true);
$this->registerArgument('configuration', 'array', 'configuration', false, ['itemsPerPage' => 10, 'insertAbove' => false, 'insertBelow' => true, 'maximumNumberOfLinks' => 99]);
}
/**
* @return string
* @throws \UnexpectedValueException
*/
public function render()
{
$objects = $this->arguments['objects'];
if (!($objects instanceof QueryResultInterface || $objects instanceof ObjectStorage || is_array($objects))) {
throw new \UnexpectedValueException('Supplied file object type ' . get_class($objects) . ' must be QueryResultInterface or ObjectStorage or be an array.', 1454510731);
}
return $this->initiateSubRequest();
}
}
Loaded 100 of 12716 files, more files were not shown because too many files have changed in this diff. Show more