diff --git a/Classes/Command/UpdateDataCommand.php b/Classes/Command/UpdateDataCommand.php
new file mode 100644
index 0000000..158861b
--- /dev/null
+++ b/Classes/Command/UpdateDataCommand.php
@@ -0,0 +1,65 @@
+
+ *
+ * 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\Repository\Pageview;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Helper\ProgressBar;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Style\SymfonyStyle;
+
+class UpdateDataCommand extends Command
+{
+ /**
+ * @var Pageview
+ */
+ private $repository;
+
+ public function __construct(Pageview $repository)
+ {
+ $this->repository = $repository;
+
+ 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.');
+ }
+
+ 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();
+
+ return 0;
+ }
+}
diff --git a/Classes/Dashboard/Provider/PageviewsPerOperatingSystem.php b/Classes/Dashboard/Provider/PageviewsPerOperatingSystem.php
new file mode 100644
index 0000000..2507de8
--- /dev/null
+++ b/Classes/Dashboard/Provider/PageviewsPerOperatingSystem.php
@@ -0,0 +1,111 @@
+
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+use TYPO3\CMS\Core\Database\Query\QueryBuilder;
+use TYPO3\CMS\Dashboard\WidgetApi;
+use TYPO3\CMS\Dashboard\Widgets\ChartDataProviderInterface;
+
+class PageviewsPerOperatingSystem implements ChartDataProviderInterface
+{
+ /**
+ * @var QueryBuilder
+ */
+ private $queryBuilder;
+
+ /**
+ * @var int
+ */
+ private $days;
+
+ /**
+ * @var int
+ */
+ private $maxResults;
+
+ public function __construct(
+ QueryBuilder $queryBuilder,
+ int $days = 31,
+ int $maxResults = 6
+ ) {
+ $this->queryBuilder = $queryBuilder;
+ $this->days = $days;
+ $this->maxResults = $maxResults;
+ }
+
+ public function getChartData(): array
+ {
+ list($labels, $data) = $this->getPageViewsPerPage();
+
+ return [
+ 'labels' => $labels,
+ 'datasets' => [
+ [
+ 'backgroundColor' => WidgetApi::getDefaultChartColors(),
+ 'data' => $data,
+ ]
+ ],
+ ];
+ }
+
+ private function getPageViewsPerPage(): array
+ {
+ $labels = [];
+ $data = [];
+
+ $constraints = [
+ $this->queryBuilder->expr()->gte(
+ 'tx_tracking_pageview.crdate',
+ strtotime('-' . $this->days . ' day 0:00:00')
+ ),
+ $this->queryBuilder->expr()->lte(
+ 'tx_tracking_pageview.crdate',
+ time()
+ ),
+ $this->queryBuilder->expr()->neq(
+ 'tx_tracking_pageview.operating_system',
+ $this->queryBuilder->createNamedParameter('')
+ ),
+ ];
+
+ $result = $this->queryBuilder
+ ->selectLiteral('count(operating_system) as total')
+ ->addSelect('operating_system')
+ ->from('tx_tracking_pageview')
+ ->where(... $constraints)
+ ->groupBy('tx_tracking_pageview.operating_system')
+ ->orderBy('total', 'desc')
+ ->setMaxResults($this->maxResults)
+ ->execute()
+ ->fetchAll();
+
+ foreach ($result as $row) {
+ $labels[] = mb_strimwidth($row['operating_system'], 0, 50, '…');
+ $data[] = $row['total'];
+ }
+
+ return [
+ $labels,
+ $data,
+ ];
+ }
+}
diff --git a/Classes/Dashboard/Provider/PageviewsPerPage.php b/Classes/Dashboard/Provider/PageviewsPerPage.php
index ba2fda1..8f08e5a 100644
--- a/Classes/Dashboard/Provider/PageviewsPerPage.php
+++ b/Classes/Dashboard/Provider/PageviewsPerPage.php
@@ -123,7 +123,7 @@ class PageviewsPerPage implements ChartDataProviderInterface
->fetchAll();
foreach ($result as $row) {
- $labels[] = mb_strimwidth($row['title'], 0, 50, '…') . ' [' . $row['uid'] . ']';
+ $labels[] = mb_strimwidth($row['title'], 0, 25, '…');
$data[] = $row['total'];
}
diff --git a/Classes/Domain/Model/Pageview.php b/Classes/Domain/Model/Pageview.php
index 83404d4..8c760bd 100644
--- a/Classes/Domain/Model/Pageview.php
+++ b/Classes/Domain/Model/Pageview.php
@@ -25,6 +25,11 @@ use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
class Pageview
{
+ /**
+ * @var int
+ */
+ private $uid = 0;
+
/**
* @var int
*/
@@ -61,8 +66,10 @@ class Pageview
\DateTimeImmutable $crdate,
int $pageType,
string $url,
- string $userAgent
+ string $userAgent,
+ int $uid = 0
) {
+ $this->uid = $uid;
$this->pageUid = $pageUid;
$this->language = $language;
$this->crdate = $crdate;
@@ -71,6 +78,11 @@ class Pageview
$this->userAgent = $userAgent;
}
+ public function getUid(): int
+ {
+ return $this->uid;
+ }
+
public function getPageUid(): int
{
return $this->pageUid;
@@ -100,4 +112,34 @@ class Pageview
{
return $this->userAgent;
}
+
+ public function getOperatingSystem(): string
+ {
+ if (mb_stripos($this->userAgent, 'Android') !== false) {
+ return 'Android';
+ }
+ if (mb_stripos($this->userAgent, 'Windows') !== false) {
+ return 'Windows';
+ }
+ if (mb_stripos($this->userAgent, 'Linux') !== false) {
+ return 'Linux';
+ }
+ if (mb_stripos($this->userAgent, 'Macintosh') !== false) {
+ return 'Macintosh';
+ }
+ if (mb_stripos($this->userAgent, 'CrOS') !== false) {
+ return 'Google Chrome OS';
+ }
+ if (mb_stripos($this->userAgent, 'OpenBSD') !== false) {
+ return 'OpenBSD';
+ }
+ if (
+ mb_stripos($this->userAgent, 'iPad') !== false
+ || mb_stripos($this->userAgent, 'iphone') !== false
+ ) {
+ return 'iOS';
+ }
+
+ return '';
+ }
}
diff --git a/Classes/Domain/Pageview/Factory.php b/Classes/Domain/Pageview/Factory.php
index 7555dac..d1e3fbb 100644
--- a/Classes/Domain/Pageview/Factory.php
+++ b/Classes/Domain/Pageview/Factory.php
@@ -24,10 +24,20 @@ namespace DanielSiepmann\Tracking\Domain\Pageview;
use DanielSiepmann\Tracking\Domain\Model\Pageview;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\Routing\PageArguments;
-use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
+use TYPO3\CMS\Core\Site\SiteFinder;
class Factory implements FromRequest
{
+ /**
+ * @var SiteFinder
+ */
+ private $siteFinder;
+
+ public function __construct(SiteFinder $siteFinder)
+ {
+ $this->siteFinder = $siteFinder;
+ }
+
public static function fromRequest(ServerRequestInterface $request): Pageview
{
return new PageView(
@@ -40,6 +50,19 @@ class Factory implements FromRequest
);
}
+ public function fromDbRow(array $dbRow): Pageview
+ {
+ return new PageView(
+ $dbRow['pid'],
+ $this->siteFinder->getSiteByPageId($dbRow['pid'])->getLanguageById($dbRow['sys_language_uid']),
+ new \DateTimeImmutable('@' . $dbRow['crdate']),
+ $dbRow['type'],
+ $dbRow['url'],
+ $dbRow['user_agent'],
+ $dbRow['uid']
+ );
+ }
+
private static function getRouting(ServerRequestInterface $request): PageArguments
{
return $request->getAttribute('routing');
diff --git a/Classes/Domain/Repository/Pageview.php b/Classes/Domain/Repository/Pageview.php
index 97af4cc..6a50733 100644
--- a/Classes/Domain/Repository/Pageview.php
+++ b/Classes/Domain/Repository/Pageview.php
@@ -22,6 +22,8 @@ namespace DanielSiepmann\Tracking\Domain\Repository;
*/
use DanielSiepmann\Tracking\Domain\Model\Pageview as Model;
+use DanielSiepmann\Tracking\Domain\Pageview\Factory;
+use Doctrine\DBAL\Driver\Statement;
use TYPO3\CMS\Core\Database\Connection;
class Pageview
@@ -31,24 +33,72 @@ class Pageview
*/
private $connection;
- public function __construct(Connection $connection)
- {
+ /**
+ * @var Factory
+ */
+ private $factory;
+
+ public function __construct(
+ Connection $connection,
+ Factory $factory
+ ) {
$this->connection = $connection;
+ $this->factory = $factory;
+ }
+
+ public function countAll(): int
+ {
+ return $this->connection->createQueryBuilder()
+ ->count('uid')
+ ->from('tx_tracking_pageview')
+ ->execute()
+ ->fetchColumn();
+ }
+
+ public function findAll(): \Generator
+ {
+ $queryBuilder = $this->connection->createQueryBuilder();
+ $pageViews = $queryBuilder->select('*')->from('tx_tracking_pageview')->execute();
+
+ if ($pageViews instanceof Statement) {
+ while ($pageView = $pageViews->fetch()) {
+ yield $this->factory->fromDbRow($pageView);
+ }
+ }
+ }
+
+ public function update(Model $pageview): void
+ {
+ if ($pageview->getUid() === 0) {
+ throw new \InvalidArgumentException('Can not update pageview if uid is 0.', 1585770573);
+ }
+
+ $this->connection->update(
+ 'tx_tracking_pageview',
+ $this->getFieldsFromModel($pageview),
+ ['uid' => $pageview->getUid()]
+ );
}
public function add(Model $pageview): void
{
$this->connection->insert(
'tx_tracking_pageview',
- [
- 'pid' => $pageview->getPageUid(),
- 'crdate' => $pageview->getCrdate()->format('U'),
- 'tstamp' => $pageview->getCrdate()->format('U'),
- 'type' => $pageview->getPageType(),
- 'sys_language_uid' => $pageview->getLanguage()->getLanguageId(),
- 'url' => $pageview->getUrl(),
- 'user_agent' => $pageview->getUserAgent(),
- ]
+ $this->getFieldsFromModel($pageview)
);
}
+
+ private function getFieldsFromModel(Model $pageview): array
+ {
+ return [
+ 'pid' => $pageview->getPageUid(),
+ 'crdate' => $pageview->getCrdate()->format('U'),
+ 'tstamp' => $pageview->getCrdate()->format('U'),
+ 'type' => $pageview->getPageType(),
+ 'sys_language_uid' => $pageview->getLanguage()->getLanguageId(),
+ 'url' => $pageview->getUrl(),
+ 'user_agent' => $pageview->getUserAgent(),
+ 'operating_system' => $pageview->getOperatingSystem(),
+ ];
+ }
}
diff --git a/Configuration/Backend/DashboardWidgets.yaml b/Configuration/Backend/DashboardWidgets.yaml
index 8c85103..da66647 100644
--- a/Configuration/Backend/DashboardWidgets.yaml
+++ b/Configuration/Backend/DashboardWidgets.yaml
@@ -1,7 +1,7 @@
services:
_defaults:
- autowire: true
- autoconfigure: true
+ autowire: false
+ autoconfigure: false
public: false
DanielSiepmann\Tracking\Dashboard\:
@@ -11,7 +11,7 @@ services:
arguments:
$queryBuilder: '@querybuilder.tx_tracking_pageview'
- dashboard.widget.danielsiepmann_tracking.pageViewsPerDay:
+ dashboard.widget.danielsiepmann.tracking.pageViewsPerDay:
class: 'TYPO3\CMS\Dashboard\Widgets\BarChartWidget'
arguments:
$view: '@dashboard.views.widget'
@@ -31,7 +31,7 @@ services:
arguments:
$queryBuilder: '@querybuilder.tx_tracking_pageview'
- dashboard.widget.danielsiepmann_tracking.pageViewsPerPage:
+ dashboard.widget.danielsiepmann.tracking.pageViewsPerPage:
class: 'TYPO3\CMS\Dashboard\Widgets\DoughnutChartWidget'
arguments:
$view: '@dashboard.views.widget'
@@ -51,7 +51,7 @@ services:
arguments:
$queryBuilder: '@querybuilder.tx_tracking_pageview'
- dashboard.widget.danielsiepmann_tracking.newestPageviews:
+ dashboard.widget.danielsiepmann.tracking.newestPageviews:
class: 'TYPO3\CMS\Dashboard\Widgets\ListWidget'
arguments:
$view: '@dashboard.views.widget'
@@ -65,3 +65,24 @@ services:
description: 'LLL:EXT:tracking/Resources/Private/Language/locallang.xlf:dashboard.widgets.newestPageviewsList.description'
height: 'medium'
width: 'small'
+
+ DanielSiepmann\Tracking\Dashboard\Provider\PageviewsPerOperatingSystem:
+ class: 'DanielSiepmann\Tracking\Dashboard\Provider\PageviewsPerOperatingSystem'
+ arguments:
+ $queryBuilder: '@querybuilder.tx_tracking_pageview'
+
+ dashboard.widget.danielsiepmann.tracking.operatingSystems:
+ class: 'TYPO3\CMS\Dashboard\Widgets\DoughnutChartWidget'
+ arguments:
+ $view: '@dashboard.views.widget'
+ $dataProvider: '@DanielSiepmann\Tracking\Dashboard\Provider\PageviewsPerOperatingSystem'
+ tags:
+ - name: 'dashboard.widget'
+ identifier: 'operatingSystemsDoughnut'
+ groupNames: 'tracking'
+ iconIdentifier: 'content-widget-chart-pie'
+ title: 'LLL:EXT:tracking/Resources/Private/Language/locallang.xlf:dashboard.widgets.operatingSystemsDoughnut.title'
+ description: 'LLL:EXT:tracking/Resources/Private/Language/locallang.xlf:dashboard.widgets.operatingSystemsDoughnut.description'
+ additionalCssClasses: 'dashboard-item--chart'
+ height: 'medium'
+ width: 'small'
diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml
index ae6fdbc..63937f9 100644
--- a/Configuration/Services.yaml
+++ b/Configuration/Services.yaml
@@ -41,3 +41,8 @@ services:
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/")
+
+ DanielSiepmann\Tracking\Command\UpdateDataCommand:
+ tags:
+ - name: 'console.command'
+ command: 'tracking:updatedata'
diff --git a/Configuration/TCA/tx_tracking_pageview.php b/Configuration/TCA/tx_tracking_pageview.php
index 436616e..47d4b87 100644
--- a/Configuration/TCA/tx_tracking_pageview.php
+++ b/Configuration/TCA/tx_tracking_pageview.php
@@ -15,7 +15,7 @@ return [
],
'types' => [
'0' => [
- 'showitem' => 'sys_language_uid, pid, url, user_agent, type, crdate',
+ 'showitem' => 'sys_language_uid, pid, url, user_agent, operating_system, type, crdate',
],
],
'columns' => [
@@ -54,6 +54,13 @@ return [
'readOnly' => true,
],
],
+ 'operating_system' => [
+ 'label' => 'LLL:EXT:tracking/Resources/Private/Language/locallang_tca.xlf:table.pageview.operating_system',
+ 'config' => [
+ 'type' => 'input',
+ 'readOnly' => true,
+ ],
+ ],
'type' => [
'label' => 'LLL:EXT:tracking/Resources/Private/Language/locallang_tca.xlf:table.pageview.type',
'config' => [
diff --git a/Resources/Private/Language/locallang.xlf b/Resources/Private/Language/locallang.xlf
index af57387..476b371 100644
--- a/Resources/Private/Language/locallang.xlf
+++ b/Resources/Private/Language/locallang.xlf
@@ -21,6 +21,12 @@
Displays total page views per page.
+
+ Page Views / Operating System
+
+
+ Displays total page views per operating system.
+
Newest Page Views
diff --git a/Resources/Private/Language/locallang_tca.xlf b/Resources/Private/Language/locallang_tca.xlf
index 03f191e..68f515f 100644
--- a/Resources/Private/Language/locallang_tca.xlf
+++ b/Resources/Private/Language/locallang_tca.xlf
@@ -27,6 +27,9 @@
User agent
+
+ Operating System
+