WIP|Add flags feature

Allow each tracking record to contain arbitrary tags.
Those tags are generated via an API and can be extended by foreign
extensions or for individual projects.
Existing operating_system was moved to this new feature.
The update command allows to migrate existing records to this new
feature.

Those flags can be used when configuring widgets.

A new flag was added bot:yes and bot:no.
Bots are now tracked but flagged.

That new feature allows to build fine grained reports and makes the
extension way more flexible. Possible new implications:
    - Show visits from none bots
    - Show visits from bots
    - Show visits from specific bot
    - Add color to page views per page (bar chart)
      to color bot or none bot

WIP:
    - Update Yaml file to work like before, no bots in widgets
    - Add documentation (widgets)
    - Add documentation (migration *.yaml)
This commit is contained in:
Daniel Siepmann 2021-08-16 09:56:19 +02:00
parent 1dbdaac9c6
commit 5f0490f493
Signed by: Daniel Siepmann
GPG key ID: 33D6629915560EF4
62 changed files with 2574 additions and 960 deletions

View file

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace DanielSiepmann\Tracking\Command;
use DanielSiepmann\Tracking\Domain\Repository\Pageview;
use DanielSiepmann\Tracking\Domain\Repository\Recordview;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Input\InputInterface;
@ -35,33 +36,58 @@ class UpdateDataCommand extends Command
/**
* @var Pageview
*/
private $repository;
private $pageviews;
public function __construct(Pageview $repository)
{
$this->repository = $repository;
/**
* @var Recordview
*/
private $recordviews;
public function __construct(
Pageview $pageviews,
Recordview $recordviews
) {
$this->pageviews = $pageviews;
$this->recordviews = $recordviews;
parent::__construct();
}
protected function configure(): void
{
$this->setDescription('Updates existing data.');
$this->setHelp('In case some more data can be extracted of the existing data.');
$this->setHelp('Converts legacy data to new format if necessary. Runs incrementel to work with large data sets.');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$io->progressStart($this->repository->countAll());
foreach ($this->repository->findAll() as $pageView) {
$this->repository->update($pageView);
$io->progressAdvance();
}
$io->progressFinish();
$io->writeln('Updating: Pageviews');
$this->update($this->pageviews, $io);
$io->writeln('Updating: Recordviews');
$this->update($this->recordviews, $io);
return 0;
}
/**
* @param Pageview|Recordview $repository
*/
private function update(
$repository,
SymfonyStyle $io
): void {
$count = $repository->findLegacyCount();
if ($count === 0) {
$io->writeln('No more data to update.');
return;
}
$io->progressStart($count);
foreach ($repository->findLegacy() as $data) {
$repository->update($data);
$io->progressAdvance();
}
$io->progressFinish();
}
}

View file

@ -0,0 +1,181 @@
<?php
declare(strict_types=1);
/*
* Copyright (C) 2021 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.
*/
namespace DanielSiepmann\Tracking\Dashboard\Provider;
use DanielSiepmann\Tracking\Dashboard\Provider\Demand\Tag;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
class Demand
{
/**
* @var int
*/
private $days;
/**
* @var int
*/
private $maxResults;
/**
* @var int[]
*/
private $pagesToExclude;
/**
* @var int[]
*/
private $languageLimitation;
/**
* @var Tag[]
*/
private $tagConstraints;
/**
* @param int[] $pagesToExclude
* @param int[] $languageLimitation
* @param Tag[] $tagConstraints
*/
public function __construct(
int $days = 31,
int $maxResults = 6,
array $pagesToExclude = [],
array $languageLimitation = [],
array $tagConstraints = []
) {
$this->days = $days;
$this->maxResults = $maxResults;
$this->pagesToExclude = array_map('intval', $pagesToExclude);
$this->languageLimitation = array_map('intval', $languageLimitation);
$this->tagConstraints = $tagConstraints;
}
public function getDays(): int
{
return $this->days;
}
public function getMaxResults(): int
{
return $this->maxResults;
}
/**
* @return int[]
*/
public function getPagesToExclude(): array
{
return $this->pagesToExclude;
}
/**
* @return int[]
*/
public function getLanguageLimitation(): array
{
return $this->languageLimitation;
}
/**
* @return Tag[]
*/
public function getTagConstraints(): array
{
return $this->tagConstraints;
}
public function addJoins(
QueryBuilder $queryBuilder,
string $tableName
): void {
if ($this->getTagConstraints() !== []) {
$queryBuilder->leftJoin(
$tableName,
'tx_tracking_tag',
'tx_tracking_tag',
(string) $queryBuilder->expr()->andX(
$queryBuilder->expr()->eq(
'tx_tracking_tag.record_table_name',
$queryBuilder->createNamedParameter($tableName)
),
$queryBuilder->expr()->eq(
'tx_tracking_tag.record_uid',
$queryBuilder->quoteIdentifier($tableName . '.uid')
)
)
);
}
}
/**
* @return string[]
*/
public function getConstraints(
QueryBuilder $queryBuilder,
string $tableName
): array {
$constraints = [];
if ($this->getPagesToExclude() !== []) {
$constraints[] = (string) $queryBuilder->expr()->notIn(
$tableName . '.pid',
$queryBuilder->createNamedParameter(
$this->getPagesToExclude(),
Connection::PARAM_INT_ARRAY
)
);
}
if ($this->getLanguageLimitation() !== []) {
$constraints[] = (string) $queryBuilder->expr()->in(
$tableName . '.sys_language_uid',
$queryBuilder->createNamedParameter(
$this->getLanguageLimitation(),
Connection::PARAM_INT_ARRAY
)
);
}
foreach ($this->getTagConstraints() as $tagConstraint) {
$constraints[] = (string) $queryBuilder->expr()->andX(
$queryBuilder->expr()->eq(
'tx_tracking_tag.name',
$queryBuilder->createNamedParameter(
$tagConstraint->getName()
)
),
$queryBuilder->expr()->eq(
'tx_tracking_tag.value',
$queryBuilder->createNamedParameter(
$tagConstraint->getValue()
)
)
);
}
return $constraints;
}
}

View file

@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
/*
* Copyright (C) 2021 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.
*/
namespace DanielSiepmann\Tracking\Dashboard\Provider\Demand;
class Tag
{
/**
* @var string
*/
private $name;
/**
* @var string
*/
private $value;
public function __construct(
string $name,
string $value
) {
// TODO: Add Validate
$this->name = $name;
$this->value = $value;
}
public static function createFromArray(array $parameters): self
{
return new self(
$parameters['name'],
$parameters['value']
);
}
public function getName(): string
{
return $this->name;
}
public function getValue(): string
{
return $this->value;
}
}

View file

@ -35,63 +35,35 @@ class NewestPageviews implements ListDataProviderInterface
private $queryBuilder;
/**
* @var int
* @var Demand
*/
private $maxResults;
/**
* @var array
*/
private $pagesToExclude;
/**
* @var array<int>
*/
private $languageLimitation;
private $demand;
public function __construct(
QueryBuilder $queryBuilder,
int $maxResults = 6,
array $pagesToExclude = [],
array $languageLimitation = []
Demand $demand
) {
$this->queryBuilder = $queryBuilder;
$this->maxResults = $maxResults;
$this->pagesToExclude = $pagesToExclude;
$this->languageLimitation = $languageLimitation;
$this->demand = $demand;
}
public function getItems(): array
{
$preparedItems = [];
$constraints = [];
if (count($this->pagesToExclude)) {
$constraints[] = $this->queryBuilder->expr()->notIn(
'tx_tracking_pageview.pid',
$this->queryBuilder->createNamedParameter(
$this->pagesToExclude,
Connection::PARAM_INT_ARRAY
)
$constraints = $this->demand->getConstraints(
$this->queryBuilder,
'tx_tracking_pageview'
);
}
if (count($this->languageLimitation)) {
$constraints[] = $this->queryBuilder->expr()->in(
'tx_tracking_pageview.sys_language_uid',
$this->queryBuilder->createNamedParameter(
$this->languageLimitation,
Connection::PARAM_INT_ARRAY
)
);
}
$this->demand->addJoins($this->queryBuilder, 'tx_tracking_pageview');
$this->queryBuilder
->select('url', 'user_agent')
->from('tx_tracking_pageview')
->orderBy('crdate', 'desc')
->addOrderBy('uid', 'desc')
->setMaxResults($this->maxResults);
->setMaxResults($this->demand->getMaxResults());
if ($constraints !== []) {
$this->queryBuilder->where(...$constraints);

View file

@ -43,38 +43,24 @@ class PageviewsPerDay implements ChartDataProviderInterface
private $queryBuilder;
/**
* @var int
* @var Demand
*/
private $days;
/**
* @var array<int>
*/
private $pagesToExclude;
private $demand;
/**
* @var string
*/
private $dateFormat;
/**
* @var array<int>
*/
private $languageLimitation;
public function __construct(
LanguageService $languageService,
QueryBuilder $queryBuilder,
int $days = 31,
array $pagesToExclude = [],
array $languageLimitation = [],
Demand $demand,
string $dateFormat = 'Y-m-d'
) {
$this->languageService = $languageService;
$this->queryBuilder = $queryBuilder;
$this->days = $days;
$this->pagesToExclude = $pagesToExclude;
$this->languageLimitation = $languageLimitation;
$this->demand = $demand;
$this->dateFormat = $dateFormat;
}
@ -102,13 +88,13 @@ class PageviewsPerDay implements ChartDataProviderInterface
$labels = [];
$data = [];
for ($daysBefore = $this->days; $daysBefore >= 0; $daysBefore--) {
for ($daysBefore = $this->demand->getDays(); $daysBefore >= 0; $daysBefore--) {
$label = date($this->dateFormat, (int) strtotime('-' . $daysBefore . ' day'));
$labels[$label] = $label;
$data[$label] = 0;
}
$start = (int) strtotime('-' . $this->days . ' day 0:00:00');
$start = (int) strtotime('-' . $this->demand->getDays() . ' day 0:00:00');
$end = (int) strtotime('tomorrow midnight');
@ -125,32 +111,19 @@ class PageviewsPerDay implements ChartDataProviderInterface
private function getPageviewsInPeriod(int $start, int $end): array
{
$constraints = [
$this->queryBuilder->expr()->gte('crdate', $start),
$this->queryBuilder->expr()->lte('crdate', $end),
$this->queryBuilder->expr()->gte('tx_tracking_pageview.crdate', $start),
$this->queryBuilder->expr()->lte('tx_tracking_pageview.crdate', $end),
];
if (count($this->pagesToExclude)) {
$constraints[] = $this->queryBuilder->expr()->notIn(
'tx_tracking_pageview.pid',
$this->queryBuilder->createNamedParameter(
$this->pagesToExclude,
Connection::PARAM_INT_ARRAY
)
);
}
$constraints = array_merge($constraints, $this->demand->getConstraints(
$this->queryBuilder,
'tx_tracking_pageview'
));
if (count($this->languageLimitation)) {
$constraints[] = $this->queryBuilder->expr()->in(
'tx_tracking_pageview.sys_language_uid',
$this->queryBuilder->createNamedParameter(
$this->languageLimitation,
Connection::PARAM_INT_ARRAY
)
);
}
$this->demand->addJoins($this->queryBuilder, 'tx_tracking_pageview');
$this->queryBuilder
->addSelectLiteral('COUNT(*) as "count"')
->addSelectLiteral('COUNT(tx_tracking_pageview.uid) as "count"')
->from('tx_tracking_pageview')
->where(...$constraints)
->groupBy('label')
@ -158,9 +131,9 @@ class PageviewsPerDay implements ChartDataProviderInterface
;
if ($this->queryBuilder->getConnection()->getDatabasePlatform()->getName() === 'sqlite') {
$this->queryBuilder->addSelectLiteral('date(crdate, "unixepoch") as "label"');
$this->queryBuilder->addSelectLiteral('date(tx_tracking_pageview.crdate, "unixepoch") as "label"');
} else {
$this->queryBuilder->addSelectLiteral('FROM_UNIXTIME(crdate, "%Y-%m-%d") as "label"');
$this->queryBuilder->addSelectLiteral('FROM_UNIXTIME(tx_tracking_pageview.crdate, "%Y-%m-%d") as "label"');
}
return $this->queryBuilder->execute()->fetchAll();

View file

@ -36,30 +36,16 @@ class PageviewsPerOperatingSystem implements ChartDataProviderInterface
private $queryBuilder;
/**
* @var int
* @var Demand
*/
private $days;
/**
* @var int
*/
private $maxResults;
/**
* @var array<int>
*/
private $languageLimitation;
private $demand;
public function __construct(
QueryBuilder $queryBuilder,
int $days = 31,
int $maxResults = 6,
array $languageLimitation = []
Demand $demand
) {
$this->queryBuilder = $queryBuilder;
$this->days = $days;
$this->maxResults = $maxResults;
$this->languageLimitation = $languageLimitation;
$this->demand = $demand;
}
public function getChartData(): array
@ -85,33 +71,42 @@ class PageviewsPerOperatingSystem implements ChartDataProviderInterface
$constraints = [
$this->queryBuilder->expr()->gte(
'tx_tracking_pageview.crdate',
strtotime('-' . $this->days . ' day 0:00:00')
strtotime('-' . $this->demand->getDays() . ' day 0:00:00')
),
$this->queryBuilder->expr()->neq(
'tx_tracking_pageview.operating_system',
'operating_system',
$this->queryBuilder->createNamedParameter('')
),
];
if (count($this->languageLimitation)) {
$constraints[] = $this->queryBuilder->expr()->in(
'tx_tracking_pageview.sys_language_uid',
$this->queryBuilder->createNamedParameter(
$this->languageLimitation,
Connection::PARAM_INT_ARRAY
)
);
}
$constraints = array_merge($constraints, $this->demand->getConstraints(
$this->queryBuilder,
'tx_tracking_pageview'
));
$this->demand->addJoins($this->queryBuilder, 'tx_tracking_pageview');
$result = $this->queryBuilder
->selectLiteral('count(operating_system) as total')
->addSelect('operating_system')
->addSelect('tag.value as operating_system')
->addSelectLiteral(
'count(' . $this->queryBuilder->quoteIdentifier('operating_system') . ') as total'
)
->from('tx_tracking_pageview')
->leftJoin(
'tx_tracking_pageview',
'tx_tracking_tag',
'tag',
(string) $this->queryBuilder->expr()->andX(
$this->queryBuilder->expr()->eq('tx_tracking_pageview.uid', $this->queryBuilder->quoteIdentifier('tag.record_uid')),
$this->queryBuilder->expr()->eq('tag.name', $this->queryBuilder->createNamedParameter('os')),
$this->queryBuilder->expr()->eq('tag.record_table_name', $this->queryBuilder->createNamedParameter('tx_tracking_pageview'))
)
)
->where(...$constraints)
->groupBy('tx_tracking_pageview.operating_system')
->groupBy('operating_system')
->orderBy('total', 'desc')
->addOrderBy('operating_system', 'asc')
->setMaxResults($this->maxResults)
->setMaxResults($this->demand->getMaxResults())
->execute()
->fetchAll();

View file

@ -45,39 +45,18 @@ class PageviewsPerPage implements ChartDataProviderInterface
private $pageRepository;
/**
* @var int
* @var Demand
*/
private $days;
/**
* @var int
*/
private $maxResults;
/**
* @var array<int>
*/
private $pagesToExclude;
/**
* @var array<int>
*/
private $languageLimitation;
private $demand;
public function __construct(
QueryBuilder $queryBuilder,
PageRepository $pageRepository,
int $days = 31,
int $maxResults = 6,
array $pagesToExclude = [],
array $languageLimitation = []
Demand $demand
) {
$this->queryBuilder = $queryBuilder;
$this->pageRepository = $pageRepository;
$this->days = $days;
$this->maxResults = $maxResults;
$this->pagesToExclude = $pagesToExclude;
$this->languageLimitation = $languageLimitation;
$this->demand = $demand;
}
public function getChartData(): array
@ -99,45 +78,33 @@ class PageviewsPerPage implements ChartDataProviderInterface
{
$labels = [];
$data = [];
$constraints = [
$this->queryBuilder->expr()->gte(
(string) $this->queryBuilder->expr()->gte(
'tx_tracking_pageview.crdate',
strtotime('-' . $this->days . ' day 0:00:00')
strtotime('-' . $this->demand->getDays() . ' day 0:00:00')
),
];
if (count($this->pagesToExclude)) {
$constraints[] = $this->queryBuilder->expr()->notIn(
'tx_tracking_pageview.pid',
$this->queryBuilder->createNamedParameter(
$this->pagesToExclude,
Connection::PARAM_INT_ARRAY
)
$constraints = array_merge($constraints, $this->demand->getConstraints(
$this->queryBuilder,
'tx_tracking_pageview'
));
$this->demand->addJoins(
$this->queryBuilder,
'tx_tracking_pageview'
);
}
if (count($this->languageLimitation)) {
$constraints[] = $this->queryBuilder->expr()->in(
'tx_tracking_pageview.sys_language_uid',
$this->queryBuilder->createNamedParameter(
$this->languageLimitation,
Connection::PARAM_INT_ARRAY
)
);
}
$result = $this->queryBuilder
->selectLiteral(
$this->queryBuilder->expr()->count('pid', 'total'),
$this->queryBuilder->expr()->max('uid', 'latest')
$this->queryBuilder->expr()->count('tx_tracking_pageview.pid', 'total'),
$this->queryBuilder->expr()->max('tx_tracking_pageview.uid', 'latest')
)
->addSelect('pid')
->addSelect('tx_tracking_pageview.pid')
->from('tx_tracking_pageview')
->where(...$constraints)
->groupBy('pid')
->groupBy('tx_tracking_pageview.pid')
->orderBy('total', 'desc')
->addOrderBy('latest', 'desc')
->setMaxResults($this->maxResults)
->setMaxResults($this->demand->getMaxResults())
->execute()
->fetchAll();
@ -159,8 +126,8 @@ class PageviewsPerPage implements ChartDataProviderInterface
private function getRecordTitle(int $uid): string
{
$record = BackendUtility::getRecord('pages', $uid);
if (count($this->languageLimitation) === 1 && $record !== null) {
$record = $this->pageRepository->getRecordOverlay('pages', $record, $this->languageLimitation[0]);
if (count($this->demand->getLanguageLimitation()) === 1 && $record !== null) {
$record = $this->pageRepository->getRecordOverlay('pages', $record, $this->demand->getLanguageLimitation()[0]);
}
if (is_array($record) === false) {

View file

@ -49,24 +49,9 @@ class Recordviews implements ChartDataProviderInterface
private $queryBuilder;
/**
* @var int
* @var Demand
*/
private $days;
/**
* @var int
*/
private $maxResults;
/**
* @var array<int>
*/
private $pagesToExclude;
/**
* @var array<int>
*/
private $languageLimitation;
private $demand;
/**
* @var array
@ -81,19 +66,13 @@ class Recordviews implements ChartDataProviderInterface
public function __construct(
PageRepository $pageRepository,
QueryBuilder $queryBuilder,
int $days = 31,
int $maxResults = 6,
array $pagesToExclude = [],
array $languageLimitation = [],
Demand $demand,
array $recordTableLimitation = [],
array $recordTypeLimitation = []
) {
$this->pageRepository = $pageRepository;
$this->queryBuilder = $queryBuilder;
$this->days = $days;
$this->pagesToExclude = $pagesToExclude;
$this->languageLimitation = $languageLimitation;
$this->maxResults = $maxResults;
$this->demand = $demand;
$this->recordTableLimitation = $recordTableLimitation;
$this->recordTypeLimitation = $recordTypeLimitation;
}
@ -152,30 +131,10 @@ class Recordviews implements ChartDataProviderInterface
$constraints = [
$this->queryBuilder->expr()->gte(
'tx_tracking_recordview.crdate',
strtotime('-' . $this->days . ' day 0:00:00')
strtotime('-' . $this->demand->getDays() . ' day 0:00:00')
)
];
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->languageLimitation)) {
$constraints[] = $this->queryBuilder->expr()->in(
'tx_tracking_recordview.sys_language_uid',
$this->queryBuilder->createNamedParameter(
$this->languageLimitation,
Connection::PARAM_INT_ARRAY
)
);
}
if (count($this->recordTableLimitation)) {
$constraints[] = $this->queryBuilder->expr()->in(
'tx_tracking_recordview.record_table_name',
@ -186,6 +145,15 @@ class Recordviews implements ChartDataProviderInterface
);
}
$constraints = array_merge($constraints, $this->demand->getConstraints(
$this->queryBuilder,
'tx_tracking_recordview'
));
$this->demand->addJoins(
$this->queryBuilder,
'tx_tracking_recordview'
);
$result = $this->queryBuilder
->selectLiteral(
$this->queryBuilder->expr()->count('record', 'total'),
@ -197,7 +165,7 @@ class Recordviews implements ChartDataProviderInterface
->groupBy('record', 'record_uid', 'record_table_name')
->orderBy('total', 'desc')
->addOrderBy('latest', 'desc')
->setMaxResults($this->maxResults)
->setMaxResults($this->demand->getMaxResults())
->execute();
while ($row = $result->fetch()) {
@ -212,8 +180,8 @@ class Recordviews implements ChartDataProviderInterface
$recordTypeField = $GLOBALS['TCA'][$table]['ctrl']['type'] ?? '';
$record = BackendUtility::getRecord($table, $uid);
if (count($this->languageLimitation) === 1 && $record !== null) {
$record = $this->pageRepository->getRecordOverlay($table, $record, $this->languageLimitation[0]);
if (count($this->demand->getLanguageLimitation()) === 1 && $record !== null) {
$record = $this->pageRepository->getRecordOverlay($table, $record, $this->demand->getLanguageLimitation()[0]);
}
if (is_array($record) === false) {

View file

@ -0,0 +1,88 @@
<?php
declare(strict_types=1);
/*
* Copyright (C) 2021 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.
*/
namespace DanielSiepmann\Tracking\Domain\Extractors;
use DanielSiepmann\Tracking\Domain\Extractors\Bots\CustomBotParser;
use DanielSiepmann\Tracking\Domain\Model\Pageview;
use DanielSiepmann\Tracking\Domain\Model\Recordview;
use DeviceDetector\DeviceDetector;
class Bots implements PageviewExtractor, RecordviewExtractor
{
/**
* @var CustomBotParser
*/
private $customBotParser;
public function __construct(
CustomBotParser $customBotParser
) {
$this->customBotParser = $customBotParser;
}
public function extractTagFromPageview(Pageview $pageview): array
{
return $this->getTagsForUserAgent($pageview->getUserAgent());
}
public function extractTagFromRecordview(Recordview $recordview): array
{
return $this->getTagsForUserAgent($recordview->getUserAgent());
}
/**
* @return Tag[]
*/
private function getTagsForUserAgent(string $userAgent): array
{
$botNameTag = new Tag('bot_name', $this->getBotName($userAgent));
if ($botNameTag->getValue() !== '') {
return [
new Tag('bot', 'yes'),
$botNameTag,
];
}
return [new Tag('bot', 'no')];
}
private function getBotName(string $userAgent): string
{
$deviceDetector = new DeviceDetector();
$deviceDetector->addBotParser($this->customBotParser);
$deviceDetector->setUserAgent($userAgent);
$deviceDetector->parse();
if ($deviceDetector->isBot() === false) {
return '';
}
$bot = $deviceDetector->getBot();
if (is_array($bot) === false) {
return '';
}
return $bot['name'] ?? '';
}
}

View file

@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
/*
* Copyright (C) 2021 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.
*/
namespace DanielSiepmann\Tracking\Domain\Extractors\Bots;
use DeviceDetector\Parser\Bot;
use TYPO3\CMS\Core\Utility\GeneralUtility;
class CustomBotParser extends Bot
{
protected $parserName = 'customBots';
/**
* @var string
*/
protected $dirName = '';
public function __construct()
{
parent::__construct();
$fixtureFile = GeneralUtility::getFileAbsFileName('EXT:tracking/Configuration/Bots.yaml');
$this->fixtureFile = basename($fixtureFile);
$this->dirName = dirname($fixtureFile);
}
protected function getRegexesDirectory(): string
{
return $this->dirName;
}
public function parse(): ?array
{
return parent::parse();
}
}

View file

@ -3,7 +3,7 @@
declare(strict_types=1);
/*
* Copyright (C) 2020 Daniel Siepmann <coding@daniel-siepmann.de>
* Copyright (C) 2021 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
@ -21,17 +21,25 @@ declare(strict_types=1);
* 02110-1301, USA.
*/
namespace DanielSiepmann\Tracking\Domain\Model;
namespace DanielSiepmann\Tracking\Domain\Extractors;
/**
* API to extract further info out of an model.
*/
class Extractor
use DanielSiepmann\Tracking\Domain\Model\Pageview;
use DanielSiepmann\Tracking\Domain\Model\Recordview;
class OperatingSystem implements PageviewExtractor, RecordviewExtractor
{
public static function getOperatingSystem(HasUserAgent $model): string
public function extractTagFromPageview(Pageview $pageview): array
{
$userAgent = $model->getUserAgent();
return [new Tag('os', $this->getOperatingSystem($pageview->getUserAgent()))];
}
public function extractTagFromRecordview(Recordview $recordview): array
{
return [new Tag('os', $this->getOperatingSystem($recordview->getUserAgent()))];
}
private function getOperatingSystem(string $userAgent): string
{
if (mb_stripos($userAgent, 'Android') !== false) {
return 'Android';
}
@ -57,6 +65,6 @@ class Extractor
return 'iOS';
}
return '';
return 'Unkown';
}
}

View file

@ -21,9 +21,17 @@ declare(strict_types=1);
* 02110-1301, USA.
*/
namespace DanielSiepmann\Tracking\Domain\Model;
namespace DanielSiepmann\Tracking\Domain\Extractors;
interface HasUserAgent
use DanielSiepmann\Tracking\Domain\Model\Pageview;
/**
* API to extract further info out of an model.
*/
interface PageviewExtractor
{
public function getUserAgent(): string;
/**
* @return Tag[]
*/
public function extractTagFromPageview(Pageview $pageview): array;
}

View file

@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
/*
* 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.
*/
namespace DanielSiepmann\Tracking\Domain\Extractors;
use DanielSiepmann\Tracking\Domain\Model\Recordview;
/**
* API to extract further info out of an model.
*/
interface RecordviewExtractor
{
/**
* @return Tag[]
*/
public function extractTagFromRecordview(Recordview $recordview): array;
}

View file

@ -0,0 +1,74 @@
<?php
declare(strict_types=1);
/*
* Copyright (C) 2021 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.
*/
namespace DanielSiepmann\Tracking\Domain\Extractors;
use DanielSiepmann\Tracking\Domain\Model\Pageview;
use DanielSiepmann\Tracking\Domain\Model\Recordview;
class Registry
{
/**
* @var PageviewExtractor[]
*/
protected $pageviewExtractors = [];
/**
* @var RecordviewExtractor[]
*/
protected $recordviewExtractors = [];
public function addPageviewExtractor(PageviewExtractor $extractor): void
{
$this->pageviewExtractors[] = $extractor;
}
public function addRecordviewExtractor(RecordviewExtractor $extractor): void
{
$this->recordviewExtractors[] = $extractor;
}
/**
* @return Tag[]
*/
public function getTagsForPageview(Pageview $pageview): array
{
$tags = [];
foreach ($this->pageviewExtractors as $extractor) {
$tags = array_merge($tags, $extractor->extractTagFromPageview($pageview));
}
return $tags;
}
/**
* @return Tag[]
*/
public function getTagsForRecordview(Recordview $recordview): array
{
$tags = [];
foreach ($this->recordviewExtractors as $extractor) {
$tags = array_merge($tags, $extractor->extractTagFromRecordview($recordview));
}
return $tags;
}
}

View file

@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
/*
* Copyright (C) 2021 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.
*/
namespace DanielSiepmann\Tracking\Domain\Extractors;
class Tag
{
/**
* E.g. "os" or "bot", some unique identifier.
*
* @var string
*/
protected $name = '';
/**
* @var string
*/
protected $value = '';
public function __construct(
string $name,
string $value
) {
$this->name = $name;
$this->value = $value;
}
public function getName(): string
{
return $this->name;
}
public function getValue(): string
{
return $this->value;
}
}

View file

@ -25,7 +25,7 @@ namespace DanielSiepmann\Tracking\Domain\Model;
use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
class Pageview implements HasUserAgent
class Pageview
{
/**
* @var int
@ -114,9 +114,4 @@ class Pageview implements HasUserAgent
{
return $this->userAgent;
}
public function getOperatingSystem(): string
{
return Extractor::getOperatingSystem($this);
}
}

View file

@ -25,8 +25,13 @@ namespace DanielSiepmann\Tracking\Domain\Model;
use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
class Recordview implements HasUserAgent
class Recordview
{
/**
* @var int
*/
private $uid = 0;
/**
* @var int
*/
@ -69,8 +74,10 @@ class Recordview implements HasUserAgent
string $url,
string $userAgent,
int $recordUid,
string $tableName
string $tableName,
int $uid = 0
) {
$this->uid = $uid;
$this->pageUid = $pageUid;
$this->language = $language;
$this->crdate = $crdate;
@ -80,6 +87,11 @@ class Recordview implements HasUserAgent
$this->tableName = $tableName;
}
public function getUid(): int
{
return $this->uid;
}
public function getPageUid(): int
{
return $this->pageUid;
@ -114,9 +126,4 @@ class Recordview implements HasUserAgent
{
return $this->tableName;
}
public function getOperatingSystem(): string
{
return Extractor::getOperatingSystem($this);
}
}

View file

@ -30,6 +30,7 @@ use Psr\Http\Message\ServerRequestInterface;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
use TYPO3\CMS\Core\Routing\PageArguments;
use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
use TYPO3\CMS\Core\Site\SiteFinder;
class Factory
{
@ -38,9 +39,16 @@ class Factory
*/
private $expressionFactory;
/**
* @var SiteFinder
*/
private $siteFinder;
public function __construct(
SiteFinder $siteFinder,
ExpressionFactory $expressionFactory
) {
$this->siteFinder = $siteFinder;
$this->expressionFactory = $expressionFactory;
}
@ -75,6 +83,20 @@ class Factory
);
}
public function fromDbRow(array $dbRow): Recordview
{
return new Recordview(
$dbRow['pid'],
$this->siteFinder->getSiteByPageId($dbRow['pid'])->getLanguageById($dbRow['sys_language_uid']),
new \DateTimeImmutable('@' . $dbRow['crdate']),
$dbRow['url'],
$dbRow['user_agent'],
$dbRow['record_uid'],
$dbRow['record_table_name'],
$dbRow['uid']
);
}
private static function getLanguage(ServerRequestInterface $request): SiteLanguage
{
$language = $request->getAttribute('language');

View file

@ -25,6 +25,7 @@ namespace DanielSiepmann\Tracking\Domain\Repository;
use DanielSiepmann\Tracking\Domain\Model\Pageview as Model;
use DanielSiepmann\Tracking\Domain\Pageview\Factory;
use DanielSiepmann\Tracking\Extension;
use TYPO3\CMS\Core\Database\Connection;
class Pageview
@ -39,33 +40,50 @@ class Pageview
*/
private $factory;
/**
* @var Tag
*/
private $tagRepository;
public function __construct(
Connection $connection,
Factory $factory
Factory $factory,
Tag $tagRepository
) {
$this->connection = $connection;
$this->factory = $factory;
$this->tagRepository = $tagRepository;
}
public function countAll(): int
public function findLegacyCount(): int
{
$result = $this->connection->createQueryBuilder()
->count('uid')
->from('tx_tracking_pageview')
->execute()
->fetchColumn();
if (is_numeric($result)) {
return (int) $result;
}
$queryBuilder = $this->connection->createQueryBuilder();
$queryBuilder->count('*');
$queryBuilder->from('tx_tracking_pageview');
$queryBuilder->where($queryBuilder->expr()->neq('compatible_version', $queryBuilder->createNamedParameter(Extension::getCompatibleVersionNow())));
$queryBuilder->setMaxResults(Extension::getMaximumRowsForUpdate());
$pageViews = $queryBuilder->execute()->fetchColumn();
if (is_numeric($pageViews) === false) {
return 0;
}
public function findAll(): \Generator
if ($pageViews > Extension::getMaximumRowsForUpdate()) {
return Extension::getMaximumRowsForUpdate();
}
return (int) $pageViews;
}
public function findLegacy(): \Generator
{
$queryBuilder = $this->connection->createQueryBuilder();
$pageViews = $queryBuilder->select('*')->from('tx_tracking_pageview')->execute();
$queryBuilder->select('*');
$queryBuilder->from('tx_tracking_pageview');
$queryBuilder->where($queryBuilder->expr()->neq('compatible_version', $queryBuilder->createNamedParameter(Extension::getCompatibleVersionNow())));
$test = Extension::getCompatibleVersionNow();
$queryBuilder->setMaxResults(Extension::getMaximumRowsForUpdate());
$pageViews = $queryBuilder->execute();
while ($pageView = $pageViews->fetch()) {
if (is_array($pageView) === false) {
@ -87,6 +105,8 @@ class Pageview
$this->getFieldsFromModel($pageview),
['uid' => $pageview->getUid()]
);
$this->tagRepository->updateForPageview($pageview);
}
public function add(Model $pageview): void
@ -95,6 +115,11 @@ class Pageview
'tx_tracking_pageview',
$this->getFieldsFromModel($pageview)
);
$this->tagRepository->addForPageview(
$pageview,
(int) $this->connection->lastInsertId('tx_tracking_pageview')
);
}
private function getFieldsFromModel(Model $pageview): array
@ -107,7 +132,7 @@ class Pageview
'sys_language_uid' => $pageview->getLanguage()->getLanguageId(),
'url' => $pageview->getUrl(),
'user_agent' => $pageview->getUserAgent(),
'operating_system' => $pageview->getOperatingSystem(),
'compatible_version' => Extension::getCompatibleVersionNow(),
];
}
}

View file

@ -24,8 +24,13 @@ declare(strict_types=1);
namespace DanielSiepmann\Tracking\Domain\Repository;
use DanielSiepmann\Tracking\Domain\Model\Recordview as Model;
use DanielSiepmann\Tracking\Domain\Recordview\Factory;
use DanielSiepmann\Tracking\Extension;
use TYPO3\CMS\Core\Database\Connection;
// TODO: Move common code to API class.
// Call API Class with table name
class Recordview
{
/**
@ -33,10 +38,77 @@ class Recordview
*/
private $connection;
/**
* @var Factory
*/
private $factory;
/**
* @var Tag
*/
private $tagRepository;
public function __construct(
Connection $connection
Connection $connection,
Factory $factory,
Tag $tagRepository
) {
$this->connection = $connection;
$this->factory = $factory;
$this->tagRepository = $tagRepository;
}
public function findLegacyCount(): int
{
$queryBuilder = $this->connection->createQueryBuilder();
$queryBuilder->count('*');
$queryBuilder->from('tx_tracking_recordview');
$queryBuilder->where($queryBuilder->expr()->neq('compatible_version', $queryBuilder->createNamedParameter(Extension::getCompatibleVersionNow())));
$queryBuilder->setMaxResults(Extension::getMaximumRowsForUpdate());
$recordviews = $queryBuilder->execute()->fetchColumn();
if (is_numeric($recordviews) === false) {
return 0;
}
if ($recordviews > Extension::getMaximumRowsForUpdate()) {
return Extension::getMaximumRowsForUpdate();
}
return (int) $recordviews;
}
public function findLegacy(): \Generator
{
$queryBuilder = $this->connection->createQueryBuilder();
$queryBuilder->select('*');
$queryBuilder->from('tx_tracking_recordview');
$queryBuilder->where($queryBuilder->expr()->neq('compatible_version', $queryBuilder->createNamedParameter(Extension::getCompatibleVersionNow())));
$queryBuilder->setMaxResults(Extension::getMaximumRowsForUpdate());
$recordviews = $queryBuilder->execute();
while ($pageView = $recordviews->fetch()) {
if (is_array($pageView) === false) {
continue;
}
yield $this->factory->fromDbRow($pageView);
}
}
public function update(Model $model): void
{
if ($model->getUid() === 0) {
throw new \InvalidArgumentException('Can not update recordview if uid is 0.', 1585770573);
}
$this->connection->update(
'tx_tracking_recordview',
$this->getFieldsFromModel($model),
['uid' => $model->getUid()]
);
$this->tagRepository->updateForRecordview($model);
}
public function add(Model $recordview): void
@ -45,6 +117,11 @@ class Recordview
'tx_tracking_recordview',
$this->getFieldsFromModel($recordview)
);
$this->tagRepository->addForRecordview(
$recordview,
(int) $this->connection->lastInsertId('tx_tracking_recordview')
);
}
private function getFieldsFromModel(Model $recordview): array
@ -56,10 +133,10 @@ class Recordview
'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(),
'compatible_version' => Extension::getCompatibleVersionNow(),
];
}
}

View file

@ -0,0 +1,119 @@
<?php
declare(strict_types=1);
/*
* Copyright (C) 2021 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.
*/
namespace DanielSiepmann\Tracking\Domain\Repository;
use DanielSiepmann\Tracking\Domain\Extractors\Registry;
use DanielSiepmann\Tracking\Domain\Extractors\Tag as Model;
use DanielSiepmann\Tracking\Domain\Model\Pageview;
use DanielSiepmann\Tracking\Domain\Model\Recordview;
use DanielSiepmann\Tracking\Extension;
use TYPO3\CMS\Core\Database\Connection;
class Tag
{
/**
* @var Connection
*/
private $connection;
/**
* @var Registry
*/
private $extractorRegistry;
public function __construct(
Connection $connection,
Registry $extractorRegistry
) {
$this->connection = $connection;
$this->extractorRegistry = $extractorRegistry;
}
public function addForPageview(
Pageview $pageview,
int $recordUid
): void {
foreach ($this->extractorRegistry->getTagsForPageview($pageview) as $tag) {
$this->connection->insert(
'tx_tracking_tag',
[
'pid' => $pageview->getPageUid(),
'crdate' => $pageview->getCrdate()->format('U'),
'tstamp' => $pageview->getCrdate()->format('U'),
'record_uid' => $recordUid,
'record_table_name' => 'tx_tracking_pageview',
'name' => $tag->getName(),
'value' => $tag->getValue(),
]
);
}
}
public function updateForPageview(
Pageview $pageview
): void {
$this->connection->delete(
'tx_tracking_tag',
[
'record_uid' => $pageview->getUid(),
'record_table_name' => 'tx_tracking_pageview',
]
);
$this->addForPageview($pageview, $pageview->getUid());
}
public function addForRecordview(
Recordview $recordview,
int $recordUid
): void {
foreach ($this->extractorRegistry->getTagsForRecordview($recordview) as $tag) {
$this->connection->insert(
'tx_tracking_tag',
[
'pid' => $recordview->getPageUid(),
'crdate' => $recordview->getCrdate()->format('U'),
'tstamp' => $recordview->getCrdate()->format('U'),
'record_uid' => $recordUid,
'record_table_name' => 'tx_tracking_recordview',
'name' => $tag->getName(),
'value' => $tag->getValue(),
'compatible_version' => Extension::getCompatibleVersionNow(),
]
);
}
}
public function updateForRecordview(
Recordview $recordview
): void {
$this->connection->delete(
'tx_tracking_tag',
[
'record_uid' => $recordview->getUid(),
'record_table_name' => 'tx_tracking_recordview',
]
);
$this->addForRecordview($recordview, $recordview->getUid());
}
}

View file

@ -28,4 +28,15 @@ final class Extension
public const EXT_KEY = 'tracking';
public const LANGUAGE_PATH = 'LLL:EXT:' . self::EXT_KEY . '/Resources/Private/Language/locallang.xlf';
public static function getCompatibleVersionNow(): string
{
return 'v2.0.0';
}
public static function getMaximumRowsForUpdate(): int
{
// TODO: Make configurable
return 3500;
}
}

18
Configuration/Bots.yaml Normal file
View file

@ -0,0 +1,18 @@
- regex: 'nettle'
name: 'Nettle'
- regex: 'crusty'
name: 'Crusty'
- regex: 'Faraday'
name: 'Faraday'
- regex: 'newspaper'
name: 'Newspaper'
- regex: 'uni-passau'
name: 'Uni Passau'
- regex: 'Upflow'
name: 'Upflow'
- regex: 'MauiBot'
name: 'Maui Bot'
- regex: 'Java'
name: 'Java'
- regex: 'python-requests'
name: 'Python Requests'

View file

@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace DanielSiepmann\Tracking;
use DanielSiepmann\Tracking\Domain\Extractors\PageviewExtractor;
use DanielSiepmann\Tracking\Domain\Extractors\RecordviewExtractor;
use DanielSiepmann\Tracking\Domain\Extractors\Registry;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return function (ContainerConfigurator $containerConfiguration, ContainerBuilder $containerBuilder) {
$containerBuilder->registerForAutoconfiguration(PageviewExtractor::class)->addTag('tracking.extractor.pageview');
$containerBuilder->registerForAutoconfiguration(RecordviewExtractor::class)->addTag('tracking.extractor.recordview');
$containerBuilder->addCompilerPass(new class() implements CompilerPassInterface {
public function process(ContainerBuilder $containerBuilder): void
{
$registry = $containerBuilder->findDefinition(Registry::class);
foreach ($containerBuilder->findTaggedServiceIds('tracking.extractor.pageview') as $id => $tags) {
$definition = $containerBuilder->findDefinition($id);
if (!$definition->isAutoconfigured() || $definition->isAbstract()) {
continue;
}
$registry->addMethodCall('addPageviewExtractor', [$definition]);
}
foreach ($containerBuilder->findTaggedServiceIds('tracking.extractor.recordview') as $id => $tags) {
$definition = $containerBuilder->findDefinition($id);
if (!$definition->isAutoconfigured() || $definition->isAbstract()) {
continue;
}
$registry->addMethodCall('addRecordviewExtractor', [$definition]);
}
}
});
};

View file

@ -43,6 +43,14 @@ services:
arguments:
- 'tx_tracking_recordview'
dbconnection.tx_tracking_tag:
class: 'TYPO3\CMS\Core\Database\Connection'
factory:
- '@TYPO3\CMS\Core\Database\ConnectionPool'
- 'getConnectionForTable'
arguments:
- 'tx_tracking_tag'
DanielSiepmann\Tracking\Domain\Repository\Pageview:
public: true
arguments:
@ -53,18 +61,17 @@ services:
arguments:
- '@dbconnection.tx_tracking_recordview'
DanielSiepmann\Tracking\Domain\Repository\Tag:
public: true
arguments:
- '@dbconnection.tx_tracking_tag'
DanielSiepmann\Tracking\Middleware\Pageview:
public: true
arguments:
$rule: >
not (context.getAspect("backend.user").isLoggedIn())
and not (context.getAspect("frontend.preview").isPreview())
and traverse(request.getHeader("User-Agent"), '0')
and not (request.getHeader("User-Agent")[0] matches "/^TYPO3|TYPO3 linkvalidator/")
and not (request.getHeader("User-Agent")[0] matches "/Wget|curl|Go-http-client/")
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/")
and not (request.getHeader("User-Agent")[0] matches "/UptimeRobot|Pingdom/")
DanielSiepmann\Tracking\Middleware\Recordview:
public: true
@ -75,3 +82,4 @@ services:
tags:
- name: 'console.command'
command: 'tracking:updatedata'
description: 'Updates existing data.'

View file

@ -17,7 +17,7 @@ return [
],
'types' => [
'0' => [
'showitem' => 'sys_language_uid, pid, url, user_agent, operating_system, type, crdate',
'showitem' => 'sys_language_uid, pid, url, user_agent, tags, type, crdate',
],
],
'columns' => [
@ -56,10 +56,13 @@ return [
'readOnly' => true,
],
],
'operating_system' => [
'label' => 'LLL:EXT:tracking/Resources/Private/Language/locallang_tca.xlf:table.pageview.operating_system',
'tags' => [
'label' => 'LLL:EXT:tracking/Resources/Private/Language/locallang_tca.xlf:table.pageview.tags',
'config' => [
'type' => 'input',
'type' => 'inline',
'foreign_table' => 'tx_tracking_tag',
'foreign_field' => 'record_uid',
'foreign_table_field' => 'record_table_name',
'readOnly' => true,
],
],

View file

@ -17,7 +17,7 @@ return [
],
'types' => [
'0' => [
'showitem' => 'sys_language_uid, pid, record, url, user_agent, operating_system, crdate',
'showitem' => 'sys_language_uid, pid, record, url, user_agent, tags, crdate',
],
],
'columns' => [
@ -56,13 +56,6 @@ return [
'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' => [
@ -82,5 +75,15 @@ return [
'size' => 1,
],
],
'tags' => [
'label' => 'LLL:EXT:tracking/Resources/Private/Language/locallang_tca.xlf:table.recordview.tags',
'config' => [
'type' => 'inline',
'foreign_table' => 'tx_tracking_tag',
'foreign_field' => 'record_uid',
'foreign_table_field' => 'record_table_name',
'readOnly' => true,
],
],
],
];

View file

@ -0,0 +1,67 @@
<?php
return [
'ctrl' => [
'label' => 'name',
'label_alt' => 'value',
'label_alt_force' => true,
'default_sortby' => 'crdate DESC',
'tstamp' => 'tstamp',
'crdate' => 'crdate',
'cruser_id' => 'cruser_id',
'hideTable' => true,
'title' => 'LLL:EXT:tracking/Resources/Private/Language/locallang_tca.xlf:table.tag',
],
'types' => [
'0' => [
'showitem' => 'pid, record_uid, record_table_name, name, value',
],
],
'columns' => [
'pid' => [
'label' => 'LLL:EXT:tracking/Resources/Private/Language/locallang_tca.xlf:table.tag.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,
],
],
'record_uid' => [
'label' => 'LLL:EXT:tracking/Resources/Private/Language/locallang_tca.xlf:table.tag.record_uid',
'config' => [
'type' => 'input',
'eval' => 'int',
'readOnly' => true,
],
],
'record_table_name' => [
'label' => 'LLL:EXT:tracking/Resources/Private/Language/locallang_tca.xlf:table.tag.record_table_name',
'config' => [
'type' => 'input',
'readOnly' => true,
],
],
'crdate' => [
'label' => 'LLL:EXT:tracking/Resources/Private/Language/locallang_tca.xlf:table.tag.crdate',
'config' => [
'type' => 'input',
'eval' => 'datetime',
],
],
'name' => [
'label' => 'LLL:EXT:tracking/Resources/Private/Language/locallang_tca.xlf:table.tag.name',
'config' => [
'type' => 'input',
'readOnly' => true,
],
],
'value' => [
'label' => 'LLL:EXT:tracking/Resources/Private/Language/locallang_tca.xlf:table.tag.value',
'config' => [
'type' => 'input',
'readOnly' => true,
],
],
],
];

View file

@ -0,0 +1,65 @@
2.0.0
=====
Breaking
--------
* No longer has ``operating_system`` database column.
* No longer ships default rule to not track requests from bots.
Does track them as bots now.
How to update:
Remove custom adjustments from :file:`Services.yaml` regarding bot detection.
Instead extend built in bot extraction and adjust custom widgets to exclude those
bots.
Execute the provided command (scheduler task) to update existing data.
Bots and operating system will be extracted from stored user agent.
See: :ref:`tags`.
* Existing widgets will no longer work with custom configuration.
Configuration needs to be adjusted.
.. todo:: document how to migrate
* Widgets will not work with old data.
How to migrate data:
Execute the provided scheduler task.
It will incrementally update pageviews and recordviews.
3500 records of each will be updated per each run.
Roughly executing every 10 minutes seems to be a good idea.
Features
--------
* Added support for tags
Tags can now be added to :ref:`pageview` and :ref:`recordview`.
The extension ships with some out of the box, but further can be added, see
:ref:`tags`.
Resolves: :issue:`46`.
Sponsored by: https://www.werkraum-media.de/
* Uses ``matomo/device-detector`` library to detect bots.
Some known bots are added on top.
Fixes
-----
Nothing
Tasks
-----
Nothing
Deprecation
-----------
Nothing

View file

@ -41,6 +41,8 @@ The extension allows to track :ref:`pageview`,
as well as views to specific TYPO3 records via :ref:`recordview`,
e.g. records from EXT:news or EXT:tt_address.
Each of them can be extended with arbitrary tags extracted from request.
Missing features
----------------
@ -90,4 +92,6 @@ in order to extract further information from them with future updates.
Installation
Pageview
Recordview
Tags
UpdateExistingRecords
Changelog

View file

@ -43,12 +43,6 @@ Let us examine an concrete example::
$rule: >
not (context.getAspect("backend.user").isLoggedIn())
and not (context.getAspect("frontend.preview").isPreview())
and traverse(request.getHeader("User-Agent"), '0')
and not (request.getHeader("User-Agent")[0] matches "/^TYPO3|TYPO3 linkvalidator/")
and not (request.getHeader("User-Agent")[0] matches "/^Codeception Testing/")
and not (request.getHeader("User-Agent")[0] matches "/Wget|curl|Go-http-client/")
and not (request.getHeader("User-Agent")[0] matches "/bot|spider|Slurp|Sogou|NextCloud-News|Feedly|XING FeedReader|SEOkicks|Seekport Crawler|ia_archiver|TrendsmapResolver|Nuzzel/")
and not (request.getHeader("User-Agent")[0] matches "/mattermost|Slackbot|WhatsApp/")
The first paragraph will not be explained, check out :ref:`t3coreapi:configure-dependency-injection-in-extensions` instead.
@ -71,8 +65,7 @@ Check `PSR-7: HTTP message interfaces <https://www.php-fig.org/psr/psr-7/#321-ps
as well as
:ref:`t3coreapi:context-api`.
The above example blocks tracking for requests with logged in backend user,
as well as specific user agents like bots, TYPO3 itself and other systems.
The above example blocks tracking for requests with logged in backend user.
Widgets
-------

View file

@ -56,11 +56,6 @@ Let us examine an concrete example::
request.getQueryParams()["tx_news_pi1"] && request.getQueryParams()["tx_news_pi1"]["news"] > 0
and not (context.getAspect("backend.user").isLoggedIn())
and not (context.getAspect("frontend.preview").isPreview())
and traverse(request.getHeader("User-Agent"), '0')
and not (request.getHeader("User-Agent")[0] matches "/^TYPO3|TYPO3 linkvalidator/")
and not (request.getHeader("User-Agent")[0] matches "/Wget|curl|Go-http-client/")
and not (request.getHeader("User-Agent")[0] matches "/bot|spider|Slurp|Sogou|NextCloud-News|Feedly|XING FeedReader|SEOkicks|Seekport Crawler|ia_archiver|TrendsmapResolver|Nuzzel/")
and not (request.getHeader("User-Agent")[0] matches "/mattermost|Slackbot|WhatsApp/")
recordUid: 'traverse(request.getQueryParams(), "tx_news_pi1", "news")'
tableName: 'tx_news_domain_model_news'

35
Documentation/Tags.rst Normal file
View file

@ -0,0 +1,35 @@
.. _tags:
Tags
=====
Tags are attached to all tracking information like :ref:`pageview` and :ref:`recordview`.
An example for a single record would be: ``bot:"yes",bot_name:"Slack",os:"Unkown"``.
Tags are extracted whenever a new record is saved, also during :ref:`updateExistingRecords`.
The extension provides some extractors to attach tags out of the box.
Further can be provided by foreign extensions or sites.
Each extractor has to implement either ``DanielSiepmann\Tracking\Domain\Extractors\PageviewExtractor`` and \ or ``DanielSiepmann\Tracking\Domain\Extractors\RecordviewExtractor`` interface.
This allows to add arbitrary data as tags to each tracking record.
Those can then be used to generate reports or build widgets.
Existing extractors
-------------------
The following are provided out of the box.
One can replace them using :file:`Services.yaml`.
Operating System
^^^^^^^^^^^^^^^^
Contains old logic to detect operating system of requests.
The operating system is added as ``os`` tag, e.g.: ``os:"Macintosh"``.
Bots
^^^^
Contains old logic to detect bots of requests.
The bot is added either as ``bot:"no"``.
If a bot is detected it is added as ``bot:"yes"`` combined with its name ``bot_name:"Slack"``.

View file

@ -0,0 +1,5 @@
.. highlight:: php
.. _updateExistingRecords:
Update existing records
=======================

View file

@ -27,8 +27,8 @@
<trans-unit id="table.pageview.user_agent">
<source>User agent</source>
</trans-unit>
<trans-unit id="table.pageview.operating_system">
<source>Operating System</source>
<trans-unit id="table.pageview.tags">
<source>Tags</source>
</trans-unit>
<trans-unit id="table.recordview">
@ -55,8 +55,24 @@
<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 id="table.recordview.tags">
<source>Tags</source>
</trans-unit>
<trans-unit id="table.tag.pid">
<source>PID</source>
</trans-unit>
<trans-unit id="table.tag.record_uid">
<source>Foreign Record UID</source>
</trans-unit>
<trans-unit id="table.tag.record_table_name">
<source>Foreign Record Table Name</source>
</trans-unit>
<trans-unit id="table.tag.name">
<source>Name</source>
</trans-unit>
<trans-unit id="table.tag.value">
<source>Value</source>
</trans-unit>
</body>
</file>

View file

@ -1,6 +1,6 @@
<?php
namespace DanielSiepmann\Tracking\Tests\Functional\Command;
declare(strict_types=1);
/*
* Copyright (C) 2020 Daniel Siepmann <coding@daniel-siepmann.de>
@ -21,13 +21,16 @@ namespace DanielSiepmann\Tracking\Tests\Functional\Command;
* 02110-1301, USA.
*/
namespace DanielSiepmann\Tracking\Tests\Functional\Command;
use DanielSiepmann\Tracking\Command\UpdateDataCommand;
use DanielSiepmann\Tracking\Extension;
use Symfony\Component\Console\Tester\CommandTester;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase as TestCase;
/**
* @covers DanielSiepmann\Tracking\Command\UpdateDataCommand
* @covers \DanielSiepmann\Tracking\Command\UpdateDataCommand
*/
class UpdateDataCommandTest extends TestCase
{
@ -44,18 +47,112 @@ class UpdateDataCommandTest extends TestCase
*/
public function updatesAllEntriesWithMissingOperatingSystem(): void
{
$this->importDataSet('EXT:tracking/Tests/Functional/Fixtures/UpdateDataCommandTest/PageviewsWithMissingOperatingSystem.xml');
$this->importDataSet('EXT:tracking/Tests/Functional/Fixtures/UpdateDataCommandTest/WithMissingOperatingSystem.xml');
$subject = GeneralUtility::makeInstance(UpdateDataCommand::class);
$tester = new CommandTester($subject);
$tester->execute([], ['capture_stderr_separately' => true]);
static::assertSame(0, $tester->getStatusCode());
self::assertSame(0, $tester->getStatusCode());
$records = $this->getAllRecords('tx_tracking_pageview');
static::assertCount(2, $records);
static::assertSame('Linux', $records[0]['operating_system']);
static::assertSame('Android', $records[1]['operating_system']);
$records = $this->getAllRecords('tx_tracking_tag');
self::assertCount(8, $records);
self::assertSame([
'uid'=> '1',
'pid'=> '1',
'tstamp'=> '1630649915',
'crdate'=> '1630649915',
'cruser_id'=> '0',
'record_uid' => '1',
'record_table_name' => 'tx_tracking_pageview',
'name' => 'bot',
'value' => 'no',
'compatible_version' => Extension::getCompatibleVersionNow(),
], array_map('strval', $records[0]));
self::assertSame([
'uid'=> '2',
'pid'=> '1',
'tstamp'=> '1630649915',
'crdate'=> '1630649915',
'cruser_id'=> '0',
'record_uid' => '1',
'record_table_name' => 'tx_tracking_pageview',
'name' => 'os',
'value' => 'Linux',
'compatible_version' => Extension::getCompatibleVersionNow(),
], array_map('strval', $records[1]));
self::assertSame([
'uid'=> '3',
'pid'=> '1',
'tstamp'=> '1630649916',
'crdate'=> '1630649916',
'cruser_id'=> '0',
'record_uid' => '2',
'record_table_name' => 'tx_tracking_pageview',
'name' => 'bot',
'value' => 'no',
'compatible_version' => Extension::getCompatibleVersionNow(),
], array_map('strval', $records[2]));
self::assertSame([
'uid'=> '4',
'pid'=> '1',
'tstamp'=> '1630649916',
'crdate'=> '1630649916',
'cruser_id'=> '0',
'record_uid' => '2',
'record_table_name' => 'tx_tracking_pageview',
'name' => 'os',
'value' => 'Android',
'compatible_version' => Extension::getCompatibleVersionNow(),
], array_map('strval', $records[3]));
self::assertSame([
'uid'=> '5',
'pid'=> '1',
'tstamp'=> '1630649915',
'crdate'=> '1630649915',
'cruser_id'=> '0',
'record_uid' => '1',
'record_table_name' => 'tx_tracking_recordview',
'name' => 'bot',
'value' => 'no',
'compatible_version' => Extension::getCompatibleVersionNow(),
], array_map('strval', $records[4]));
self::assertSame([
'uid'=> '6',
'pid'=> '1',
'tstamp'=> '1630649915',
'crdate'=> '1630649915',
'cruser_id'=> '0',
'record_uid' => '1',
'record_table_name' => 'tx_tracking_recordview',
'name' => 'os',
'value' => 'Linux',
'compatible_version' => Extension::getCompatibleVersionNow(),
], array_map('strval', $records[5]));
self::assertSame([
'uid'=> '7',
'pid'=> '1',
'tstamp'=> '1630649916',
'crdate'=> '1630649916',
'cruser_id'=> '0',
'record_uid' => '2',
'record_table_name' => 'tx_tracking_recordview',
'name' => 'bot',
'value' => 'no',
'compatible_version' => Extension::getCompatibleVersionNow(),
], array_map('strval', $records[6]));
self::assertSame([
'uid'=> '8',
'pid'=> '1',
'tstamp'=> '1630649916',
'crdate'=> '1630649916',
'cruser_id'=> '0',
'record_uid' => '2',
'record_table_name' => 'tx_tracking_recordview',
'name' => 'os',
'value' => 'Android',
'compatible_version' => Extension::getCompatibleVersionNow(),
], array_map('strval', $records[7]));
}
/**
@ -63,18 +160,64 @@ class UpdateDataCommandTest extends TestCase
*/
public function doesNotChangeExistingOperatingSystem(): void
{
$this->importDataSet('EXT:tracking/Tests/Functional/Fixtures/UpdateDataCommandTest/PageviewsWithOperatingSystem.xml');
$this->importDataSet('EXT:tracking/Tests/Functional/Fixtures/UpdateDataCommandTest/WithOperatingSystem.xml');
$subject = GeneralUtility::makeInstance(UpdateDataCommand::class);
$tester = new CommandTester($subject);
$tester->execute([], ['capture_stderr_separately' => true]);
static::assertSame(0, $tester->getStatusCode());
self::assertSame(0, $tester->getStatusCode());
$records = $this->getAllRecords('tx_tracking_pageview');
static::assertCount(2, $records);
static::assertSame('Linux', $records[0]['operating_system']);
static::assertSame('Android', $records[1]['operating_system']);
$records = $this->getAllRecords('tx_tracking_tag');
self::assertCount(4, $records);
self::assertSame([
'uid' => '3',
'pid' => '1',
'tstamp'=> '1630649915',
'crdate'=> '1630649915',
'cruser_id' => '0',
'record_uid' => '1',
'record_table_name' => 'tx_tracking_pageview',
'name' => 'bot',
'value' => 'no',
'compatible_version' => Extension::getCompatibleVersionNow(),
], array_map('strval', $records[0]));
self::assertSame([
'uid' => '4',
'pid' => '1',
'tstamp'=> '1630649915',
'crdate'=> '1630649915',
'cruser_id' => '0',
'record_uid' => '1',
'record_table_name' => 'tx_tracking_pageview',
'name' => 'os',
'value' => 'Linux',
'compatible_version' => Extension::getCompatibleVersionNow(),
], array_map('strval', $records[1]));
self::assertSame([
'uid' => '5',
'pid' => '1',
'tstamp'=> '1630649916',
'crdate'=> '1630649916',
'cruser_id' => '0',
'record_uid' => '2',
'record_table_name' => 'tx_tracking_pageview',
'name' => 'bot',
'value' => 'no',
'compatible_version' => Extension::getCompatibleVersionNow(),
], array_map('strval', $records[2]));
self::assertSame([
'uid' => '6',
'pid' => '1',
'tstamp'=> '1630649916',
'crdate'=> '1630649916',
'cruser_id' => '0',
'record_uid' => '2',
'record_table_name' => 'tx_tracking_pageview',
'name' => 'os',
'value' => 'Android',
'compatible_version' => Extension::getCompatibleVersionNow(),
], array_map('strval', $records[3]));
}
/**
@ -88,9 +231,38 @@ class UpdateDataCommandTest extends TestCase
$tester = new CommandTester($subject);
$tester->execute([], ['capture_stderr_separately' => true]);
static::assertSame(0, $tester->getStatusCode());
self::assertSame(0, $tester->getStatusCode());
$records = $this->getAllRecords('tx_tracking_pageview');
static::assertCount(0, $records);
self::assertCount(0, $records);
$records = $this->getAllRecords('tx_tracking_tag');
self::assertCount(0, $records);
}
/**
* @test
*/
public function doesNothingIfAllRecordsAreCompatible(): void
{
$this->importDataSet('EXT:tracking/Tests/Functional/Fixtures/UpdateDataCommandTest/WithCompatibleVersion.xml');
$subject = GeneralUtility::makeInstance(UpdateDataCommand::class);
$tester = new CommandTester($subject);
$tester->execute([], ['capture_stderr_separately' => true]);
self::assertSame(0, $tester->getStatusCode());
$records = $this->getAllRecords('tx_tracking_pageview');
self::assertCount(1, $records);
$records = $this->getAllRecords('tx_tracking_recordview');
self::assertCount(1, $records);
$records = $this->getAllRecords('tx_tracking_tag');
self::assertCount(4, $records);
foreach ($records as $record) {
self::assertSame(1663773639, $record['crdate']);
}
}
}

View file

@ -1,6 +1,6 @@
<?php
namespace DanielSiepmann\Tracking\Tests\Functional\Dashboard\Provider;
declare(strict_types=1);
/*
* Copyright (C) 2020 Daniel Siepmann <coding@daniel-siepmann.de>
@ -21,13 +21,16 @@ namespace DanielSiepmann\Tracking\Tests\Functional\Dashboard\Provider;
* 02110-1301, USA.
*/
namespace DanielSiepmann\Tracking\Tests\Functional\Dashboard\Provider;
use DanielSiepmann\Tracking\Dashboard\Provider\Demand;
use DanielSiepmann\Tracking\Dashboard\Provider\NewestPageviews;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase as TestCase;
/**
* @covers DanielSiepmann\Tracking\Dashboard\Provider\NewestPageviews
* @covers \DanielSiepmann\Tracking\Dashboard\Provider\NewestPageviews
*/
class NewestPageviewsTest extends TestCase
{
@ -51,10 +54,11 @@ class NewestPageviewsTest extends TestCase
}
$subject = new NewestPageviews(
GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_tracking_pageview')
GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_tracking_pageview'),
new Demand()
);
static::assertSame([
self::assertSame([
'Url 10 - User-Agent 10',
'Url 9 - User-Agent 9',
'Url 8 - User-Agent 8',
@ -81,10 +85,10 @@ class NewestPageviewsTest extends TestCase
$subject = new NewestPageviews(
GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_tracking_pageview'),
2
new Demand(31, 2)
);
static::assertSame([
self::assertSame([
'Url 10 - User-Agent 10',
'Url 9 - User-Agent 9',
], $subject->getItems());
@ -107,11 +111,10 @@ class NewestPageviewsTest extends TestCase
$subject = new NewestPageviews(
GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_tracking_pageview'),
6,
[9]
new Demand(31, 6, [9])
);
static::assertSame([
self::assertSame([
'Url 10 - User-Agent 10',
'Url 8 - User-Agent 8',
'Url 7 - User-Agent 7',
@ -139,12 +142,10 @@ class NewestPageviewsTest extends TestCase
$subject = new NewestPageviews(
GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_tracking_pageview'),
6,
[],
[1]
new Demand(31, 6, [], [1])
);
static::assertSame([
self::assertSame([
'Url 9 - User-Agent 9',
'Url 7 - User-Agent 7',
'Url 5 - User-Agent 5',
@ -152,4 +153,6 @@ class NewestPageviewsTest extends TestCase
'Url 1 - User-Agent 1',
], $subject->getItems());
}
// TODO: Add tests for new feature regarding tags
}

View file

@ -1,6 +1,6 @@
<?php
namespace DanielSiepmann\Tracking\Tests\Functional\Dashboard\Provider;
declare(strict_types=1);
/*
* Copyright (C) 2020 Daniel Siepmann <coding@daniel-siepmann.de>
@ -21,6 +21,10 @@ namespace DanielSiepmann\Tracking\Tests\Functional\Dashboard\Provider;
* 02110-1301, USA.
*/
namespace DanielSiepmann\Tracking\Tests\Functional\Dashboard\Provider;
use DanielSiepmann\Tracking\Dashboard\Provider\Demand;
use DanielSiepmann\Tracking\Dashboard\Provider\Demand\Tag;
use DanielSiepmann\Tracking\Dashboard\Provider\PageviewsPerDay;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Localization\LanguageService;
@ -28,7 +32,7 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase as TestCase;
/**
* @covers DanielSiepmann\Tracking\Dashboard\Provider\PageviewsPerDay
* @covers \DanielSiepmann\Tracking\Dashboard\Provider\PageviewsPerDay
*/
class PageviewsPerDayTest extends TestCase
{
@ -51,12 +55,13 @@ class PageviewsPerDayTest extends TestCase
$subject = new PageviewsPerDay(
GeneralUtility::makeInstance(LanguageService::class),
GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_tracking_pageview')
GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_tracking_pageview'),
new Demand()
);
$result = $subject->getChartData();
static::assertCount(32, $result['labels']);
static::assertCount(32, $result['datasets'][0]['data']);
self::assertCount(32, $result['labels']);
self::assertCount(32, $result['datasets'][0]['data']);
}
/**
@ -76,12 +81,12 @@ class PageviewsPerDayTest extends TestCase
$subject = new PageviewsPerDay(
GeneralUtility::makeInstance(LanguageService::class),
GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_tracking_pageview'),
3
new Demand(3)
);
$result = $subject->getChartData();
static::assertCount(4, $result['labels']);
static::assertSame([
self::assertCount(4, $result['labels']);
self::assertSame([
1,
1,
1,
@ -106,13 +111,12 @@ class PageviewsPerDayTest extends TestCase
$subject = new PageviewsPerDay(
GeneralUtility::makeInstance(LanguageService::class),
GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_tracking_pageview'),
3,
[2]
new Demand(3, 0, [2])
);
$result = $subject->getChartData();
static::assertCount(4, $result['labels']);
static::assertSame([
self::assertCount(4, $result['labels']);
self::assertSame([
1,
0,
1,
@ -131,18 +135,16 @@ class PageviewsPerDayTest extends TestCase
$subject = new PageviewsPerDay(
GeneralUtility::makeInstance(LanguageService::class),
GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_tracking_pageview'),
1,
[],
[],
new Demand(1, 0, [], []),
'd.m.Y'
);
$result = $subject->getChartData();
static::assertSame([
self::assertSame([
date('d.m.Y', strtotime('-1 day')),
date('d.m.Y'),
], $result['labels']);
static::assertCount(2, $result['datasets'][0]['data']);
self::assertCount(2, $result['datasets'][0]['data']);
}
/**
@ -162,13 +164,11 @@ class PageviewsPerDayTest extends TestCase
$subject = new PageviewsPerDay(
GeneralUtility::makeInstance(LanguageService::class),
GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_tracking_pageview'),
11,
[],
[1]
new Demand(11, 0, [], [1])
);
$result = $subject->getChartData();
static::assertSame([
self::assertSame([
0 => 0,
1 => 0,
2 => 1,
@ -183,4 +183,140 @@ class PageviewsPerDayTest extends TestCase
11 => 0,
], $result['datasets'][0]['data']);
}
/**
* @test
*/
public function respectedConfiguredTagRuleToNotIncludeBots(): void
{
$connection = $this->getConnectionPool()->getConnectionForTable('tx_tracking_pageview');
for ($i = 1; $i <= 10; $i++) {
$connection->insert('tx_tracking_pageview', [
'pid' => $i,
'uid' => $i,
'crdate' => strtotime('-' . $i . ' days'),
]);
$connection->insert('tx_tracking_tag', [
'pid' => $i,
'record_uid' => $i,
'record_table_name' => 'tx_tracking_pageview',
'name' => 'bot',
'value' => 'no',
'crdate' => strtotime('-' . $i . ' days'),
]);
$connection->insert('tx_tracking_pageview', [
'pid' => $i,
'uid' => $i * 20,
'crdate' => strtotime('-' . $i . ' days'),
]);
$connection->insert('tx_tracking_tag', [
'pid' => $i,
'record_uid' => $i * 20,
'record_table_name' => 'tx_tracking_pageview',
'name' => 'bot',
'value' => 'no',
'crdate' => strtotime('-' . $i . ' days'),
]);
$connection->insert('tx_tracking_pageview', [
'pid' => $i,
'uid' => $i * 300,
'crdate' => strtotime('-' . $i . ' days'),
]);
$connection->insert('tx_tracking_tag', [
'pid' => $i,
'record_uid' => $i * 300,
'record_table_name' => 'tx_tracking_pageview',
'name' => 'bot',
'value' => 'yes',
'crdate' => strtotime('-' . $i . ' days'),
]);
}
$subject = new PageviewsPerDay(
GeneralUtility::makeInstance(LanguageService::class),
GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_tracking_pageview'),
new Demand(2, 0, [], [], [
Tag::createFromArray([
'name' => 'bot',
'value' => 'no',
]),
])
);
$result = $subject->getChartData();
self::assertSame([
0 => 2,
1 => 2,
2 => 0,
], $result['datasets'][0]['data']);
}
/**
* @test
*/
public function respectedConfiguredTagRuleToIncludeBots(): void
{
$connection = $this->getConnectionPool()->getConnectionForTable('tx_tracking_pageview');
for ($i = 1; $i <= 10; $i++) {
$connection->insert('tx_tracking_pageview', [
'pid' => $i,
'uid' => $i,
'crdate' => strtotime('-' . $i . ' days'),
]);
$connection->insert('tx_tracking_tag', [
'pid' => $i,
'record_uid' => $i,
'record_table_name' => 'tx_tracking_pageview',
'name' => 'bot',
'value' => 'no',
'crdate' => strtotime('-' . $i . ' days'),
]);
$connection->insert('tx_tracking_pageview', [
'pid' => $i,
'uid' => $i * 20,
'crdate' => strtotime('-' . $i . ' days'),
]);
$connection->insert('tx_tracking_tag', [
'pid' => $i,
'record_uid' => $i * 20,
'record_table_name' => 'tx_tracking_pageview',
'name' => 'bot',
'value' => 'no',
'crdate' => strtotime('-' . $i . ' days'),
]);
$connection->insert('tx_tracking_pageview', [
'pid' => $i,
'uid' => $i * 300,
'crdate' => strtotime('-' . $i . ' days'),
]);
$connection->insert('tx_tracking_tag', [
'pid' => $i,
'record_uid' => $i * 300,
'record_table_name' => 'tx_tracking_pageview',
'name' => 'bot',
'value' => 'yes',
'crdate' => strtotime('-' . $i . ' days'),
]);
}
$subject = new PageviewsPerDay(
GeneralUtility::makeInstance(LanguageService::class),
GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_tracking_pageview'),
new Demand(2, 0, [], [], [
Tag::createFromArray([
'name' => 'bot',
'value' => 'yes',
]),
])
);
$result = $subject->getChartData();
self::assertSame([
0 => 1,
1 => 1,
2 => 0,
], $result['datasets'][0]['data']);
}
}

View file

@ -1,6 +1,6 @@
<?php
namespace DanielSiepmann\Tracking\Tests\Functional\Dashboard\Provider;
declare(strict_types=1);
/*
* Copyright (C) 2020 Daniel Siepmann <coding@daniel-siepmann.de>
@ -21,13 +21,16 @@ namespace DanielSiepmann\Tracking\Tests\Functional\Dashboard\Provider;
* 02110-1301, USA.
*/
namespace DanielSiepmann\Tracking\Tests\Functional\Dashboard\Provider;
use DanielSiepmann\Tracking\Dashboard\Provider\Demand;
use DanielSiepmann\Tracking\Dashboard\Provider\PageviewsPerOperatingSystem;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase as TestCase;
/**
* @covers DanielSiepmann\Tracking\Dashboard\Provider\PageviewsPerOperatingSystem
* @covers \DanielSiepmann\Tracking\Dashboard\Provider\PageviewsPerOperatingSystem
*/
class PageviewsPerOperatingSystemTest extends TestCase
{
@ -41,20 +44,36 @@ class PageviewsPerOperatingSystemTest extends TestCase
public function listsSixResultsForLast31DaysByDefault(): void
{
$connection = $this->getConnectionPool()->getConnectionForTable('tx_tracking_pageview');
$connection->insert('tx_tracking_pageview', [
'pid' => 1,
'crdate' => time(),
]);
$connection->insert('tx_tracking_tag', [
'record_uid' => 1,
'record_table_name' => 'tx_tracking_pageview',
'name' => 'os',
'value' => 'System ' . 1,
]);
for ($i = 1; $i <= 10; $i++) {
$connection->insert('tx_tracking_pageview', [
'pid' => $i,
'operating_system' => 'System ' . $i,
'crdate' => time(),
]);
$connection->insert('tx_tracking_tag', [
'record_uid' => $i,
'record_table_name' => 'tx_tracking_pageview',
'name' => 'os',
'value' => 'System ' . $i,
]);
}
$subject = new PageviewsPerOperatingSystem(
GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_tracking_pageview')
GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_tracking_pageview'),
new Demand()
);
$result = $subject->getChartData();
static::assertSame([
self::assertSame([
'System 1',
'System 10',
'System 2',
@ -62,7 +81,14 @@ class PageviewsPerOperatingSystemTest extends TestCase
'System 4',
'System 5',
], $result['labels']);
static::assertCount(6, $result['datasets'][0]['data']);
self::assertSame([
'2',
'1',
'1',
'1',
'1',
'1',
], array_map('strval', $result['datasets'][0]['data']));
}
/**
@ -74,36 +100,61 @@ class PageviewsPerOperatingSystemTest extends TestCase
$connection = $this->getConnectionPool()->getConnectionForTable('tx_tracking_pageview');
$connection->insert('tx_tracking_pageview', [
'pid' => 1,
'operating_system' => 'System 1',
'crdate' => time(),
]);
$connection->insert('tx_tracking_tag', [
'record_uid' => '1',
'record_table_name' => 'tx_tracking_pageview',
'name' => 'os',
'value' => 'System 1',
]);
$connection->insert('tx_tracking_pageview', [
'pid' => 2,
'operating_system' => 'System 2',
'crdate' => time(),
]);
$connection->insert('tx_tracking_tag', [
'record_uid' => '2',
'record_table_name' => 'tx_tracking_pageview',
'name' => 'os',
'value' => 'System 2',
]);
$connection->insert('tx_tracking_pageview', [
'pid' => 3,
'operating_system' => 'System 3',
'crdate' => time(),
]);
$connection->insert('tx_tracking_tag', [
'record_uid' => '3',
'record_table_name' => 'tx_tracking_pageview',
'name' => 'os',
'value' => 'System 3',
]);
$connection->insert('tx_tracking_pageview', [
'pid' => 2,
'operating_system' => 'System 2',
'pid' => 3,
'crdate' => time(),
]);
$connection->insert('tx_tracking_tag', [
'record_uid' => '4',
'record_table_name' => 'tx_tracking_pageview',
'name' => 'os',
'value' => 'System 2',
]);
$subject = new PageviewsPerOperatingSystem(
GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_tracking_pageview')
GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_tracking_pageview'),
new Demand()
);
$result = $subject->getChartData();
static::assertSame([
self::assertSame([
'System 2',
'System 1',
'System 3',
], $result['labels']);
static::assertCount(3, $result['datasets'][0]['data']);
self::assertSame([
'2',
'1',
'1',
], array_map('strval', $result['datasets'][0]['data']));
}
/**
@ -115,31 +166,49 @@ class PageviewsPerOperatingSystemTest extends TestCase
$connection = $this->getConnectionPool()->getConnectionForTable('tx_tracking_pageview');
$connection->insert('tx_tracking_pageview', [
'pid' => 1,
'operating_system' => 'System 1',
'crdate' => strtotime('-3 days'),
]);
$connection->insert('tx_tracking_tag', [
'record_uid' => '1',
'record_table_name' => 'tx_tracking_pageview',
'name' => 'os',
'value' => 'System 1',
]);
$connection->insert('tx_tracking_pageview', [
'pid' => 2,
'operating_system' => 'System 2',
'crdate' => strtotime('-2 days'),
]);
$connection->insert('tx_tracking_tag', [
'record_uid' => '2',
'record_table_name' => 'tx_tracking_pageview',
'name' => 'os',
'value' => 'System 2',
]);
$connection->insert('tx_tracking_pageview', [
'pid' => 3,
'operating_system' => 'System 3',
'crdate' => strtotime('-1 days'),
]);
$connection->insert('tx_tracking_tag', [
'record_uid' => '3',
'record_table_name' => 'tx_tracking_pageview',
'name' => 'os',
'value' => 'System 3',
]);
$subject = new PageviewsPerOperatingSystem(
GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_tracking_pageview'),
2
new Demand(2)
);
$result = $subject->getChartData();
static::assertSame([
self::assertSame([
'System 2',
'System 3',
], $result['labels']);
static::assertCount(2, $result['datasets'][0]['data']);
self::assertSame([
'1',
'1',
], array_map('strval', $result['datasets'][0]['data']));
}
/**
@ -152,25 +221,34 @@ class PageviewsPerOperatingSystemTest extends TestCase
for ($i = 1; $i <= 10; $i++) {
$connection->insert('tx_tracking_pageview', [
'pid' => $i,
'operating_system' => 'System ' . $i,
'crdate' => time(),
]);
$connection->insert('tx_tracking_tag', [
'record_uid' => $i,
'record_table_name' => 'tx_tracking_pageview',
'name' => 'os',
'value' => 'System ' . $i,
]);
}
$subject = new PageviewsPerOperatingSystem(
GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_tracking_pageview'),
31,
4
new Demand(31, 4)
);
$result = $subject->getChartData();
static::assertSame([
self::assertSame([
'System 1',
'System 10',
'System 2',
'System 3',
], $result['labels']);
static::assertCount(4, $result['datasets'][0]['data']);
self::assertSame([
'1',
'1',
'1',
'1',
], array_map('strval', $result['datasets'][0]['data']));
}
/**
@ -184,26 +262,37 @@ class PageviewsPerOperatingSystemTest extends TestCase
$connection->insert('tx_tracking_pageview', [
'pid' => $i,
'sys_language_uid' => $i % 2,
'operating_system' => 'System ' . $i,
'crdate' => time(),
]);
$connection->insert('tx_tracking_tag', [
'record_uid' => $i,
'record_table_name' => 'tx_tracking_pageview',
'name' => 'os',
'value' => 'System ' . $i,
]);
}
$subject = new PageviewsPerOperatingSystem(
GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_tracking_pageview'),
31,
6,
[1]
new Demand(31, 6, [], [1])
);
$result = $subject->getChartData();
static::assertSame([
self::assertSame([
'System 1',
'System 3',
'System 5',
'System 7',
'System 9',
], $result['labels']);
static::assertCount(5, $result['datasets'][0]['data']);
self::assertSame([
'1',
'1',
'1',
'1',
'1',
], array_map('strval', $result['datasets'][0]['data']));
}
// TODO: Add tests for new feature regarding tags
}

View file

@ -1,6 +1,6 @@
<?php
namespace DanielSiepmann\Tracking\Tests\Functional\Dashboard\Provider;
declare(strict_types=1);
/*
* Copyright (C) 2020 Daniel Siepmann <coding@daniel-siepmann.de>
@ -21,6 +21,10 @@ namespace DanielSiepmann\Tracking\Tests\Functional\Dashboard\Provider;
* 02110-1301, USA.
*/
namespace DanielSiepmann\Tracking\Tests\Functional\Dashboard\Provider;
use DanielSiepmann\Tracking\Dashboard\Provider\Demand;
use DanielSiepmann\Tracking\Dashboard\Provider\Demand\Tag;
use DanielSiepmann\Tracking\Dashboard\Provider\PageviewsPerPage;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Domain\Repository\PageRepository;
@ -28,7 +32,7 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase as TestCase;
/**
* @covers DanielSiepmann\Tracking\Dashboard\Provider\PageviewsPerPage
* @covers \DanielSiepmann\Tracking\Dashboard\Provider\PageviewsPerPage
*/
class PageviewsPerPageTest extends TestCase
{
@ -46,17 +50,19 @@ class PageviewsPerPageTest extends TestCase
for ($i = 1; $i <= 10; $i++) {
$connection->insert('tx_tracking_pageview', [
'pid' => $i,
'uid' => $i,
'crdate' => time(),
]);
}
$subject = new PageviewsPerPage(
GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_tracking_pageview'),
GeneralUtility::makeInstance(PageRepository::class)
GeneralUtility::makeInstance(PageRepository::class),
new Demand()
);
$result = $subject->getChartData();
static::assertSame([
self::assertSame([
'Page 10',
'Page 9',
'Page 8',
@ -64,7 +70,7 @@ class PageviewsPerPageTest extends TestCase
'Page 6',
'Page 5',
], $result['labels']);
static::assertCount(6, $result['datasets'][0]['data']);
self::assertCount(6, $result['datasets'][0]['data']);
}
/**
@ -93,16 +99,17 @@ class PageviewsPerPageTest extends TestCase
$subject = new PageviewsPerPage(
GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_tracking_pageview'),
GeneralUtility::makeInstance(PageRepository::class)
GeneralUtility::makeInstance(PageRepository::class),
new Demand()
);
$result = $subject->getChartData();
static::assertSame([
self::assertSame([
'Page 2',
'Page 3',
'Page 1',
], $result['labels']);
static::assertCount(3, $result['datasets'][0]['data']);
self::assertCount(3, $result['datasets'][0]['data']);
}
/**
@ -128,15 +135,15 @@ class PageviewsPerPageTest extends TestCase
$subject = new PageviewsPerPage(
GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_tracking_pageview'),
GeneralUtility::makeInstance(PageRepository::class),
2
new Demand(2)
);
$result = $subject->getChartData();
static::assertSame([
self::assertSame([
'Page 3',
'Page 2',
], $result['labels']);
static::assertCount(2, $result['datasets'][0]['data']);
self::assertCount(2, $result['datasets'][0]['data']);
}
/**
@ -156,18 +163,17 @@ class PageviewsPerPageTest extends TestCase
$subject = new PageviewsPerPage(
GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_tracking_pageview'),
GeneralUtility::makeInstance(PageRepository::class),
31,
4
new Demand(31, 4)
);
$result = $subject->getChartData();
static::assertSame([
self::assertSame([
'Page 10',
'Page 9',
'Page 8',
'Page 7',
], $result['labels']);
static::assertCount(4, $result['datasets'][0]['data']);
self::assertCount(4, $result['datasets'][0]['data']);
}
/**
@ -187,19 +193,17 @@ class PageviewsPerPageTest extends TestCase
$subject = new PageviewsPerPage(
GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_tracking_pageview'),
GeneralUtility::makeInstance(PageRepository::class),
31,
6,
[1, 2, 3, 4, 5, 6]
new Demand(31, 6, [1, 2, 3, 4, 5, 6])
);
$result = $subject->getChartData();
static::assertSame([
self::assertSame([
'Page 10',
'Page 9',
'Page 8',
'Page 7',
], $result['labels']);
static::assertCount(4, $result['datasets'][0]['data']);
self::assertCount(4, $result['datasets'][0]['data']);
}
/**
@ -235,18 +239,15 @@ class PageviewsPerPageTest extends TestCase
$subject = new PageviewsPerPage(
GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_tracking_pageview'),
GeneralUtility::makeInstance(PageRepository::class),
31,
6,
[],
[1]
new Demand(31, 6, [], [1])
);
$result = $subject->getChartData();
static::assertSame([
self::assertSame([
'Page 2',
'Page 1',
], $result['labels']);
static::assertCount(2, $result['datasets'][0]['data']);
self::assertCount(2, $result['datasets'][0]['data']);
}
/**
@ -282,18 +283,141 @@ class PageviewsPerPageTest extends TestCase
$subject = new PageviewsPerPage(
GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_tracking_pageview'),
GeneralUtility::makeInstance(PageRepository::class),
31,
6,
[],
[1, '0']
new Demand(31, 6, [], [1, 0])
);
$result = $subject->getChartData();
static::assertSame([
self::assertSame([
'Page 2',
'Page 1',
'Page 3',
], $result['labels']);
static::assertCount(3, $result['datasets'][0]['data']);
self::assertCount(3, $result['datasets'][0]['data']);
}
/**
* @test
*/
public function respectedConfiguredTagRuleToNotIncludeBots(): void
{
$this->importDataSet('EXT:tracking/Tests/Functional/Fixtures/Pages.xml');
$connection = $this->getConnectionPool()->getConnectionForTable('tx_tracking_pageview');
for ($i = 1; $i <= 10; $i++) {
$connection->insert('tx_tracking_pageview', [
'pid' => $i,
'uid' => $i,
'crdate' => time(),
]);
$connection->insert('tx_tracking_tag', [
'pid' => $i,
'uid' => $i,
'record_uid' => $i,
'record_table_name' => 'tx_tracking_pageview',
'name' => 'bot',
'value' => 'no',
'crdate' => time(),
]);
}
for ($i = 11; $i <= 20; $i++) {
$connection->insert('tx_tracking_pageview', [
'pid' => $i,
'uid' => $i,
'crdate' => time(),
]);
$connection->insert('tx_tracking_tag', [
'pid' => $i,
'uid' => $i,
'record_uid' => $i,
'record_table_name' => 'tx_tracking_pageview',
'name' => 'bot',
'value' => 'yes',
'crdate' => time(),
]);
}
$subject = new PageviewsPerPage(
GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_tracking_pageview'),
GeneralUtility::makeInstance(PageRepository::class),
new Demand(31, 6, [], [], [
Tag::createFromArray([
'name' => 'bot',
'value' => 'no',
]),
])
);
$result = $subject->getChartData();
self::assertSame([
'Page 10',
'Page 9',
'Page 8',
'Page 7',
'Page 6',
'Page 5',
], $result['labels']);
self::assertCount(6, $result['datasets'][0]['data']);
}
/**
* @test
*/
public function respectedConfiguredTagRuleToIncludeBots(): void
{
$this->importDataSet('EXT:tracking/Tests/Functional/Fixtures/Pages.xml');
$connection = $this->getConnectionPool()->getConnectionForTable('tx_tracking_pageview');
for ($i = 1; $i <= 10; $i++) {
$connection->insert('tx_tracking_pageview', [
'pid' => $i,
'uid' => $i,
'crdate' => time(),
]);
$connection->insert('tx_tracking_tag', [
'pid' => $i,
'uid' => $i,
'record_uid' => $i,
'record_table_name' => 'tx_tracking_pageview',
'name' => 'bot',
'value' => 'no',
'crdate' => time(),
]);
}
for ($i = 11; $i <= 20; $i++) {
$connection->insert('tx_tracking_pageview', [
'pid' => $i,
'uid' => $i,
'crdate' => time(),
]);
$connection->insert('tx_tracking_tag', [
'pid' => $i,
'uid' => $i,
'record_uid' => $i,
'record_table_name' => 'tx_tracking_pageview',
'name' => 'bot',
'value' => 'yes',
'crdate' => time(),
]);
}
$subject = new PageviewsPerPage(
GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_tracking_pageview'),
GeneralUtility::makeInstance(PageRepository::class),
new Demand(31, 6, [], [], [
Tag::createFromArray([
'name' => 'bot',
'value' => 'yes',
]),
])
);
$result = $subject->getChartData();
self::assertSame([
'Page 20',
'Page 19',
'Page 18',
'Page 17',
'Page 16',
'Page 15',
], $result['labels']);
self::assertCount(6, $result['datasets'][0]['data']);
}
}

View file

@ -1,6 +1,6 @@
<?php
namespace DanielSiepmann\Tracking\Tests\Functional\Dashboard\Provider;
declare(strict_types=1);
/*
* Copyright (C) 2020 Daniel Siepmann <coding@daniel-siepmann.de>
@ -21,13 +21,16 @@ namespace DanielSiepmann\Tracking\Tests\Functional\Dashboard\Provider;
* 02110-1301, USA.
*/
namespace DanielSiepmann\Tracking\Tests\Functional\Dashboard\Provider;
use DanielSiepmann\Tracking\Dashboard\Provider\Demand;
use DanielSiepmann\Tracking\Dashboard\Provider\Recordviews;
use TYPO3\CMS\Core\Domain\Repository\PageRepository;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase as TestCase;
/**
* @covers DanielSiepmann\Tracking\Dashboard\Provider\Recordviews
* @covers \DanielSiepmann\Tracking\Dashboard\Provider\Recordviews
*/
class RecordviewsTest extends TestCase
{
@ -53,11 +56,12 @@ class RecordviewsTest extends TestCase
$subject = new Recordviews(
GeneralUtility::makeInstance(PageRepository::class),
$this->getConnectionPool()->getQueryBuilderForTable('tx_tracking_recordview')
$this->getConnectionPool()->getQueryBuilderForTable('tx_tracking_recordview'),
new Demand()
);
$result = $subject->getChartData();
static::assertSame([
self::assertSame([
'Category 10',
'Category 9',
'Category 8',
@ -65,7 +69,7 @@ class RecordviewsTest extends TestCase
'Category 6',
'Category 5',
], $result['labels']);
static::assertCount(6, $result['datasets'][0]['data']);
self::assertCount(6, $result['datasets'][0]['data']);
}
/**
@ -103,16 +107,16 @@ class RecordviewsTest extends TestCase
$subject = new Recordviews(
GeneralUtility::makeInstance(PageRepository::class),
$this->getConnectionPool()->getQueryBuilderForTable('tx_tracking_recordview'),
2
new Demand(2)
);
$result = $subject->getChartData();
static::assertSame([
self::assertSame([
'Category 2',
'Category 3',
'Category 1',
], $result['labels']);
static::assertCount(3, $result['datasets'][0]['data']);
self::assertCount(3, $result['datasets'][0]['data']);
}
/**
@ -144,15 +148,15 @@ class RecordviewsTest extends TestCase
$subject = new Recordviews(
GeneralUtility::makeInstance(PageRepository::class),
$this->getConnectionPool()->getQueryBuilderForTable('tx_tracking_recordview'),
2
new Demand(2)
);
$result = $subject->getChartData();
static::assertSame([
self::assertSame([
'Category 3',
'Category 2',
], $result['labels']);
static::assertCount(2, $result['datasets'][0]['data']);
self::assertCount(2, $result['datasets'][0]['data']);
}
/**
@ -174,16 +178,15 @@ class RecordviewsTest extends TestCase
$subject = new Recordviews(
GeneralUtility::makeInstance(PageRepository::class),
$this->getConnectionPool()->getQueryBuilderForTable('tx_tracking_recordview'),
31,
2
new Demand(31, 2)
);
$result = $subject->getChartData();
static::assertSame([
self::assertSame([
'Category 10',
'Category 9',
], $result['labels']);
static::assertCount(2, $result['datasets'][0]['data']);
self::assertCount(2, $result['datasets'][0]['data']);
}
/**
@ -206,20 +209,18 @@ class RecordviewsTest extends TestCase
$subject = new Recordviews(
GeneralUtility::makeInstance(PageRepository::class),
$this->getConnectionPool()->getQueryBuilderForTable('tx_tracking_recordview'),
31,
6,
[1, 2, 3, 4, 5]
new Demand(31, 6, [1, 2, 3, 4, 5])
);
$result = $subject->getChartData();
static::assertSame([
self::assertSame([
'Category 10',
'Category 9',
'Category 8',
'Category 7',
'Category 6',
], $result['labels']);
static::assertCount(5, $result['datasets'][0]['data']);
self::assertCount(5, $result['datasets'][0]['data']);
}
/**
@ -247,20 +248,17 @@ class RecordviewsTest extends TestCase
$subject = new Recordviews(
GeneralUtility::makeInstance(PageRepository::class),
$this->getConnectionPool()->getQueryBuilderForTable('tx_tracking_recordview'),
31,
6,
[],
[],
new Demand(31, 6),
['sys_category']
);
$result = $subject->getChartData();
static::assertSame([
self::assertSame([
'Category 3',
'Category 2',
'Category 1',
], $result['labels']);
static::assertCount(3, $result['datasets'][0]['data']);
self::assertCount(3, $result['datasets'][0]['data']);
}
/**
@ -286,20 +284,17 @@ class RecordviewsTest extends TestCase
$subject = new Recordviews(
GeneralUtility::makeInstance(PageRepository::class),
$this->getConnectionPool()->getQueryBuilderForTable('tx_tracking_recordview'),
31,
6,
[],
[],
new Demand(31, 6),
[],
['1', 2]
);
$result = $subject->getChartData();
static::assertSame([
self::assertSame([
'Content element 2',
'Content element 1',
], $result['labels']);
static::assertCount(2, $result['datasets'][0]['data']);
self::assertCount(2, $result['datasets'][0]['data']);
}
/**
@ -334,20 +329,15 @@ class RecordviewsTest extends TestCase
$subject = new Recordviews(
GeneralUtility::makeInstance(PageRepository::class),
$this->getConnectionPool()->getQueryBuilderForTable('tx_tracking_recordview'),
31,
6,
[],
[1],
[],
[]
new Demand(31, 6, [], [1])
);
$result = $subject->getChartData();
static::assertSame([
self::assertSame([
'Category 2',
'Kategorie 1',
], $result['labels']);
static::assertCount(2, $result['datasets'][0]['data']);
self::assertCount(2, $result['datasets'][0]['data']);
}
/**
@ -382,19 +372,16 @@ class RecordviewsTest extends TestCase
$subject = new Recordviews(
GeneralUtility::makeInstance(PageRepository::class),
$this->getConnectionPool()->getQueryBuilderForTable('tx_tracking_recordview'),
31,
6,
[],
[1, 0],
[],
[]
new Demand(31, 6, [], [1, 0])
);
$result = $subject->getChartData();
static::assertSame([
self::assertSame([
'Category 1',
'Category 2',
], $result['labels']);
static::assertCount(2, $result['datasets'][0]['data']);
self::assertCount(2, $result['datasets'][0]['data']);
}
// TODO: Add tests for new feature regarding tags
}

View file

@ -0,0 +1,183 @@
<?php
declare(strict_types=1);
/*
* 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.
*/
namespace DanielSiepmann\Tracking\Tests\Functional\Domain\Extractors;
use DanielSiepmann\Tracking\Domain\Extractors\Bots;
use DanielSiepmann\Tracking\Domain\Extractors\Bots\CustomBotParser;
use DanielSiepmann\Tracking\Domain\Model\Extractor;
use DanielSiepmann\Tracking\Domain\Model\Pageview;
use DanielSiepmann\Tracking\Domain\Model\Recordview;
use DeviceDetector\DeviceDetector;
use Prophecy\PhpUnit\ProphecyTrait;
use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase as TestCase;
/**
* @covers \DanielSiepmann\Tracking\Domain\Extractors\Bots
*/
class BotsTest extends TestCase
{
use ProphecyTrait;
protected $testExtensionsToLoad = [
'typo3conf/ext/tracking',
];
/**
* @test
* @dataProvider possibleUserStringWithBots
* @testdox Bot $expectedBot is extracted from Pageview UserAgent string: $userAgent
*/
public function returnsBotForPageview(string $userAgent, string $expectedBot): void
{
$model = $this->prophesize(Pageview::class);
$model->getUserAgent()->willReturn($userAgent);
$extractor = new Bots(new CustomBotParser());
$tags = $extractor->extractTagFromPageview($model->reveal());
self::assertCount(2, $tags);
self::assertSame('bot', $tags[0]->getName());
self::assertSame('yes', $tags[0]->getValue());
self::assertSame('bot_name', $tags[1]->getName());
self::assertSame($expectedBot, $tags[1]->getValue());
}
/**
* @test
* @dataProvider possibleUserStringWithBots
* @testdox Bot $expectedBot is extracted from Recordview UserAgent string: $userAgent
*/
public function returnsBotForRecordview(string $userAgent, string $expectedBot): void
{
$model = $this->prophesize(Recordview::class);
$model->getUserAgent()->willReturn($userAgent);
$extractor = new Bots(new CustomBotParser());
$tags = $extractor->extractTagFromRecordview($model->reveal());
self::assertCount(2, $tags);
self::assertSame('bot', $tags[0]->getName());
self::assertSame('yes', $tags[0]->getValue());
self::assertSame('bot_name', $tags[1]->getName());
self::assertSame($expectedBot, $tags[1]->getValue());
}
public function possibleUserStringWithBots(): array
{
return [
0 => [
'userAgent' => 'nettle (+https://www.nettle.sk)',
'expectedBot' => 'Nettle',
],
1 => [
'userAgent' => 'MauiBot (crawler.feedback+wc@gmail.com)',
'expectedBot' => 'Generic Bot',
],
2 => [
'userAgent' => 'facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)',
'expectedBot' => 'Facebook External Hit',
],
3 => [
'userAgent' => 'Java/11.0.10',
'expectedBot' => 'Java',
],
4 => [
'userAgent' => 'newspaper/0.2.8',
'expectedBot' => 'Newspaper',
],
5 => [
'userAgent' => 'Tiny Tiny RSS/21.05-326850845 (http://tt-rss.org/)',
'expectedBot' => 'Tiny Tiny RSS',
],
6 => [
'userAgent' => 'BacklinkCrawler (http://www.backlinktest.com/crawler.html)',
'expectedBot' => 'BacklinkCrawler',
],
7 => [
'userAgent' => 'CCBot/2.0 (https://commoncrawl.org/faq/)',
'expectedBot' => 'ccBot crawler',
],
8 => [
'userAgent' => 'Tiny Tiny RSS/21.04-e8f78181f (http://tt-rss.org/)',
'expectedBot' => 'Tiny Tiny RSS',
],
9 => [
'userAgent' => 'ltx71 - (http://ltx71.com/)',
'expectedBot' => 'LTX71',
],
10 => [
'userAgent' => 'FeedFetcher-Google; (+http://www.google.com/feedfetcher.html)',
'expectedBot' => 'Googlebot',
],
11 => [
'userAgent' => 'colly - https://github.com/gocolly/colly',
'expectedBot' => 'colly',
],
12 => [
'userAgent' => 'WordPress.com; https://serdargunes.wordpress.com',
'expectedBot' => 'WordPress',
],
13 => [
'userAgent' => 'Tiny Tiny RSS/21.03-2f402d598 (http://tt-rss.org/)',
'expectedBot' => 'Tiny Tiny RSS',
],
14 => [
'userAgent' => 'netEstate NE Crawler (+http://www.website-datenbank.de/)',
'expectedBot' => 'netEstate',
],
15 => [
'userAgent' => 'python-requests/2.18.1',
'expectedBot' => 'Python Requests',
],
16 => [
'userAgent' => 'PocketParser/2.0 (+https://getpocket.com/pocketparser_ua)',
'expectedBot' => 'PocketParser',
],
17 => [
'userAgent' => 'Faraday v0.17.3',
'expectedBot' => 'Faraday',
],
18 => [
'userAgent' => 'hgfAlphaXCrawl/0.1 (+https://www.fim.uni-passau.de/data-science/forschung/open-search)',
'expectedBot' => 'Generic Bot',
],
19 => [
'userAgent' => 'Upflow/1.0',
'expectedBot' => 'Upflow',
],
20 => [
'userAgent' => 'crusty/0.12.0',
'expectedBot' => 'Crusty',
],
21 => [
'userAgent' => 'TelegramBot (like TwitterBot)',
'expectedBot' => 'TelegramBot',
],
22 => [
'userAgent' => 'python-requests/2.25.1',
'expectedBot' => 'Python Requests',
],
];
}
}

View file

@ -1,6 +1,6 @@
<?php
namespace DanielSiepmann\Tracking\Tests\Unit\Domain\Recordview;
declare(strict_types=1);
/*
* Copyright (C) 2020 Daniel Siepmann <coding@daniel-siepmann.de>
@ -21,6 +21,8 @@ namespace DanielSiepmann\Tracking\Tests\Unit\Domain\Recordview;
* 02110-1301, USA.
*/
namespace DanielSiepmann\Tracking\Tests\Unit\Domain\Recordview;
use DanielSiepmann\Tracking\Domain\Model\RecordRule;
use DanielSiepmann\Tracking\Domain\Model\Recordview;
use DanielSiepmann\Tracking\Domain\Recordview\Factory;
@ -31,7 +33,7 @@ use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
/**
* @covers DanielSiepmann\Tracking\Domain\Recordview\Factory
* @covers \DanielSiepmann\Tracking\Domain\Recordview\Factory
*/
class FactoryTest extends FunctionalTestCase
{
@ -67,7 +69,7 @@ class FactoryTest extends FunctionalTestCase
$subject = $this->get(Factory::class);
$result = $subject->fromRequest($request->reveal(), $rule->reveal());
static::assertInstanceOf(Recordview::class, $result);
self::assertInstanceOf(Recordview::class, $result);
}
/**
@ -96,7 +98,7 @@ class FactoryTest extends FunctionalTestCase
$subject = $this->get(Factory::class);
$result = $subject->fromRequest($request->reveal(), $rule->reveal());
static::assertSame('Some User Agent', $result->getUserAgent());
self::assertSame('Some User Agent', $result->getUserAgent());
}
/**
@ -125,7 +127,7 @@ class FactoryTest extends FunctionalTestCase
$subject = $this->get(Factory::class);
$result = $subject->fromRequest($request->reveal(), $rule->reveal());
static::assertSame('https://example.com', $result->getUrl());
self::assertSame('https://example.com', $result->getUrl());
}
/**
@ -154,7 +156,7 @@ class FactoryTest extends FunctionalTestCase
$subject = $this->get(Factory::class);
$result = $subject->fromRequest($request->reveal(), $rule->reveal());
static::assertInstanceOf(\DateTimeImmutable::class, $result->getCrdate());
self::assertInstanceOf(\DateTimeImmutable::class, $result->getCrdate());
}
/**
@ -183,7 +185,7 @@ class FactoryTest extends FunctionalTestCase
$subject = $this->get(Factory::class);
$result = $subject->fromRequest($request->reveal(), $rule->reveal());
static::assertSame($language->reveal(), $result->getLanguage());
self::assertSame($language->reveal(), $result->getLanguage());
}
/**
@ -212,7 +214,7 @@ class FactoryTest extends FunctionalTestCase
$subject = $this->get(Factory::class);
$result = $subject->fromRequest($request->reveal(), $rule->reveal());
static::assertSame(10, $result->getPageUid());
self::assertSame(10, $result->getPageUid());
}
/**
@ -241,7 +243,7 @@ class FactoryTest extends FunctionalTestCase
$subject = $this->get(Factory::class);
$result = $subject->fromRequest($request->reveal(), $rule->reveal());
static::assertSame(20, $result->getRecordUid());
self::assertSame(20, $result->getRecordUid());
}
/**
@ -270,6 +272,6 @@ class FactoryTest extends FunctionalTestCase
$subject = $this->get(Factory::class);
$result = $subject->fromRequest($request->reveal(), $rule->reveal());
static::assertSame('sys_category', $result->getTableName());
self::assertSame('sys_category', $result->getTableName());
}
}

View file

@ -58,4 +58,54 @@
<uid>10</uid>
<title>Page 10</title>
</pages>
<pages>
<pid>1</pid>
<uid>12</uid>
<title>Page 12</title>
</pages>
<pages>
<pid>1</pid>
<uid>13</uid>
<title>Page 13</title>
</pages>
<pages>
<pid>1</pid>
<uid>14</uid>
<title>Page 14</title>
</pages>
<pages>
<pid>1</pid>
<uid>15</uid>
<title>Page 15</title>
</pages>
<pages>
<pid>1</pid>
<uid>16</uid>
<title>Page 16</title>
</pages>
<pages>
<pid>1</pid>
<uid>17</uid>
<title>Page 17</title>
</pages>
<pages>
<pid>1</pid>
<uid>18</uid>
<title>Page 18</title>
</pages>
<pages>
<pid>1</pid>
<uid>19</uid>
<title>Page 19</title>
</pages>
<pages>
<pid>1</pid>
<uid>20</uid>
<title>Page 20</title>
</pages>
<pages>
<pid>1</pid>
<uid>21</uid>
<title>Page 21</title>
</pages>
</dataset>

View file

@ -10,7 +10,6 @@
<uid>1</uid>
<url>https://example.com/path</url>
<user_agent>Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36</user_agent>
<operating_system></operating_system>
</tx_tracking_pageview>
<tx_tracking_recordview>
<pid>1</pid>

View file

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<dataset>
<pages>
<pid>0</pid>
<uid>1</uid>
</pages>
<tx_tracking_pageview>
<pid>1</pid>
<uid>1</uid>
<url>https://example.com/path</url>
<user_agent>Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36</user_agent>
<operating_system></operating_system>
</tx_tracking_pageview>
<tx_tracking_pageview>
<pid>1</pid>
<uid>2</uid>
<url>https://example.com/path</url>
<user_agent>Dalvik/2.1.0 (Linux; U; Android 9; ONEPLUS A3003 Build/PKQ1.181203.001)</user_agent>
<operating_system></operating_system>
</tx_tracking_pageview>
</dataset>

View file

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<dataset>
<pages>
<pid>0</pid>
<uid>1</uid>
</pages>
<tx_tracking_pageview>
<pid>1</pid>
<uid>1</uid>
<url>https://example.com/path</url>
<user_agent>Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36</user_agent>
<operating_system>Linux</operating_system>
</tx_tracking_pageview>
<tx_tracking_pageview>
<pid>1</pid>
<uid>2</uid>
<url>https://example.com/path</url>
<user_agent>Dalvik/2.1.0 (Linux; U; Android 9; ONEPLUS A3003 Build/PKQ1.181203.001)</user_agent>
<operating_system>Android</operating_system>
</tx_tracking_pageview>
</dataset>

View file

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="utf-8"?>
<dataset>
<pages>
<pid>0</pid>
<uid>1</uid>
</pages>
<tx_tracking_pageview>
<pid>1</pid>
<uid>1</uid>
<tstamp>1630649915</tstamp>
<cruser_id>0</cruser_id>
<url>https://example.com/path</url>
<user_agent>Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36</user_agent>
<compatible_version>v2.0.0</compatible_version>
</tx_tracking_pageview>
<tx_tracking_recordview>
<pid>1</pid>
<uid>1</uid>
<tstamp>1630649915</tstamp>
<cruser_id>0</cruser_id>
<url>https://example.com/path</url>
<user_agent>Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36</user_agent>
<record_uid>1</record_uid>
<record_table_name>sys_category</record_table_name>
<compatible_version>v2.0.0</compatible_version>
</tx_tracking_recordview>
<tx_tracking_tag>
<pid>1</pid>
<uid>1</uid>
<crdate>1663773639</crdate>
<record_uid>1</record_uid>
<record_table_name>tx_tracking_pageview</record_table_name>
<name>bot</name>
<value>no</value>
<compatible_version>v2.0.0</compatible_version>
</tx_tracking_tag>
<tx_tracking_tag>
<pid>1</pid>
<uid>v2</uid>
<crdate>1663773639</crdate>
<record_uid>1</record_uid>
<record_table_name>tx_tracking_pageview</record_table_name>
<name>os</name>
<value>Linux</value>
<compatible_version>v2.0.0</compatible_version>
</tx_tracking_tag>
<tx_tracking_tag>
<pid>1</pid>
<uid>3</uid>
<crdate>1663773639</crdate>
<record_uid>1</record_uid>
<record_table_name>tx_tracking_recordview</record_table_name>
<name>bot</name>
<value>no</value>
<compatible_version>v2.0.0</compatible_version>
</tx_tracking_tag>
<tx_tracking_tag>
<pid>1</pid>
<uid>4</uid>
<crdate>1663773639</crdate>
<record_uid>1</record_uid>
<record_table_name>tx_tracking_recordview</record_table_name>
<name>os</name>
<value>Linux</value>
<compatible_version>v2.0.0</compatible_version>
</tx_tracking_tag>
</dataset>

View file

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<dataset>
<pages>
<pid>0</pid>
<uid>1</uid>
</pages>
<tx_tracking_pageview>
<pid>1</pid>
<uid>1</uid>
<crdate>1630649915</crdate>
<tstamp>1630649915</tstamp>
<cruser_id>0</cruser_id>
<url>https://example.com/path</url>
<user_agent>Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36</user_agent>
</tx_tracking_pageview>
<tx_tracking_pageview>
<pid>1</pid>
<uid>2</uid>
<crdate>1630649916</crdate>
<tstamp>1630649916</tstamp>
<cruser_id>0</cruser_id>
<url>https://example.com/path</url>
<user_agent>Dalvik/2.1.0 (Linux; U; Android 9; ONEPLUS A3003 Build/PKQ1.181203.001)</user_agent>
</tx_tracking_pageview>
<tx_tracking_recordview>
<pid>1</pid>
<uid>1</uid>
<crdate>1630649915</crdate>
<tstamp>1630649915</tstamp>
<cruser_id>0</cruser_id>
<url>https://example.com/path</url>
<user_agent>Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36</user_agent>
<record_uid>1</record_uid>
<record_table_name>sys_category</record_table_name>
</tx_tracking_recordview>
<tx_tracking_recordview>
<pid>1</pid>
<uid>2</uid>
<crdate>1630649916</crdate>
<tstamp>1630649916</tstamp>
<cruser_id>0</cruser_id>
<url>https://example.com/path</url>
<user_agent>Dalvik/2.1.0 (Linux; U; Android 9; ONEPLUS A3003 Build/PKQ1.181203.001)</user_agent>
<record_uid>1</record_uid>
<record_table_name>sys_category</record_table_name>
</tx_tracking_recordview>
</dataset>

View file

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<dataset>
<pages>
<pid>0</pid>
<uid>1</uid>
</pages>
<tx_tracking_pageview>
<pid>1</pid>
<uid>1</uid>
<crdate>1630649915</crdate>
<tstamp>1630649915</tstamp>
<cruser_id>0</cruser_id>
<url>https://example.com/path</url>
<user_agent>Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36</user_agent>
</tx_tracking_pageview>
<tx_tracking_tag>
<pid>1</pid>
<uid>1</uid>
<crdate>1630649915</crdate>
<tstamp>1630649915</tstamp>
<cruser_id>0</cruser_id>
<record_uid>1</record_uid>
<record_table_name>tx_tracking_pageview</record_table_name>
<name>os</name>
<value>Linux</value>
<compatible_version>2.0.0</compatible_version>
</tx_tracking_tag>
<tx_tracking_pageview>
<pid>1</pid>
<uid>2</uid>
<crdate>1630649916</crdate>
<tstamp>1630649916</tstamp>
<cruser_id>0</cruser_id>
<url>https://example.com/path</url>
<user_agent>Dalvik/2.1.0 (Linux; U; Android 9; ONEPLUS A3003 Build/PKQ1.181203.001)</user_agent>
</tx_tracking_pageview>
<tx_tracking_tag>
<pid>1</pid>
<uid>2</uid>
<crdate>1630649916</crdate>
<tstamp>1630649916</tstamp>
<cruser_id>0</cruser_id>
<record_uid>2</record_uid>
<record_table_name>tx_tracking_pageview</record_table_name>
<name>os</name>
<value>Android</value>
<compatible_version>2.0.0</compatible_version>
</tx_tracking_tag>
</dataset>

View file

@ -1,6 +1,6 @@
<?php
namespace DanielSiepmann\Tracking\Tests\Functional;
declare(strict_types=1);
/*
* Copyright (C) 2021 Daniel Siepmann <coding@daniel-siepmann.de>
@ -21,6 +21,8 @@ namespace DanielSiepmann\Tracking\Tests\Functional;
* 02110-1301, USA.
*/
namespace DanielSiepmann\Tracking\Tests\Functional;
use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\InternalRequest;
use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\InternalRequestContext;
use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase as TestCase;
@ -62,12 +64,68 @@ class PageviewTest extends TestCase
$records = $this->getAllRecords('tx_tracking_pageview');
self::assertCount(1, $records);
self::assertSame('1', (string)$records[0]['pid']);
self::assertSame('1', (string)$records[0]['uid']);
self::assertSame(1, $records[0]['pid']);
self::assertSame(1, $records[0]['uid']);
self::assertSame('http://localhost/?id=1', $records[0]['url']);
self::assertSame('Mozilla/5.0 (Macintosh; Intel Mac OS X x.y; rv:42.0) Gecko/20100101 Firefox/42.0', $records[0]['user_agent']);
self::assertSame('Macintosh', $records[0]['operating_system']);
self::assertSame('0', (string)$records[0]['type']);
$records = $this->getAllRecords('tx_tracking_tag');
self::assertCount(2, $records);
self::assertSame(1, $records[0]['pid']);
self::assertSame(1, $records[0]['record_uid']);
self::assertSame('tx_tracking_pageview', $records[0]['record_table_name']);
self::assertSame('bot', $records[0]['name']);
self::assertSame('no', $records[0]['value']);
self::assertSame(1, $records[1]['pid']);
self::assertSame(1, $records[1]['record_uid']);
self::assertSame('tx_tracking_pageview', $records[1]['record_table_name']);
self::assertSame('os', $records[1]['name']);
self::assertSame('Macintosh', $records[1]['value']);
}
/**
* @test
*/
public function trackedWithBotResolvedToTags(): void
{
$request = new InternalRequest();
$request = $request->withPageId(1);
$request = $request->withHeader('User-Agent', 'Slackbot-LinkExpanding 1.0 (+https://api.slack.com/robots)');
$response = $this->executeFrontendRequest($request);
self::assertSame(200, $response->getStatusCode());
$records = $this->getAllRecords('tx_tracking_pageview');
self::assertCount(1, $records);
self::assertSame(1, $records[0]['pid']);
self::assertSame(1, $records[0]['uid']);
self::assertSame('http://localhost/?id=1', $records[0]['url']);
self::assertSame('Slackbot-LinkExpanding 1.0 (+https://api.slack.com/robots)', $records[0]['user_agent']);
self::assertSame('0', (string)$records[0]['type']);
$records = $this->getAllRecords('tx_tracking_tag');
self::assertCount(3, $records);
self::assertSame(1, $records[0]['pid']);
self::assertSame(1, $records[0]['record_uid']);
self::assertSame('tx_tracking_pageview', $records[0]['record_table_name']);
self::assertSame('bot', $records[0]['name']);
self::assertSame('yes', $records[0]['value']);
self::assertSame(1, $records[1]['pid']);
self::assertSame(1, $records[1]['record_uid']);
self::assertSame('tx_tracking_pageview', $records[1]['record_table_name']);
self::assertSame('bot_name', $records[1]['name']);
self::assertSame('Slackbot', $records[1]['value']);
self::assertSame(1, $records[2]['pid']);
self::assertSame(1, $records[2]['record_uid']);
self::assertSame('tx_tracking_pageview', $records[2]['record_table_name']);
self::assertSame('os', $records[2]['name']);
self::assertSame('Unkown', $records[2]['value']);
}
/**
@ -85,7 +143,7 @@ class PageviewTest extends TestCase
self::assertSame(200, $response->getStatusCode());
$records = $this->getAllRecords('tx_tracking_pageview');
self::assertCount(0, $records);
self::assertCount(0, $this->getAllRecords('tx_tracking_pageview'));
self::assertCount(0, $this->getAllRecords('tx_tracking_tag'));
}
}

View file

@ -1,6 +1,6 @@
<?php
namespace DanielSiepmann\Tracking\Tests\Functional;
declare(strict_types=1);
/*
* Copyright (C) 2021 Daniel Siepmann <coding@daniel-siepmann.de>
@ -21,6 +21,8 @@ namespace DanielSiepmann\Tracking\Tests\Functional;
* 02110-1301, USA.
*/
namespace DanielSiepmann\Tracking\Tests\Functional;
use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\InternalRequest;
use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\InternalRequestContext;
use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase as TestCase;
@ -54,6 +56,7 @@ class RecordviewTest extends TestCase
*/
public function trackedWhenAllowed(): void
{
$this->setUpBackendUserFromFixture(1);
$request = new InternalRequest();
$request = $request->withPageId(1);
$request = $request->withQueryParameter('topic_id', 1);
@ -64,14 +67,28 @@ class RecordviewTest extends TestCase
$records = $this->getAllRecords('tx_tracking_recordview');
self::assertCount(1, $records);
self::assertSame('1', (string)$records[0]['pid']);
self::assertSame('1', (string)$records[0]['uid']);
self::assertSame(1, $records[0]['pid']);
self::assertSame(1, $records[0]['uid']);
self::assertSame('http://localhost/?id=1&topic_id=1', $records[0]['url']);
self::assertSame('Mozilla/5.0 (Macintosh; Intel Mac OS X x.y; rv:42.0) Gecko/20100101 Firefox/42.0', $records[0]['user_agent']);
self::assertSame('Macintosh', $records[0]['operating_system']);
self::assertSame('sys_category_1', $records[0]['record']);
self::assertSame('1', (string)$records[0]['record_uid']);
self::assertSame(1, $records[0]['record_uid']);
self::assertSame('sys_category', $records[0]['record_table_name']);
$records = $this->getAllRecords('tx_tracking_tag');
self::assertCount(4, $records);
self::assertSame(1, $records[2]['pid']);
self::assertSame(1, $records[2]['record_uid']);
self::assertSame('tx_tracking_recordview', $records[2]['record_table_name']);
self::assertSame('bot', $records[2]['name']);
self::assertSame('no', $records[2]['value']);
self::assertSame(1, $records[3]['pid']);
self::assertSame(1, $records[3]['record_uid']);
self::assertSame('tx_tracking_recordview', $records[3]['record_table_name']);
self::assertSame('os', $records[3]['name']);
self::assertSame('Macintosh', $records[3]['value']);
}
/**

View file

@ -1,6 +1,6 @@
<?php
namespace DanielSiepmann\Tracking\Tests\Functional;
declare(strict_types=1);
/*
* Copyright (C) 2021 Daniel Siepmann <coding@daniel-siepmann.de>
@ -21,13 +21,15 @@ namespace DanielSiepmann\Tracking\Tests\Functional;
* 02110-1301, USA.
*/
namespace DanielSiepmann\Tracking\Tests\Functional;
use DanielSiepmann\Tracking\Functional\CopyingPageWithRecordsWorks;
use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Core\Localization\LanguageServiceFactory;
use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase as TestCase;
/**
* @covers \DanielSiepmann\Tracking\Functional\CopyingPageWithRecordsWorks
* @covers \DanielSiepmann\Tracking\Hooks\DataHandler
* @testdox This extension works with TYPO3 feature:
*/
class Typo3FeaturesTest extends TestCase

View file

@ -1,6 +1,6 @@
<?php
namespace DanielSiepmann\Tracking\Tests\Unit\Domain\Model;
declare(strict_types=1);
/*
* Copyright (C) 2020 Daniel Siepmann <coding@daniel-siepmann.de>
@ -21,32 +21,54 @@ namespace DanielSiepmann\Tracking\Tests\Unit\Domain\Model;
* 02110-1301, USA.
*/
namespace DanielSiepmann\Tracking\Tests\Unit\Domain\Extractors;
use DanielSiepmann\Tracking\Domain\Extractors\OperatingSystem;
use DanielSiepmann\Tracking\Domain\Model\Extractor;
use DanielSiepmann\Tracking\Domain\Model\HasUserAgent;
use DanielSiepmann\Tracking\Domain\Model\Pageview;
use DanielSiepmann\Tracking\Domain\Model\Recordview;
use Prophecy\PhpUnit\ProphecyTrait;
use TYPO3\TestingFramework\Core\Unit\UnitTestCase as TestCase;
/**
* @covers DanielSiepmann\Tracking\Domain\Model\Extractor
* @covers \DanielSiepmann\Tracking\Domain\Extractors\OperatingSystem
*/
class ExtractorTest extends TestCase
class OperatingSystemTest extends TestCase
{
use ProphecyTrait;
/**
* @test
* @dataProvider possibleUserStringWithOperatingSystems
* @testdox Operating system $expectedOperatingSystem is extracted from UserAgent string: $userAgent
* @testdox Operating system $expectedOperatingSystem is extracted from Pageview UserAgent string: $userAgent
*/
public function returnsOperatingSystem(string $userAgent, string $expectedOperatingSystem): void
public function returnsOperatingSystemForPageview(string $userAgent, string $expectedOperatingSystem): void
{
$model = $this->prophesize(HasUserAgent::class);
$model = $this->prophesize(Pageview::class);
$model->getUserAgent()->willReturn($userAgent);
static::assertSame(
$expectedOperatingSystem,
Extractor::getOperatingSystem($model->reveal())
);
$extractor = new OperatingSystem();
$tags = $extractor->extractTagFromPageview($model->reveal());
self::assertCount(1, $tags);
self::assertSame($expectedOperatingSystem, $tags[0]->getValue());
}
/**
* @test
* @dataProvider possibleUserStringWithOperatingSystems
* @testdox Operating system $expectedOperatingSystem is extracted from Recordview UserAgent string: $userAgent
*/
public function returnsOperatingSystemForRecordview(string $userAgent, string $expectedOperatingSystem): void
{
$model = $this->prophesize(Recordview::class);
$model->getUserAgent()->willReturn($userAgent);
$extractor = new OperatingSystem();
$tags = $extractor->extractTagFromRecordview($model->reveal());
self::assertCount(1, $tags);
self::assertSame($expectedOperatingSystem, $tags[0]->getValue());
}
public function possibleUserStringWithOperatingSystems(): array
@ -62,11 +84,11 @@ class ExtractorTest extends TestCase
],
[
'userAgent' => 'Apache-HttpClient/4.5.2 (Java/1.8.0_151)',
'expectedOperatingSystem' => '',
'expectedOperatingSystem' => 'Unkown',
],
[
'userAgent' => 'AwarioSmartBot/1.0 (+https://awario.com/bots.html; bots@awario.com)',
'expectedOperatingSystem' => '',
'expectedOperatingSystem' => 'Unkown',
],
[
'userAgent' => 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)',

View file

@ -1,6 +1,6 @@
<?php
namespace DanielSiepmann\Tracking\Tests\Unit\Domain\Model;
declare(strict_types=1);
/*
* Copyright (C) 2020 Daniel Siepmann <coding@daniel-siepmann.de>
@ -21,13 +21,15 @@ namespace DanielSiepmann\Tracking\Tests\Unit\Domain\Model;
* 02110-1301, USA.
*/
namespace DanielSiepmann\Tracking\Tests\Unit\Domain\Model;
use DanielSiepmann\Tracking\Domain\Model\Pageview;
use Prophecy\PhpUnit\ProphecyTrait;
use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
use TYPO3\TestingFramework\Core\Unit\UnitTestCase as TestCase;
/**
* @covers DanielSiepmann\Tracking\Domain\Model\Pageview
* @covers \DanielSiepmann\Tracking\Domain\Model\Pageview
*/
class PageviewTest extends TestCase
{
@ -49,7 +51,7 @@ class PageviewTest extends TestCase
''
);
static::assertInstanceOf(Pageview::class, $subject);
self::assertInstanceOf(Pageview::class, $subject);
}
/**
@ -68,7 +70,7 @@ class PageviewTest extends TestCase
''
);
static::assertSame(500, $subject->getPageUid());
self::assertSame(500, $subject->getPageUid());
}
/**
@ -87,7 +89,7 @@ class PageviewTest extends TestCase
''
);
static::assertSame($language->reveal(), $subject->getLanguage());
self::assertSame($language->reveal(), $subject->getLanguage());
}
/**
@ -107,7 +109,7 @@ class PageviewTest extends TestCase
''
);
static::assertSame($crdate, $subject->getCrdate());
self::assertSame($crdate, $subject->getCrdate());
}
/**
@ -126,7 +128,7 @@ class PageviewTest extends TestCase
''
);
static::assertSame(999, $subject->getPageType());
self::assertSame(999, $subject->getPageType());
}
/**
@ -145,7 +147,7 @@ class PageviewTest extends TestCase
''
);
static::assertSame('https://example.com/path.html', $subject->getUrl());
self::assertSame('https://example.com/path.html', $subject->getUrl());
}
/**
@ -164,7 +166,7 @@ class PageviewTest extends TestCase
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0'
);
static::assertSame(
self::assertSame(
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0',
$subject->getUserAgent()
);
@ -186,7 +188,7 @@ class PageviewTest extends TestCase
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0'
);
static::assertSame(
self::assertSame(
0,
$subject->getUid()
);
@ -209,31 +211,9 @@ class PageviewTest extends TestCase
10
);
static::assertSame(
self::assertSame(
10,
$subject->getUid()
);
}
/**
* @test
*/
public function returnsOperatingSystem(): void
{
$language = $this->prophesize(SiteLanguage::class);
$subject = new Pageview(
0,
$language->reveal(),
new \DateTimeImmutable(),
0,
'',
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36'
);
static::assertSame(
'Linux',
$subject->getOperatingSystem()
);
}
}

View file

@ -1,6 +1,6 @@
<?php
namespace DanielSiepmann\Tracking\Tests\Unit\Domain\Model;
declare(strict_types=1);
/*
* Copyright (C) 2020 Daniel Siepmann <coding@daniel-siepmann.de>
@ -21,11 +21,13 @@ namespace DanielSiepmann\Tracking\Tests\Unit\Domain\Model;
* 02110-1301, USA.
*/
namespace DanielSiepmann\Tracking\Tests\Unit\Domain\Model;
use DanielSiepmann\Tracking\Domain\Model\RecordRule;
use TYPO3\TestingFramework\Core\Unit\UnitTestCase as TestCase;
/**
* @covers DanielSiepmann\Tracking\Domain\Model\RecordRule
* @covers \DanielSiepmann\Tracking\Domain\Model\RecordRule
*/
class RecordRuleTest extends TestCase
{
@ -40,7 +42,7 @@ class RecordRuleTest extends TestCase
''
);
static::assertInstanceOf(RecordRule::class, $subject);
self::assertInstanceOf(RecordRule::class, $subject);
}
/**
@ -55,7 +57,7 @@ class RecordRuleTest extends TestCase
'tableName' => '',
]);
static::assertInstanceOf(RecordRule::class, $subject);
self::assertInstanceOf(RecordRule::class, $subject);
}
/**
@ -76,9 +78,9 @@ class RecordRuleTest extends TestCase
],
]);
static::assertCount(2, $result);
static::assertInstanceOf(RecordRule::class, $result[0]);
static::assertInstanceOf(RecordRule::class, $result[1]);
self::assertCount(2, $result);
self::assertInstanceOf(RecordRule::class, $result[0]);
self::assertInstanceOf(RecordRule::class, $result[1]);
}
/**
@ -92,7 +94,7 @@ class RecordRuleTest extends TestCase
''
);
static::assertSame('match expression', $subject->getMatchesExpression());
self::assertSame('match expression', $subject->getMatchesExpression());
}
/**
@ -106,7 +108,7 @@ class RecordRuleTest extends TestCase
''
);
static::assertSame('match expression', $subject->getUidExpression());
self::assertSame('match expression', $subject->getUidExpression());
}
/**
@ -120,6 +122,6 @@ class RecordRuleTest extends TestCase
'table_name'
);
static::assertSame('table_name', $subject->getTableName());
self::assertSame('table_name', $subject->getTableName());
}
}

View file

@ -1,6 +1,6 @@
<?php
namespace DanielSiepmann\Tracking\Tests\Unit\Domain\Model;
declare(strict_types=1);
/*
* Copyright (C) 2020 Daniel Siepmann <coding@daniel-siepmann.de>
@ -21,13 +21,15 @@ namespace DanielSiepmann\Tracking\Tests\Unit\Domain\Model;
* 02110-1301, USA.
*/
namespace DanielSiepmann\Tracking\Tests\Unit\Domain\Model;
use DanielSiepmann\Tracking\Domain\Model\Recordview;
use Prophecy\PhpUnit\ProphecyTrait;
use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
use TYPO3\TestingFramework\Core\Unit\UnitTestCase as TestCase;
/**
* @covers DanielSiepmann\Tracking\Domain\Model\Recordview
* @covers \DanielSiepmann\Tracking\Domain\Model\Recordview
*/
class RecordviewTest extends TestCase
{
@ -50,7 +52,7 @@ class RecordviewTest extends TestCase
'sys_category'
);
static::assertInstanceOf(Recordview::class, $subject);
self::assertInstanceOf(Recordview::class, $subject);
}
/**
@ -70,7 +72,7 @@ class RecordviewTest extends TestCase
'sys_category'
);
static::assertSame(500, $subject->getPageUid());
self::assertSame(500, $subject->getPageUid());
}
/**
@ -90,7 +92,7 @@ class RecordviewTest extends TestCase
'sys_category'
);
static::assertSame($language->reveal(), $subject->getLanguage());
self::assertSame($language->reveal(), $subject->getLanguage());
}
/**
@ -111,7 +113,7 @@ class RecordviewTest extends TestCase
'sys_category'
);
static::assertSame($crdate, $subject->getCrdate());
self::assertSame($crdate, $subject->getCrdate());
}
/**
@ -131,7 +133,7 @@ class RecordviewTest extends TestCase
'sys_category'
);
static::assertSame('https://example.com/path.html', $subject->getUrl());
self::assertSame('https://example.com/path.html', $subject->getUrl());
}
/**
@ -151,7 +153,7 @@ class RecordviewTest extends TestCase
'sys_category'
);
static::assertSame(
self::assertSame(
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0',
$subject->getUserAgent()
);
@ -174,7 +176,7 @@ class RecordviewTest extends TestCase
'sys_category'
);
static::assertSame(
self::assertSame(
10,
$subject->getRecordUid()
);
@ -197,32 +199,9 @@ class RecordviewTest extends TestCase
'sys_category'
);
static::assertSame(
self::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()
);
}
}

View file

@ -1,6 +1,6 @@
<?php
namespace DanielSiepmann\Tracking\Tests\Unit\Domain\Pageview;
declare(strict_types=1);
/*
* Copyright (C) 2020 Daniel Siepmann <coding@daniel-siepmann.de>
@ -21,6 +21,8 @@ namespace DanielSiepmann\Tracking\Tests\Unit\Domain\Pageview;
* 02110-1301, USA.
*/
namespace DanielSiepmann\Tracking\Tests\Unit\Domain\Pageview;
use DanielSiepmann\Tracking\Domain\Model\Pageview;
use DanielSiepmann\Tracking\Domain\Pageview\Factory;
use Prophecy\PhpUnit\ProphecyTrait;
@ -33,7 +35,7 @@ use TYPO3\CMS\Core\Site\SiteFinder;
use TYPO3\TestingFramework\Core\Unit\UnitTestCase as TestCase;
/**
* @covers DanielSiepmann\Tracking\Domain\Pageview\Factory
* @covers \DanielSiepmann\Tracking\Domain\Pageview\Factory
*/
class FactoryTest extends TestCase
{
@ -59,7 +61,7 @@ class FactoryTest extends TestCase
$subject = new Factory($this->prophesize(SiteFinder::class)->reveal());
$result = $subject->fromRequest($request->reveal());
static::assertInstanceOf(Pageview::class, $result);
self::assertInstanceOf(Pageview::class, $result);
}
/**
@ -84,7 +86,7 @@ class FactoryTest extends TestCase
$subject = new Factory($this->prophesize(SiteFinder::class)->reveal());
$result = $subject->fromRequest($request->reveal());
static::assertSame(
self::assertSame(
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0',
$result->getUserAgent()
);
@ -110,7 +112,7 @@ class FactoryTest extends TestCase
$subject = new Factory($this->prophesize(SiteFinder::class)->reveal());
$result = $subject->fromRequest($request->reveal());
static::assertSame(
self::assertSame(
'https://example.com/path?query=params&some=more#anchor',
$result->getUrl()
);
@ -136,7 +138,7 @@ class FactoryTest extends TestCase
$subject = new Factory($this->prophesize(SiteFinder::class)->reveal());
$result = $subject->fromRequest($request->reveal());
static::assertSame(
self::assertSame(
50,
$result->getPageType()
);
@ -162,7 +164,7 @@ class FactoryTest extends TestCase
$subject = new Factory($this->prophesize(SiteFinder::class)->reveal());
$result = $subject->fromRequest($request->reveal());
static::assertInstanceOf(\DateTimeImmutable::class, $result->getCrdate());
self::assertInstanceOf(\DateTimeImmutable::class, $result->getCrdate());
}
/**
@ -185,7 +187,7 @@ class FactoryTest extends TestCase
$subject = new Factory($this->prophesize(SiteFinder::class)->reveal());
$result = $subject->fromRequest($request->reveal());
static::assertInstanceOf(SiteLanguage::class, $result->getLanguage());
self::assertInstanceOf(SiteLanguage::class, $result->getLanguage());
}
/**
@ -208,7 +210,7 @@ class FactoryTest extends TestCase
$subject = new Factory($this->prophesize(SiteFinder::class)->reveal());
$result = $subject->fromRequest($request->reveal());
static::assertSame(
self::assertSame(
10,
$result->getPageUid()
);
@ -237,13 +239,13 @@ class FactoryTest extends TestCase
'user_agent' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36',
]);
static::assertInstanceOf(Pageview::class, $result);
static::assertSame(1, $result->getUid());
static::assertSame(2, $result->getPageUid());
static::assertSame($siteLanguage->reveal(), $result->getLanguage());
static::assertSame('1533906435', $result->getCrdate()->format('U'));
static::assertSame(0, $result->getPageType());
static::assertSame('https://example.com/path', $result->getUrl());
static::assertSame('Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36', $result->getUserAgent());
self::assertInstanceOf(Pageview::class, $result);
self::assertSame(1, $result->getUid());
self::assertSame(2, $result->getPageUid());
self::assertSame($siteLanguage->reveal(), $result->getLanguage());
self::assertSame('1533906435', $result->getCrdate()->format('U'));
self::assertSame(0, $result->getPageType());
self::assertSame('https://example.com/path', $result->getUrl());
self::assertSame('Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36', $result->getUserAgent());
}
}

View file

@ -1,231 +0,0 @@
<?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\Pageview as Model;
use DanielSiepmann\Tracking\Domain\Pageview\Factory;
use DanielSiepmann\Tracking\Domain\Repository\Pageview;
use Doctrine\DBAL\Statement;
use Prophecy\PhpUnit\ProphecyTrait;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
use TYPO3\TestingFramework\Core\Unit\UnitTestCase as TestCase;
/**
* @covers DanielSiepmann\Tracking\Domain\Repository\Pageview
*/
class PageviewTest extends TestCase
{
use ProphecyTrait;
/**
* @test
*/
public function modelCanBeAdded(): void
{
$connection = $this->prophesize(Connection::class);
$factory = $this->prophesize(Factory::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->getPageType()->willReturn(999);
$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');
$connection->insert(
'tx_tracking_pageview',
[
'pid' => 10,
'crdate' => 1582660189,
'tstamp' => 1582660189,
'type' => 999,
'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',
]
)->willReturn(1)->shouldBeCalledTimes(1);
$subject = new Pageview($connection->reveal(), $factory->reveal());
$subject->add($model->reveal());
}
/**
* @test
*/
public function throwsExceptionIfModelToUodateHasNoUid(): void
{
$connection = $this->prophesize(Connection::class);
$factory = $this->prophesize(Factory::class);
$model = $this->prophesize(Model::class);
$model->getUid()->willReturn(0);
$subject = new Pageview($connection->reveal(), $factory->reveal());
$this->expectExceptionMessage('Can not update pageview if uid is 0.');
$subject->update($model->reveal());
}
/**
* @test
*/
public function modelCanBeUpdated(): void
{
$connection = $this->prophesize(Connection::class);
$factory = $this->prophesize(Factory::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->getUid()->willReturn(1);
$model->getPageUid()->willReturn(10);
$model->getCrdate()->willReturn($dateTime->reveal());
$model->getPageType()->willReturn(999);
$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');
$connection->update(
'tx_tracking_pageview',
[
'pid' => 10,
'crdate' => 1582660189,
'tstamp' => 1582660189,
'type' => 999,
'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',
],
[
'uid' => 1
]
)->willReturn(1)->shouldBeCalledTimes(1);
$subject = new Pageview($connection->reveal(), $factory->reveal());
$subject->update($model->reveal());
}
/**
* @test
*/
public function returnsACountOfAllModels(): void
{
$statement = $this->prophesize(Statement::class);
$statement->fetchColumn()->willReturn(10);
$queryBuilder = $this->prophesize(QueryBuilder::class);
$queryBuilder->count('uid')->willReturn($queryBuilder->reveal());
$queryBuilder->from('tx_tracking_pageview')->willReturn($queryBuilder->reveal());
$queryBuilder->execute()->willReturn($statement->reveal());
$connection = $this->prophesize(Connection::class);
$connection->createQueryBuilder()->willReturn($queryBuilder->reveal());
$factory = $this->prophesize(Factory::class);
$subject = new Pageview($connection->reveal(), $factory->reveal());
static::assertSame(10, $subject->countAll());
}
/**
* @test
*/
public function returnsAllModells(): void
{
$statement = $this->prophesize(Statement::class);
$statement->fetch()->willReturn(
[
'pid' => '10',
'crdate' => '1595948372',
'type' => '0',
'sys_language_uid' => '0',
'url' => 'https://example.com/path/file.html',
'user_agent' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36',
],
[
'pid' => '9',
'crdate' => '1595948376',
'type' => '0',
'sys_language_uid' => '0',
'url' => 'https://example.com/path/file.html',
'user_agent' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36',
],
false
);
$queryBuilder = $this->prophesize(QueryBuilder::class);
$queryBuilder->select('*')->willReturn($queryBuilder->reveal());
$queryBuilder->from('tx_tracking_pageview')->willReturn($queryBuilder->reveal());
$queryBuilder->execute()->willReturn($statement->reveal());
$connection = $this->prophesize(Connection::class);
$connection->createQueryBuilder()->willReturn($queryBuilder->reveal());
$model1 = $this->prophesize(Model::class);
$model1->getPageUid()->willReturn(10);
$model2 = $this->prophesize(Model::class);
$model2->getPageUid()->willReturn(9);
$factory = $this->prophesize(Factory::class);
$factory->fromDbRow([
'pid' => '10',
'crdate' => '1595948372',
'type' => '0',
'sys_language_uid' => '0',
'url' => 'https://example.com/path/file.html',
'user_agent' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36',
])->willReturn($model1->reveal());
$factory->fromDbRow([
'pid' => '9',
'crdate' => '1595948376',
'type' => '0',
'sys_language_uid' => '0',
'url' => 'https://example.com/path/file.html',
'user_agent' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36',
])->willReturn($model2->reveal());
$subject = new Pageview($connection->reveal(), $factory->reveal());
static::assertCount(2, $subject->findAll());
$pageUid = 10;
foreach ($subject->findAll() as $model) {
static::assertSame($pageUid, $model->getPageUid());
--$pageUid;
}
}
}

View file

@ -1,80 +0,0 @@
<?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 Prophecy\PhpUnit\ProphecyTrait;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
use TYPO3\TestingFramework\Core\Unit\UnitTestCase as TestCase;
/**
* @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());
}
}

View file

@ -36,7 +36,8 @@
"symfony/expression-language": "^5.2",
"typo3/cms-backend": "^10.4 || ^11.5",
"typo3/cms-core": "^10.4 || ^11.5",
"typo3/cms-dashboard": "^10.4 || ^11.5"
"typo3/cms-dashboard": "^10.4 || ^11.5",
"matomo/device-detector": "^6.0"
},
"require-dev": {
"phpunit/phpunit": "^9.0",

View file

@ -1,21 +1,34 @@
CREATE TABLE tx_tracking_pageview (
url text,
user_agent text,
operating_system varchar(255) DEFAULT '' NOT NULL,
type int(11) unsigned DEFAULT '0' NOT NULL,
compatible_version varchar(11) DEFAULT 'v1.1.4' NOT NULL,
KEY page_views_per_page (pid,uid,crdate),
KEY language (l10n_parent,sys_language_uid),
KEY compatible_version (compatible_version),
);
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,
compatible_version varchar(11) DEFAULT 'v1.1.4' NOT NULL,
KEY record_views_per_page (pid,uid,crdate),
KEY language (l10n_parent,sys_language_uid),
KEY compatible_version (compatible_version),
);
CREATE TABLE tx_tracking_tag (
record_uid int(11) unsigned DEFAULT '0' NOT NULL,
record_table_name varchar(255) DEFAULT '' NOT NULL,
name varchar(255) DEFAULT '' NOT NULL,
value longtext DEFAULT '' NOT NULL,
compatible_version varchar(11) DEFAULT 'v2.0.0' NOT NULL,
KEY combined_identifier (record_uid,record_table_name,name,value),
KEY compatible_version (compatible_version),
);