diff --git a/assets/js/src/ajax.js b/assets/js/src/ajax.js index eccef1c790..e76f3ad76d 100644 --- a/assets/js/src/ajax.js +++ b/assets/js/src/ajax.js @@ -27,11 +27,6 @@ define('ajax', ['mailpoet', 'jquery', 'underscore'], function(MailPoet, jQuery, if(this.options.token === null) { this.options.token = window.mailpoet_token; } - - // set default API version - if(this.options.api_version === null) { - this.options.api_version = window.mailpoet_api_version; - } }, getParams: function() { return { diff --git a/assets/js/src/form/form.jsx b/assets/js/src/form/form.jsx index ca61b30d8c..bfffe4eb86 100644 --- a/assets/js/src/form/form.jsx +++ b/assets/js/src/form/form.jsx @@ -64,6 +64,7 @@ define( this.setState({ loading: true }); MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: this.props.endpoint, action: 'get', data: { @@ -112,6 +113,7 @@ define( } MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: this.props.endpoint, action: 'save', data: item diff --git a/assets/js/src/forms/list.jsx b/assets/js/src/forms/list.jsx index 43d3c7f261..979374c58e 100644 --- a/assets/js/src/forms/list.jsx +++ b/assets/js/src/forms/list.jsx @@ -97,6 +97,7 @@ const item_actions = [ label: MailPoet.I18n.t('duplicate'), onClick: function(item, refresh) { return MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'forms', action: 'duplicate', data: { @@ -125,6 +126,7 @@ const item_actions = [ const FormList = React.createClass({ createForm() { MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'forms', action: 'create' }).done((response) => { diff --git a/assets/js/src/listing/listing.jsx b/assets/js/src/listing/listing.jsx index 0af6825e7f..2c28bf5cbb 100644 --- a/assets/js/src/listing/listing.jsx +++ b/assets/js/src/listing/listing.jsx @@ -445,6 +445,7 @@ const Listing = React.createClass({ this.clearSelection(); MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: this.props.endpoint, action: 'listing', data: { @@ -495,6 +496,7 @@ const Listing = React.createClass({ }); MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: this.props.endpoint, action: 'restore', data: { @@ -522,6 +524,7 @@ const Listing = React.createClass({ }); MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: this.props.endpoint, action: 'trash', data: { @@ -549,6 +552,7 @@ const Listing = React.createClass({ }); MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: this.props.endpoint, action: 'delete', data: { @@ -611,6 +615,7 @@ const Listing = React.createClass({ } return MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: this.props.endpoint, action: 'bulkAction', data: data diff --git a/assets/js/src/newsletter_editor/components/communication.js b/assets/js/src/newsletter_editor/components/communication.js index 56368103dc..2b765b8144 100644 --- a/assets/js/src/newsletter_editor/components/communication.js +++ b/assets/js/src/newsletter_editor/components/communication.js @@ -9,6 +9,7 @@ define([ Module._query = function(args) { return MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'automatedLatestContent', action: args.action, data: args.options || {} @@ -81,6 +82,7 @@ define([ Module.saveNewsletter = function(options) { return MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'newsletters', action: 'save', data: options || {} @@ -89,6 +91,7 @@ define([ Module.previewNewsletter = function(options) { return MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'newsletters', action: 'sendPreview', data: options || {} diff --git a/assets/js/src/newsletter_editor/components/save.js b/assets/js/src/newsletter_editor/components/save.js index e09d592455..2c45fa0ba2 100644 --- a/assets/js/src/newsletter_editor/components/save.js +++ b/assets/js/src/newsletter_editor/components/save.js @@ -109,6 +109,7 @@ define([ }); return MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'newsletterTemplates', action: 'save', data: data diff --git a/assets/js/src/newsletter_editor/components/sidebar.js b/assets/js/src/newsletter_editor/components/sidebar.js index 4419b7bf05..cbc57def62 100644 --- a/assets/js/src/newsletter_editor/components/sidebar.js +++ b/assets/js/src/newsletter_editor/components/sidebar.js @@ -243,6 +243,7 @@ define([ MailPoet.Modal.loading(true); MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'newsletters', action: 'showPreview', data: json, diff --git a/assets/js/src/newsletters/listings/mixins.jsx b/assets/js/src/newsletters/listings/mixins.jsx index 3973ff9e65..98555a8815 100644 --- a/assets/js/src/newsletters/listings/mixins.jsx +++ b/assets/js/src/newsletters/listings/mixins.jsx @@ -8,6 +8,7 @@ import jQuery from 'jquery' const _QueueMixin = { pauseSending: function(newsletter) { MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'sendingQueue', action: 'pause', data: { @@ -27,6 +28,7 @@ const _QueueMixin = { }, resumeSending: function(newsletter) { MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'sendingQueue', action: 'resume', data: { @@ -225,6 +227,7 @@ const _MailerMixin = { }, resumeMailerSending() { MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'mailer', action: 'resumeSending' }).done(function() { diff --git a/assets/js/src/newsletters/listings/notification.jsx b/assets/js/src/newsletters/listings/notification.jsx index 1f59fc36d4..256bcf4046 100644 --- a/assets/js/src/newsletters/listings/notification.jsx +++ b/assets/js/src/newsletters/listings/notification.jsx @@ -129,6 +129,7 @@ const newsletter_actions = [ label: MailPoet.I18n.t('duplicate'), onClick: function(newsletter, refresh) { return MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'newsletters', action: 'duplicate', data: { @@ -164,6 +165,7 @@ const NewsletterListNotification = React.createClass({ e.persist(); MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'newsletters', action: 'setStatus', data: { diff --git a/assets/js/src/newsletters/listings/standard.jsx b/assets/js/src/newsletters/listings/standard.jsx index 1c56229585..f399df617e 100644 --- a/assets/js/src/newsletters/listings/standard.jsx +++ b/assets/js/src/newsletters/listings/standard.jsx @@ -124,6 +124,7 @@ const newsletter_actions = [ label: MailPoet.I18n.t('duplicate'), onClick: function(newsletter, refresh) { return MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'newsletters', action: 'duplicate', data: { diff --git a/assets/js/src/newsletters/listings/welcome.jsx b/assets/js/src/newsletters/listings/welcome.jsx index bae199461f..ac4d269089 100644 --- a/assets/js/src/newsletters/listings/welcome.jsx +++ b/assets/js/src/newsletters/listings/welcome.jsx @@ -126,6 +126,7 @@ const newsletter_actions = [ label: MailPoet.I18n.t('duplicate'), onClick: function(newsletter, refresh) { return MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'newsletters', action: 'duplicate', data: { @@ -161,6 +162,7 @@ const NewsletterListWelcome = React.createClass({ e.persist(); MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'newsletters', action: 'setStatus', data: { diff --git a/assets/js/src/newsletters/send.jsx b/assets/js/src/newsletters/send.jsx index 1874b12513..7ccc781b94 100644 --- a/assets/js/src/newsletters/send.jsx +++ b/assets/js/src/newsletters/send.jsx @@ -64,6 +64,7 @@ define( this.setState({ loading: true }); MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'newsletters', action: 'get', data: { @@ -97,6 +98,7 @@ define( case 'notification': case 'welcome': return MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'newsletters', action: 'setStatus', data: { @@ -120,6 +122,7 @@ define( }).fail(this._showError); default: return MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'sendingQueue', action: 'add', data: { @@ -184,6 +187,7 @@ define( ); return MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'newsletters', action: 'save', data: newsletterData, diff --git a/assets/js/src/newsletters/send/notification.jsx b/assets/js/src/newsletters/send/notification.jsx index 539616778c..d66f637f4a 100644 --- a/assets/js/src/newsletters/send/notification.jsx +++ b/assets/js/src/newsletters/send/notification.jsx @@ -37,8 +37,9 @@ define( tip: MailPoet.I18n.t('segmentsTip'), type: 'selection', placeholder: MailPoet.I18n.t('selectSegmentPlaceholder'), - id: "mailpoet_segments", - endpoint: "segments", + id: 'mailpoet_segments', + api_version: window.mailpoet_api_version, + endpoint: 'segments', multiple: true, filter: function(segment) { return !!(!segment.deleted_at); diff --git a/assets/js/src/newsletters/send/standard.jsx b/assets/js/src/newsletters/send/standard.jsx index fcf5ece48b..a8187521b8 100644 --- a/assets/js/src/newsletters/send/standard.jsx +++ b/assets/js/src/newsletters/send/standard.jsx @@ -341,8 +341,9 @@ define( tip: MailPoet.I18n.t('segmentsTip'), type: 'selection', placeholder: MailPoet.I18n.t('selectSegmentPlaceholder'), - id: "mailpoet_segments", - endpoint: "segments", + id: 'mailpoet_segments', + api_version: window.mailpoet_api_version, + endpoint: 'segments', multiple: true, filter: function(segment) { return !!(!segment.deleted_at); diff --git a/assets/js/src/newsletters/templates.jsx b/assets/js/src/newsletters/templates.jsx index 5ef22ff2a6..f292c5a0ff 100644 --- a/assets/js/src/newsletters/templates.jsx +++ b/assets/js/src/newsletters/templates.jsx @@ -27,6 +27,7 @@ define( MailPoet.Modal.loading(true); MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'newsletterTemplates', action: 'save', data: template @@ -97,6 +98,7 @@ define( MailPoet.Modal.loading(true); MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'newsletterTemplates', action: 'getAll', }).always(() => { @@ -130,6 +132,7 @@ define( } MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'newsletters', action: 'save', data: { @@ -158,6 +161,7 @@ define( ) ) { MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'newsletterTemplates', action: 'delete', data: { diff --git a/assets/js/src/newsletters/types.jsx b/assets/js/src/newsletters/types.jsx index 65cadff270..dca520ac69 100644 --- a/assets/js/src/newsletters/types.jsx +++ b/assets/js/src/newsletters/types.jsx @@ -22,6 +22,7 @@ define( }, createNewsletter: function(type) { MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'newsletters', action: 'create', data: { diff --git a/assets/js/src/newsletters/types/notification/notification.jsx b/assets/js/src/newsletters/types/notification/notification.jsx index 591eea0955..10210b7a02 100644 --- a/assets/js/src/newsletters/types/notification/notification.jsx +++ b/assets/js/src/newsletters/types/notification/notification.jsx @@ -44,6 +44,7 @@ define( }, handleNext: function() { MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'newsletters', action: 'create', data: _.extend({}, this.state, { diff --git a/assets/js/src/newsletters/types/standard.jsx b/assets/js/src/newsletters/types/standard.jsx index 5762902826..9d776a0c66 100644 --- a/assets/js/src/newsletters/types/standard.jsx +++ b/assets/js/src/newsletters/types/standard.jsx @@ -22,6 +22,7 @@ define( componentDidMount: function() { // No options for this type, create a newsletter upon mounting MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'newsletters', action: 'create', data: { diff --git a/assets/js/src/newsletters/types/welcome/scheduling.jsx b/assets/js/src/newsletters/types/welcome/scheduling.jsx index c7e1fa2418..d43593514d 100644 --- a/assets/js/src/newsletters/types/welcome/scheduling.jsx +++ b/assets/js/src/newsletters/types/welcome/scheduling.jsx @@ -104,6 +104,7 @@ const WelcomeScheduling = React.createClass({ }, handleNext: function() { MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'newsletters', action: 'create', data: { diff --git a/assets/js/src/newsletters/types/welcome/welcome.jsx b/assets/js/src/newsletters/types/welcome/welcome.jsx index 18bb917065..78facf4009 100644 --- a/assets/js/src/newsletters/types/welcome/welcome.jsx +++ b/assets/js/src/newsletters/types/welcome/welcome.jsx @@ -52,6 +52,7 @@ define( }, handleNext: function() { MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'newsletters', action: 'create', data: _.extend({}, this.state, { diff --git a/assets/js/src/public.js b/assets/js/src/public.js index 186025c280..4b3d570603 100644 --- a/assets/js/src/public.js +++ b/assets/js/src/public.js @@ -40,8 +40,8 @@ function( // ajax request MailPoet.Ajax.post({ url: MailPoetForm.ajax_url, - api_version: form_data.api_version, token: form_data.token, + api_version: form_data.api_version, endpoint: 'subscribers', action: 'subscribe', data: form_data.data diff --git a/assets/js/src/segments/list.jsx b/assets/js/src/segments/list.jsx index cfc15bc4f1..f27ebfacef 100644 --- a/assets/js/src/segments/list.jsx +++ b/assets/js/src/segments/list.jsx @@ -112,6 +112,7 @@ const item_actions = [ label: MailPoet.I18n.t('duplicate'), onClick: (item, refresh) => { return MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'segments', action: 'duplicate', data: { @@ -153,6 +154,7 @@ const item_actions = [ onClick: function(item, refresh) { MailPoet.Modal.loading(true); MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'segments', action: 'synchronize' }).done(function(response) { diff --git a/assets/js/src/subscribers/form.jsx b/assets/js/src/subscribers/form.jsx index cdccd7e310..7e5964b798 100644 --- a/assets/js/src/subscribers/form.jsx +++ b/assets/js/src/subscribers/form.jsx @@ -60,7 +60,8 @@ define( label: MailPoet.I18n.t('lists'), type: 'selection', placeholder: MailPoet.I18n.t('selectList'), - endpoint: "segments", + api_version: window.mailpoet_api_version, + endpoint: 'segments', multiple: true, selected: function(subscriber) { if (Array.isArray(subscriber.subscriptions) === false) { diff --git a/assets/js/src/subscribers/importExport/export.js b/assets/js/src/subscribers/importExport/export.js index 07989cec42..f17c78273a 100644 --- a/assets/js/src/subscribers/importExport/export.js +++ b/assets/js/src/subscribers/importExport/export.js @@ -138,6 +138,7 @@ define( } MailPoet.Modal.loading(true); MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'ImportExport', action: 'processExport', data: JSON.stringify({ diff --git a/assets/js/src/subscribers/importExport/import.js b/assets/js/src/subscribers/importExport/import.js index d2ae4da01d..e263ed90be 100644 --- a/assets/js/src/subscribers/importExport/import.js +++ b/assets/js/src/subscribers/importExport/import.js @@ -190,6 +190,7 @@ define( mailChimpKeyVerifyButtonElement.click(function () { MailPoet.Modal.loading(true); MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'importExport', action: 'getMailChimpLists', data: { @@ -225,6 +226,7 @@ define( } MailPoet.Modal.loading(true); MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'importExport', action: 'getMailChimpSubscribers', data: { @@ -572,6 +574,7 @@ define( var segmentDescription = jQuery('#new_segment_description').val().trim(); MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'ImportExport', action: 'addSegment', data: { @@ -729,6 +732,7 @@ define( // save custom field MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'customFields', action: 'save', data: data @@ -990,6 +994,7 @@ define( queue.add(function(queue) { queue.pause(); MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'ImportExport', action: 'processImport', data: JSON.stringify({ diff --git a/assets/js/src/subscribers/list.jsx b/assets/js/src/subscribers/list.jsx index 7f3d1dd279..cb263a5501 100644 --- a/assets/js/src/subscribers/list.jsx +++ b/assets/js/src/subscribers/list.jsx @@ -91,6 +91,7 @@ const bulk_actions = [ onSelect: function() { let field = { id: 'move_to_segment', + api_version: window.mailpoet_api_version, endpoint: 'segments', filter: function(segment) { return !!( @@ -122,6 +123,7 @@ const bulk_actions = [ onSelect: function() { let field = { id: 'add_to_segment', + api_version: window.mailpoet_api_version, endpoint: 'segments', filter: function(segment) { return !!( @@ -153,6 +155,7 @@ const bulk_actions = [ onSelect: function() { let field = { id: 'remove_from_segment', + api_version: window.mailpoet_api_version, endpoint: 'segments', filter: function(segment) { return !!( @@ -255,7 +258,7 @@ const SubscriberList = React.createClass({ case 'unsubscribed': status = MailPoet.I18n.t('unsubscribed'); break; - + case 'bounced': status = MailPoet.I18n.t('bounced'); break; diff --git a/lib/API/API.php b/lib/API/API.php index f70f0f257e..e1ec585bcb 100644 --- a/lib/API/API.php +++ b/lib/API/API.php @@ -1,6 +1,7 @@ addEndpointNamespace(__NAMESPACE__ . "\\Endpoints"); + foreach($this->_available_api_versions as $version) { + $this->addEndpointNamespace( + array( + 'name' => sprintf('%s\%s', __NAMESPACE__, self::ENDPOINTS_LOCATION), + 'version' => $version + ) + ); + } } function init() { @@ -44,19 +55,14 @@ class API { } function setupAjax() { + $this->_ajax_request = true; Hooks::doAction('mailpoet_api_setup', array($this)); - $this->getRequestData($_POST); if($this->checkToken() === false) { - $error_response = new ErrorResponse( - array( - Error::UNAUTHORIZED => __('Invalid request', 'mailpoet') - ), - array(), - Response::STATUS_UNAUTHORIZED - ); - $error_response->send(); + $error_message = __('Invalid API request.', 'mailpoet'); + $error_response = $this->createErrorResponse(Error::UNAUTHORIZED, $error_message, Response::STATUS_UNAUTHORIZED); + return $error_response; } $response = $this->processRoute(); @@ -64,61 +70,56 @@ class API { } function getRequestData($data) { - $this->_api_version = sprintf( - 'v%s', - isset($data['api_version']) - ? $data['api_version'] - : self::CURRENT_VERSION - ); + $this->_request_api_version = !empty($data['api_version']) ? $data['api_version']: false; - $this->_endpoint = isset($data['endpoint']) + $this->_request_endpoint = isset($data['endpoint']) ? Helpers::underscoreToCamelCase(trim($data['endpoint'])) : null; // JS part of /wp-admin/customize.php does not like a 'method' field in a form widget $method_param_name = isset($data['mailpoet_method']) ? 'mailpoet_method' : 'method'; - $this->_method = isset($data[$method_param_name]) + $this->_request_method = isset($data[$method_param_name]) ? Helpers::underscoreToCamelCase(trim($data[$method_param_name])) : null; - $this->_token = isset($data['token']) + $this->_request_token = isset($data['token']) ? trim($data['token']) : null; - if(!$this->_endpoint || !$this->_method) { - // throw exception bad request - $error_response = new ErrorResponse( - array( - Error::BAD_REQUEST => __('Invalid request', 'mailpoet') - ), - array(), - Response::STATUS_BAD_REQUEST - ); - $error_response->send(); + if(!$this->_request_endpoint || !$this->_request_method || !$this->_request_api_version) { + $error_message = __('Invalid API request.', 'mailpoet'); + $error_response = $this->createErrorResponse(Error::BAD_REQUEST, $error_message, Response::STATUS_BAD_REQUEST); + return $error_response; } else { foreach($this->_endpoint_namespaces as $namespace) { - $class_name = $namespace . "\\" . $this->_api_version . "\\" . ucfirst($this->_endpoint); - if(class_exists($class_name)) { - $this->_endpoint_class = $class_name; + if($namespace['version'] !== $this->_request_api_version) continue; + $endpoint_class = sprintf( + '%s\%s\%s', + $namespace['name'], + $namespace['version'], + ucfirst($this->_request_endpoint) + ); + if(class_exists($endpoint_class)) { + $this->_request_endpoint_class = $endpoint_class; } } - - $this->_data = isset($data['data']) + $this->_request_data = isset($data['data']) ? stripslashes_deep($data['data']) : array(); // remove reserved keywords from data - if(is_array($this->_data) && !empty($this->_data)) { + if(is_array($this->_request_data) && !empty($this->_request_data)) { // filter out reserved keywords from data $reserved_keywords = array( 'token', 'endpoint', 'method', + 'api_version', 'mailpoet_method', // alias of 'method' 'mailpoet_redirect' ); - $this->_data = array_diff_key( - $this->_data, + $this->_request_data = array_diff_key( + $this->_request_data, array_flip($reserved_keywords) ); } @@ -127,57 +128,46 @@ class API { function processRoute() { try { - if(empty($this->_endpoint_class)) { - throw new \Exception('Invalid endpoint'); + if(empty($this->_request_endpoint_class)) { + throw new \Exception(__('Invalid API endpoint.', 'mailpoet')); } - $endpoint = new $this->_endpoint_class(); + $endpoint = new $this->_request_endpoint_class(); // check the accessibility of the requested endpoint's action // by default, an endpoint's action is considered "private" $permissions = $endpoint->permissions; - if( - array_key_exists($this->_method, $permissions) === false - || - $permissions[$this->_method] !== Access::ALL + if(array_key_exists($this->_request_method, $permissions) === false || + $permissions[$this->_request_method] !== Access::ALL ) { if($this->checkPermissions() === false) { - $error_response = new ErrorResponse( - array( - Error::FORBIDDEN => __( - 'You do not have the required permissions.', - 'mailpoet' - ) - ), - array(), - Response::STATUS_FORBIDDEN - ); + $error_message = __('You do not have the required permissions.', 'mailpoet'); + $error_response = $this->createErrorResponse(Error::FORBIDDEN, $error_message, Response::STATUS_FORBIDDEN); return $error_response; } } - $response = $endpoint->{$this->_method}($this->_data); + $response = $endpoint->{$this->_request_method}($this->_request_data); return $response; } catch(\Exception $e) { - $error_response = new ErrorResponse( - array($e->getCode() => $e->getMessage()) - ); + $error_message = $e->getMessage(); + $error_response = $this->createErrorResponse(Error::BAD_REQUEST, $error_message, Response::STATUS_BAD_REQUEST); return $error_response; } } function checkPermissions() { - return current_user_can('manage_options'); + return current_user_can(Env::$required_permission); } function checkToken() { - return wp_verify_nonce($this->_token, 'mailpoet_token'); + return wp_verify_nonce($this->_request_token, 'mailpoet_token'); } function setTokenAndAPIVersion() { $global = ''; echo sprintf( $global, @@ -186,7 +176,10 @@ class API { ); } - function addEndpointNamespace($namespace) { + function addEndpointNamespace(array $namespace) { + if(empty($namespace['version'])) { + throw new \Exception(__('Namespace version is required.', 'mailpoet')); + } $this->_endpoint_namespaces[] = $namespace; } @@ -195,10 +188,21 @@ class API { } function getRequestedEndpointClass() { - return $this->_endpoint_class; + return $this->_request_endpoint_class; } function getRequestedAPIVersion() { - return $this->_api_version; + return $this->_request_api_version; + } + + function createErrorResponse($error_type, $error_message, $response_status) { + $error_response = new ErrorResponse( + array( + $error_type => $error_message + ), + array(), + $response_status + ); + return ($this->_ajax_request === true) ? $error_response->send() : $error_response; } } \ No newline at end of file diff --git a/tests/unit/API/APITest.php b/tests/unit/API/APITest.php index 2a1ebbf823..ab0348a70c 100644 --- a/tests/unit/API/APITest.php +++ b/tests/unit/API/APITest.php @@ -1,7 +1,9 @@ api->getEndpointNamespaces())->count(1); - $namespace = "MailPoet\\Dummy\\Name\\Space"; + $namespace = array( + 'name' => 'MailPoet\\Dummy\\Name\\Space', + 'version' => 'v2' + ); $this->api->addEndpointNamespace($namespace); $namespaces = $this->api->getEndpointNamespaces(); @@ -67,30 +72,39 @@ class APITest extends MailPoetTest { expect($namespaces[1])->equals($namespace); } - function testItDefaultsToCurrentAPIVersionWhenNoVersionIsSpecified() { - $namespace = "MailPoet\\Some\\Name\\Space\\Endpoints"; - $this->api->addEndpointNamespace($namespace); + function testItRequiresNamespaceVersionToBeSpecified() { + $namespace = array( + 'name' => 'MailPoet\\Dummy\\Name\\Space' + ); + try { + $this->api->addEndpointNamespace($namespace); + $this->fail('Exception was not thrown'); + } catch(Exception $e) { + expect($e->getMessage())->equals('Namespace version is required.'); + } + } + + function testItReturns400ErrorWhenAPIVersionIsNotSpecified() { $data = array( 'endpoint' => 'namespaced_endpoint_stub', 'method' => 'test' ); - $this->api->getRequestData($data); - expect($this->api->getRequestedEndpointClass())->equals( - sprintf( - 'MailPoet\API\Endpoints\v%d\NamespacedEndpointStub', - API::CURRENT_VERSION - ) - ); + + $response = $this->api->getRequestData($data); + expect($response->status)->equals(APIResponse::STATUS_BAD_REQUEST); } function testItAcceptsAndProcessesAPIVersion() { - $namespace = "MailPoet\\Some\\Name\\Space\\Endpoints"; + $namespace = array( + 'name' => 'MailPoet\API\Endpoints', + 'version' => 'v2' + ); $this->api->addEndpointNamespace($namespace); $data = array( 'endpoint' => 'namespaced_endpoint_stub', - 'api_version' => 2, + 'api_version' => 'v2', 'method' => 'test' ); $this->api->getRequestData($data); @@ -102,12 +116,16 @@ class APITest extends MailPoetTest { } function testItCallsAddedEndpoints() { - $namespace = "MailPoet\\Some\\Name\\Space\\Endpoints"; + $namespace = array( + 'name' => 'MailPoet\API\Endpoints', + 'version' => 'v1' + ); $this->api->addEndpointNamespace($namespace); $data = array( 'endpoint' => 'namespaced_endpoint_stub', 'method' => 'test', + 'api_version' => 'v1', 'data' => array('test' => 'data') ); $this->api->getRequestData($data); @@ -117,18 +135,21 @@ class APITest extends MailPoetTest { } function testItCallsAddedEndpointsForSpecificAPIVersion() { - $namespace = "MailPoet\\Some\\Name\\Space\\Endpoints"; + $namespace = array( + 'name' => 'MailPoet\API\Endpoints', + 'version' => 'v2' + ); $this->api->addEndpointNamespace($namespace); $data = array( 'endpoint' => 'namespaced_endpoint_stub', - 'api_version' => 2, + 'api_version' => 'v2', 'method' => 'testVersion' ); $this->api->getRequestData($data); $response = $this->api->processRoute(); - expect($response->getData()['data'])->equals('version_test_succeeded'); + expect($response->getData()['data'])->equals($data['api_version']); } function _after() { diff --git a/tests/unit/API/APITestNamespacedEndpointStubV2.php b/tests/unit/API/APITestNamespacedEndpointStubV2.php index 96824812ac..5e00ba8e75 100644 --- a/tests/unit/API/APITestNamespacedEndpointStubV2.php +++ b/tests/unit/API/APITestNamespacedEndpointStubV2.php @@ -13,6 +13,6 @@ class NamespacedEndpointStub extends APIEndpoint { ); function testVersion() { - return $this->successResponse('version_test_succeeded'); + return $this->successResponse('v2'); } } diff --git a/views/form/editor.html b/views/form/editor.html index 970ca17ecb..60593ede98 100644 --- a/views/form/editor.html +++ b/views/form/editor.html @@ -275,6 +275,7 @@ var template = Handlebars.compile($('#form_template_fields').html()); return MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'customFields', action: 'getAll', }).done(function(response) { @@ -403,6 +404,7 @@ // save change if necessary if(new_value !== '' && current_value !== new_value) { MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'forms', action: 'save', data: { @@ -453,6 +455,7 @@ function mailpoet_form_preview() { MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'forms', action: 'previewEditor', data: WysijaForm.save() @@ -496,6 +499,7 @@ } else { // save form MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'forms', action: 'saveEditor', data: form @@ -550,6 +554,7 @@ function mailpoet_form_export() { var template = Handlebars.compile($('#form_template_exports').html()); MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'forms', action: 'exportsEditor', data: { @@ -590,6 +595,7 @@ var id = $(this).data('id'); MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'customFields', action: 'get', data: { @@ -621,6 +627,7 @@ "<%= __('This field will be deleted for all your subscribers. Are you sure?') %>" )) { MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'customFields', action: 'delete', data: { diff --git a/views/form/templates/settings/field_form.hbs b/views/form/templates/settings/field_form.hbs index 90717a4729..61d6927d4e 100644 --- a/views/form/templates/settings/field_form.hbs +++ b/views/form/templates/settings/field_form.hbs @@ -74,6 +74,7 @@ // save custom field MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'customFields', action: 'save', data: data diff --git a/views/newsletter/editor.html b/views/newsletter/editor.html index a982925cea..176d3f3a79 100644 --- a/views/newsletter/editor.html +++ b/views/newsletter/editor.html @@ -1201,6 +1201,7 @@ }; MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'newsletters', action: 'get', data: { diff --git a/views/settings.html b/views/settings.html index a3b4f9a93e..82f379e056 100644 --- a/views/settings.html +++ b/views/settings.html @@ -74,6 +74,7 @@ MailPoet.Modal.loading(true); MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'settings', action: 'set', data: settings_data diff --git a/views/settings/advanced.html b/views/settings/advanced.html index 37c1896744..8f0efa6d6e 100644 --- a/views/settings/advanced.html +++ b/views/settings/advanced.html @@ -172,6 +172,7 @@ )) { MailPoet.Modal.loading(true); MailPoet.Ajax.post({ + 'api_version': window.mailpoet_api_version, 'endpoint': 'setup', 'action': 'reset' }).always(function() { diff --git a/views/settings/mta.html b/views/settings/mta.html index f464c43649..1ed9a8e999 100644 --- a/views/settings/mta.html +++ b/views/settings/mta.html @@ -707,6 +707,7 @@ MailPoet.Modal.loading(true); MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'mailer', action: 'send', data: { @@ -751,6 +752,7 @@ MailPoet.Modal.loading(true); MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: 'services', action: 'verifyMailPoetKey', data: { diff --git a/views/welcome.html b/views/welcome.html index d27f1a90db..1f0eba41ed 100644 --- a/views/welcome.html +++ b/views/welcome.html @@ -109,6 +109,7 @@ jQuery(function($) { $("#mailpoet_analytics_enabled").on("click", function() { var is_enabled = $(this).is(":checked") ? true : ""; MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, endpoint: "settings", action: "set", data: {