Move GA tracking to free

[MAILPOET-2397]
This commit is contained in:
Pavel Dohnal
2019-10-22 15:45:08 +02:00
committed by Jack Kitterhing
parent 787cd8373a
commit a42a971efd
4 changed files with 170 additions and 1 deletions

View File

@@ -13,6 +13,7 @@ use MailPoet\Models\SendingQueue as SendingQueueModel;
use MailPoet\Newsletter\Links\Links as NewsletterLinks; use MailPoet\Newsletter\Links\Links as NewsletterLinks;
use MailPoet\Newsletter\Renderer\PostProcess\OpenTracking; use MailPoet\Newsletter\Renderer\PostProcess\OpenTracking;
use MailPoet\Settings\SettingsController; use MailPoet\Settings\SettingsController;
use MailPoet\Statistics\GATracking;
use MailPoet\Util\Helpers; use MailPoet\Util\Helpers;
use MailPoet\WP\Functions as WPFunctions; use MailPoet\WP\Functions as WPFunctions;
@@ -26,10 +27,13 @@ class Newsletter {
/** @var PostsTask */ /** @var PostsTask */
private $posts_task; private $posts_task;
/** @var GATracking */
private $ga_tracking;
/** @var LoggerFactory */ /** @var LoggerFactory */
private $logger_factory; private $logger_factory;
function __construct(WPFunctions $wp = null, PostsTask $posts_task = null) { function __construct(WPFunctions $wp = null, PostsTask $posts_task = null, GATracking $ga_tracking = null) {
$settings = new SettingsController(); $settings = new SettingsController();
$this->tracking_enabled = (boolean)$settings->get('tracking.enabled'); $this->tracking_enabled = (boolean)$settings->get('tracking.enabled');
if ($wp === null) { if ($wp === null) {
@@ -40,6 +44,10 @@ class Newsletter {
$posts_task = new PostsTask; $posts_task = new PostsTask;
} }
$this->posts_task = $posts_task; $this->posts_task = $posts_task;
if ($ga_tracking === null) {
$ga_tracking = new GATracking;
}
$this->ga_tracking = $ga_tracking;
$this->logger_factory = LoggerFactory::getInstance(); $this->logger_factory = LoggerFactory::getInstance();
} }
@@ -93,6 +101,7 @@ class Newsletter {
$rendered_newsletter, $rendered_newsletter,
$newsletter $newsletter
); );
$rendered_newsletter = $this->ga_tracking->applyGATracking($rendered_newsletter, $newsletter);
// hash and save all links // hash and save all links
$rendered_newsletter = LinksTask::process($rendered_newsletter, $newsletter, $sending_task); $rendered_newsletter = LinksTask::process($rendered_newsletter, $newsletter, $sending_task);
} else { } else {
@@ -103,6 +112,7 @@ class Newsletter {
$rendered_newsletter, $rendered_newsletter,
$newsletter $newsletter
); );
$rendered_newsletter = $this->ga_tracking->applyGATracking($rendered_newsletter, $newsletter);
} }
// check if this is a post notification and if it contains at least 1 ALC post // check if this is a post notification and if it contains at least 1 ALC post
if ($newsletter->type === NewsletterModel::TYPE_NOTIFICATION_HISTORY && if ($newsletter->type === NewsletterModel::TYPE_NOTIFICATION_HISTORY &&

View File

@@ -39,6 +39,7 @@ use function MailPoet\Util\array_column;
* @property string|null $schedule * @property string|null $schedule
* @property boolean|null $isScheduled * @property boolean|null $isScheduled
* @property string|null $scheduledAt * @property string|null $scheduledAt
* @property string $ga_campaign
*/ */
class Newsletter extends Model { class Newsletter extends Model {

View File

@@ -0,0 +1,70 @@
<?php
namespace MailPoet\Statistics;
use MailPoet\Models\Newsletter;
use MailPoet\Newsletter\Links\Links as NewsletterLinks;
use MailPoet\Util\Helpers;
use MailPoet\WP\Functions as WPFunctions;
class GATracking {
function applyGATracking($rendered_newsletter, $newsletter, $internal_host = null) {
if ($newsletter instanceof Newsletter && $newsletter->type == Newsletter::TYPE_NOTIFICATION_HISTORY) {
$parent_newsletter = $newsletter->parent()->findOne();
$field = $parent_newsletter->ga_campaign;
} else {
$field = $newsletter->ga_campaign;
}
if (!empty($field)) {
$rendered_newsletter = $this->addGAParamsToLinks($rendered_newsletter, $field, $internal_host);
}
return $rendered_newsletter;
}
private function addGAParamsToLinks($rendered_newsletter, $ga_campaign, $internal_host = null) {
// join HTML and TEXT rendered body into a text string
$content = Helpers::joinObject($rendered_newsletter);
$extracted_links = NewsletterLinks::extract($content);
$processed_links = $this->addParams($extracted_links, $ga_campaign, $internal_host);
list($content, $links) = NewsletterLinks::replace($content, $processed_links);
// split the processed body with hashed links back to HTML and TEXT
list($rendered_newsletter['html'], $rendered_newsletter['text'])
= Helpers::splitObject($content);
return $rendered_newsletter;
}
private function addParams($extracted_links, $ga_campaign, $internal_host = null) {
$processed_links = [];
$params = [
'utm_source' => 'mailpoet',
'utm_medium' => 'email',
'utm_campaign' => urlencode($ga_campaign),
];
$internal_host = $internal_host ?: parse_url(home_url(), PHP_URL_HOST);
$internal_host = self::getSecondLevelDomainName($internal_host);
foreach ($extracted_links as $extracted_link) {
if ($extracted_link['type'] !== NewsletterLinks::LINK_TYPE_URL) {
continue;
} elseif (strpos(parse_url($extracted_link['link'], PHP_URL_HOST), $internal_host) === false) {
// Process only internal links (i.e. pointing to current site)
continue;
}
$processed_link = WPFunctions::get()->addQueryArg($params, $extracted_link['link']);
$link = $extracted_link['link'];
$processed_links[$link] = [
'type' => $extracted_link['type'],
'link' => $link,
'processed_link' => $processed_link,
];
}
return $processed_links;
}
public static function getSecondLevelDomainName($host) {
if (preg_match('/[^.]*\.[^.]{2,3}(?:\.[^.]{2,3})?$/', $host, $matches)) {
return $matches[0];
}
return $host;
}
}

View File

@@ -0,0 +1,88 @@
<?php
namespace MailPoet\Statistics;
use MailPoet\Models\Newsletter;
class GATrackingTest extends \MailPoetTest {
/** @var string */
private $internal_host;
/** @var string */
private $ga_campaign;
/** @var string */
private $link;
/** @var string[] */
private $rendered_newsletter;
function _before() {
$this->internal_host = 'newsletters.mailpoet.com';
$this->ga_campaign = 'Spring email';
$this->link = add_query_arg(['foo' => 'bar', 'baz' => 'xyz'], 'http://www.mailpoet.com/');
$this->rendered_newsletter = [
'html' => '<p><a href="' . $this->link . '">Click here</a>. <a href="http://somehost.com/fff/?abc=123">Do not process this</a> [link:some_link_shortcode]</p>',
'text' => '[Click here](' . $this->link . '). [Do not process this](http://somehost.com/fff/?abc=123) [link:some_link_shortcode]',
];
}
function testItConditionallyAppliesGATracking() {
// No process (empty GA campaign)
$newsletter = Newsletter::createOrUpdate(['id' => 123]);
$tracking = new GATracking();
$result = $tracking->applyGATracking($this->rendered_newsletter, $newsletter, $this->internal_host);
expect($result)->equals($this->rendered_newsletter);
// Process (filled GA campaign)
$newsletter->ga_campaign = $this->ga_campaign;
$newsletter->save();
$result = $tracking->applyGATracking($this->rendered_newsletter, $newsletter, $this->internal_host);
expect($result)->notEquals($this->rendered_newsletter);
}
function testItGetsGACampaignFromParentNewsletterForPostNotifications() {
$tracking = new GATracking();
$notification = Newsletter::create();
$notification->hydrate([
'type' => Newsletter::TYPE_NOTIFICATION,
'ga_campaign' => $this->ga_campaign,
]);
$notification->save();
$notification_history = Newsletter::create();
$notification_history->hydrate([
'parent_id' => $notification->id,
'type' => Newsletter::TYPE_NOTIFICATION_HISTORY,
]);
$notification_history->save();
$result = $tracking->applyGATracking($this->rendered_newsletter, $notification_history, $this->internal_host);
expect($result)->notEquals($this->rendered_newsletter);
}
function testItCanAddGAParamsToLinks() {
$tracking = new GATracking();
$newsletter = Newsletter::createOrUpdate([
'ga_campaign' => $this->ga_campaign,
]);
$result = $tracking->applyGATracking($this->rendered_newsletter, $newsletter, $this->internal_host);
expect($result['text'])->contains('utm_campaign=' . urlencode($this->ga_campaign));
expect($result['html'])->contains('utm_campaign=' . urlencode($this->ga_campaign));
}
function testItGetsSecondLevelDomainName() {
$cases = [
'mailpoet.com' => 'mailpoet.com',
'newsletters.mailpoet.com' => 'mailpoet.com',
'test.example.co.uk' => 'example.co.uk',
'example.co.uk' => 'example.co.uk',
'localhost' => 'localhost',
];
foreach ($cases as $subdomain => $domain) {
expect(GATracking::getSecondLevelDomainName($subdomain))->equals($domain);
}
}
function _after() {
\ORM::raw_execute('TRUNCATE ' . Newsletter::$_table);
}
}