Skip to content

Commit 67187f1

Browse files
committed
[FEATURE] Add source view in backend
1 parent 6261e76 commit 67187f1

File tree

18 files changed

+908
-26
lines changed

18 files changed

+908
-26
lines changed

Classes/Controller/AnalysisController.php

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use In2code\Lux\Domain\DataProvider\NewsvisistsDataProvider;
1616
use In2code\Lux\Domain\DataProvider\PagevisistsDataProvider;
1717
use In2code\Lux\Domain\DataProvider\ReferrerAmountDataProvider;
18+
use In2code\Lux\Domain\DataProvider\ReferrerCategoryDataProvider;
1819
use In2code\Lux\Domain\DataProvider\SearchDataProvider;
1920
use In2code\Lux\Domain\DataProvider\SocialMediaDataProvider;
2021
use In2code\Lux\Domain\DataProvider\UtmCampaignDataProvider;
@@ -25,6 +26,7 @@
2526
use In2code\Lux\Domain\Model\News;
2627
use In2code\Lux\Domain\Model\Page;
2728
use In2code\Lux\Domain\Model\Transfer\FilterDto;
29+
use In2code\Lux\Domain\Service\Referrer\Readable;
2830
use In2code\Lux\Exception\ArgumentsException;
2931
use In2code\Lux\Exception\AuthenticationException;
3032
use In2code\Lux\Utility\BackendUtility;
@@ -126,6 +128,54 @@ public function contentCsvAction(FilterDto $filter): ResponseInterface
126128
return $this->csvResponse();
127129
}
128130

131+
/**
132+
* @return void
133+
* @throws NoSuchArgumentException
134+
*/
135+
public function initializeSourcesAction(): void
136+
{
137+
$this->setFilter();
138+
}
139+
140+
/**
141+
* Sources with referrers
142+
*
143+
* @param FilterDto $filter
144+
* @param string $export
145+
* @return ResponseInterface
146+
* @throws ExceptionDbal
147+
*/
148+
public function sourcesAction(FilterDto $filter, string $export = ''): ResponseInterface
149+
{
150+
if ($export === 'csv') {
151+
return (new ForwardResponse('sourcesCsv'))->withArguments(['filter' => $filter]);
152+
}
153+
154+
$values = [
155+
'filter' => $filter,
156+
'referrers' => $this->pagevisitsRepository->getReferrers($filter),
157+
'sourceCategories' => GeneralUtility::makeInstance(Readable::class)->getAllKeys(),
158+
'categoryData' => GeneralUtility::makeInstance(ReferrerCategoryDataProvider::class, $filter),
159+
];
160+
$this->moduleTemplate->assignMultiple($values);
161+
162+
$this->addDocumentHeaderForCurrentController();
163+
return $this->defaultRendering();
164+
}
165+
166+
/**
167+
* @param FilterDto $filter
168+
* @return ResponseInterface
169+
* @throws ExceptionDbal
170+
*/
171+
public function sourcesCsvAction(FilterDto $filter): ResponseInterface
172+
{
173+
$this->view->assignMultiple([
174+
'referrers' => $this->pagevisitsRepository->getReferrers($filter),
175+
]);
176+
return $this->csvResponse();
177+
}
178+
129179
/**
130180
* @return void
131181
* @throws NoSuchArgumentException
@@ -620,7 +670,7 @@ public function detailAjaxLinklistener(ServerRequestInterface $request): Respons
620670
*/
621671
protected function addDocumentHeaderForCurrentController(): void
622672
{
623-
$actions = ['dashboard', 'content'];
673+
$actions = ['dashboard', 'content', 'sources'];
624674
if ($this->newsvisitRepository->isTableFilled()) {
625675
$actions[] = 'news';
626676
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
namespace In2code\Lux\Domain\DataProvider;
5+
6+
use Doctrine\DBAL\Driver\Exception as ExceptionDbalDriver;
7+
use Doctrine\DBAL\Exception as ExceptionDbal;
8+
use In2code\Lux\Domain\Repository\PagevisitRepository;
9+
use In2code\Lux\Utility\LocalizationUtility;
10+
use TYPO3\CMS\Core\Utility\GeneralUtility;
11+
12+
class ReferrerCategoryDataProvider extends AbstractDataProvider
13+
{
14+
/**
15+
* Set values like:
16+
* [
17+
* 'amounts' => [
18+
* 120,
19+
* 88
20+
* ],
21+
* 'titles' => [
22+
* 'socialMedia',
23+
* 'aiChats',
24+
* ]
25+
* ]
26+
*
27+
* @return void
28+
*/
29+
public function prepareData(): void
30+
{
31+
/** @var PagevisitRepository $pagevisitRepository */
32+
$pagevisitRepository = GeneralUtility::makeInstance(PagevisitRepository::class);
33+
$titles = $amounts = [];
34+
$counter = 0;
35+
foreach ($pagevisitRepository->getReferrerCategoryAmounts($this->filter) as $sourceKey => $amount) {
36+
$titles[] = LocalizationUtility::translateByKey('readablereferrer.' . $sourceKey);
37+
$amounts[] = $amount;
38+
if ($counter >= 5) {
39+
break;
40+
}
41+
$counter++;
42+
}
43+
$this->data = ['amounts' => $amounts, 'titles' => $titles];
44+
}
45+
}

Classes/Domain/Repository/PagevisitRepository.php

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,101 @@ public function getAmountOfSocialMediaReferrers(FilterDto $filter): array
363363
return $result;
364364
}
365365

366+
/**
367+
* [
368+
* [
369+
* 'referrer_domain' => 'x.com',
370+
* 'count' => 123,
371+
* 'identified_count' => 34,
372+
* 'children' => [QueryResult],
373+
* ],
374+
* [
375+
* 'referrer_domain' => 'openai.com',
376+
* 'count' => 25,
377+
* 'identified_count' => 12,
378+
* 'children' => [QueryResult],
379+
* ],
380+
* ]
381+
*
382+
* @param FilterDto $filter
383+
* @return array
384+
* @throws ExceptionDbal
385+
*/
386+
public function getReferrers(FilterDto $filter): array
387+
{
388+
$readable = GeneralUtility::makeInstance(Readable::class);
389+
$grouped = [];
390+
foreach ($this->getAmountOfReferrerDomains($filter) as $row) {
391+
$row['identified_count'] = $this->extendRowIdentified($row, $filter);
392+
$row = $this->extendRowWithChildren($row, $filter);
393+
$key = $readable->getKeyFromHost($row['referrer_domain']) ?: 'other';
394+
$grouped[$key][] = $row;
395+
}
396+
return $grouped;
397+
}
398+
399+
/**
400+
* [
401+
* [
402+
* 'referrer_domain' => 'x.com',
403+
* 'count' => 123,
404+
* ],
405+
* [
406+
* 'referrer_domain' => 'openai.com',
407+
* 'count' => 25,
408+
* ],
409+
* ]
410+
*
411+
* @param FilterDto $filter
412+
* @return array
413+
* @throws ExceptionDbal
414+
*/
415+
protected function getAmountOfReferrerDomains(FilterDto $filter): array
416+
{
417+
$connection = DatabaseUtility::getConnectionForTable(Pagevisit::TABLE_NAME);
418+
$sql = 'select substring_index(substring_index(referrer, \'://\', -1), \'/\', 1) referrer_domain, count(*) count';
419+
$sql .= ' from ' . Pagevisit::TABLE_NAME . ' pv';
420+
$sql .= ' where pv.deleted = 0 and pv.hidden = 0 and pv.referrer != \'\'';
421+
$sql .= $this->extendWhereClauseWithFilterSearchterms($filter, 'pv', 'referrer');
422+
$sql .= $this->extendWhereClauseWithFilterTime($filter);
423+
$sql .= $this->extendWhereClauseWithFilterSite($filter);
424+
$sql .= $this->extendWhereClauseWithFilterDomain($filter);
425+
$sql .= ' group by referrer_domain order by count desc;';
426+
$rows = $connection->executeQuery($sql)->fetchAllAssociative();
427+
return $rows;
428+
}
429+
430+
protected function extendRowIdentified(array $row, FilterDto $filter): int
431+
{
432+
$connection = DatabaseUtility::getConnectionForTable(Pagevisit::TABLE_NAME);
433+
$sql = 'select count(*) identified_count';
434+
$sql .= ' from tx_lux_domain_model_visitor v';
435+
$sql .= ' where v.identified = 1';
436+
$sql .= ' and exists (';
437+
$sql .= 'select 1';
438+
$sql .= ' from tx_lux_domain_model_pagevisit pv';
439+
$sql .= ' where pv.visitor = v.uid and pv.referrer like "https://' . $row['referrer_domain'] . '%"';
440+
$sql .= $this->extendWhereClauseWithFilterTime($filter, true, 'pv');
441+
$sql .= $this->extendWhereClauseWithFilterSite($filter, 'pv');
442+
$sql .= ')';
443+
return (int)$connection->executeQuery($sql)->fetchOne();
444+
}
445+
446+
protected function extendRowWithChildren(array $row, FilterDto $filter): array
447+
{
448+
$query = $this->createQuery();
449+
$logicalAnd = [
450+
$query->like('referrer', 'https://' . $row['referrer_domain'] . '%'),
451+
];
452+
$logicalAnd = $this->extendLogicalAndWithFilterConstraintsForCrdate($filter, $query, $logicalAnd);
453+
$logicalAnd = $this->extendLogicalAndWithFilterConstraintsForSite($filter, $query, $logicalAnd);
454+
$query->matching($query->logicalAnd(...$logicalAnd));
455+
$query->setOrderings(['crdate' => QueryInterface::ORDER_DESCENDING]);
456+
$query->setLimit($row['count']);
457+
$row['children'] = $query->execute();
458+
return $row;
459+
}
460+
366461
/**
367462
* Get social media amount of referrers from link shortener (part of luxenterprise)
368463
*
@@ -380,6 +475,25 @@ protected function getAmountOfSocialMediaReferrersFromShorteners(array $result,
380475
return $result;
381476
}
382477

478+
public function getReferrerCategoryAmounts(FilterDto $filter): array
479+
{
480+
$readable = GeneralUtility::makeInstance(Readable::class);
481+
$amounts = [];
482+
foreach ($readable->getAllKeysWithDomainsForQuery() as $key => $valueRegEx) {
483+
$connection = DatabaseUtility::getConnectionForTable(Pagevisit::TABLE_NAME);
484+
$sql = 'SELECT count(*) as count';
485+
$sql .= ' from ' . Pagevisit::TABLE_NAME . ' pv';
486+
$sql .= ' where referrer RLIKE \'^https://(' . $valueRegEx . ')/\'';
487+
$sql .= $this->extendWhereClauseWithFilterSearchterms($filter, 'pv', 'referrer');
488+
$sql .= $this->extendWhereClauseWithFilterTime($filter);
489+
$sql .= $this->extendWhereClauseWithFilterSite($filter);
490+
$sql .= $this->extendWhereClauseWithFilterDomain($filter);
491+
$amounts[$key] = $connection->executeQuery($sql)->fetchOne();
492+
}
493+
arsort($amounts);
494+
return $amounts;
495+
}
496+
383497
/**
384498
* @param FilterDto $filter
385499
* @return array
@@ -509,4 +623,33 @@ public function compareAmountPerPage(int $pageIdentifier, FilterDto $filter1, Fi
509623
$amount2 = $this->findAmountPerPage($pageIdentifier, $filter2);
510624
return $amount1 - $amount2;
511625
}
626+
627+
/**
628+
* Domain is misleading here - this function is reused to search for given domains by a category
629+
*
630+
* @param FilterDto $filter
631+
* @param string $table
632+
* @return string
633+
*/
634+
protected function extendWhereClauseWithFilterDomain(FilterDto $filter, string $table = ''): string
635+
{
636+
$sql = '';
637+
if ($filter->isDomainSet()) {
638+
$field = 'referrer';
639+
if ($table !== '') {
640+
$field = $table . '.' . $field;
641+
}
642+
$readable = GeneralUtility::makeInstance(Readable::class);
643+
$domains = $readable->getDomainsFromCategory($filter->getDomain());
644+
645+
if ($domains !== []) {
646+
$conditions = [];
647+
foreach ($domains as $domain) {
648+
$conditions[] = $field . ' LIKE "https://' . $domain . '%"';
649+
}
650+
$sql .= ' and (' . implode(' OR ', $conditions) . ')';
651+
}
652+
}
653+
return $sql;
654+
}
512655
}

0 commit comments

Comments
 (0)