83
lib/API/API.php
Normal file
83
lib/API/API.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
namespace MailPoet\API;
|
||||
|
||||
use MailPoet\Util\Helpers;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class API {
|
||||
public $api_request;
|
||||
public $endpoint;
|
||||
public $action;
|
||||
public $data;
|
||||
const NAME = 'mailpoet_api';
|
||||
const ENDPOINT_NAMESPACE = '\MailPoet\API\Endpoints\\';
|
||||
const RESPONSE_ERROR = 404;
|
||||
|
||||
function __construct($api_data = false) {
|
||||
$api_data = ($api_data) ? $api_data : $_GET;
|
||||
$this->api_request = isset($api_data[self::NAME]);
|
||||
$this->endpoint = isset($api_data['endpoint']) ?
|
||||
Helpers::underscoreToCamelCase($api_data['endpoint']) :
|
||||
false;
|
||||
$this->action = isset($api_data['action']) ?
|
||||
Helpers::underscoreToCamelCase($api_data['action']) :
|
||||
false;
|
||||
$this->data = isset($api_data['data']) ?
|
||||
self::decodeRequestData($api_data['data']) :
|
||||
false;
|
||||
}
|
||||
|
||||
function init() {
|
||||
$endpoint = self::ENDPOINT_NAMESPACE . ucfirst($this->endpoint);
|
||||
if(!$this->api_request) return;
|
||||
if(!$this->endpoint || !class_exists($endpoint)) {
|
||||
$this->terminateRequest(self::RESPONSE_ERROR, __('Invalid API endpoint.'));
|
||||
}
|
||||
$this->callEndpoint(
|
||||
$endpoint,
|
||||
$this->action,
|
||||
$this->data
|
||||
);
|
||||
}
|
||||
|
||||
function callEndpoint($endpoint, $action, $data) {
|
||||
if(!method_exists($endpoint, $action)) {
|
||||
$this->terminateRequest(self::RESPONSE_ERROR, __('Invalid API action.'));
|
||||
}
|
||||
call_user_func(
|
||||
array(
|
||||
$endpoint,
|
||||
$action
|
||||
),
|
||||
$data
|
||||
);
|
||||
}
|
||||
|
||||
static function decodeRequestData($data) {
|
||||
$data = base64_decode($data);
|
||||
return (is_serialized($data)) ?
|
||||
unserialize($data) :
|
||||
self::terminateRequest(self::RESPONSE_ERROR, __('Invalid API data format.'));
|
||||
}
|
||||
|
||||
static function encodeRequestData($data) {
|
||||
return rtrim(base64_encode(serialize($data)), '=');
|
||||
}
|
||||
|
||||
static function buildRequest($endpoint, $action, $data) {
|
||||
$data = self::encodeRequestData($data);
|
||||
$params = array(
|
||||
self::NAME => '',
|
||||
'endpoint' => $endpoint,
|
||||
'action' => $action,
|
||||
'data' => $data
|
||||
);
|
||||
return add_query_arg($params, home_url());
|
||||
}
|
||||
|
||||
function terminateRequest($code, $message) {
|
||||
status_header($code, $message);
|
||||
exit;
|
||||
}
|
||||
}
|
16
lib/API/Endpoints/Queue.php
Normal file
16
lib/API/Endpoints/Queue.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
namespace MailPoet\API\Endpoints;
|
||||
|
||||
use MailPoet\Cron\Daemon;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class Queue {
|
||||
const ENDPOINT = 'queue';
|
||||
const ACTION_RUN = 'run';
|
||||
|
||||
static function run($data) {
|
||||
$queue = new Daemon($data);
|
||||
$queue->run();
|
||||
}
|
||||
}
|
22
lib/API/Endpoints/Subscription.php
Normal file
22
lib/API/Endpoints/Subscription.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
namespace MailPoet\API\Endpoints;
|
||||
|
||||
use MailPoet\Subscription as UserSubscription;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class Subscription {
|
||||
const ENDPOINT = 'subscription';
|
||||
|
||||
static function confirm($data) {
|
||||
$subscription = new UserSubscription\Pages('confirm', $data);
|
||||
}
|
||||
|
||||
static function manage($data) {
|
||||
$subscription = new UserSubscription\Pages('manage', $data);
|
||||
}
|
||||
|
||||
static function unsubscribe($data) {
|
||||
$subscription = new UserSubscription\Pages('unsubscribe', $data);
|
||||
}
|
||||
}
|
23
lib/API/Endpoints/Track.php
Normal file
23
lib/API/Endpoints/Track.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
namespace MailPoet\API\Endpoints;
|
||||
|
||||
use MailPoet\Statistics\Track\Clicks;
|
||||
use MailPoet\Statistics\Track\Opens;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class Track {
|
||||
const ENDPOINT = 'track';
|
||||
const ACTION_CLICK = 'click';
|
||||
const ACTION_OPEN = 'open';
|
||||
|
||||
static function click($data) {
|
||||
$clicks = new Clicks($data);
|
||||
$clicks->track();
|
||||
}
|
||||
|
||||
static function open($data) {
|
||||
$opens = new Opens($data);
|
||||
$opens->track();
|
||||
}
|
||||
}
|
16
lib/API/Endpoints/ViewInBrowser.php
Normal file
16
lib/API/Endpoints/ViewInBrowser.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
namespace MailPoet\API\Endpoints;
|
||||
|
||||
use MailPoet\Newsletter\ViewInBrowser as NewsletterViewInBrowser;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class ViewInBrowser {
|
||||
const ENDPOINT = 'view_in_browser';
|
||||
const ACTION_VIEW = 'view';
|
||||
|
||||
static function view($data) {
|
||||
$viewer = new NewsletterViewInBrowser($data);
|
||||
$viewer->view();
|
||||
}
|
||||
}
|
@@ -48,7 +48,7 @@ class Initializer {
|
||||
|
||||
function onInit() {
|
||||
$this->setupRouter();
|
||||
$this->setupPublicAPI();
|
||||
$this->setupAPI();
|
||||
$this->setupPages();
|
||||
}
|
||||
|
||||
@@ -165,9 +165,9 @@ class Initializer {
|
||||
$hooks->init();
|
||||
}
|
||||
|
||||
function setupPublicAPI() {
|
||||
$publicAPI = new PublicAPI();
|
||||
$publicAPI->init();
|
||||
function setupAPI() {
|
||||
$API = new \MailPoet\API\API();
|
||||
$API->init();
|
||||
}
|
||||
|
||||
function runQueueSupervisor() {
|
||||
|
@@ -1,77 +0,0 @@
|
||||
<?php
|
||||
namespace MailPoet\Config;
|
||||
|
||||
use MailPoet\Cron\Daemon;
|
||||
use MailPoet\Newsletter\ViewInBrowser;
|
||||
use MailPoet\Statistics\Track\Clicks;
|
||||
use MailPoet\Statistics\Track\Opens;
|
||||
use MailPoet\Subscription;
|
||||
use MailPoet\Util\Helpers;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class PublicAPI {
|
||||
public $api;
|
||||
public $endpoint;
|
||||
public $action;
|
||||
public $data;
|
||||
|
||||
function __construct() {
|
||||
# http://example.com/?mailpoet&endpoint=&action=&data=
|
||||
$this->api = isset($_GET['mailpoet']) ? true : false;
|
||||
$this->endpoint = isset($_GET['endpoint']) ?
|
||||
Helpers::underscoreToCamelCase($_GET['endpoint']) :
|
||||
false;
|
||||
$this->action = isset($_GET['action']) ?
|
||||
Helpers::underscoreToCamelCase($_GET['action']) :
|
||||
false;
|
||||
$this->data = isset($_GET['data']) ?
|
||||
unserialize(base64_decode($_GET['data'])) :
|
||||
false;
|
||||
}
|
||||
|
||||
function init() {
|
||||
if(!$this->api && !$this->endpoint) return;
|
||||
$this->_checkAndCallMethod($this, $this->endpoint, $terminate_request = true);
|
||||
}
|
||||
|
||||
function queue() {
|
||||
$queue = new Daemon($this->data);
|
||||
$this->_checkAndCallMethod($queue, $this->action);
|
||||
}
|
||||
|
||||
function subscription() {
|
||||
$subscription = new Subscription\Pages($this->action, $this->data);
|
||||
$this->_checkAndCallMethod($subscription, $this->action);
|
||||
}
|
||||
|
||||
function track() {
|
||||
if($this->action === 'click') {
|
||||
$track_class = new Clicks($this->data);
|
||||
}
|
||||
if($this->action === 'open') {
|
||||
$track_class = new Opens($this->data);
|
||||
}
|
||||
if(!isset($track_class)) return;
|
||||
$track_class->track();
|
||||
}
|
||||
|
||||
function viewInBrowser() {
|
||||
$viewer = new ViewInBrowser($this->data);
|
||||
$viewer->view();
|
||||
}
|
||||
|
||||
private function _checkAndCallMethod($class, $method, $terminate_request = false) {
|
||||
if(!method_exists($class, $method)) {
|
||||
if(!$terminate_request) return;
|
||||
header('HTTP/1.0 404 Not Found');
|
||||
exit;
|
||||
}
|
||||
call_user_func(
|
||||
array(
|
||||
$class,
|
||||
$method
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,6 +1,8 @@
|
||||
<?php
|
||||
namespace MailPoet\Cron;
|
||||
|
||||
use MailPoet\API\API;
|
||||
use MailPoet\API\Endpoints\Queue as QueueAPI;
|
||||
use MailPoet\Models\Setting;
|
||||
use MailPoet\Util\Security;
|
||||
|
||||
@@ -14,7 +16,6 @@ class CronHelper {
|
||||
static function createDaemon($token) {
|
||||
$daemon = array(
|
||||
'status' => Daemon::STATUS_STARTING,
|
||||
'counter' => 0,
|
||||
'token' => $token
|
||||
);
|
||||
self::saveDaemon($daemon);
|
||||
@@ -38,17 +39,17 @@ class CronHelper {
|
||||
}
|
||||
|
||||
static function accessDaemon($token, $timeout = self::DAEMON_REQUEST_TIMEOUT) {
|
||||
$data = serialize(array('token' => $token));
|
||||
$url = '/?mailpoet&endpoint=queue&action=run&data=' .
|
||||
base64_encode($data);
|
||||
$data = array('token' => $token);
|
||||
$url = API::buildRequest(
|
||||
QueueAPI::ENDPOINT,
|
||||
QueueAPI::ACTION_RUN,
|
||||
$data
|
||||
);
|
||||
$args = array(
|
||||
'timeout' => $timeout,
|
||||
'user-agent' => 'MailPoet (www.mailpoet.com) Cron'
|
||||
);
|
||||
$result = wp_remote_get(
|
||||
self::getSiteUrl() . $url,
|
||||
$args
|
||||
);
|
||||
$result = wp_remote_get($url, $args);
|
||||
return wp_remote_retrieve_body($result);
|
||||
}
|
||||
|
||||
|
@@ -30,7 +30,6 @@ class Daemon {
|
||||
|
||||
function run() {
|
||||
$daemon = $this->daemon;
|
||||
set_time_limit(0);
|
||||
if(!$daemon) {
|
||||
$this->abortWithError(__('Daemon does not exist.'));
|
||||
}
|
||||
@@ -39,6 +38,8 @@ class Daemon {
|
||||
) {
|
||||
$this->abortWithError(__('Invalid or missing token.'));
|
||||
}
|
||||
$daemon['token'] = $this->token;
|
||||
CronHelper::saveDaemon($daemon);
|
||||
$this->abortIfStopped($daemon);
|
||||
try {
|
||||
$scheduler = new SchedulerWorker($this->timer);
|
||||
@@ -55,27 +56,25 @@ class Daemon {
|
||||
// after each execution, re-read daemon data in case its status was changed
|
||||
// its status has changed
|
||||
$daemon = CronHelper::getDaemon();
|
||||
if(!$daemon || $daemon['token'] !== $this->data['token']) {
|
||||
self::terminate();
|
||||
if(!$daemon || $daemon['token'] !== $this->token) {
|
||||
$this->terminateRequest();
|
||||
}
|
||||
$daemon['counter']++;
|
||||
$this->abortIfStopped($daemon);
|
||||
if($daemon['status'] === self::STATUS_STARTING) {
|
||||
$daemon['status'] = self::STATUS_STARTED;
|
||||
}
|
||||
$daemon['token'] = $this->token;
|
||||
CronHelper::saveDaemon($daemon);
|
||||
$this->callSelf();
|
||||
}
|
||||
|
||||
function abortIfStopped($daemon) {
|
||||
if($daemon['status'] === self::STATUS_STOPPED) {
|
||||
self::terminate();
|
||||
$this->terminateRequest();
|
||||
}
|
||||
if($daemon['status'] === self::STATUS_STOPPING) {
|
||||
$daemon['status'] = self::STATUS_STOPPED;
|
||||
CronHelper::saveDaemon($daemon);
|
||||
self::terminate();
|
||||
$this->terminateRequest();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,10 +84,10 @@ class Daemon {
|
||||
|
||||
function callSelf() {
|
||||
CronHelper::accessDaemon($this->token, self::REQUEST_TIMEOUT);
|
||||
self::terminate();
|
||||
$this->terminateRequest();
|
||||
}
|
||||
|
||||
function terminate() {
|
||||
function terminateRequest() {
|
||||
exit;
|
||||
}
|
||||
}
|
@@ -1,13 +1,15 @@
|
||||
<?php
|
||||
namespace MailPoet\Newsletter\Links;
|
||||
|
||||
use MailPoet\API\API;
|
||||
use MailPoet\API\Endpoints\Track as TrackAPI;
|
||||
use MailPoet\Models\NewsletterLink;
|
||||
use MailPoet\Newsletter\Shortcodes\Shortcodes;
|
||||
use MailPoet\Util\Helpers;
|
||||
use MailPoet\Util\Security;
|
||||
|
||||
class Links {
|
||||
const DATA_TAG = '[mailpoet_data]';
|
||||
const DATA_TAG_CLICK = '[mailpoet_click_data]';
|
||||
const DATA_TAG_OPEN = '[mailpoet_open_data]';
|
||||
const HASH_LENGTH = 5;
|
||||
|
||||
static function extract($content) {
|
||||
@@ -32,7 +34,7 @@ class Links {
|
||||
// extract shortcodes with [link:*] format
|
||||
$shortcodes = new Shortcodes();
|
||||
$shortcodes = $shortcodes->extract($content, $categories = array('link'));
|
||||
$extracted_links = array_map(function($shortcode) {
|
||||
$extracted_links = array_map(function ($shortcode) {
|
||||
return array(
|
||||
'html' => $shortcode,
|
||||
'link' => $shortcode
|
||||
@@ -61,13 +63,9 @@ class Links {
|
||||
'hash' => $hash,
|
||||
'url' => $extracted_link['link']
|
||||
);
|
||||
$params = array(
|
||||
'mailpoet' => '',
|
||||
'endpoint' => 'track',
|
||||
'action' => 'click',
|
||||
'data' => self::DATA_TAG . '-' . $hash
|
||||
);
|
||||
$tracked_link = add_query_arg($params, home_url());
|
||||
// replace link with a temporary data tag + hash
|
||||
// it will be further replaced with the proper track API URL during sending
|
||||
$tracked_link = self::DATA_TAG_CLICK . '-' . $hash;
|
||||
// first, replace URL in the extracted HTML source with encoded link
|
||||
$tracked_link_html_source = str_replace(
|
||||
$extracted_link['link'], $tracked_link,
|
||||
@@ -99,12 +97,17 @@ class Links {
|
||||
$queue_id,
|
||||
$content
|
||||
) {
|
||||
$regex = sprintf('/data=(%s(?:-\w+)?)/', preg_quote(self::DATA_TAG));
|
||||
preg_match_all($regex, $content, $links);
|
||||
foreach($links[1] as $link) {
|
||||
// match data tags
|
||||
$regex = sprintf(
|
||||
'/((%s|%s)(?:-\w+)?)/',
|
||||
preg_quote(self::DATA_TAG_CLICK),
|
||||
preg_quote(self::DATA_TAG_OPEN)
|
||||
);
|
||||
preg_match_all($regex, $content, $matches);
|
||||
foreach($matches[1] as $index => $match) {
|
||||
$hash = null;
|
||||
if(preg_match('/-/', $link)) {
|
||||
list(, $hash) = explode('-', $link);
|
||||
if(preg_match('/-/', $match)) {
|
||||
list(, $hash) = explode('-', $match);
|
||||
}
|
||||
$data = array(
|
||||
'newsletter' => $newsletter_id,
|
||||
@@ -112,8 +115,15 @@ class Links {
|
||||
'queue' => $queue_id,
|
||||
'hash' => $hash
|
||||
);
|
||||
$data = rtrim(base64_encode(serialize($data)), '=');
|
||||
$content = str_replace($link, $data, $content);
|
||||
$API_action = ($matches[2][$index] === self::DATA_TAG_CLICK) ?
|
||||
TrackAPI::ACTION_CLICK :
|
||||
TrackAPI::ACTION_OPEN;
|
||||
$link = API::buildRequest(
|
||||
TrackAPI::ENDPOINT,
|
||||
$API_action,
|
||||
$data
|
||||
);
|
||||
$content = str_replace($match, $link, $content);
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
@@ -129,4 +139,4 @@ class Links {
|
||||
$newsletter_link->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -9,10 +9,12 @@ class OpenTracking {
|
||||
$DOM = new \pQuery();
|
||||
$DOM = $DOM->parseStr($template);
|
||||
$template = $DOM->query('body');
|
||||
// url is a temporary data tag that will be further replaced with
|
||||
// the proper track API URL during sending
|
||||
$url = Links::DATA_TAG_OPEN;
|
||||
$open_tracking_image = sprintf(
|
||||
'<img alt="" class="" src="%s/%s"/>',
|
||||
home_url(),
|
||||
esc_attr('?mailpoet&endpoint=track&action=open&data=' . Links::DATA_TAG)
|
||||
'<img alt="" class="" src="%s"/>',
|
||||
$url
|
||||
);
|
||||
$template->html($template->html() . $open_tracking_image);
|
||||
return $DOM->__toString();
|
||||
@@ -24,4 +26,4 @@ class OpenTracking {
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,6 +1,8 @@
|
||||
<?php
|
||||
namespace MailPoet\Newsletter;
|
||||
|
||||
use MailPoet\API\API;
|
||||
use MailPoet\API\Endpoints\ViewInBrowser as ViewInBrowserAPI;
|
||||
use MailPoet\Models\Subscriber;
|
||||
|
||||
class Url {
|
||||
@@ -32,11 +34,10 @@ class Url {
|
||||
$queue['id'] :
|
||||
$queue
|
||||
);
|
||||
$params = array(
|
||||
'mailpoet' => '',
|
||||
'endpoint' => 'view_in_browser',
|
||||
'data' => base64_encode(serialize($data))
|
||||
return API::buildRequest(
|
||||
ViewInBrowserAPI::ENDPOINT,
|
||||
ViewInBrowserAPI::ACTION_VIEW,
|
||||
$data
|
||||
);
|
||||
return add_query_arg($params, home_url());
|
||||
}
|
||||
}
|
@@ -70,7 +70,7 @@ class ViewInBrowser {
|
||||
}
|
||||
|
||||
private function abort() {
|
||||
header('HTTP/1.0 404 Not Found');
|
||||
status_header(404);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
@@ -89,7 +89,7 @@ class Clicks {
|
||||
}
|
||||
|
||||
function abort() {
|
||||
header('HTTP/1.0 404 Not Found');
|
||||
status_header(404);
|
||||
exit;
|
||||
}
|
||||
|
||||
|
@@ -1,8 +1,10 @@
|
||||
<?php
|
||||
namespace MailPoet\Subscription;
|
||||
|
||||
use \MailPoet\Models\Subscriber;
|
||||
use \MailPoet\Models\Setting;
|
||||
use MailPoet\API\API;
|
||||
use MailPoet\API\Endpoints\Subscription;
|
||||
use MailPoet\Models\Subscriber;
|
||||
use MailPoet\Models\Setting;
|
||||
|
||||
class Url {
|
||||
static function getConfirmationUrl($subscriber = false) {
|
||||
@@ -43,9 +45,10 @@ class Url {
|
||||
}
|
||||
|
||||
$params = array(
|
||||
'endpoint=subscription',
|
||||
API::NAME,
|
||||
'endpoint='.Subscription::ENDPOINT,
|
||||
'action='.$action,
|
||||
'data='.rtrim(base64_encode(serialize($data)), '=')
|
||||
'data='.API::encodeRequestData($data)
|
||||
);
|
||||
|
||||
// add parameters
|
||||
|
Reference in New Issue
Block a user