TYPO3 v15 dev-main snapshot ()

This commit is contained in:
2026-08-10 22:31:14 +02:00
commit ff4622ba97
138 changed files with 9045 additions and 0 deletions
+111
View File
@@ -0,0 +1,111 @@
.. include:: /Includes.rst.txt
.. _adding-buttons:
=======================
Adding button to Widget
=======================
.. php:namespace:: TYPO3\CMS\Dashboard\Widgets
In order to add a button to a widget, a new dependency to an :php:`ButtonProviderInterface` can be added.
.. _adding-buttons-template:
Template
--------
The output itself is done inside of the Fluid template, for example :file:`Resources/Private/Templates/Widget/RssWidget.html`:
.. code-block:: html
<f:if condition="{button}">
<a href="{button.link}" target="{button.target}" class="widget-cta">
{f:translate(id: button.title, default: button.title)}
</a>
</f:if>
.. _adding-buttons-configuration:
Configuration
-------------
The configuration is done through an configured Instance of the dependency, for example :file:`Services.yaml`:
.. code-block:: yaml
services:
# …
dashboard.buttons.t3news:
class: 'TYPO3\CMS\Dashboard\Widgets\Provider\ButtonProvider'
arguments:
$title: 'LLL:EXT:dashboard/Resources/Private/Language/locallang.xlf:widgets.t3news.moreItems'
$link: 'https://news.typo3.com'
$target: '_blank'
dashboard.widget.t3news:
class: 'TYPO3\CMS\Dashboard\Widgets\RssWidget'
arguments:
# …
$buttonProvider: '@dashboard.buttons.t3news'
# …
See also: :php:`\TYPO3\CMS\Dashboard\Widgets\Provider\ButtonProvider`.
.. confval:: $title
:type: string
:name: button-title
The title used for the button. E.g. an ``LLL:EXT:`` reference like
``LLL:EXT:dashboard/Resources/Private/Language/locallang.xlf:widgets.t3news.moreItems``.
.. confval:: $link
:type: string
:name: button-link
The link to use for the button. Clicking the button will open the link.
.. confval:: $target
:type: string
:name: button-target
The target of the link, e.g. ``_blank``.
``LLL:EXT:dashboard/Resources/Private/Language/locallang.xlf:widgets.t3news.moreItems``.
.. _adding-buttons-implementation:
Implementation
--------------
An example implementation could look like this:
.. code-block:: php
:caption: Classes/Widgets/RssWidget.php
class RssWidget implements WidgetInterface
{
public function __construct(
// …
private readonly ButtonProviderInterface $buttonProvider = null,
// …
) {
}
public function renderWidgetContent(): string
{
// …
$this->view->assignMultiple([
// …
'button' => $this->buttonProvider,
// …
]);
// …
}
public function getOptions(): array
{
return $this->options;
}
}
@@ -0,0 +1,34 @@
:navigation-title: Configurable widgets
.. include:: /Includes.rst.txt
.. _configurable-widget-implementation:
============================================
Configurable dashboard widget implementation
============================================
.. versionadded:: 14.0
`Configurable Dashboard Widgets <https://docs.typo3.org/permalink/changelog:feature-107036-1738837673>`_
have been introduced with TYPO3 14.0.
Widget authors can implement configurable widgets by using to the
renderer interface :php:`TYPO3\CMS\Dashboard\Widgets\WidgetRendererInterface`
which allows to defining settings in their widget renderer.
Settings are automatically validated and processed using the Settings API.
All types that are available for site settings definition are available:
`Definition types <https://docs.typo3.org/permalink/t3coreapi:definition-types>`_.
.. seealso::
:php:`TYPO3\CMS\Dashboard\Widgets\RssWidget` is a configurable widget
implementation.
.. _configurable-widget-implementation-example:
Example: A configurable widget implementation
=============================================
.. literalinclude:: _codesnippets/_ConfigurableWidget.php.inc
:language: php
:caption: EXT:my_extension/Classes/Widgets/ConfigurableWidget.php
@@ -0,0 +1,174 @@
.. include:: /Includes.rst.txt
.. _graph-widget-implementation:
======================
Implement graph widget
======================
.. php:namespace:: TYPO3\CMS\Dashboard\Widgets
First of all a new data provider is required, which will provide the data for the chart.
Next the data will be provided to the widget instance, which will be rendered with JavaScript modules and Css.
To make the dashboard aware of this workflow, some interfaces come together:
* :php:`EventDataInterface`
* :php:`AdditionalCssInterface`
Also the existing template file :file:`Widget/ChartWidget` is used, which provides necessary HTML to render the chart.
The provided ``eventData`` will be rendered as a chart and therefore has to match the expected structure.
An example would be :file:`Classes/Widgets/BarChartWidget.php`:
.. code-block:: php
:caption: Classes/Widgets/BarChartWidget.php
class BarChartWidget implements WidgetInterface, EventDataInterface, AdditionalCssInterface
{
public function __construct(
// …
private readonly ChartDataProviderInterface $dataProvider,
// …
) {
// …
$this->dataProvider = $dataProvider;
// …
}
public function renderWidgetContent(): string
{
// …
$this->view->assignMultiple([
// …
'configuration' => $this->configuration,
// …
]);
// …
}
public function getEventData(): array
{
return [
'graphConfig' => [
'type' => 'bar',
'options' => [
// …
],
'data' => $this->dataProvider->getChartData(),
],
];
}
public function getCssFiles(): array
{
return [];
}
public function getOptions(): array
{
return $this->options;
}
}
Together with :file:`Services.yaml`:
.. code-block:: yaml
services:
dashboard.widget.sysLogErrors:
class: 'TYPO3\CMS\Dashboard\Widgets\BarChartWidget'
arguments:
# …
$dataProvider: '@TYPO3\CMS\Dashboard\Widgets\Provider\SysLogErrorsDataProvider'
# …
tags:
- name: dashboard.widget
The configuration adds necessary CSS classes, as well as the ``dataProvider`` to use.
The provider implements :php:`ChartDataProviderInterface` and could look like the following.
.. code-block:: php
:caption: Classes/Widgets/Provider/SysLogErrorsDataProvider
class SysLogErrorsDataProvider implements ChartDataProviderInterface
{
/**
* Number of days to gather information for.
*
* @var int
*/
protected $days = 31;
/**
* @var array
*/
protected $labels = [];
/**
* @var array
*/
protected $data = [];
public function __construct(int $days = 31)
{
$this->days = $days;
}
public function getChartData(): array
{
$this->calculateDataForLastDays();
return [
'labels' => $this->labels,
'datasets' => [
[
'label' => $this->getLanguageService()->sL('LLL:EXT:dashboard/Resources/Private/Language/locallang.xlf:widgets.sysLogErrors.chart.dataSet.0'),
'backgroundColor' => WidgetApi::getDefaultChartColors()[0],
'border' => 0,
'data' => $this->data
]
]
];
}
protected function getNumberOfErrorsInPeriod(int $start, int $end): int
{
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_log');
return (int)$queryBuilder
->count('*')
->from('sys_log')
->where(
$queryBuilder->expr()->eq(
'type',
$queryBuilder->createNamedParameter(SystemLogType::ERROR, Connection::PARAM_INT)
),
$queryBuilder->expr()->gte(
'tstamp',
$queryBuilder->createNamedParameter($start, Connection::PARAM_INT)
),
$queryBuilder->expr()->lte(
'tstamp',
$queryBuilder->createNamedParameter($end, Connection::PARAM_INT)
)
)
->execute()
->fetchColumn();
}
protected function calculateDataForLastDays(): void
{
$format = $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] ?: 'Y-m-d';
for ($daysBefore = $this->days; $daysBefore >= 0; $daysBefore--) {
$this->labels[] = date($format, strtotime('-' . $daysBefore . ' day'));
$startPeriod = strtotime('-' . $daysBefore . ' day 0:00:00');
$endPeriod = strtotime('-' . $daysBefore . ' day 23:59:59');
$this->data[] = $this->getNumberOfErrorsInPeriod($startPeriod, $endPeriod);
}
}
protected function getLanguageService(): LanguageService
{
return $GLOBALS['LANG'];
}
}
+25
View File
@@ -0,0 +1,25 @@
.. include:: /Includes.rst.txt
.. _for-developer:
==============
For Developers
==============
Target group: **Developers**
Welcome to our small dashboard introduction.
We will explain how to create widget groups and implement widgets.
.. include:: /Shared/DifferenceRegistrationAndImplementation.rst.txt
.. toctree::
:maxdepth: 3
:titlesonly:
WidgetImplementation
ConfigurableWidgets
MakeRefreshable
AddingButtons
GraphWidgetImplementation
Interfaces
+178
View File
@@ -0,0 +1,178 @@
.. include:: /Includes.rst.txt
.. _interfaces:
==========
Interfaces
==========
The following list provides information for all necessary interfaces that are used inside of this documentation.
For up to date information, please check the source code.
.. php:namespace:: TYPO3\CMS\Dashboard\Widgets
.. php:class:: WidgetInterface
Has to be implemented by all widgets.
This interface defines public API used by ext:dashboard to interact with widgets.
.. php:method:: renderWidgetContent()
:returntype: string
:returns: The rendered HTML to display.
.. php:method:: getOptions()
:returntype: array
:returns: The options of the widget as set in the registration.
.. php:class:: RequestAwareWidgetInterface
This interface declares a widget has a dependency to the current PSR-7 request.
When implemented, the dashboard controller will call :php:`setRequest()` immediately
after widget instantiation to hand over the current request. Widgets that rely on
:php:`BackendViewFactory` typically need the current request.
.. php:method:: setRequest(ServerRequestInterface $request)
:returntype: void
.. php:class:: WidgetConfigurationInterface
Used internally in ext:dashboard.
Used to separate internal configuration from widgets.
Can be required in widget classes and passed to view.
.. php:method:: getIdentifier()
:returntype: string
:returns: Unique identifer of a widget.
.. php:method:: getServiceName()
:returntype: string
:returns: Service name providing the widget implementation.
.. php:method:: getGroupNames()
:returntype: array
:returns: Group names associated to this widget.
.. php:method:: getTitle()
:returntype: string
:returns: Title of a widget, this is used for the widget selector.
.. php:method:: getDescription()
:returntype: string
:returns: Description of a widget, this is used for the widget selector.
.. php:method:: getIconIdentifier()
:returntype: string
:returns: Icon identifier of a widget, this is used for the widget selector.
.. php:method:: getHeight()
:returntype: int
:returns: Height of a widget in rows (1-6).
.. php:method:: getWidth()
:returntype: int
:returns: Width of a widget in columns (1-4).
.. php:class:: AdditionalJavaScriptInterface
Widgets implementing this interface will add the provided JavaScript files.
Those files will be loaded in dashboard view if the widget is added at least once.
.. php:method:: getJsFiles()
Returns a list of JavaScript file names that should be included, e.g.:
.. code-block:: php
return [
'EXT:my_extension/Resources/Public/JavaScript/file.js',
'EXT:my_extension/Resources/Public/JavaScript/file2.js',
];
:returntype: array
:returns: List of JS files to load.
.. php:class:: AdditionalCssInterface
Widgets implementing this interface will add the provided Css files.
Those files will be loaded in dashboard view if the widget is added at least once.
.. php:method:: getCssFiles()
Returns a list of Css file names that should be included, e.g.:
.. code-block:: php
return [
'EXT:my_extension/Resources/Public/Css/widgets.css',
'EXT:my_extension/Resources/Public/Css/list-widget.css',
];
:returntype: array
:returns: List of Css files to load.
.. php:class:: ButtonProviderInterface
.. php:method:: getTitle()
:returntype: string
:returns: The title used for the button. E.g. an ``LLL:EXT:`` reference like
``LLL:EXT:dashboard/Resources/Private/Language/locallang.xlf:widgets.t3news.moreItems``.
.. php:method:: getLink()
:returntype: string
:returns: The link to use for the button. Clicking the button will open the link.
.. php:method:: getTarget()
:returntype: string
:returns: The target of the link, e.g. ``_blank``.
``LLL:EXT:dashboard/Resources/Private/Language/locallang.xlf:widgets.t3news.moreItems``.
.. php:class:: NumberWithIconDataProviderInterface
.. php:method:: getNumber()
:returntype: integer
:returns: The number to display for an number widget.
.. php:class:: EventDataInterface
.. php:method:: getEventData()
:returntype: array
:returns: Returns data which should be send to the widget as JSON encoded value.
.. php:class:: ChartDataProviderInterface
.. php:method:: getChartData()
:returntype: array
:returns: Provide the data for a graph.
The data and options you have depend on the type of chart.
More information can be found in the documentation of the specific type:
Bar
https://www.chartjs.org/docs/latest/charts/bar.html#data-structure
Doughnut
https://www.chartjs.org/docs/latest/charts/doughnut.html#data-structure
.. php:class:: ListDataProviderInterface
.. php:method:: getItems()
:returntype: array
:returns: Provide the array if items.
Each entry should be a single string.
@@ -0,0 +1,83 @@
.. include:: /Includes.rst.txt
.. _make-refreshable:
==================
The refresh option
==================
In each widget the refresh option can be enabled. If the option is enabled the
widget displays a reload button in the top right corner. It can then be
refreshed via user interaction or via a javascript api.
To enable the refresh action button, you have to define the
:yaml:`refreshAvailable` option in the :yaml:`$options` part of the widget
registration. Below is an example of a RSS widget with the refresh option enabled.
.. code-block:: yaml
dashboard.widget.myOwnRSSWidget:
class: 'TYPO3\CMS\Dashboard\Widgets\RssWidget'
arguments:
$options:
rssFile: 'https://typo3.org/rss'
lifeTime: 43200
refreshAvailable: true
tags:
- name: dashboard.widget
identifier: 'myOwnRSSWidget'
groupNames: 'general'
title: 'LLL:EXT:extension/Resources/Private/Language/locallang.xlf:widgets.myOwnRSSWidget.title'
description: 'LLL:EXT:extension/Resources/Private/Language/locallang.xlf:widgets.myOwnRSSWidget.description'
iconIdentifier: 'content-widget-rss'
height: 'medium'
width: 'medium'
.. note::
In this example, the TYPO3 core :php:`TYPO3\CMS\Dashboard\Widgets\RssWidget`
widget class is used. In case you have implemented own widget classes, you
have to implement the :php:`getOptions()` method, returning :php:`$this->options`,
to the corresponding classes. Otherwise the refresh option won't have any
effect.
.. _refresh-button:
Enable the refresh button
-------------------------
Widgets can render a refresh button to allow users to manually refresh them.
This is done by passing the value :php:`['refreshAvailable'] = true;` back
via :php:`getOptions()` method of the widget.
All TYPO3 Core widgets implement this behaviour and allow integrators to
configure the option:
.. include:: /Widgets/Options/RefreshAvailable.rst.txt
.. _refresh-javascript:
JavaScript API
--------------
It is possible for all widgets to dispatch an event, which will cause
the widget being refreshed. This is possible for all widgets on the dashboard
even when the :yaml:`refreshAvailable` option is not defined, or set to `false`.
This will give developers the option to refresh the widgets whenever they think
it is appropriate.
To refresh a widget, dispatch the :js:`widgetRefresh` event on the
widget container (the :html:`div` element with the :html:`dashboard-item` class).
You can identify the container by the data attribute :html:`widget-hash`, which
is a unique hash for every widget, even if you have more widgets of the same
type on your dashboard.
A small example below:
.. code-block:: javascript
document
.querySelector('[data-widget-hash="{your-unique-widget-hash}"]')
.dispatchEvent(new Event('widgetRefresh', {bubbles: true}));
See :ref:`implement-new-widget-custom-js` to learn how to add custom JavaScript.
@@ -0,0 +1,195 @@
.. include:: /Includes.rst.txt
.. _implement-new-widget:
====================
Implement new widget
====================
.. versionadded:: 14.0
`Configurable Dashboard Widgets <https://docs.typo3.org/permalink/changelog:feature-107036-1738837673>`_
have been introduced with TYPO3 14.0.
See also
.. php:namespace:: TYPO3\CMS\Dashboard\Widgets
.. seealso::
For information regarding registration of widgets, see: :ref:`register-new-widget`.
This section describes the implementation of new widgets for developers.
Each extension can provide multiple Widgets.
ext:dashboard already ships with some widget implementations.
Each widget has to be implemented as a PHP class.
The PHP class defines the concrete implementation and features of a widget,
while registration adds necessary options for a concrete instance of a widget.
For example a TYPO3.org RSS Widget would consist of an :php:`RssWidget` PHP class.
This class would provide the implementation to fetch rss news and display them.
The concrete registration will provide the URL to RSS feed.
.. _widget-php-class:
PHP class
---------
Each Widget has to be a PHP class.
This class has to implement the :php:`WidgetInterface` and could look like this:
.. code-block:: php
class RssWidget implements WidgetInterface, RequestAwareWidgetInterface
{
private ServerRequestInterface $request;
public function __construct(
private readonly WidgetConfigurationInterface $configuration,
private readonly Cache $cache,
private readonly BackendViewFactory $backendViewFactory,
private readonly ?ButtonProviderInterface $buttonProvider = null,
private readonly array $options = []
) {
}
public function setRequest(ServerRequestInterface $request): void
{
$this->request = $request;
}
public function renderWidgetContent(): string
{
$view = $this->backendViewFactory->create($this->request);
$view->assignMultiple([
'items' => $this->getRssItems(),
'options' => $this->options,
'button' => $this->getButton(),
'configuration' => $this->configuration,
]);
return $view->render('Widget/RssWidget');
}
protected function getRssItems(): array
{
$items = [];
// Logic to populate $items array
return $items;
}
public function getOptions(): array
{
return $this->options;
}
}
The class should always provide documentation how to use in :file:`Services.yaml`.
The above class is documented at :ref:`rss-widget`.
The documentation should provide all possible options and an concrete example.
It should make it possible for integrators to register new widgets using the implementation.
The difference between :php:`$options` and :php:`$configuration` in above example is the following:
:php:`$options` are the options for this implementation which can be provided through :file:`Services.yaml`.
:php:`$configuration` is an instance of :php:`WidgetConfigurationInterface`
holding all internal configuration, like icon identifier.
.. _implement-new-widget-fluid:
Using Fluid
-----------
Most widgets will need a template.
Therefore each widget can define :php:`BackendViewFactory` as requirement for DI in
constructor, like done in RSS example.
.. _implement-new-widget-custom-js:
Providing custom JS
-------------------
There are two ways to add JavaScript for an widget:
JavaScript module
Implement :php:`\TYPO3\CMS\Dashboard\Widgets\JavaScriptInterface`:
.. code-block:: php
class ExampleChartWidget implements JavaScriptInterface
{
// ...
public function getJavaScriptModuleInstructions(): array
{
return [
JavaScriptModuleInstruction::create(
'@myvendor/my-extension/module-name.js'
)->invoke('initialize'),
JavaScriptModuleInstruction::create(
'@myvendor/my-extension/module-name2.js'
)->invoke('initialize'),
];
}
}
.. seealso::
:ref:`t3coreapi:backend-javascript-es6` for more info about JavaScript in TYPO3 Backend.
Plain JS files
Implement :php:`AdditionalJavaScriptInterface`:
.. code-block:: php
class RssWidget implements WidgetInterface, AdditionalJavaScriptInterface
{
public function getJsFiles(): array
{
return [
'EXT:my_extension/Resources/Public/JavaScript/file.js',
'EXT:my_extension/Resources/Public/JavaScript/file2.js',
];
}
}
JavaScript
Implement :php:`\TYPO3\CMS\Dashboard\Widgets\JavaScriptInterface`:
.. code-block:: php
class ExampleChartWidget implements JavaScriptInterface
{
// ...
public function getJavaScriptModuleInstructions(): array
{
return [
JavaScriptModuleInstruction::create(
'@typo3/dashboard/chart-initializer.js'
)->invoke('initialize'),
];
}
}
All ways can be combined.
.. _custom-css:
Providing custom CSS
--------------------
It is possible to add custom Css to style widgets.
Implement :php:`AdditionalCssInterface`:
.. code-block:: php
class RssWidget implements WidgetInterface, AdditionalCssInterface
{
public function getCssFiles(): array
{
return [
'EXT:my_extension/Resources/Public/Css/widgets.css',
'EXT:my_extension/Resources/Public/Css/list-widget.css',
];
}
}
@@ -0,0 +1,43 @@
<?php
use TYPO3\CMS\Dashboard\Widgets\WidgetContext;
use TYPO3\CMS\Dashboard\Widgets\WidgetRendererInterface;
use TYPO3\CMS\Dashboard\Widgets\WidgetResult;
use TYPO3\CMS\Core\Settings\SettingDefinition;
class ConfigurableWidget implements WidgetRendererInterface
{
public function getSettingsDefinitions(): array
{
return [
new SettingDefinition(
key: 'title',
type: 'string',
default: 'Default Title',
label: 'LLL:EXT:my_extension/Resources/Private/Language/locallang_my_widget.xlf:settings.label',
description: 'LLL:EXT:my_extension/Resources/Private/Language/locallang_my_widget.xlf:settings.description.label',
),
new SettingDefinition(
key: 'limit',
type: 'int',
default: 10,
label: 'LLL:EXT:my_extension/Resources/Private/Language/locallang_my_widget.xlf:settings.limit',
description: 'LLL:EXT:my_extension/Resources/Private/Language/locallang_my_widget.xlf:settings.description.limit',
),
];
}
public function renderWidget(WidgetContext $context): WidgetResult
{
$settings = $context->settings;
$title = $settings->get('title');
$limit = $settings->get('limit');
// Use settings to customize widget output
return new WidgetResult(
content: '<!-- widget content -->',
label: $title,
refreshable: true
);
}
}