- Refactors front router and endpoints to use dynamic methods
This commit is contained in:
@@ -14,19 +14,24 @@ class Track {
|
|||||||
const ENDPOINT = 'track';
|
const ENDPOINT = 'track';
|
||||||
const ACTION_CLICK = 'click';
|
const ACTION_CLICK = 'click';
|
||||||
const ACTION_OPEN = 'open';
|
const ACTION_OPEN = 'open';
|
||||||
public $allowed_actions = array(
|
const ALLOWED_ACTIONS = array(
|
||||||
self::ACTION_CLICK,
|
self::ACTION_CLICK,
|
||||||
self::ACTION_OPEN
|
self::ACTION_OPEN
|
||||||
);
|
);
|
||||||
|
public $data;
|
||||||
|
|
||||||
function click($data) {
|
function __construct($data) {
|
||||||
$click_event = new Clicks();
|
$this->data = $this->_processTrackData($data);
|
||||||
return $click_event->track(self::_processTrackData($data));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function open($data) {
|
function click() {
|
||||||
|
$click_event = new Clicks();
|
||||||
|
return $click_event->track($this->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function open() {
|
||||||
$open_event = new Opens();
|
$open_event = new Opens();
|
||||||
return $open_event->track(self::_processTrackData($data));
|
return $open_event->track($this->data);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _processTrackData($data) {
|
function _processTrackData($data) {
|
||||||
@@ -45,10 +50,10 @@ class Track {
|
|||||||
if(!empty($data->link_hash)) {
|
if(!empty($data->link_hash)) {
|
||||||
$data->link = NewsletterLink::getByHash($data->link_hash);
|
$data->link = NewsletterLink::getByHash($data->link_hash);
|
||||||
}
|
}
|
||||||
return self::_validateTrackData($data);
|
return $this->_validateTrackData($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static function _validateTrackData($data) {
|
function _validateTrackData($data) {
|
||||||
if(!$data->subscriber || !$data->queue || !$data->newsletter) return false;
|
if(!$data->subscriber || !$data->queue || !$data->newsletter) return false;
|
||||||
$subscriber_token_match =
|
$subscriber_token_match =
|
||||||
Subscriber::verifyToken($data->subscriber->email, $data->subscriber_token);
|
Subscriber::verifyToken($data->subscriber->email, $data->subscriber_token);
|
||||||
|
@@ -12,11 +12,15 @@ class ViewInBrowser {
|
|||||||
const ENDPOINT = 'view_in_browser';
|
const ENDPOINT = 'view_in_browser';
|
||||||
const ACTION_VIEW = 'view';
|
const ACTION_VIEW = 'view';
|
||||||
public $allowed_actions = array(self::ACTION_VIEW);
|
public $allowed_actions = array(self::ACTION_VIEW);
|
||||||
|
public $data;
|
||||||
|
|
||||||
function view($data) {
|
function __construct($data) {
|
||||||
$data = $this->_processBrowserPreviewData($data);
|
$this->data = $this->_processBrowserPreviewData($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function view() {
|
||||||
$view_in_browser = new NewsletterViewInBrowser();
|
$view_in_browser = new NewsletterViewInBrowser();
|
||||||
return $this->_displayNewsletter($view_in_browser->view($data));
|
return $this->_displayNewsletter($view_in_browser->view($this->data));
|
||||||
}
|
}
|
||||||
|
|
||||||
function _processBrowserPreviewData($data) {
|
function _processBrowserPreviewData($data) {
|
||||||
@@ -54,9 +58,9 @@ class ViewInBrowser {
|
|||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _displayNewsletter($rendered_newsletter) {
|
function _displayNewsletter($result) {
|
||||||
header('Content-Type: text/html; charset=utf-8');
|
header('Content-Type: text/html; charset=utf-8');
|
||||||
echo $rendered_newsletter;
|
echo $result;
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace MailPoet\Router;
|
namespace MailPoet\Router;
|
||||||
|
|
||||||
use MailPoet\Util\Helpers;
|
use MailPoet\Util\Helpers;
|
||||||
|
|
||||||
if(!defined('ABSPATH')) exit;
|
if(!defined('ABSPATH')) exit;
|
||||||
@@ -27,44 +28,31 @@ class Front {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
$class = __NAMESPACE__ . "\\Endpoints\\" . ucfirst($this->endpoint);
|
$endpoint_class = __NAMESPACE__ . "\\Endpoints\\" . ucfirst($this->endpoint);
|
||||||
|
|
||||||
if(!$this->api_request) return;
|
if(!$this->api_request) return;
|
||||||
if(!$this->endpoint || !class_exists($class)) {
|
if(!$this->endpoint || !class_exists($endpoint_class)) {
|
||||||
self::terminateRequest(self::RESPONSE_ERROR, __('Invalid router endpoint.'));
|
return $this->terminateRequest(self::RESPONSE_ERROR, __('Invalid router endpoint.'));
|
||||||
}
|
}
|
||||||
$this->callEndpoint(
|
$endpoint = new $endpoint_class($this->data);
|
||||||
$class,
|
if(!method_exists($endpoint, $this->action) || !in_array($this->action, $endpoint->allowed_actions)) {
|
||||||
$this->action,
|
return $this->terminateRequest(self::RESPONSE_ERROR, __('Invalid router action.'));
|
||||||
$this->data
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function callEndpoint($endpoint, $action, $data) {
|
|
||||||
$endpoint = new $endpoint();
|
|
||||||
if(!method_exists($endpoint, $action) || !in_array($action, $endpoint->allowed_actions)) {
|
|
||||||
self::terminateRequest(self::RESPONSE_ERROR, __('Invalid router action.'));
|
|
||||||
}
|
}
|
||||||
call_user_func(
|
return call_user_func(
|
||||||
array(
|
array(
|
||||||
$endpoint,
|
$endpoint,
|
||||||
$action
|
$this->action
|
||||||
),
|
)
|
||||||
$data
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static function decodeRequestData($data) {
|
static function decodeRequestData($data) {
|
||||||
$data = base64_decode($data);
|
$data = base64_decode($data);
|
||||||
|
|
||||||
if(is_serialized($data)) {
|
if(is_serialized($data)) {
|
||||||
$data = unserialize($data);
|
$data = unserialize($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!is_array($data)) {
|
if(!is_array($data)) {
|
||||||
$data = array();
|
$data = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +71,7 @@ class Front {
|
|||||||
return add_query_arg($params, home_url());
|
return add_query_arg($params, home_url());
|
||||||
}
|
}
|
||||||
|
|
||||||
static function terminateRequest($code, $message) {
|
function terminateRequest($code, $message) {
|
||||||
status_header($code, $message);
|
status_header($code, $message);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
133
tests/unit/Router/FrontTest.php
Normal file
133
tests/unit/Router/FrontTest.php
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Codeception\Util\Stub;
|
||||||
|
use MailPoet\Models\Newsletter;
|
||||||
|
use MailPoet\Models\SendingQueue;
|
||||||
|
use MailPoet\Models\Subscriber;
|
||||||
|
use MailPoet\Router\Endpoints\ViewInBrowser;
|
||||||
|
|
||||||
|
class ViewInBrowserRouterTest extends MailPoetTest {
|
||||||
|
function _before() {
|
||||||
|
// create newsletter
|
||||||
|
$newsletter = Newsletter::create();
|
||||||
|
$newsletter->type = 'type';
|
||||||
|
$this->newsletter = $newsletter->save();
|
||||||
|
// create subscriber
|
||||||
|
$subscriber = Subscriber::create();
|
||||||
|
$subscriber->email = 'test@example.com';
|
||||||
|
$subscriber->first_name = 'First';
|
||||||
|
$subscriber->last_name = 'Last';
|
||||||
|
$this->subscriber = $subscriber->save();
|
||||||
|
// create queue
|
||||||
|
$queue = SendingQueue::create();
|
||||||
|
$queue->newsletter_id = $newsletter->id;
|
||||||
|
$queue->subscribers = array('processed' => array($subscriber->id));
|
||||||
|
$this->queue = $queue->save();
|
||||||
|
// build browser preview data
|
||||||
|
$this->browser_preview_data = array(
|
||||||
|
'queue_id' => $queue->id,
|
||||||
|
'subscriber_id' => $subscriber->id,
|
||||||
|
'newsletter_id' => $newsletter->id,
|
||||||
|
'subscriber_token' => Subscriber::generateToken($subscriber->email),
|
||||||
|
'preview' => false
|
||||||
|
);
|
||||||
|
// instantiate class
|
||||||
|
$this->view_in_browser = new ViewInBrowser();
|
||||||
|
}
|
||||||
|
|
||||||
|
function testItAbortsWhenTrackDataIsMissing() {
|
||||||
|
$view_in_browser = Stub::make($this->view_in_browser, array(
|
||||||
|
'_abort' => Stub::exactly(3, function() { })
|
||||||
|
), $this);
|
||||||
|
// newsletter ID is required
|
||||||
|
$data = $this->browser_preview_data;
|
||||||
|
unset($data['newsletter_id']);
|
||||||
|
$view_in_browser->_processBrowserPreviewData($data);
|
||||||
|
// subscriber ID is required
|
||||||
|
$data = $this->browser_preview_data;
|
||||||
|
unset($data['subscriber_id']);
|
||||||
|
$view_in_browser->_processBrowserPreviewData($data);
|
||||||
|
// subscriber token is required
|
||||||
|
$data = $this->browser_preview_data;
|
||||||
|
unset($data['subscriber_token']);
|
||||||
|
$view_in_browser->_processBrowserPreviewData($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testItAbortsWhenTrackDataIsInvalid() {
|
||||||
|
$view_in_browser = Stub::make($this->view_in_browser, array(
|
||||||
|
'_abort' => Stub::exactly(3, function() { })
|
||||||
|
), $this);
|
||||||
|
// newsletter ID is invalid
|
||||||
|
$data = $this->browser_preview_data;
|
||||||
|
$data['newsletter_id'] = 99;
|
||||||
|
$view_in_browser->_processBrowserPreviewData($data);
|
||||||
|
// subscriber ID is invalid
|
||||||
|
$data = $this->browser_preview_data;
|
||||||
|
$data['subscriber_id'] = 99;
|
||||||
|
$view_in_browser->_processBrowserPreviewData($data);
|
||||||
|
// subscriber token is invalid
|
||||||
|
$data = $this->browser_preview_data;
|
||||||
|
$data['subscriber_token'] = 'invalid';
|
||||||
|
$view_in_browser->_processBrowserPreviewData($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testItFailsValidationWhenSubscriberTokenDoesNotMatch() {
|
||||||
|
$data = (object)array_merge(
|
||||||
|
$this->browser_preview_data,
|
||||||
|
array(
|
||||||
|
'queue' => $this->queue,
|
||||||
|
'subscriber' => $this->subscriber,
|
||||||
|
'newsletter' => $this->newsletter
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$data->subscriber->email = 'random@email.com';
|
||||||
|
expect($this->view_in_browser->_validateBrowserPreviewData($data))->false();
|
||||||
|
}
|
||||||
|
|
||||||
|
function testItFailsValidationWhenSubscriberIsNotOnProcessedList() {
|
||||||
|
$data = (object)array_merge(
|
||||||
|
$this->browser_preview_data,
|
||||||
|
array(
|
||||||
|
'queue' => $this->queue,
|
||||||
|
'subscriber' => $this->subscriber,
|
||||||
|
'newsletter' => $this->newsletter
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$data->subscriber->id = 99;
|
||||||
|
expect($this->view_in_browser->_validateBrowserPreviewData($data))->false();
|
||||||
|
}
|
||||||
|
|
||||||
|
function testItDoesNotRequireWpUsersToBeOnProcessedListWhenPreviewIsEnabled() {
|
||||||
|
$data = (object)array_merge(
|
||||||
|
$this->browser_preview_data,
|
||||||
|
array(
|
||||||
|
'queue' => $this->queue,
|
||||||
|
'subscriber' => $this->subscriber,
|
||||||
|
'newsletter' => $this->newsletter
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$data->subscriber->wp_user_id = 99;
|
||||||
|
$data->preview = true;
|
||||||
|
expect($this->view_in_browser->_validateBrowserPreviewData($data))->equals($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testItCanProcessBrowserPreviewData() {
|
||||||
|
$processed_data = $this->view_in_browser->_processBrowserPreviewData($this->browser_preview_data);
|
||||||
|
expect($processed_data->queue->id)->equals($this->queue->id);
|
||||||
|
expect($processed_data->subscriber->id)->equals($this->subscriber->id);
|
||||||
|
expect($processed_data->newsletter->id)->equals($this->newsletter->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testItCanViewNewsletter() {
|
||||||
|
$view_in_browser = Stub::make($this->view_in_browser, array(
|
||||||
|
'_displayNewsletter' => Stub::exactly(1, function() { })
|
||||||
|
), $this);
|
||||||
|
$view_in_browser->view($this->browser_preview_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _after() {
|
||||||
|
ORM::raw_execute('TRUNCATE ' . Newsletter::$_table);
|
||||||
|
ORM::raw_execute('TRUNCATE ' . Subscriber::$_table);
|
||||||
|
ORM::raw_execute('TRUNCATE ' . SendingQueue::$_table);
|
||||||
|
}
|
||||||
|
}
|
143
tests/unit/Router/FrontTestMockEndpoint.php
Normal file
143
tests/unit/Router/FrontTestMockEndpoint.php
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Codeception\Util\Stub;
|
||||||
|
use MailPoet\Router\Front;
|
||||||
|
|
||||||
|
class FrontTest extends MailPoetTest {
|
||||||
|
|
||||||
|
function __construct() {
|
||||||
|
/* $this->api_request = 'mailpoet_api';
|
||||||
|
$this->api_endpoint = 'view_in_browser';
|
||||||
|
$this->api_action = 'view';
|
||||||
|
$this->api_data = base64_encode(serialize(array('data' => 'dummy data')));
|
||||||
|
$this->url = "http://example.com/?{$this->api_request}&endpoint={$this->api_endpoint}&action={$this->api_action}&data={$this->api_data}";*/
|
||||||
|
$this->router_data = array(
|
||||||
|
'mailpoet_api' => '',
|
||||||
|
'endpoint' => 'view_in_browser',
|
||||||
|
'action' => 'view',
|
||||||
|
'data' => base64_encode(serialize(array('data' => 'dummy data')))
|
||||||
|
);
|
||||||
|
$this->router = new Front($this->router_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* function testItCanGetAPIDataFromGetRequest() {
|
||||||
|
$data = array('data' => 'dummy data');
|
||||||
|
$url = 'http://example.com/?mailpoet_api&endpoint=view_in_browser&action=view&data='
|
||||||
|
. base64_encode(serialize($data));
|
||||||
|
parse_str(parse_url($url, PHP_URL_QUERY), $_GET);
|
||||||
|
$router = new Front();
|
||||||
|
expect($router->api_request)->equals(true);
|
||||||
|
expect($router->endpoint)->equals('viewInBrowser');
|
||||||
|
expect($router->action)->equals('view');
|
||||||
|
expect($router->data)->equals($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
function testItContinuesExecutionWhenAPIRequestNotDetected() {
|
||||||
|
$router_data = $this->router_data;
|
||||||
|
unset($router_data['mailpoet_api']);
|
||||||
|
$router = Stub::construct(
|
||||||
|
new Front(),
|
||||||
|
array($router_data),
|
||||||
|
array(
|
||||||
|
'terminateRequest' => function() { return 'terminated'; },
|
||||||
|
'callEndpoint' => function() { return 'endpoint called'; }
|
||||||
|
)
|
||||||
|
);
|
||||||
|
expect($router->init())->null();
|
||||||
|
}
|
||||||
|
|
||||||
|
function testItTerminatesRequestWhenEndpointNotFound() {
|
||||||
|
$router_data = $this->router_data;
|
||||||
|
$router_data['endpoint'] = 'invalidEndpoint';
|
||||||
|
$router = Stub::construct(
|
||||||
|
new Front(),
|
||||||
|
array($router_data),
|
||||||
|
array(
|
||||||
|
'terminateRequest' => function($code, $error) {
|
||||||
|
return array(
|
||||||
|
$code,
|
||||||
|
$error
|
||||||
|
);
|
||||||
|
},
|
||||||
|
'callEndpointActoin' => function() { return 'endpoint called'; }
|
||||||
|
)
|
||||||
|
);
|
||||||
|
expect($router->init())
|
||||||
|
->equals(
|
||||||
|
array(
|
||||||
|
404,
|
||||||
|
'Invalid router endpoint.'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testItCallsEndpointAction() {
|
||||||
|
$router_data = $this->router_data;
|
||||||
|
$router = Stub::construct(
|
||||||
|
new Front(),
|
||||||
|
array($router_data),
|
||||||
|
array(
|
||||||
|
'terminateRequest' => function($code, $error) {
|
||||||
|
return array(
|
||||||
|
$code,
|
||||||
|
$error
|
||||||
|
);
|
||||||
|
},
|
||||||
|
'callEndpointAction' => function($class, $action, $data) {
|
||||||
|
return array(
|
||||||
|
$class,
|
||||||
|
$action,
|
||||||
|
$data
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
expect($router->init())
|
||||||
|
->equals(
|
||||||
|
array(
|
||||||
|
'MailPoet\Router\Endpoints\ViewInBrowser',
|
||||||
|
'view',
|
||||||
|
array('data' => 'dummy data')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testItAbortsWhenEndpointActionNotFound() {
|
||||||
|
$router_data = $this->router_data;
|
||||||
|
$router = Stub::construct(
|
||||||
|
new Front(),
|
||||||
|
array($router_data),
|
||||||
|
array(
|
||||||
|
'terminateRequest' => function($code, $error) {
|
||||||
|
return array(
|
||||||
|
$code,
|
||||||
|
$error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
expect($router->callEndpointAction())
|
||||||
|
->equals(
|
||||||
|
404,
|
||||||
|
'Invalid router action.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
function testItCallsEndpointWhenItIsFound() {
|
||||||
|
$router_data = $this->router_data;
|
||||||
|
$router = Stub::construct(
|
||||||
|
new Front(),
|
||||||
|
array($router_data),
|
||||||
|
array(
|
||||||
|
'terminateRequest' => function() { return 'terminated'; },
|
||||||
|
'callEndpoint' => function() { return 'endpoint called'; }
|
||||||
|
)
|
||||||
|
);
|
||||||
|
expect($router->init())->equals('endpoint called');
|
||||||
|
}*/
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Reference in New Issue
Block a user