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,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();
}
}
@@ -0,0 +1,121 @@
<?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\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* A ViewHelper for creating URIs to Extbase actions within widgets.
*
* Examples
* ========
*
* URI to the show-action of the current controller::
*
* <f:widget.uri action="show" />
*
* ``/page/path/name.html?tx_myextension_plugin[widgetIdentifier][action]=show&tx_myextension_plugin[widgetIdentifier][controller]=Standard&cHash=xyz``
*
* Depending on current page, routing and page path configuration.
*/
class UriViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* Initialize arguments
*/
public function initializeArguments()
{
$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);
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return string
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$ajax = $arguments['ajax'];
if ($ajax === true) {
return static::getAjaxUri($renderingContext, $arguments);
}
return static::getWidgetUri($renderingContext, $arguments);
}
/**
* Get the URI for an AJAX Request.
*
* @param RenderingContextInterface $renderingContext
* @param array $arguments
* @return string the AJAX URI
*/
protected static function getAjaxUri(RenderingContextInterface $renderingContext, array $arguments)
{
$controllerContext = $renderingContext->getControllerContext();
$action = $arguments['action'];
$arguments = $arguments['arguments'];
if ($action === null) {
$action = $controllerContext->getRequest()->getControllerActionName();
}
$arguments['id'] = $GLOBALS['TSFE']->id;
// @todo page type should be configurable
$arguments['type'] = 7076;
$arguments['fluid-widget-id'] = $controllerContext->getRequest()->getWidgetContext()->getAjaxWidgetIdentifier();
$arguments['action'] = $action;
return '?' . http_build_query($arguments, null, '&');
}
/**
* Get the URI for a non-AJAX Request.
*
* @param RenderingContextInterface $renderingContext
* @param array $arguments
* @return string the Widget URI
*/
protected static function getWidgetUri(RenderingContextInterface $renderingContext, array $arguments)
{
$controllerContext = $renderingContext->getControllerContext();
$uriBuilder = $controllerContext->getUriBuilder();
$argumentPrefix = $controllerContext->getRequest()->getArgumentPrefix();
$parameters = $arguments['arguments'] ?? [];
if ($arguments['action'] ?? false) {
$parameters['action'] = $arguments['action'];
}
if (($arguments['format'] ?? '') !== '') {
$parameters['format'] = $arguments['format'];
}
return $uriBuilder->reset()
->setArguments([$argumentPrefix => $parameters])
->setSection($arguments['section'])
->setUseCacheHash($arguments['useCacheHash'])
->setAddQueryString(true)
->setAddQueryStringMethod($arguments['addQueryStringMethod'])
->setArgumentsToBeExcludedFromQueryString([$argumentPrefix, 'cHash'])
->setFormat($arguments['format'])
->build();
}
}