Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Neos 9 #48

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
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
7 changes: 3 additions & 4 deletions Classes/Service/DataSource/MatomoDataSource.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
*/

use Flowpack\Neos\Matomo\Service\Reporting;
use Neos\ContentRepository\Core\Projection\ContentGraph\Node;
use Neos\Flow\Annotations as Flow;
use Neos\Neos\Service\DataSource\AbstractDataSource;
use Neos\ContentRepository\Domain\Model\NodeInterface;
use Flowpack\Neos\Matomo\Domain\Dto\ErrorDataResult;

class MatomoDataSource extends AbstractDataSource
Expand All @@ -35,9 +35,9 @@ class MatomoDataSource extends AbstractDataSource
*
* {@inheritdoc}
*/
public function getData(NodeInterface $node = NULL, array $arguments = [])
public function getData(Node $node = NULL, array $arguments = [])
{
$data = $this->reportingService->getNodeStatistics($node, $this->controllerContext, $arguments);
$data = $this->reportingService->getNodeStatistics($node, $this->controllerContext->getRequest(), $arguments);

if ($data instanceof ErrorDataResult) {
return [
Expand All @@ -49,5 +49,4 @@ public function getData(NodeInterface $node = NULL, array $arguments = [])
'data' => $data
];
}

}
101 changes: 49 additions & 52 deletions Classes/Service/Reporting.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,32 @@
* source code.
*/

use Flowpack\Neos\Matomo\Domain\Dto\ErrorDataResult;
use Flowpack\Neos\Matomo\Exception\StatisticsNotAvailableException;
use Flowpack\Neos\Matomo\Domain\Dto\AbstractDataResult;
use Flowpack\Neos\Matomo\Domain\Dto\TimeSeriesDataResult;
use Flowpack\Neos\Matomo\Domain\Dto\BrowserDataResult;
use Flowpack\Neos\Matomo\Domain\Dto\ColumnDataResult;
use Flowpack\Neos\Matomo\Domain\Dto\DeviceDataResult;
use Flowpack\Neos\Matomo\Domain\Dto\ErrorDataResult;
use Flowpack\Neos\Matomo\Domain\Dto\OperatingSystemDataResult;
use Flowpack\Neos\Matomo\Domain\Dto\BrowserDataResult;
use Flowpack\Neos\Matomo\Domain\Dto\OutlinkDataResult;
use Flowpack\Neos\Matomo\Domain\Dto\TimeSeriesDataResult;
use GuzzleHttp\Psr7\Uri;
use Neos\Cache\Frontend\VariableFrontend;
use Neos\ContentRepository\Domain\Service\ContextFactoryInterface;
use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindClosestNodeFilter;
use Neos\ContentRepository\Core\Projection\ContentGraph\Node;
use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints;
use Neos\ContentRepository\Core\SharedModel\Node\NodeAddress;
use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName;
use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\I18n\Translator;
use Neos\Flow\Mvc\Controller\ControllerContext;
use Neos\Flow\Http\Client\CurlEngine;
use Neos\Flow\Http\Client\Browser;
use Neos\ContentRepository\Domain\Model\NodeInterface;
use Neos\Neos\Domain\Service\ContentContext;
use Neos\Flow\Http\Client\CurlEngine;
use Neos\Flow\I18n\Translator;
use Neos\Flow\Mvc\ActionRequest;
use Neos\Neos\Domain\Service\NodeTypeNameFactory;
use Neos\Neos\FrontendRouting\NodeUriBuilderFactory;
use Neos\Neos\FrontendRouting\Options;
use Neos\Neos\Service\Controller\AbstractServiceController;
use Neos\Neos\Service\LinkingService;
use Psr\Http\Message\UriInterface;

/**
* Class Reporting
Expand All @@ -48,15 +53,15 @@ class Reporting extends AbstractServiceController

/**
* @Flow\Inject
* @var LinkingService
* @var NodeUriBuilderFactory
*/
protected $linkingService;
protected $nodeUriBuilderFactory;

/**
* @Flow\Inject
* @var ContextFactoryInterface
* @var ContentRepositoryRegistry
*/
protected $contextFactory;
protected $contentRepositoryRegistry;

/**
* @Flow\Inject
Expand Down Expand Up @@ -102,25 +107,29 @@ public function callAPI(
/**
* Call the Matomo Reporting API for node specific statistics
*
* @param NodeInterface $node the node for which the statistics should be retrieved
* @param ControllerContext $controllerContext needed to build a valid node uri
* @param Node $node the node for which the statistics should be retrieved
* @param array $arguments contains the httpRequest arguments for the apiCall
* @param bool $useCache will return previously return data from Matomo if true
* @return AbstractDataResult
*/
public function getNodeStatistics(
NodeInterface $node = null,
ControllerContext $controllerContext = null,
array $arguments = [],
$useCache = true
?Node $node,
ActionRequest $actionRequest,
array $arguments,
bool $useCache = true
): ?AbstractDataResult {
if (!empty($this->settings['host']) && !empty($this->settings['protocol']) && !empty($this->settings['token_auth']) && !empty($this->settings['idSite'])) {
$contextProperties = $node->getContext()->getProperties();
$contextProperties['workspaceName'] = 'live';
$contentRepository = $this->contentRepositoryRegistry->get($node->contentRepositoryId);
$subgraph = $contentRepository->getContentGraph(WorkspaceName::forLive())->getSubgraph(
$node->dimensionSpacePoint,
VisibilityConstraints::frontend()
);

/** @var ContentContext $liveContext */
$liveContext = $this->contextFactory->create($contextProperties);
$liveNode = $liveContext->getNodeByIdentifier($node->getIdentifier());
if ($node->workspaceName->isLive()) {
$liveNode = $node;
} else {
$liveNode = $subgraph->findNodeById($node->aggregateId);
}

if ($liveNode === null) {
return new ErrorDataResult([
Expand All @@ -129,7 +138,7 @@ public function getNodeStatistics(
}

try {
$pageUrl = $this->getLiveNodeUri($liveNode, $controllerContext)->__toString();
$pageUrl = (string)$this->getLiveNodeUri($liveNode, $actionRequest);
} catch (\Exception $e) {
$this->logger->warning($e->getMessage(), \Neos\Flow\Log\Utility\LogEnvironment::fromMethodName(__METHOD__));
return new ErrorDataResult([
Expand All @@ -143,7 +152,12 @@ public function getNodeStatistics(
}
$arguments['pageUrl'] = $pageUrl;

$apiCallUrl = $this->buildApiCallUrl($liveContext->getCurrentSite()->getNodeName(), $arguments);
$siteNode = $subgraph->findClosestNode(
$node->aggregateId,
FindClosestNodeFilter::create(nodeTypes: NodeTypeNameFactory::NAME_SITE)
);

$apiCallUrl = $this->buildApiCallUrl($siteNode->name->value, $arguments);
$cacheLifetime = $this->getCacheLifetimeForArguments($arguments);
$results = $this->request($apiCallUrl, $useCache, $cacheLifetime);

Expand All @@ -154,59 +168,42 @@ public function getNodeStatistics(
switch ($arguments['view']) {
case 'TimeSeriesView':
return new TimeSeriesDataResult($results);
break;
case 'ColumnView':
return new ColumnDataResult($results);
break;
}
switch ($arguments['type']) {
case 'device':
return new DeviceDataResult($results);
break;
case 'osFamilies':
return new OperatingSystemDataResult($results);
break;
case 'browsers':
return new BrowserDataResult($results);
break;
case 'outlinks':
return new OutlinkDataResult($results);
break;
}
}
return null;
}

/**
* Resolve an URI for the given node in the live workspace (this is where analytics usually are collected)
*
* @param NodeInterface|null $liveNode
* @param ControllerContext $controllerContext
* @return Uri
* @throws StatisticsNotAvailableException If the node was not yet published and no live workspace URI can be resolved
* @throws \Exception
*/
protected function getLiveNodeUri(NodeInterface $liveNode, ControllerContext $controllerContext): Uri
protected function getLiveNodeUri(Node $liveNode, ActionRequest $actionRequest): UriInterface
{
if ($liveNode === null) {
throw new StatisticsNotAvailableException('Matomo Statistics are only available on a published node',
1445812693);
}
$nodeUriString = $this->linkingService->createNodeUri($controllerContext, $liveNode, null, 'html', true);
$nodeUri = new Uri($nodeUriString);
$nodeUriBuilder = $this->nodeUriBuilderFactory->forActionRequest($actionRequest);

return $nodeUri;
return $nodeUriBuilder->uriFor(NodeAddress::fromNode($liveNode), Options::createForceAbsolute()->withCustomFormat('html'));
}

/**
* Send a request via curl to the api endpoint and returns the response
*
* @param Uri $apiCallUrl
* @param UriInterface $apiCallUrl
* @param bool $useCache
* @param integer $cacheLifetime of this entry in seconds. If NULL is specified, the default lifetime is used. "0" means unlimited lifetime.
* @return array|null the json decoded content of the api response or null if an error occurs
*/
protected function request(Uri $apiCallUrl, bool $useCache = true, ?int $cacheLifetime = null): ?array
protected function request(UriInterface $apiCallUrl, bool $useCache = true, ?int $cacheLifetime = null): ?array
{
$cacheIdentifier = sha1((string)$apiCallUrl);
if ($useCache) {
Expand Down Expand Up @@ -244,9 +241,9 @@ protected function request(Uri $apiCallUrl, bool $useCache = true, ?int $cacheLi
*
* @param string $sitename
* @param array $arguments
* @return Uri
* @return UriInterface
*/
protected function buildApiCallUrl(string $sitename = '', array $arguments = []): Uri
protected function buildApiCallUrl(string $sitename = '', array $arguments = []): UriInterface
{
$arguments = array_filter($arguments, function ($value, $key) {
return !empty($value) && !in_array($key, ['view', 'device', 'type']);
Expand Down
5 changes: 4 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@
"description": "Track visits of your Neos site with the Matomo Open Analytics Platform!",
"license": "GPL-3.0+",
"require": {
"neos/neos": "^5.3 || ^7.0 || ^8.0",
"neos/neos": "^5.3 || ^7.0 || ^8.0 || ^9.0",
"ext-curl": "*",
"guzzlehttp/psr7": "*"

},
"scripts": {
"lint:phpstan": "../../../bin/phpstan analyse"
},
"replace": {
"portachtzig/neos-piwik": "*"
},
Expand Down
4 changes: 4 additions & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
parameters:
level: 5
paths:
- Classes