Save user agent on open
[MAILPOET-3735]
This commit is contained in:
@ -443,6 +443,7 @@ class Migrator {
|
|||||||
'newsletter_id int(11) unsigned NOT NULL,',
|
'newsletter_id int(11) unsigned NOT NULL,',
|
||||||
'subscriber_id int(11) unsigned NOT NULL,',
|
'subscriber_id int(11) unsigned NOT NULL,',
|
||||||
'queue_id int(11) unsigned NOT NULL,',
|
'queue_id int(11) unsigned NOT NULL,',
|
||||||
|
'user_agent_id int(11) unsigned NULL,',
|
||||||
'created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,',
|
'created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,',
|
||||||
'PRIMARY KEY (id),',
|
'PRIMARY KEY (id),',
|
||||||
'KEY newsletter_id_subscriber_id (newsletter_id, subscriber_id),',
|
'KEY newsletter_id_subscriber_id (newsletter_id, subscriber_id),',
|
||||||
|
@ -37,6 +37,12 @@ class StatisticsOpenEntity {
|
|||||||
*/
|
*/
|
||||||
private $subscriber;
|
private $subscriber;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\ManyToOne(targetEntity="MailPoet\Entities\UserAgentEntity")
|
||||||
|
* @var UserAgentEntity|null
|
||||||
|
*/
|
||||||
|
private $userAgent;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
NewsletterEntity $newsletter,
|
NewsletterEntity $newsletter,
|
||||||
SendingQueueEntity $queue,
|
SendingQueueEntity $queue,
|
||||||
@ -83,4 +89,12 @@ class StatisticsOpenEntity {
|
|||||||
public function setSubscriber($subscriber) {
|
public function setSubscriber($subscriber) {
|
||||||
$this->subscriber = $subscriber;
|
$this->subscriber = $subscriber;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getUserAgent(): ?UserAgentEntity {
|
||||||
|
return $this->userAgent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUserAgent(?UserAgentEntity $userAgent): void {
|
||||||
|
$this->userAgent = $userAgent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,6 +98,7 @@ class Track {
|
|||||||
'queue' => $data->queue_id, // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
|
'queue' => $data->queue_id, // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
$data->userAgent = $_SERVER['HTTP_USER_AGENT'] ?? null;
|
||||||
return $this->_validateTrackData($data);
|
return $this->_validateTrackData($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,13 +7,21 @@ use MailPoet\Entities\SendingQueueEntity;
|
|||||||
use MailPoet\Entities\StatisticsOpenEntity;
|
use MailPoet\Entities\StatisticsOpenEntity;
|
||||||
use MailPoet\Entities\SubscriberEntity;
|
use MailPoet\Entities\SubscriberEntity;
|
||||||
use MailPoet\Statistics\StatisticsOpensRepository;
|
use MailPoet\Statistics\StatisticsOpensRepository;
|
||||||
|
use MailPoet\Statistics\UserAgentsRepository;
|
||||||
|
|
||||||
class Opens {
|
class Opens {
|
||||||
/** @var StatisticsOpensRepository */
|
/** @var StatisticsOpensRepository */
|
||||||
private $statisticsOpensRepository;
|
private $statisticsOpensRepository;
|
||||||
|
|
||||||
public function __construct(StatisticsOpensRepository $statisticsOpensRepository) {
|
/** @var UserAgentsRepository */
|
||||||
|
private $userAgentsRepository;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
StatisticsOpensRepository $statisticsOpensRepository,
|
||||||
|
UserAgentsRepository $userAgentsRepository
|
||||||
|
) {
|
||||||
$this->statisticsOpensRepository = $statisticsOpensRepository;
|
$this->statisticsOpensRepository = $statisticsOpensRepository;
|
||||||
|
$this->userAgentsRepository = $userAgentsRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function track($data, $displayImage = true) {
|
public function track($data, $displayImage = true) {
|
||||||
@ -40,6 +48,9 @@ class Opens {
|
|||||||
return $this->returnResponse($displayImage);
|
return $this->returnResponse($displayImage);
|
||||||
}
|
}
|
||||||
$statistics = new StatisticsOpenEntity($newsletter, $queue, $subscriber);
|
$statistics = new StatisticsOpenEntity($newsletter, $queue, $subscriber);
|
||||||
|
if (!empty($data->userAgent)) {
|
||||||
|
$statistics->setUserAgent($this->userAgentsRepository->findOrCreate($data->userAgent));
|
||||||
|
}
|
||||||
$this->statisticsOpensRepository->persist($statistics);
|
$this->statisticsOpensRepository->persist($statistics);
|
||||||
$this->statisticsOpensRepository->flush();
|
$this->statisticsOpensRepository->flush();
|
||||||
$this->statisticsOpensRepository->recalculateSubscriberScore($subscriber);
|
$this->statisticsOpensRepository->recalculateSubscriberScore($subscriber);
|
||||||
|
24
lib/Statistics/UserAgentsRepository.php
Normal file
24
lib/Statistics/UserAgentsRepository.php
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MailPoet\Statistics;
|
||||||
|
|
||||||
|
use MailPoet\Doctrine\Repository;
|
||||||
|
use MailPoet\Entities\UserAgentEntity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends Repository<UserAgentEntity>
|
||||||
|
*/
|
||||||
|
class UserAgentsRepository extends Repository {
|
||||||
|
protected function getEntityClassName() {
|
||||||
|
return UserAgentEntity::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findOrCreate(string $userAgent): UserAgentEntity {
|
||||||
|
$hash = (string)crc32($userAgent);
|
||||||
|
$userAgentEntity = $this->findOneBy(['hash' => $hash]);
|
||||||
|
if ($userAgentEntity) return $userAgentEntity;
|
||||||
|
$userAgentEntity = new UserAgentEntity($userAgent);
|
||||||
|
$this->persist($userAgentEntity);
|
||||||
|
return $userAgentEntity;
|
||||||
|
}
|
||||||
|
}
|
@ -7,17 +7,13 @@ use Codeception\Stub\Expected;
|
|||||||
use MailPoet\Entities\NewsletterEntity;
|
use MailPoet\Entities\NewsletterEntity;
|
||||||
use MailPoet\Entities\ScheduledTaskEntity;
|
use MailPoet\Entities\ScheduledTaskEntity;
|
||||||
use MailPoet\Entities\SendingQueueEntity;
|
use MailPoet\Entities\SendingQueueEntity;
|
||||||
|
use MailPoet\Entities\StatisticsOpenEntity;
|
||||||
use MailPoet\Entities\SubscriberEntity;
|
use MailPoet\Entities\SubscriberEntity;
|
||||||
use MailPoet\Models\Newsletter;
|
|
||||||
use MailPoet\Models\ScheduledTask;
|
|
||||||
use MailPoet\Models\SendingQueue;
|
|
||||||
use MailPoet\Models\StatisticsOpens;
|
use MailPoet\Models\StatisticsOpens;
|
||||||
use MailPoet\Models\Subscriber;
|
|
||||||
use MailPoet\Statistics\StatisticsOpensRepository;
|
use MailPoet\Statistics\StatisticsOpensRepository;
|
||||||
use MailPoet\Statistics\Track\Opens;
|
use MailPoet\Statistics\Track\Opens;
|
||||||
use MailPoet\Subscribers\LinkTokens;
|
use MailPoet\Subscribers\LinkTokens;
|
||||||
use MailPoet\Tasks\Sending as SendingTask;
|
use MailPoet\Tasks\Sending as SendingTask;
|
||||||
use MailPoetVendor\Idiorm\ORM;
|
|
||||||
|
|
||||||
class OpensTest extends \MailPoetTest {
|
class OpensTest extends \MailPoetTest {
|
||||||
public $opens;
|
public $opens;
|
||||||
@ -26,6 +22,9 @@ class OpensTest extends \MailPoetTest {
|
|||||||
public $subscriber;
|
public $subscriber;
|
||||||
public $newsletter;
|
public $newsletter;
|
||||||
|
|
||||||
|
/** @var StatisticsOpensRepository */
|
||||||
|
private $statisticsOpensRepository;
|
||||||
|
|
||||||
public function _before() {
|
public function _before() {
|
||||||
parent::_before();
|
parent::_before();
|
||||||
$this->cleanup();
|
$this->cleanup();
|
||||||
@ -68,7 +67,8 @@ class OpensTest extends \MailPoetTest {
|
|||||||
'preview' => false,
|
'preview' => false,
|
||||||
];
|
];
|
||||||
// instantiate class
|
// instantiate class
|
||||||
$this->opens = new Opens($this->diContainer->get(StatisticsOpensRepository::class));
|
$this->statisticsOpensRepository = $this->diContainer->get(StatisticsOpensRepository::class);
|
||||||
|
$this->opens = new Opens($this->statisticsOpensRepository);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testItReturnsImageWhenTrackDataIsEmpty() {
|
public function testItReturnsImageWhenTrackDataIsEmpty() {
|
||||||
@ -119,15 +119,36 @@ class OpensTest extends \MailPoetTest {
|
|||||||
$opens->track($this->trackData);
|
$opens->track($this->trackData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testItSavesNewUserAgent() {
|
||||||
|
$this->trackData->userAgent = 'User agent';
|
||||||
|
$opens = Stub::construct($this->opens, [$this->diContainer->get(StatisticsOpensRepository::class)], [
|
||||||
|
'returnResponse' => null,
|
||||||
|
], $this);
|
||||||
|
$opens->track($this->trackData);
|
||||||
|
$opens = $this->statisticsOpensRepository->findAll();
|
||||||
|
expect($opens)->count(1);
|
||||||
|
$open = $opens[0];
|
||||||
|
$userAgent = $open->getUserAgent();
|
||||||
|
expect($userAgent)->notNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testItSavesOpenWithExistingUserAgent() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testItOverridesOldUserAgent() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public function _after() {
|
public function _after() {
|
||||||
$this->cleanup();
|
$this->cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function cleanup() {
|
public function cleanup() {
|
||||||
ORM::raw_execute('TRUNCATE ' . Newsletter::$_table);
|
$this->truncateEntity(NewsletterEntity::class);
|
||||||
ORM::raw_execute('TRUNCATE ' . Subscriber::$_table);
|
$this->truncateEntity(SubscriberEntity::class);
|
||||||
ORM::raw_execute('TRUNCATE ' . ScheduledTask::$_table);
|
$this->truncateEntity(ScheduledTaskEntity::class);
|
||||||
ORM::raw_execute('TRUNCATE ' . SendingQueue::$_table);
|
$this->truncateEntity(SendingQueueEntity::class);
|
||||||
ORM::raw_execute('TRUNCATE ' . StatisticsOpens::$_table);
|
$this->truncateEntity(StatisticsOpenEntity::class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user