Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ The Product Changelog at **[matomo.org/changelog](https://matomo.org/changelog)*

## Matomo 5.10.0

### New APIs
* Widgets can now be declared as client-rendered through `WidgetConfig::setClientSideComponent()` and `WidgetConfig::setClientSideProps()`. `API.getWidgetMetadata` and `API.getReportPagesMetadata` now expose a `clientComponent` field for these widgets, and Widgetize/dashboard rendering supports bootstrapping them without an extra widget controller request.

### Deprecations
* The methods `ArchiveTableCreator::getNumericTable()` and `ArchiveTableCreator::getBlobTable()` now support a `$createIfMissing` parameter. Omitting this parameter is deprecated; pass `true` to create missing archive tables or `false` to return only existing tables. In Matomo 6 the default behavior for omitted calls will change to lookup-only.

Expand Down
67 changes: 67 additions & 0 deletions core/Widget/WidgetConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ class WidgetConfig
protected $action = '';
protected $parameters = array();
protected $middlewareParameters = array();
protected $clientSideComponent = array();
protected $clientSideProps = array();
protected $name = '';
protected $order = 99;
protected $isEnabled = true;
Expand Down Expand Up @@ -124,6 +126,10 @@ public function getAction()
* Sets (overwrites) the parameters of the widget. These parameters will be added to the URL when rendering the
* widget. You can access these parameters via `Piwik\Common::getRequestVar(...)`.
*
* This applies to widgets rendered through their controller/action request. Client-rendered widgets do not receive
* these parameters automatically and should instead derive request state from the browser context or load data via
* API requests.
*
* @param array $parameters eg. ('urlparam' => 'urlvalue')
* @return static
*/
Expand All @@ -137,6 +143,9 @@ public function setParameters($parameters)
/**
* Add new parameters and only overwrite parameters that have the same name. See {@link setParameters()}
*
* Like {@link setParameters()}, these parameters are only used for widgets rendered through their
* controller/action request and are not forwarded automatically to client-rendered widgets.
*
* @param array $parameters eg. ('urlparam' => 'urlvalue')
* @return static
*/
Expand Down Expand Up @@ -352,6 +361,64 @@ public function getMiddlewareParameters()
return $this->middlewareParameters;
}

/**
* Marks this widget as client-rendered by a Vue component exported by the given plugin bundle.
*
* Client-rendered widgets do not execute the widget controller/action in a separate request before rendering.
* They should derive dynamic state from the current browser request or load data through API requests instead.
*
* @param string $plugin eg 'Transitions'
* @param string $component eg 'TransitionsPage'
* @return static
* @since 5.10.0
*/
public function setClientSideComponent(string $plugin, string $component)
{
$this->clientSideComponent = array(
'plugin' => $plugin,
'name' => $component,
);

return $this;
}

/**
* Returns the configured client-rendered component definition.
* @return array{}|array{plugin: string, name: string}
* @since 5.10.0
*/
public function getClientSideComponent(): array
{
return $this->clientSideComponent;
}

/**
* Sets static props that should be passed to the client-rendered Vue widget.
*
* Use this for configuration known when the widget is registered. Request-specific widget parameters are not
* forwarded to client-rendered widgets through this mechanism.
*
* @param array $props
* @return static
* @since 5.10.0
*/
public function setClientSideProps(array $props)
{
$this->clientSideProps = $props;

return $this;
}

/**
* Returns props configured for the client-rendered Vue widget.
* @return array
* @since 5.10.0
*/
public function getClientSideProps(): array
{
return $this->clientSideProps;
}

/**
* Marks this widget as a "wide" widget that requires the full width.
*
Expand Down
11 changes: 11 additions & 0 deletions plugins/API/WidgetMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,17 @@ public function buildWidgetMetadata(WidgetConfig $widget, $categoryList = null,
$item['middlewareParameters'] = $middleware;
}

$clientComponent = $widget->getClientSideComponent();

if (!empty($clientComponent)) {
$item['clientComponent'] = $clientComponent;

$clientProps = $widget->getClientSideProps();
if (!empty($clientProps)) {
$item['clientComponent']['props'] = $clientProps;
}
}

if ($widget instanceof ReportWidgetConfig) {
$item['viewDataTable'] = $widget->getViewDataTable();
$item['isReport'] = true;
Expand Down
14 changes: 14 additions & 0 deletions plugins/API/tests/Unit/WidgetMetadataTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,20 @@ public function testBuildWidgetMetadataShouldAddOptionalMiddlewareParameters()
$this->assertSame(array('module' => 'Goals', 'action' => 'hasAnyConversions'), $metadata['middlewareParameters']);
}

public function testBuildWidgetMetadataShouldAddOptionalClientComponent()
{
$config = $this->createWidgetConfig('Test', 'CategoryId', 'SubcategoryId');
$config->setClientSideComponent('Transitions', 'TransitionsPage');
$config->setClientSideProps(array('foo' => 'bar'));
$metadata = $this->metadata->buildWidgetMetadata($config);

$this->assertSame(array(
'plugin' => 'Transitions',
'name' => 'TransitionsPage',
'props' => array('foo' => 'bar'),
), $metadata['clientComponent']);
}

public function testBuildWidgetMetadataShouldAddReportInformtionIfReportWidgetConfigGiven()
{
$config = new ReportWidgetConfig();
Expand Down
Loading
Loading