mirror of
https://github.com/DanielSiepmann/tracking.git
synced 2024-11-22 05:56:08 +01:00
Add tracking of record views
Allow Integrator to define rules to track views of records. This allows to add tracking to extensions like tx_news. Integrators are able to define matching rules on current request. If a request matches, the record is stored as individual view beside existing pageview. Relates: #14
This commit is contained in:
parent
44ca6a2d3c
commit
33685d1735
25 changed files with 1843 additions and 95 deletions
207
Classes/Dashboard/Provider/Recordviews.php
Normal file
207
Classes/Dashboard/Provider/Recordviews.php
Normal file
|
@ -0,0 +1,207 @@
|
|||
<?php
|
||||
|
||||
namespace DanielSiepmann\Tracking\Dashboard\Provider;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2020 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use DanielSiepmann\Tracking\Extension;
|
||||
use Doctrine\DBAL\ParameterType;
|
||||
use Doctrine\DBAL\Statement;
|
||||
use TYPO3\CMS\Core\Database\Connection;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
|
||||
use TYPO3\CMS\Core\Database\Query\Restriction\EndTimeRestriction;
|
||||
use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
|
||||
use TYPO3\CMS\Core\Database\Query\Restriction\StartTimeRestriction;
|
||||
use TYPO3\CMS\Dashboard\WidgetApi;
|
||||
use TYPO3\CMS\Dashboard\Widgets\ChartDataProviderInterface;
|
||||
|
||||
class Recordviews implements ChartDataProviderInterface
|
||||
{
|
||||
/**
|
||||
* @var ConnectionPool
|
||||
*/
|
||||
private $connectionPool;
|
||||
|
||||
/**
|
||||
* @var QueryBuilder
|
||||
*/
|
||||
private $queryBuilder;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $days;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $maxResults;
|
||||
|
||||
/**
|
||||
* @var array<int>
|
||||
*/
|
||||
private $pagesToExclude;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $recordTableLimitation;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $recordTypeLimitation;
|
||||
|
||||
public function __construct(
|
||||
ConnectionPool $connectionPool,
|
||||
QueryBuilder $queryBuilder,
|
||||
int $days = 31,
|
||||
int $maxResults = 6,
|
||||
array $pagesToExclude = [],
|
||||
array $recordTableLimitation = [],
|
||||
array $recordTypeLimitation = []
|
||||
) {
|
||||
$this->connectionPool = $connectionPool;
|
||||
$this->queryBuilder = $queryBuilder;
|
||||
$this->days = $days;
|
||||
$this->pagesToExclude = $pagesToExclude;
|
||||
$this->maxResults = $maxResults;
|
||||
$this->recordTableLimitation = $recordTableLimitation;
|
||||
$this->recordTypeLimitation = $recordTypeLimitation;
|
||||
}
|
||||
|
||||
public function getChartData(): array
|
||||
{
|
||||
list($labels, $data) = $this->getRecordviews();
|
||||
|
||||
return [
|
||||
'labels' => $labels,
|
||||
'datasets' => [
|
||||
[
|
||||
'backgroundColor' => WidgetApi::getDefaultChartColors(),
|
||||
'data' => $data,
|
||||
]
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
private function getRecordviews(): array
|
||||
{
|
||||
$labels = [];
|
||||
$data = [];
|
||||
|
||||
foreach ($this->getRecordviewsRecords() as $recordview) {
|
||||
$record = $this->getRecord($recordview['record_uid'], $recordview['record_table_name']);
|
||||
|
||||
if (
|
||||
$this->recordTypeLimitation !== []
|
||||
&& in_array($record['type'], $this->recordTypeLimitation) === false
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$labels[] = mb_strimwidth($record['title'], 0, 25, '…');
|
||||
$data[] = $recordview['total'];
|
||||
}
|
||||
|
||||
return [
|
||||
$labels,
|
||||
$data,
|
||||
];
|
||||
}
|
||||
|
||||
private function getRecordviewsRecords(): \Generator
|
||||
{
|
||||
$constraints = [
|
||||
$this->queryBuilder->expr()->gte(
|
||||
'tx_tracking_recordview.crdate',
|
||||
strtotime('-' . $this->days . ' day 0:00:00')
|
||||
),
|
||||
$this->queryBuilder->expr()->lte(
|
||||
'tx_tracking_recordview.crdate',
|
||||
time()
|
||||
),
|
||||
];
|
||||
|
||||
if (count($this->pagesToExclude)) {
|
||||
$constraints[] = $this->queryBuilder->expr()->notIn(
|
||||
'tx_tracking_recordview.pid',
|
||||
$this->queryBuilder->createNamedParameter(
|
||||
$this->pagesToExclude,
|
||||
Connection::PARAM_INT_ARRAY
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (count($this->recordTableLimitation)) {
|
||||
$constraints[] = $this->queryBuilder->expr()->in(
|
||||
'tx_tracking_recordview.record_table_name',
|
||||
$this->queryBuilder->createNamedParameter(
|
||||
$this->recordTableLimitation,
|
||||
Connection::PARAM_STR_ARRAY
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$result = $this->queryBuilder
|
||||
->selectLiteral('count(record) as total')
|
||||
->addSelect('record_uid', 'record_table_name')
|
||||
->from('tx_tracking_recordview')
|
||||
->where(... $constraints)
|
||||
->groupBy('record')
|
||||
->orderBy('total', 'desc')
|
||||
->setMaxResults($this->maxResults)
|
||||
->execute();
|
||||
|
||||
while ($row = $result->fetch()) {
|
||||
yield $row;
|
||||
}
|
||||
}
|
||||
|
||||
private function getRecord(int $recordUid, string $recordTable): array
|
||||
{
|
||||
$titlefield = $GLOBALS['TCA'][$recordTable]['ctrl']['label'];
|
||||
$recordTypeField = $GLOBALS['TCA'][$recordTable]['ctrl']['type'] ?? '';
|
||||
|
||||
$queryBuilder = $this->connectionPool->getQueryBuilderForTable($recordTable);
|
||||
|
||||
$queryBuilder->getRestrictions()
|
||||
->removeByType(StartTimeRestriction::class)
|
||||
->removeByType(EndTimeRestriction::class)
|
||||
->removeByType(HiddenRestriction::class)
|
||||
;
|
||||
|
||||
$queryBuilder->select($titlefield)
|
||||
->from($recordTable)
|
||||
->where('uid = ' . $recordUid);
|
||||
|
||||
if ($recordTypeField !== '') {
|
||||
$queryBuilder->addSelect($recordTypeField);
|
||||
}
|
||||
|
||||
$record = $queryBuilder->execute()->fetch();
|
||||
|
||||
return [
|
||||
'title' => $record[$titlefield],
|
||||
'type' => $record[$recordTypeField] ?? '',
|
||||
];
|
||||
}
|
||||
}
|
60
Classes/Domain/Model/Extractor.php
Normal file
60
Classes/Domain/Model/Extractor.php
Normal file
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
namespace DanielSiepmann\Tracking\Domain\Model;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2020 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* API to extract further info out of an model.
|
||||
*/
|
||||
class Extractor
|
||||
{
|
||||
public static function getOperatingSystem(HasUserAgent $model): string
|
||||
{
|
||||
$userAgent = $model->getUserAgent();
|
||||
|
||||
if (mb_stripos($userAgent, 'Android') !== false) {
|
||||
return 'Android';
|
||||
}
|
||||
if (mb_stripos($userAgent, 'Windows') !== false) {
|
||||
return 'Windows';
|
||||
}
|
||||
if (mb_stripos($userAgent, 'Linux') !== false) {
|
||||
return 'Linux';
|
||||
}
|
||||
if (mb_stripos($userAgent, 'Macintosh') !== false) {
|
||||
return 'Macintosh';
|
||||
}
|
||||
if (mb_stripos($userAgent, 'CrOS') !== false) {
|
||||
return 'Google Chrome OS';
|
||||
}
|
||||
if (mb_stripos($userAgent, 'OpenBSD') !== false) {
|
||||
return 'OpenBSD';
|
||||
}
|
||||
if (
|
||||
mb_stripos($userAgent, 'iPad') !== false
|
||||
|| mb_stripos($userAgent, 'iphone') !== false
|
||||
) {
|
||||
return 'iOS';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace DanielSiepmann\Tracking\Domain\Pageview;
|
||||
namespace DanielSiepmann\Tracking\Domain\Model;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2020 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
@ -21,10 +21,7 @@ namespace DanielSiepmann\Tracking\Domain\Pageview;
|
|||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use DanielSiepmann\Tracking\Domain\Model\Pageview;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
interface FromRequest
|
||||
interface HasUserAgent
|
||||
{
|
||||
public static function fromRequest(ServerRequestInterface $request): Pageview;
|
||||
public function getUserAgent(): string;
|
||||
}
|
|
@ -23,7 +23,7 @@ namespace DanielSiepmann\Tracking\Domain\Model;
|
|||
|
||||
use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
|
||||
|
||||
class Pageview
|
||||
class Pageview implements HasUserAgent
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
|
@ -115,31 +115,6 @@ class Pageview
|
|||
|
||||
public function getOperatingSystem(): string
|
||||
{
|
||||
if (mb_stripos($this->userAgent, 'Android') !== false) {
|
||||
return 'Android';
|
||||
}
|
||||
if (mb_stripos($this->userAgent, 'Windows') !== false) {
|
||||
return 'Windows';
|
||||
}
|
||||
if (mb_stripos($this->userAgent, 'Linux') !== false) {
|
||||
return 'Linux';
|
||||
}
|
||||
if (mb_stripos($this->userAgent, 'Macintosh') !== false) {
|
||||
return 'Macintosh';
|
||||
}
|
||||
if (mb_stripos($this->userAgent, 'CrOS') !== false) {
|
||||
return 'Google Chrome OS';
|
||||
}
|
||||
if (mb_stripos($this->userAgent, 'OpenBSD') !== false) {
|
||||
return 'OpenBSD';
|
||||
}
|
||||
if (
|
||||
mb_stripos($this->userAgent, 'iPad') !== false
|
||||
|| mb_stripos($this->userAgent, 'iphone') !== false
|
||||
) {
|
||||
return 'iOS';
|
||||
}
|
||||
|
||||
return '';
|
||||
return Extractor::getOperatingSystem($this);
|
||||
}
|
||||
}
|
||||
|
|
96
Classes/Domain/Model/RecordRule.php
Normal file
96
Classes/Domain/Model/RecordRule.php
Normal file
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
|
||||
namespace DanielSiepmann\Tracking\Domain\Model;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2020 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
class RecordRule
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $identifier;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $matches;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $recordUid;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $tableName;
|
||||
|
||||
public function __construct(
|
||||
string $identifier,
|
||||
string $matches,
|
||||
string $recordUid,
|
||||
string $tableName
|
||||
) {
|
||||
$this->identifier = $identifier;
|
||||
$this->matches = $matches;
|
||||
$this->recordUid = $recordUid;
|
||||
$this->tableName = $tableName;
|
||||
}
|
||||
|
||||
public static function fromArray(array $config): self
|
||||
{
|
||||
return new RecordRule(
|
||||
$config['identifier'],
|
||||
$config['matches'],
|
||||
$config['recordUid'],
|
||||
$config['tableName']
|
||||
);
|
||||
}
|
||||
|
||||
public static function multipleFromArray(array $configs): array
|
||||
{
|
||||
$rules = [];
|
||||
foreach ($configs as $identifier => $config) {
|
||||
$rules[] = static::fromArray(array_merge(
|
||||
[
|
||||
'identifier' => $identifier,
|
||||
],
|
||||
$config
|
||||
));
|
||||
}
|
||||
return $rules;
|
||||
}
|
||||
|
||||
public function getMatchesExpression(): string
|
||||
{
|
||||
return $this->matches;
|
||||
}
|
||||
|
||||
public function getUidExpression(): string
|
||||
{
|
||||
return $this->recordUid;
|
||||
}
|
||||
|
||||
public function getTableName(): string
|
||||
{
|
||||
return $this->tableName;
|
||||
}
|
||||
}
|
120
Classes/Domain/Model/Recordview.php
Normal file
120
Classes/Domain/Model/Recordview.php
Normal file
|
@ -0,0 +1,120 @@
|
|||
<?php
|
||||
|
||||
namespace DanielSiepmann\Tracking\Domain\Model;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2020 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
|
||||
|
||||
class Recordview implements HasUserAgent
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $pageUid;
|
||||
|
||||
/**
|
||||
* @var SiteLanguage
|
||||
*/
|
||||
private $language;
|
||||
|
||||
/**
|
||||
* @var \DateTimeImmutable
|
||||
*/
|
||||
private $crdate;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $url;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $userAgent;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $recordUid;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $tableName;
|
||||
|
||||
public function __construct(
|
||||
int $pageUid,
|
||||
SiteLanguage $language,
|
||||
\DateTimeImmutable $crdate,
|
||||
string $url,
|
||||
string $userAgent,
|
||||
int $recordUid,
|
||||
string $tableName
|
||||
) {
|
||||
$this->pageUid = $pageUid;
|
||||
$this->language = $language;
|
||||
$this->crdate = $crdate;
|
||||
$this->url = $url;
|
||||
$this->userAgent = $userAgent;
|
||||
$this->recordUid = $recordUid;
|
||||
$this->tableName = $tableName;
|
||||
}
|
||||
|
||||
public function getPageUid(): int
|
||||
{
|
||||
return $this->pageUid;
|
||||
}
|
||||
|
||||
public function getLanguage(): SiteLanguage
|
||||
{
|
||||
return $this->language;
|
||||
}
|
||||
|
||||
public function getCrdate(): \DateTimeImmutable
|
||||
{
|
||||
return $this->crdate;
|
||||
}
|
||||
|
||||
public function getUrl(): string
|
||||
{
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
public function getUserAgent(): string
|
||||
{
|
||||
return $this->userAgent;
|
||||
}
|
||||
|
||||
public function getRecordUid(): int
|
||||
{
|
||||
return $this->recordUid;
|
||||
}
|
||||
|
||||
public function getTableName(): string
|
||||
{
|
||||
return $this->tableName;
|
||||
}
|
||||
|
||||
public function getOperatingSystem(): string
|
||||
{
|
||||
return Extractor::getOperatingSystem($this);
|
||||
}
|
||||
}
|
|
@ -26,7 +26,7 @@ use Psr\Http\Message\ServerRequestInterface;
|
|||
use TYPO3\CMS\Core\Routing\PageArguments;
|
||||
use TYPO3\CMS\Core\Site\SiteFinder;
|
||||
|
||||
class Factory implements FromRequest
|
||||
class Factory
|
||||
{
|
||||
/**
|
||||
* @var SiteFinder
|
||||
|
@ -40,7 +40,7 @@ class Factory implements FromRequest
|
|||
|
||||
public static function fromRequest(ServerRequestInterface $request): Pageview
|
||||
{
|
||||
return new PageView(
|
||||
return new Pageview(
|
||||
static::getRouting($request)->getPageId(),
|
||||
$request->getAttribute('language'),
|
||||
new \DateTimeImmutable(),
|
||||
|
@ -52,7 +52,7 @@ class Factory implements FromRequest
|
|||
|
||||
public function fromDbRow(array $dbRow): Pageview
|
||||
{
|
||||
return new PageView(
|
||||
return new Pageview(
|
||||
$dbRow['pid'],
|
||||
$this->siteFinder->getSiteByPageId($dbRow['pid'])->getLanguageById($dbRow['sys_language_uid']),
|
||||
new \DateTimeImmutable('@' . $dbRow['crdate']),
|
||||
|
|
57
Classes/Domain/Recordview/Factory.php
Normal file
57
Classes/Domain/Recordview/Factory.php
Normal file
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
namespace DanielSiepmann\Tracking\Domain\Recordview;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2020 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use DanielSiepmann\Tracking\Domain\Model\RecordRule;
|
||||
use DanielSiepmann\Tracking\Domain\Model\Recordview;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
|
||||
use TYPO3\CMS\Core\Routing\PageArguments;
|
||||
|
||||
class Factory
|
||||
{
|
||||
public static function fromRequest(
|
||||
ServerRequestInterface $request,
|
||||
RecordRule $rule
|
||||
): Recordview {
|
||||
// Need silent, as expression language doens't provide a way to check for array keys
|
||||
$recordUid = @(new ExpressionLanguage())->evaluate(
|
||||
$rule->getUidExpression(),
|
||||
['request' => $request]
|
||||
);
|
||||
|
||||
return new Recordview(
|
||||
static::getRouting($request)->getPageId(),
|
||||
$request->getAttribute('language'),
|
||||
new \DateTimeImmutable(),
|
||||
(string) $request->getUri(),
|
||||
$request->getHeader('User-Agent')[0] ?? '',
|
||||
$recordUid,
|
||||
$rule->getTableName()
|
||||
);
|
||||
}
|
||||
|
||||
private static function getRouting(ServerRequestInterface $request): PageArguments
|
||||
{
|
||||
return $request->getAttribute('routing');
|
||||
}
|
||||
}
|
|
@ -23,7 +23,6 @@ namespace DanielSiepmann\Tracking\Domain\Repository;
|
|||
|
||||
use DanielSiepmann\Tracking\Domain\Model\Pageview as Model;
|
||||
use DanielSiepmann\Tracking\Domain\Pageview\Factory;
|
||||
use Doctrine\DBAL\Driver\Statement;
|
||||
use TYPO3\CMS\Core\Database\Connection;
|
||||
|
||||
class Pageview
|
||||
|
@ -60,12 +59,10 @@ class Pageview
|
|||
$queryBuilder = $this->connection->createQueryBuilder();
|
||||
$pageViews = $queryBuilder->select('*')->from('tx_tracking_pageview')->execute();
|
||||
|
||||
if ($pageViews instanceof Statement) {
|
||||
while ($pageView = $pageViews->fetch()) {
|
||||
yield $this->factory->fromDbRow($pageView);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function update(Model $pageview): void
|
||||
{
|
||||
|
|
63
Classes/Domain/Repository/Recordview.php
Normal file
63
Classes/Domain/Repository/Recordview.php
Normal file
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
namespace DanielSiepmann\Tracking\Domain\Repository;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2020 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use DanielSiepmann\Tracking\Domain\Model\Recordview as Model;
|
||||
use TYPO3\CMS\Core\Database\Connection;
|
||||
|
||||
class Recordview
|
||||
{
|
||||
/**
|
||||
* @var Connection
|
||||
*/
|
||||
private $connection;
|
||||
|
||||
public function __construct(
|
||||
Connection $connection
|
||||
) {
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
public function add(Model $recordview): void
|
||||
{
|
||||
$this->connection->insert(
|
||||
'tx_tracking_recordview',
|
||||
$this->getFieldsFromModel($recordview)
|
||||
);
|
||||
}
|
||||
|
||||
private function getFieldsFromModel(Model $recordview): array
|
||||
{
|
||||
return [
|
||||
'pid' => $recordview->getPageUid(),
|
||||
'crdate' => $recordview->getCrdate()->format('U'),
|
||||
'tstamp' => $recordview->getCrdate()->format('U'),
|
||||
'sys_language_uid' => $recordview->getLanguage()->getLanguageId(),
|
||||
'url' => $recordview->getUrl(),
|
||||
'user_agent' => $recordview->getUserAgent(),
|
||||
'operating_system' => $recordview->getOperatingSystem(),
|
||||
'record_uid' => $recordview->getRecordUid(),
|
||||
'record_table_name' => $recordview->getTableName(),
|
||||
'record' => $recordview->getTableName() . '_' . $recordview->getRecordUid(),
|
||||
];
|
||||
}
|
||||
}
|
89
Classes/Middleware/Recordview.php
Normal file
89
Classes/Middleware/Recordview.php
Normal file
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
|
||||
namespace DanielSiepmann\Tracking\Middleware;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2020 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use DanielSiepmann\Tracking\Domain\Model\RecordRule;
|
||||
use DanielSiepmann\Tracking\Domain\Recordview\Factory;
|
||||
use DanielSiepmann\Tracking\Domain\Repository\Recordview as Repository;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
|
||||
use TYPO3\CMS\Core\Context\Context;
|
||||
|
||||
class Recordview implements MiddlewareInterface
|
||||
{
|
||||
/**
|
||||
* @var Repository
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* @var Context
|
||||
*/
|
||||
private $context;
|
||||
|
||||
/**
|
||||
* @var array<RecordRule>
|
||||
*/
|
||||
private $rules = [];
|
||||
|
||||
public function __construct(
|
||||
Repository $repository,
|
||||
Context $context,
|
||||
array $rules
|
||||
) {
|
||||
$this->repository = $repository;
|
||||
$this->context = $context;
|
||||
|
||||
$this->rules = RecordRule::multipleFromArray($rules);
|
||||
}
|
||||
|
||||
public function process(
|
||||
ServerRequestInterface $request,
|
||||
RequestHandlerInterface $handler
|
||||
): ResponseInterface {
|
||||
foreach ($this->rules as $rule) {
|
||||
if ($this->shouldTrack($request, $this->context, $rule)) {
|
||||
$this->repository->add(Factory::fromRequest($request, $rule));
|
||||
}
|
||||
}
|
||||
|
||||
return $handler->handle($request);
|
||||
}
|
||||
|
||||
private function shouldTrack(
|
||||
ServerRequestInterface $request,
|
||||
Context $context,
|
||||
RecordRule $rule
|
||||
): bool {
|
||||
// Need silent, as expression language doens't provide a way to check for array keys
|
||||
return @(bool) (new ExpressionLanguage())->evaluate(
|
||||
$rule->getMatchesExpression(),
|
||||
[
|
||||
'request' => $request,
|
||||
'context' => $context,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
return [
|
||||
'frontend' => [
|
||||
'tracking-pageview' => [
|
||||
'danielsiepmann/tracking/pageview' => [
|
||||
'target' => \DanielSiepmann\Tracking\Middleware\Pageview::class,
|
||||
'before' => [
|
||||
'typo3/cms-frontend/content-length-headers',
|
||||
|
@ -11,5 +11,14 @@ return [
|
|||
'typo3/cms-frontend/shortcut-and-mountpoint-redirect',
|
||||
],
|
||||
],
|
||||
'danielsiepmann/tracking/recordview' => [
|
||||
'target' => \DanielSiepmann\Tracking\Middleware\Recordview::class,
|
||||
'before' => [
|
||||
'typo3/cms-frontend/content-length-headers',
|
||||
],
|
||||
'after' => [
|
||||
'typo3/cms-frontend/shortcut-and-mountpoint-redirect',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
|
|
@ -27,11 +27,32 @@ services:
|
|||
arguments:
|
||||
- 'tx_tracking_pageview'
|
||||
|
||||
dbconnection.tx_tracking_recordview:
|
||||
class: 'TYPO3\CMS\Core\Database\Connection'
|
||||
factory:
|
||||
- '@TYPO3\CMS\Core\Database\ConnectionPool'
|
||||
- 'getConnectionForTable'
|
||||
arguments:
|
||||
- 'tx_tracking_recordview'
|
||||
|
||||
querybuilder.tx_tracking_recordview:
|
||||
class: 'TYPO3\CMS\Core\Database\Query\QueryBuilder'
|
||||
factory:
|
||||
- '@TYPO3\CMS\Core\Database\ConnectionPool'
|
||||
- 'getQueryBuilderForTable'
|
||||
arguments:
|
||||
- 'tx_tracking_recordview'
|
||||
|
||||
DanielSiepmann\Tracking\Domain\Repository\Pageview:
|
||||
public: true
|
||||
arguments:
|
||||
- '@dbconnection.tx_tracking_pageview'
|
||||
|
||||
DanielSiepmann\Tracking\Domain\Repository\Recordview:
|
||||
public: true
|
||||
arguments:
|
||||
- '@dbconnection.tx_tracking_recordview'
|
||||
|
||||
DanielSiepmann\Tracking\Middleware\Pageview:
|
||||
public: true
|
||||
arguments:
|
||||
|
@ -42,6 +63,11 @@ services:
|
|||
and not (request.getHeader("User-Agent")[0] matches "/Googlebot|Bingbot|bingbot|Slurp|DuckDuckBot|Baiduspider|YandexBot|Sogou|Exabot|NextCloud-News|Feedly|XING FeedReader|CCBot|SemrushBot|SEOkicks|Twitterbot|Seekport Crawler|SemanticScholarBot|ia_archiver|PaperLiBot|TrendsmapResolver|AhrefsBot|Nuzzel/")
|
||||
and not (request.getHeader("User-Agent")[0] matches "/mattermost|Slackbot|WhatsApp/")
|
||||
|
||||
DanielSiepmann\Tracking\Middleware\Recordview:
|
||||
public: true
|
||||
arguments:
|
||||
$rules: []
|
||||
|
||||
DanielSiepmann\Tracking\Command\UpdateDataCommand:
|
||||
tags:
|
||||
- name: 'console.command'
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'ctrl' => [
|
||||
'label' => 'url',
|
||||
|
@ -22,7 +23,7 @@ return [
|
|||
'pid' => [
|
||||
'label' => 'LLL:EXT:tracking/Resources/Private/Language/locallang_tca.xlf:table.pageview.pid',
|
||||
'config' => [
|
||||
// TODO: TYPO3 v10 does no longer allow to resolve PID relations, e.g. via select or group
|
||||
// TYPO3 v10 does no longer allow to resolve PID relations, e.g. via select or group
|
||||
// This will break internal PID handling.
|
||||
'type' => 'input',
|
||||
'readOnly' => true,
|
||||
|
@ -75,7 +76,6 @@ return [
|
|||
'readOnly' => true,
|
||||
'type' => 'input',
|
||||
'size' => 50,
|
||||
'max' => 255,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
|
85
Configuration/TCA/tx_tracking_recordview.php
Normal file
85
Configuration/TCA/tx_tracking_recordview.php
Normal file
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'ctrl' => [
|
||||
'label' => 'record',
|
||||
'label_alt' => 'crdate',
|
||||
'label_alt_force' => true,
|
||||
'default_sortby' => 'crdate DESC',
|
||||
'tstamp' => 'tstamp',
|
||||
'crdate' => 'crdate',
|
||||
'cruser_id' => 'cruser_id',
|
||||
'languageField' => 'sys_language_uid',
|
||||
'title' => 'LLL:EXT:tracking/Resources/Private/Language/locallang_tca.xlf:table.recordview',
|
||||
'searchFields' => 'uid, url',
|
||||
'iconfile' => 'EXT:core/Resources/Public/Icons/T3Icons/apps/apps-pagetree-page-default.svg',
|
||||
],
|
||||
'types' => [
|
||||
'0' => [
|
||||
'showitem' => 'sys_language_uid, pid, record, url, user_agent, operating_system, crdate',
|
||||
],
|
||||
],
|
||||
'columns' => [
|
||||
'pid' => [
|
||||
'label' => 'LLL:EXT:tracking/Resources/Private/Language/locallang_tca.xlf:table.recordview.pid',
|
||||
'config' => [
|
||||
// TYPO3 v10 does no longer allow to resolve PID relations, e.g. via select or group
|
||||
// This will break internal PID handling.
|
||||
'type' => 'input',
|
||||
'readOnly' => true,
|
||||
],
|
||||
],
|
||||
'crdate' => [
|
||||
'label' => 'LLL:EXT:tracking/Resources/Private/Language/locallang_tca.xlf:table.recordview.crdate',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
'eval' => 'datetime',
|
||||
],
|
||||
],
|
||||
'sys_language_uid' => [
|
||||
'label' => 'LLL:EXT:tracking/Resources/Private/Language/locallang_tca.xlf:table.recordview.sys_language',
|
||||
'config' => [
|
||||
'type' => 'select',
|
||||
'renderType' => 'selectSingle',
|
||||
'foreign_table' => 'sys_language',
|
||||
'items' => [
|
||||
['LLL:EXT:tracking/Resources/Private/Language/locallang_tca.xlf:table.recordview.sys_language.0', 0],
|
||||
],
|
||||
'readOnly' => true,
|
||||
]
|
||||
],
|
||||
'user_agent' => [
|
||||
'label' => 'LLL:EXT:tracking/Resources/Private/Language/locallang_tca.xlf:table.recordview.user_agent',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
'readOnly' => true,
|
||||
],
|
||||
],
|
||||
'operating_system' => [
|
||||
'label' => 'LLL:EXT:tracking/Resources/Private/Language/locallang_tca.xlf:table.recordview.operating_system',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
'readOnly' => true,
|
||||
],
|
||||
],
|
||||
'url' => [
|
||||
'label' => 'LLL:EXT:tracking/Resources/Private/Language/locallang_tca.xlf:table.recordview.url',
|
||||
'config' => [
|
||||
'readOnly' => true,
|
||||
'type' => 'input',
|
||||
'size' => 50,
|
||||
],
|
||||
],
|
||||
'record' => [
|
||||
'label' => 'LLL:EXT:tracking/Resources/Private/Language/locallang_tca.xlf:table.recordview.record',
|
||||
'config' => [
|
||||
'type' => 'group',
|
||||
'allowed' => 'tt_content,sys_category,pages',
|
||||
'internal_type' => 'db',
|
||||
'maxitems' => 1,
|
||||
'minitems' => 1,
|
||||
'size' => 1,
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
|
@ -30,6 +30,34 @@
|
|||
<trans-unit id="table.pageview.operating_system">
|
||||
<source>Operating System</source>
|
||||
</trans-unit>
|
||||
|
||||
<trans-unit id="table.recordview">
|
||||
<source>Recordview</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="table.recordview.pid">
|
||||
<source>Page</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="table.recordview.record">
|
||||
<source>Record</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="table.recordview.url">
|
||||
<source>URL</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="table.recordview.sys_language">
|
||||
<source>System language</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="table.recordview.sys_language.0">
|
||||
<source>Default system language</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="table.recordview.crdate">
|
||||
<source>Date + Time</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="table.recordview.user_agent">
|
||||
<source>User agent</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="table.recordview.operating_system">
|
||||
<source>Operating System</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
|
97
Tests/Unit/Domain/Model/ExtractorTest.php
Normal file
97
Tests/Unit/Domain/Model/ExtractorTest.php
Normal file
|
@ -0,0 +1,97 @@
|
|||
<?php
|
||||
|
||||
namespace DanielSiepmann\Tracking\Tests\Unit\Domain\Model;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2020 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use DanielSiepmann\Tracking\Domain\Model\Extractor;
|
||||
use DanielSiepmann\Tracking\Domain\Model\HasUserAgent;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Prophecy\PhpUnit\ProphecyTrait;
|
||||
|
||||
/**
|
||||
* @covers DanielSiepmann\Tracking\Domain\Model\Extractor
|
||||
*/
|
||||
class ExtractorTest extends TestCase
|
||||
{
|
||||
use ProphecyTrait;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @dataProvider possibleUserStringWithOperatingSystems
|
||||
* @testdox Operating system $expectedOperatingSystem is extracted from UserAgent string: $userAgent
|
||||
*/
|
||||
public function returnsOperatingSystem(string $userAgent, string $expectedOperatingSystem): void
|
||||
{
|
||||
$model = $this->prophesize(HasUserAgent::class);
|
||||
$model->getUserAgent()->willReturn($userAgent);
|
||||
|
||||
static::assertSame(
|
||||
$expectedOperatingSystem,
|
||||
Extractor::getOperatingSystem($model->reveal())
|
||||
);
|
||||
}
|
||||
|
||||
public function possibleUserStringWithOperatingSystems(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'userAgent' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36',
|
||||
'expectedOperatingSystem' => 'Linux',
|
||||
],
|
||||
[
|
||||
'userAgent' => 'Dalvik/2.1.0 (Linux; U; Android 9; ONEPLUS A3003 Build/PKQ1.181203.001)',
|
||||
'expectedOperatingSystem' => 'Android',
|
||||
],
|
||||
[
|
||||
'userAgent' => 'Apache-HttpClient/4.5.2 (Java/1.8.0_151)',
|
||||
'expectedOperatingSystem' => '',
|
||||
],
|
||||
[
|
||||
'userAgent' => 'AwarioSmartBot/1.0 (+https://awario.com/bots.html; bots@awario.com)',
|
||||
'expectedOperatingSystem' => '',
|
||||
],
|
||||
[
|
||||
'userAgent' => 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)',
|
||||
'expectedOperatingSystem' => 'Windows',
|
||||
],
|
||||
[
|
||||
'userAgent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:73.0) Gecko/20100101 Firefox/73.0',
|
||||
'expectedOperatingSystem' => 'Macintosh',
|
||||
],
|
||||
[
|
||||
'userAgent' => 'Mozilla/5.0 (X11; CrOS x86_64 12607.82.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.123 Safari/537.36',
|
||||
'expectedOperatingSystem' => 'Google Chrome OS',
|
||||
],
|
||||
[
|
||||
'userAgent' => 'Mozilla/5.0 (X11; U; OpenBSD i386; en-US; rv:1.8.1.4) Gecko/20070704 Firefox/52.0',
|
||||
'expectedOperatingSystem' => 'OpenBSD',
|
||||
],
|
||||
[
|
||||
'userAgent' => 'Mozilla/5.0 (iPad; CPU OS 13_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/80.0.3987.95 Mobile/15E148 Safari/604.1',
|
||||
'expectedOperatingSystem' => 'iOS',
|
||||
],
|
||||
[
|
||||
'userAgent' => 'Mozilla/5.0 (iPhone; CPU OS 13_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/22.0 Mobile/15E148 Safari/605.1.15',
|
||||
'expectedOperatingSystem' => 'iOS',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
|
@ -217,10 +217,8 @@ class PageviewTest extends TestCase
|
|||
|
||||
/**
|
||||
* @test
|
||||
* @dataProvider possibleUserStringWithOperatingSystems
|
||||
* @testdox Operating system $expectedOperatingSystem is extracted from UserAgent string: $userAgent
|
||||
*/
|
||||
public function returnsOperatingSystem(string $userAgent, string $expectedOperatingSystem): void
|
||||
public function returnsOperatingSystem(): void
|
||||
{
|
||||
$language = $this->prophesize(SiteLanguage::class);
|
||||
|
||||
|
@ -230,58 +228,12 @@ class PageviewTest extends TestCase
|
|||
new \DateTimeImmutable(),
|
||||
0,
|
||||
'',
|
||||
$userAgent
|
||||
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36'
|
||||
);
|
||||
|
||||
static::assertSame(
|
||||
$expectedOperatingSystem,
|
||||
'Linux',
|
||||
$subject->getOperatingSystem()
|
||||
);
|
||||
}
|
||||
|
||||
public function possibleUserStringWithOperatingSystems(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'userAgent' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36',
|
||||
'expectedOperatingSystem' => 'Linux',
|
||||
],
|
||||
[
|
||||
'userAgent' => 'Dalvik/2.1.0 (Linux; U; Android 9; ONEPLUS A3003 Build/PKQ1.181203.001)',
|
||||
'expectedOperatingSystem' => 'Android',
|
||||
],
|
||||
[
|
||||
'userAgent' => 'Apache-HttpClient/4.5.2 (Java/1.8.0_151)',
|
||||
'expectedOperatingSystem' => '',
|
||||
],
|
||||
[
|
||||
'userAgent' => 'AwarioSmartBot/1.0 (+https://awario.com/bots.html; bots@awario.com)',
|
||||
'expectedOperatingSystem' => '',
|
||||
],
|
||||
[
|
||||
'userAgent' => 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)',
|
||||
'expectedOperatingSystem' => 'Windows',
|
||||
],
|
||||
[
|
||||
'userAgent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:73.0) Gecko/20100101 Firefox/73.0',
|
||||
'expectedOperatingSystem' => 'Macintosh',
|
||||
],
|
||||
[
|
||||
'userAgent' => 'Mozilla/5.0 (X11; CrOS x86_64 12607.82.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.123 Safari/537.36',
|
||||
'expectedOperatingSystem' => 'Google Chrome OS',
|
||||
],
|
||||
[
|
||||
'userAgent' => 'Mozilla/5.0 (X11; U; OpenBSD i386; en-US; rv:1.8.1.4) Gecko/20070704 Firefox/52.0',
|
||||
'expectedOperatingSystem' => 'OpenBSD',
|
||||
],
|
||||
[
|
||||
'userAgent' => 'Mozilla/5.0 (iPad; CPU OS 13_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/80.0.3987.95 Mobile/15E148 Safari/604.1',
|
||||
'expectedOperatingSystem' => 'iOS',
|
||||
],
|
||||
[
|
||||
'userAgent' => 'Mozilla/5.0 (iPhone; CPU OS 13_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/22.0 Mobile/15E148 Safari/605.1.15',
|
||||
'expectedOperatingSystem' => 'iOS',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
129
Tests/Unit/Domain/Model/RecordRuleTest.php
Normal file
129
Tests/Unit/Domain/Model/RecordRuleTest.php
Normal file
|
@ -0,0 +1,129 @@
|
|||
<?php
|
||||
|
||||
namespace DanielSiepmann\Tracking\Tests\Unit\Domain\Model;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2020 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use DanielSiepmann\Tracking\Domain\Model\RecordRule;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers DanielSiepmann\Tracking\Domain\Model\RecordRule
|
||||
*/
|
||||
class RecordRuleTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function canBeCreatedViaConstructor(): void
|
||||
{
|
||||
$subject = new RecordRule(
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
''
|
||||
);
|
||||
|
||||
static::assertInstanceOf(RecordRule::class, $subject);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function canBeCreatedFromArray(): void
|
||||
{
|
||||
$subject = RecordRule::fromArray([
|
||||
'identifier' => '',
|
||||
'matches' => '',
|
||||
'recordUid' => '',
|
||||
'tableName' => '',
|
||||
]);
|
||||
|
||||
static::assertInstanceOf(RecordRule::class, $subject);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function multipleCanBeCratedFromArray(): void
|
||||
{
|
||||
$result = RecordRule::multipleFromArray([
|
||||
'identifier1' => [
|
||||
'matches' => '',
|
||||
'recordUid' => '',
|
||||
'tableName' => '',
|
||||
],
|
||||
'identifier2' => [
|
||||
'matches' => '',
|
||||
'recordUid' => '',
|
||||
'tableName' => '',
|
||||
],
|
||||
]);
|
||||
|
||||
static::assertCount(2, $result);
|
||||
static::assertInstanceOf(RecordRule::class, $result[0]);
|
||||
static::assertInstanceOf(RecordRule::class, $result[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function returnsMatchExpression(): void
|
||||
{
|
||||
$subject = new RecordRule(
|
||||
'',
|
||||
'match expression',
|
||||
'',
|
||||
''
|
||||
);
|
||||
|
||||
static::assertSame('match expression', $subject->getMatchesExpression());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function returnsUidExpression(): void
|
||||
{
|
||||
$subject = new RecordRule(
|
||||
'',
|
||||
'',
|
||||
'match expression',
|
||||
''
|
||||
);
|
||||
|
||||
static::assertSame('match expression', $subject->getUidExpression());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function returnsTableName(): void
|
||||
{
|
||||
$subject = new RecordRule(
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'table_name'
|
||||
);
|
||||
|
||||
static::assertSame('table_name', $subject->getTableName());
|
||||
}
|
||||
}
|
228
Tests/Unit/Domain/Model/RecordviewTest.php
Normal file
228
Tests/Unit/Domain/Model/RecordviewTest.php
Normal file
|
@ -0,0 +1,228 @@
|
|||
<?php
|
||||
|
||||
namespace DanielSiepmann\Tracking\Tests\Unit\Domain\Model;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2020 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use DanielSiepmann\Tracking\Domain\Model\Recordview;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Prophecy\PhpUnit\ProphecyTrait;
|
||||
use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
|
||||
|
||||
/**
|
||||
* @covers DanielSiepmann\Tracking\Domain\Model\Recordview
|
||||
*/
|
||||
class RecordviewTest extends TestCase
|
||||
{
|
||||
use ProphecyTrait;
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function canBeCreated(): void
|
||||
{
|
||||
$language = $this->prophesize(SiteLanguage::class);
|
||||
|
||||
$subject = new Recordview(
|
||||
0,
|
||||
$language->reveal(),
|
||||
new \DateTimeImmutable(),
|
||||
'',
|
||||
'',
|
||||
10,
|
||||
'sys_category'
|
||||
);
|
||||
|
||||
static::assertInstanceOf(Recordview::class, $subject);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function returnsPageUid(): void
|
||||
{
|
||||
$language = $this->prophesize(SiteLanguage::class);
|
||||
|
||||
$subject = new Recordview(
|
||||
500,
|
||||
$language->reveal(),
|
||||
new \DateTimeImmutable(),
|
||||
'',
|
||||
'',
|
||||
10,
|
||||
'sys_category'
|
||||
);
|
||||
|
||||
static::assertSame(500, $subject->getPageUid());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function returnsLanguage(): void
|
||||
{
|
||||
$language = $this->prophesize(SiteLanguage::class);
|
||||
|
||||
$subject = new Recordview(
|
||||
0,
|
||||
$language->reveal(),
|
||||
new \DateTimeImmutable(),
|
||||
'',
|
||||
'',
|
||||
10,
|
||||
'sys_category'
|
||||
);
|
||||
|
||||
static::assertSame($language->reveal(), $subject->getLanguage());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function returnsCrdate(): void
|
||||
{
|
||||
$language = $this->prophesize(SiteLanguage::class);
|
||||
$crdate = new \DateTimeImmutable();
|
||||
|
||||
$subject = new Recordview(
|
||||
0,
|
||||
$language->reveal(),
|
||||
$crdate,
|
||||
'',
|
||||
'',
|
||||
10,
|
||||
'sys_category'
|
||||
);
|
||||
|
||||
static::assertSame($crdate, $subject->getCrdate());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function returnsUrl(): void
|
||||
{
|
||||
$language = $this->prophesize(SiteLanguage::class);
|
||||
|
||||
$subject = new Recordview(
|
||||
0,
|
||||
$language->reveal(),
|
||||
new \DateTimeImmutable(),
|
||||
'https://example.com/path.html',
|
||||
'',
|
||||
10,
|
||||
'sys_category'
|
||||
);
|
||||
|
||||
static::assertSame('https://example.com/path.html', $subject->getUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function returnsUserAgent(): void
|
||||
{
|
||||
$language = $this->prophesize(SiteLanguage::class);
|
||||
|
||||
$subject = new Recordview(
|
||||
0,
|
||||
$language->reveal(),
|
||||
new \DateTimeImmutable(),
|
||||
'',
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0',
|
||||
10,
|
||||
'sys_category'
|
||||
);
|
||||
|
||||
static::assertSame(
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0',
|
||||
$subject->getUserAgent()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function returnsRecordUid(): void
|
||||
{
|
||||
$language = $this->prophesize(SiteLanguage::class);
|
||||
|
||||
$subject = new Recordview(
|
||||
0,
|
||||
$language->reveal(),
|
||||
new \DateTimeImmutable(),
|
||||
'',
|
||||
'',
|
||||
10,
|
||||
'sys_category'
|
||||
);
|
||||
|
||||
static::assertSame(
|
||||
10,
|
||||
$subject->getRecordUid()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function returnsTableName(): void
|
||||
{
|
||||
$language = $this->prophesize(SiteLanguage::class);
|
||||
|
||||
$subject = new Recordview(
|
||||
0,
|
||||
$language->reveal(),
|
||||
new \DateTimeImmutable(),
|
||||
'',
|
||||
'',
|
||||
10,
|
||||
'sys_category'
|
||||
);
|
||||
|
||||
static::assertSame(
|
||||
'sys_category',
|
||||
$subject->getTableName()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function returnsOperatingSystem(): void
|
||||
{
|
||||
$language = $this->prophesize(SiteLanguage::class);
|
||||
|
||||
$subject = new Recordview(
|
||||
0,
|
||||
$language->reveal(),
|
||||
new \DateTimeImmutable(),
|
||||
'',
|
||||
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36',
|
||||
10,
|
||||
'sys_category'
|
||||
);
|
||||
|
||||
static::assertSame(
|
||||
'Linux',
|
||||
$subject->getOperatingSystem()
|
||||
);
|
||||
}
|
||||
}
|
255
Tests/Unit/Domain/Recordview/FactoryTest.php
Normal file
255
Tests/Unit/Domain/Recordview/FactoryTest.php
Normal file
|
@ -0,0 +1,255 @@
|
|||
<?php
|
||||
|
||||
namespace DanielSiepmann\Tracking\Tests\Unit\Domain\Recordview;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2020 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use DanielSiepmann\Tracking\Domain\Model\RecordRule;
|
||||
use DanielSiepmann\Tracking\Domain\Model\Recordview;
|
||||
use DanielSiepmann\Tracking\Domain\Recordview\Factory;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Prophecy\PhpUnit\ProphecyTrait;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Core\Routing\PageArguments;
|
||||
use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
|
||||
|
||||
/**
|
||||
* @covers DanielSiepmann\Tracking\Domain\Recordview\Factory
|
||||
*/
|
||||
class FactoryTest extends TestCase
|
||||
{
|
||||
use ProphecyTrait;
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function returnsRecordviewFromRequest(): void
|
||||
{
|
||||
$rule = $this->prophesize(RecordRule::class);
|
||||
$rule->getUidExpression()->willReturn('request.getQueryParams()["category"] > 0');
|
||||
$rule->getTableName()->willReturn('sys_category');
|
||||
|
||||
$routing = $this->prophesize(PageArguments::class);
|
||||
$routing->getPageId()->willReturn(10);
|
||||
|
||||
$language = $this->prophesize(SiteLanguage::class);
|
||||
|
||||
$request = $this->prophesize(ServerRequestInterface::class);
|
||||
$request->getAttribute('routing')->willReturn($routing->reveal());
|
||||
$request->getAttribute('language')->willReturn($language->reveal());
|
||||
$request->getUri()->willReturn('');
|
||||
$request->getHeader('User-Agent')->willReturn([]);
|
||||
$request->getQueryParams()->willReturn([
|
||||
'category' => 10,
|
||||
]);
|
||||
|
||||
$result = Factory::fromRequest($request->reveal(), $rule->reveal());
|
||||
static::assertInstanceOf(Recordview::class, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function returnedRecordviewContainsUserAgent(): void
|
||||
{
|
||||
$rule = $this->prophesize(RecordRule::class);
|
||||
$rule->getUidExpression()->willReturn('request.getQueryParams()["category"] > 0');
|
||||
$rule->getTableName()->willReturn('sys_category');
|
||||
|
||||
$routing = $this->prophesize(PageArguments::class);
|
||||
$routing->getPageId()->willReturn(10);
|
||||
|
||||
$language = $this->prophesize(SiteLanguage::class);
|
||||
|
||||
$request = $this->prophesize(ServerRequestInterface::class);
|
||||
$request->getAttribute('routing')->willReturn($routing->reveal());
|
||||
$request->getAttribute('language')->willReturn($language->reveal());
|
||||
$request->getUri()->willReturn('');
|
||||
$request->getHeader('User-Agent')->willReturn(['Some User Agent']);
|
||||
$request->getQueryParams()->willReturn([
|
||||
'category' => 10,
|
||||
]);
|
||||
|
||||
$result = Factory::fromRequest($request->reveal(), $rule->reveal());
|
||||
static::assertSame('Some User Agent', $result->getUserAgent());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function returnedRecordviewContainsUri(): void
|
||||
{
|
||||
$rule = $this->prophesize(RecordRule::class);
|
||||
$rule->getUidExpression()->willReturn('request.getQueryParams()["category"] > 0');
|
||||
$rule->getTableName()->willReturn('sys_category');
|
||||
|
||||
$routing = $this->prophesize(PageArguments::class);
|
||||
$routing->getPageId()->willReturn(10);
|
||||
|
||||
$language = $this->prophesize(SiteLanguage::class);
|
||||
|
||||
$request = $this->prophesize(ServerRequestInterface::class);
|
||||
$request->getAttribute('routing')->willReturn($routing->reveal());
|
||||
$request->getAttribute('language')->willReturn($language->reveal());
|
||||
$request->getUri()->willReturn('https://example.com');
|
||||
$request->getHeader('User-Agent')->willReturn(['']);
|
||||
$request->getQueryParams()->willReturn([
|
||||
'category' => 10,
|
||||
]);
|
||||
|
||||
$result = Factory::fromRequest($request->reveal(), $rule->reveal());
|
||||
static::assertSame('https://example.com', $result->getUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function returnedRecordviewContainsDateTime(): void
|
||||
{
|
||||
$rule = $this->prophesize(RecordRule::class);
|
||||
$rule->getUidExpression()->willReturn('request.getQueryParams()["category"] > 0');
|
||||
$rule->getTableName()->willReturn('sys_category');
|
||||
|
||||
$routing = $this->prophesize(PageArguments::class);
|
||||
$routing->getPageId()->willReturn(10);
|
||||
|
||||
$language = $this->prophesize(SiteLanguage::class);
|
||||
|
||||
$request = $this->prophesize(ServerRequestInterface::class);
|
||||
$request->getAttribute('routing')->willReturn($routing->reveal());
|
||||
$request->getAttribute('language')->willReturn($language->reveal());
|
||||
$request->getUri()->willReturn('https://example.com');
|
||||
$request->getHeader('User-Agent')->willReturn(['']);
|
||||
$request->getQueryParams()->willReturn([
|
||||
'category' => 10,
|
||||
]);
|
||||
|
||||
$result = Factory::fromRequest($request->reveal(), $rule->reveal());
|
||||
static::assertInstanceOf(\DateTimeImmutable::class, $result->getCrdate());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function returnedRecordviewContainsLanguage(): void
|
||||
{
|
||||
$rule = $this->prophesize(RecordRule::class);
|
||||
$rule->getUidExpression()->willReturn('request.getQueryParams()["category"] > 0');
|
||||
$rule->getTableName()->willReturn('sys_category');
|
||||
|
||||
$routing = $this->prophesize(PageArguments::class);
|
||||
$routing->getPageId()->willReturn(10);
|
||||
|
||||
$language = $this->prophesize(SiteLanguage::class);
|
||||
|
||||
$request = $this->prophesize(ServerRequestInterface::class);
|
||||
$request->getAttribute('routing')->willReturn($routing->reveal());
|
||||
$request->getAttribute('language')->willReturn($language->reveal());
|
||||
$request->getUri()->willReturn('https://example.com');
|
||||
$request->getHeader('User-Agent')->willReturn(['']);
|
||||
$request->getQueryParams()->willReturn([
|
||||
'category' => 10,
|
||||
]);
|
||||
|
||||
$result = Factory::fromRequest($request->reveal(), $rule->reveal());
|
||||
static::assertSame($language->reveal(), $result->getLanguage());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function returnedRecordviewContainsPageId(): void
|
||||
{
|
||||
$rule = $this->prophesize(RecordRule::class);
|
||||
$rule->getUidExpression()->willReturn('request.getQueryParams()["category"] > 0');
|
||||
$rule->getTableName()->willReturn('sys_category');
|
||||
|
||||
$routing = $this->prophesize(PageArguments::class);
|
||||
$routing->getPageId()->willReturn(10);
|
||||
|
||||
$language = $this->prophesize(SiteLanguage::class);
|
||||
|
||||
$request = $this->prophesize(ServerRequestInterface::class);
|
||||
$request->getAttribute('routing')->willReturn($routing->reveal());
|
||||
$request->getAttribute('language')->willReturn($language->reveal());
|
||||
$request->getUri()->willReturn('https://example.com');
|
||||
$request->getHeader('User-Agent')->willReturn(['']);
|
||||
$request->getQueryParams()->willReturn([
|
||||
'category' => 10,
|
||||
]);
|
||||
|
||||
$result = Factory::fromRequest($request->reveal(), $rule->reveal());
|
||||
static::assertSame(10, $result->getPageUid());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function returnedRecordviewContainsRecordUid(): void
|
||||
{
|
||||
$rule = $this->prophesize(RecordRule::class);
|
||||
$rule->getUidExpression()->willReturn('request.getQueryParams()["category"]');
|
||||
$rule->getTableName()->willReturn('sys_category');
|
||||
|
||||
$routing = $this->prophesize(PageArguments::class);
|
||||
$routing->getPageId()->willReturn(10);
|
||||
|
||||
$language = $this->prophesize(SiteLanguage::class);
|
||||
|
||||
$request = $this->prophesize(ServerRequestInterface::class);
|
||||
$request->getAttribute('routing')->willReturn($routing->reveal());
|
||||
$request->getAttribute('language')->willReturn($language->reveal());
|
||||
$request->getUri()->willReturn('https://example.com');
|
||||
$request->getHeader('User-Agent')->willReturn(['']);
|
||||
$request->getQueryParams()->willReturn([
|
||||
'category' => 20,
|
||||
]);
|
||||
|
||||
$result = Factory::fromRequest($request->reveal(), $rule->reveal());
|
||||
static::assertSame(20, $result->getRecordUid());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function returnedRecordviewContainsTableName(): void
|
||||
{
|
||||
$rule = $this->prophesize(RecordRule::class);
|
||||
$rule->getUidExpression()->willReturn('request.getQueryParams()["category"] > 0');
|
||||
$rule->getTableName()->willReturn('sys_category');
|
||||
|
||||
$routing = $this->prophesize(PageArguments::class);
|
||||
$routing->getPageId()->willReturn(10);
|
||||
|
||||
$language = $this->prophesize(SiteLanguage::class);
|
||||
|
||||
$request = $this->prophesize(ServerRequestInterface::class);
|
||||
$request->getAttribute('routing')->willReturn($routing->reveal());
|
||||
$request->getAttribute('language')->willReturn($language->reveal());
|
||||
$request->getUri()->willReturn('https://example.com');
|
||||
$request->getHeader('User-Agent')->willReturn(['']);
|
||||
$request->getQueryParams()->willReturn([
|
||||
'category' => 20,
|
||||
]);
|
||||
|
||||
$result = Factory::fromRequest($request->reveal(), $rule->reveal());
|
||||
static::assertSame('sys_category', $result->getTableName());
|
||||
}
|
||||
}
|
80
Tests/Unit/Domain/Repository/RecordviewTest.php
Normal file
80
Tests/Unit/Domain/Repository/RecordviewTest.php
Normal file
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
|
||||
namespace DanielSiepmann\Tracking\Tests\Unit\Domain\Repository;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2020 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use DanielSiepmann\Tracking\Domain\Model\Recordview as Model;
|
||||
use DanielSiepmann\Tracking\Domain\Repository\Recordview;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Prophecy\PhpUnit\ProphecyTrait;
|
||||
use TYPO3\CMS\Core\Database\Connection;
|
||||
use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
|
||||
|
||||
/**
|
||||
* @covers DanielSiepmann\Tracking\Domain\Repository\Recordview
|
||||
*/
|
||||
class RecordviewTest extends TestCase
|
||||
{
|
||||
use ProphecyTrait;
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function modelCanBeAdded(): void
|
||||
{
|
||||
$connection = $this->prophesize(Connection::class);
|
||||
|
||||
$dateTime = $this->prophesize(\DateTimeImmutable::class);
|
||||
$dateTime->format('U')->willReturn(1582660189);
|
||||
|
||||
$language = $this->prophesize(SiteLanguage::class);
|
||||
$language->getLanguageId()->willReturn(2);
|
||||
|
||||
$model = $this->prophesize(Model::class);
|
||||
$model->getPageUid()->willReturn(10);
|
||||
$model->getCrdate()->willReturn($dateTime->reveal());
|
||||
$model->getLanguage()->willReturn($language->reveal());
|
||||
$model->getUrl()->willReturn('https://example.com/path.html');
|
||||
$model->getUserAgent()->willReturn('Mozilla/5.0 (Windows NT 10.0) Gecko/20100101 Firefox/74.0');
|
||||
$model->getOperatingSystem()->willReturn('Linux');
|
||||
$model->getRecordUid()->willReturn(10);
|
||||
$model->getTableName()->willReturn('sys_category');
|
||||
|
||||
$connection->insert(
|
||||
'tx_tracking_recordview',
|
||||
[
|
||||
'pid' => 10,
|
||||
'crdate' => 1582660189,
|
||||
'tstamp' => 1582660189,
|
||||
'sys_language_uid' => 2,
|
||||
'url' => 'https://example.com/path.html',
|
||||
'user_agent' => 'Mozilla/5.0 (Windows NT 10.0) Gecko/20100101 Firefox/74.0',
|
||||
'operating_system' => 'Linux',
|
||||
'record_uid' => 10,
|
||||
'record_table_name' => 'sys_category',
|
||||
'record' => 'sys_category_10',
|
||||
]
|
||||
)->willReturn(1)->shouldBeCalledTimes(1);
|
||||
|
||||
$subject = new Recordview($connection->reveal());
|
||||
$subject->add($model->reveal());
|
||||
}
|
||||
}
|
188
Tests/Unit/Middleware/RecordviewTest.php
Normal file
188
Tests/Unit/Middleware/RecordviewTest.php
Normal file
|
@ -0,0 +1,188 @@
|
|||
<?php
|
||||
|
||||
namespace DanielSiepmann\Tracking\Tests\Unit\Middleware;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2020 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use DanielSiepmann\Tracking\Domain\Model\Recordview as Model;
|
||||
use DanielSiepmann\Tracking\Domain\Repository\Recordview as Repository;
|
||||
use DanielSiepmann\Tracking\Middleware\Recordview;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Prophecy\Argument;
|
||||
use Prophecy\PhpUnit\ProphecyTrait;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use TYPO3\CMS\Core\Context\Context;
|
||||
use TYPO3\CMS\Core\Routing\PageArguments;
|
||||
use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
|
||||
|
||||
/**
|
||||
* @covers DanielSiepmann\Tracking\Middleware\Recordview
|
||||
*/
|
||||
class RecordviewTest extends TestCase
|
||||
{
|
||||
use ProphecyTrait;
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function proceedsWithNextHandlerIfNoRuleIsConfigured(): void
|
||||
{
|
||||
$repository = $this->prophesize(Repository::class);
|
||||
$repository->add()->shouldNotBeCalled();
|
||||
$context = $this->prophesize(Context::class);
|
||||
|
||||
$request = $this->prophesize(ServerRequestInterface::class);
|
||||
$response = $this->prophesize(ResponseInterface::class);
|
||||
$handler = $this->prophesize(RequestHandlerInterface::class);
|
||||
$handler->handle($request->reveal())->willReturn($response);
|
||||
|
||||
$subject = new Recordview($repository->reveal(), $context->reveal(), []);
|
||||
$result = $subject->process($request->reveal(), $handler->reveal());
|
||||
|
||||
static::assertInstanceOf(ResponseInterface::class, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function doesntAddViewIfRuleDoesNotMatchRequest(): void
|
||||
{
|
||||
$repository = $this->prophesize(Repository::class);
|
||||
$repository->add()->shouldNotBeCalled();
|
||||
$context = $this->prophesize(Context::class);
|
||||
|
||||
$request = $this->prophesize(ServerRequestInterface::class);
|
||||
$request->getQueryParams()->willReturn([]);
|
||||
$response = $this->prophesize(ResponseInterface::class);
|
||||
$handler = $this->prophesize(RequestHandlerInterface::class);
|
||||
$handler->handle($request->reveal())->willReturn($response);
|
||||
|
||||
$subject = new Recordview($repository->reveal(), $context->reveal(), [
|
||||
'topic' => [
|
||||
'matches' => 'request.getQueryParams()["topic_id"] > 0',
|
||||
'recordUid' => '',
|
||||
'tableName' => '',
|
||||
],
|
||||
]);
|
||||
$result = $subject->process($request->reveal(), $handler->reveal());
|
||||
|
||||
static::assertInstanceOf(ResponseInterface::class, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function addsSingleViewIfRuleMatches(): void
|
||||
{
|
||||
$repository = $this->prophesize(Repository::class);
|
||||
$repository->add(Argument::that(function (Model $recordview) {
|
||||
return $recordview->getPageUid() === 10
|
||||
&& $recordview->getRecordUid() === 10
|
||||
&& $recordview->getTableName() === 'topics'
|
||||
;
|
||||
}))->shouldBeCalled();
|
||||
$context = $this->prophesize(Context::class);
|
||||
|
||||
$routing = $this->prophesize(PageArguments::class);
|
||||
$routing->getPageId()->willReturn(10);
|
||||
|
||||
$language = $this->prophesize(SiteLanguage::class);
|
||||
|
||||
$request = $this->prophesize(ServerRequestInterface::class);
|
||||
$request->getAttribute('routing')->willReturn($routing->reveal());
|
||||
$request->getAttribute('language')->willReturn($language->reveal());
|
||||
$request->getUri()->willReturn('');
|
||||
$request->getHeader('User-Agent')->willReturn([]);
|
||||
$request->getQueryParams()->willReturn([
|
||||
'topic_id' => '10',
|
||||
]);
|
||||
$response = $this->prophesize(ResponseInterface::class);
|
||||
$handler = $this->prophesize(RequestHandlerInterface::class);
|
||||
$handler->handle($request->reveal())->willReturn($response);
|
||||
|
||||
$subject = new Recordview($repository->reveal(), $context->reveal(), [
|
||||
'topic' => [
|
||||
'matches' => 'request.getQueryParams()["topic_id"] > 0',
|
||||
'recordUid' => 'request.getQueryParams()["topic_id"]',
|
||||
'tableName' => 'topics',
|
||||
],
|
||||
]);
|
||||
$result = $subject->process($request->reveal(), $handler->reveal());
|
||||
|
||||
static::assertInstanceOf(ResponseInterface::class, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function canAddMultipleViewsIfMultipleRulesApply(): void
|
||||
{
|
||||
$repository = $this->prophesize(Repository::class);
|
||||
$repository->add(Argument::that(function (Model $recordview) {
|
||||
return $recordview->getPageUid() === 10
|
||||
&& $recordview->getRecordUid() === 10
|
||||
&& $recordview->getTableName() === 'topics'
|
||||
;
|
||||
}))->shouldBeCalled();
|
||||
$repository->add(Argument::that(function (Model $recordview) {
|
||||
return $recordview->getPageUid() === 10
|
||||
&& $recordview->getRecordUid() === 20
|
||||
&& $recordview->getTableName() === 'news'
|
||||
;
|
||||
}))->shouldBeCalled();
|
||||
$context = $this->prophesize(Context::class);
|
||||
|
||||
$routing = $this->prophesize(PageArguments::class);
|
||||
$routing->getPageId()->willReturn(10);
|
||||
|
||||
$language = $this->prophesize(SiteLanguage::class);
|
||||
|
||||
$request = $this->prophesize(ServerRequestInterface::class);
|
||||
$request->getAttribute('routing')->willReturn($routing->reveal());
|
||||
$request->getAttribute('language')->willReturn($language->reveal());
|
||||
$request->getUri()->willReturn('');
|
||||
$request->getHeader('User-Agent')->willReturn([]);
|
||||
$request->getQueryParams()->willReturn([
|
||||
'topic_id' => '10',
|
||||
'news' => '20',
|
||||
]);
|
||||
$response = $this->prophesize(ResponseInterface::class);
|
||||
$handler = $this->prophesize(RequestHandlerInterface::class);
|
||||
$handler->handle($request->reveal())->willReturn($response);
|
||||
|
||||
$subject = new Recordview($repository->reveal(), $context->reveal(), [
|
||||
'topic' => [
|
||||
'matches' => 'request.getQueryParams()["topic_id"] > 0',
|
||||
'recordUid' => 'request.getQueryParams()["topic_id"]',
|
||||
'tableName' => 'topics',
|
||||
],
|
||||
'news' => [
|
||||
'matches' => 'request.getQueryParams()["news"] > 0',
|
||||
'recordUid' => 'request.getQueryParams()["news"]',
|
||||
'tableName' => 'news',
|
||||
],
|
||||
]);
|
||||
$result = $subject->process($request->reveal(), $handler->reveal());
|
||||
|
||||
static::assertInstanceOf(ResponseInterface::class, $result);
|
||||
}
|
||||
}
|
|
@ -4,3 +4,12 @@ CREATE TABLE tx_tracking_pageview (
|
|||
operating_system varchar(255) DEFAULT '' NOT NULL,
|
||||
type int(11) unsigned DEFAULT '0' NOT NULL,
|
||||
);
|
||||
|
||||
CREATE TABLE tx_tracking_recordview (
|
||||
url text,
|
||||
user_agent text,
|
||||
operating_system varchar(255) DEFAULT '' NOT NULL,
|
||||
record varchar(255) DEFAULT '' NOT NULL,
|
||||
record_uid int(11) unsigned DEFAULT '0' NOT NULL,
|
||||
record_table_name varchar(255) DEFAULT '' NOT NULL,
|
||||
);
|
||||
|
|
|
@ -6,5 +6,6 @@ parameters:
|
|||
checkGenericClassInNonGenericObjectType: false
|
||||
checkMissingIterableValueType: false
|
||||
ignoreErrors:
|
||||
- '#Cannot call method fetch\(\) on Doctrine\\DBAL\\Driver\\Statement\|int\.#'
|
||||
- '#Cannot call method fetchAll\(\) on Doctrine\\DBAL\\Driver\\Statement\|int\.#'
|
||||
- '#Cannot call method fetchColumn\(\) on Doctrine\\DBAL\\Driver\\Statement\|int\.#'
|
||||
|
|
Loading…
Reference in a new issue