Rework 'Send with...' tab UI, make a single license key field [MAILPOET-890]

This commit is contained in:
stoletniy
2017-05-18 16:45:42 +03:00
parent 2e31e3d37c
commit 7aa0f21d11
13 changed files with 211 additions and 152 deletions

View File

@ -38,6 +38,7 @@
font-weight bold
.mailpoet_active
.mailpoet_status
background-color #088b00
span
visibility visible
#mailpoet_mta_activate
@ -52,7 +53,7 @@
margin 0 -6px -4px 0
// premium key
.mailpoet_premium_key
.mailpoet_key
&_valid
&::before
content ' '

View File

@ -18,6 +18,11 @@ define('modal', ['mailpoet', 'jquery'],
// loading mode
MailPoet.Modal.loading(bool);
// loading mode for parallel requests:
// if called N times to show loading modal,
// should be called N times to hide loading modal
MailPoet.Modal.parallelLoading(bool);
***************************************************************************/
MailPoet.Modal = {
@ -28,6 +33,9 @@ define('modal', ['mailpoet', 'jquery'],
opened: false,
locked: false,
// used by parallel loading mode
requestsCount: 0,
// previously focused element
prevFocus: null,
@ -517,6 +525,27 @@ define('modal', ['mailpoet', 'jquery'],
return this;
},
parallelLoading: function(toggle) {
// make sure the overlay is initialized and that it's visible
this.initOverlay(true);
// handle loading for parallel requests
if(toggle === true) {
if(this.requestsCount == 0) {
this.showLoading();
}
this.requestsCount++;
} else {
if(this.requestsCount <= 1) {
this.hideLoading();
this.requestsCount = 0;
} else {
this.requestsCount--;
}
}
return this;
},
showLoading: function() {
jQuery('#mailpoet_loading').show();

View File

@ -15,6 +15,7 @@ define(
MailPoet.Router = new (Backbone.Router.extend({
routes: {
'': 'sendingMethodGroup', // the default tab is currently mta, needs its own method
'mta(/:group)': 'sendingMethodGroup',
'(:tab)': 'tabs',
},
@ -32,7 +33,7 @@ define(
if(group === null) {
// show sending methods
jQuery('.mailpoet_sending_methods').fadeIn();
jQuery('.mailpoet_sending_methods, .mailpoet_sending_methods_help').fadeIn();
} else {
// toggle SPF (hidden if the sending method is MailPoet)
jQuery('#mailpoet_mta_spf')[
@ -42,7 +43,7 @@ define(
]();
// hide sending methods
jQuery('.mailpoet_sending_methods').hide();
jQuery('.mailpoet_sending_methods, .mailpoet_sending_methods_help').hide();
// display selected sending method's settings
jQuery('.mailpoet_sending_method[data-group="'+ group +'"]').show();
@ -51,7 +52,7 @@ define(
},
tabs: function(tab, section) {
// set default tab
tab = tab || 'basics';
tab = tab || 'mta';
// reset all active tabs
jQuery('.nav-tab-wrapper a').removeClass('nav-tab-active');

View File

@ -39,10 +39,10 @@ class Services extends APIEndpoint {
$success_message = null;
if($state == Bridge::MAILPOET_KEY_VALID) {
$success_message = __('Your MailPoet API key is valid!', 'mailpoet');
$success_message = __('Your MailPoet Sending Service key has been successfully validated.', 'mailpoet');
} elseif($state == Bridge::MAILPOET_KEY_EXPIRING) {
$success_message = sprintf(
__('Your MailPoet key expires on %s!', 'mailpoet'),
__('Your MailPoet Sending Service key expires on %s!', 'mailpoet'),
$this->date_time->formatDate(strtotime($result['data']['expire_at']))
);
}
@ -53,12 +53,12 @@ class Services extends APIEndpoint {
switch($state) {
case Bridge::MAILPOET_KEY_INVALID:
$error = __('Your MailPoet key is invalid!', 'mailpoet');
$error = __('Your MailPoet Sending Service key is invalid.', 'mailpoet');
break;
default:
$code = !empty($result['code']) ? $result['code'] : Bridge::CHECK_ERROR_UNKNOWN;
$error = sprintf(
__('Error validating API key, please try again later (code: %s)', 'mailpoet'),
__('Error validating MailPoet Sending Service key, please try again later (code: %s)', 'mailpoet'),
$code
);
break;
@ -88,10 +88,10 @@ class Services extends APIEndpoint {
$success_message = null;
if($state == Bridge::PREMIUM_KEY_VALID) {
$success_message = __('Your license key has been successfully validated.', 'mailpoet');
$success_message = __('Your Premium key has been successfully validated.', 'mailpoet');
} elseif($state == Bridge::PREMIUM_KEY_EXPIRING) {
$success_message = sprintf(
__('Your license key expires on %s.', 'mailpoet'),
__('Your Premium key expires on %s.', 'mailpoet'),
$this->date_time->formatDate(strtotime($result['data']['expire_at']))
);
}
@ -106,15 +106,15 @@ class Services extends APIEndpoint {
switch($state) {
case Bridge::PREMIUM_KEY_INVALID:
$error = __('Your license key is invalid.', 'mailpoet');
$error = __('Your Premium key is invalid.', 'mailpoet');
break;
case Bridge::PREMIUM_KEY_ALREADY_USED:
$error = __('Your license key is already used on another site.', 'mailpoet');
$error = __('Your Premium key is already used on another site.', 'mailpoet');
break;
default:
$code = !empty($result['code']) ? $result['code'] : Bridge::CHECK_ERROR_UNKNOWN;
$error = sprintf(
__('Error validating license key, please try again later (code: %s)', 'mailpoet'),
__('Error validating Premium key, please try again later (code: %s)', 'mailpoet'),
$code
);
break;

View File

@ -312,6 +312,7 @@ class Menu {
'total_subscribers' => Subscriber::getTotalSubscribers(),
'premium_plugin_active' => License::getLicense(),
'premium_key_valid' => isset($this->premium_key_valid) ? $this->premium_key_valid : null,
'mss_key_valid' => isset($this->mp_api_key_valid) ? $this->mp_api_key_valid : null,
'pages' => Pages::getAll(),
'flags' => $flags,
'current_user' => wp_get_current_user(),

View File

@ -14,7 +14,7 @@ if(!defined('ABSPATH')) exit;
class ServicesChecker {
function isMailPoetAPIKeyValid($display_error_notice = true) {
if(!Bridge::isMPSendingServiceEnabled()) {
return null;
$display_error_notice = false;
}
$mss_key = Setting::getValue(Bridge::API_KEY_STATE_SETTING_NAME);

View File

@ -8,6 +8,7 @@ use MailPoet\Models\Subscriber;
if(!defined('ABSPATH')) exit;
class Bridge {
const API_KEY_SETTING_NAME = 'mta.mailpoet_api_key';
const API_KEY_STATE_SETTING_NAME = 'mta.mailpoet_api_key_state';
const PREMIUM_KEY_SETTING_NAME = 'premium.premium_key';
@ -76,6 +77,14 @@ class Bridge {
$key_state = self::MAILPOET_KEY_CHECK_ERROR;
}
// store the key itself
if($update_settings) {
Setting::setValue(
self::API_KEY_SETTING_NAME,
$this->api->getKey()
);
}
return $this->buildKeyState(
$key_state,
$result,
@ -112,6 +121,14 @@ class Bridge {
$key_state = self::PREMIUM_KEY_CHECK_ERROR;
}
// store the key itself
if($update_settings) {
Setting::setValue(
self::PREMIUM_KEY_SETTING_NAME,
$this->api->getKey()
);
}
return $this->buildKeyState(
$key_state,
$result,
@ -157,10 +174,12 @@ class Bridge {
function onSettingsSave($settings) {
$api_key_set = !empty($settings[Mailer::MAILER_CONFIG_SETTING_NAME]['mailpoet_api_key']);
$premium_key_set = !empty($settings['premium']['premium_key']);
if($api_key_set && self::isMPSendingServiceEnabled()) {
if($api_key_set) {
$result = $this->checkMSSKey($settings[Mailer::MAILER_CONFIG_SETTING_NAME]['mailpoet_api_key']);
if(self::isMPSendingServiceEnabled()) {
$this->updateSubscriberCount($result);
}
}
if($premium_key_set) {
$this->checkPremiumKey($settings['premium']['premium_key']);
}

View File

@ -11,10 +11,10 @@ class ServicesCheckerTest extends MailPoetTest {
$this->fillPremiumKey();
}
function testItDoesNotCheckMSSKeyIfMPSendingServiceIsDisabled() {
function testItChecksMSSKeyIfMPSendingServiceIsDisabled() {
$this->disableMailPoetSendingMethod();
$result = ServicesChecker::isMailPoetAPIKeyValid();
expect($result)->null();
expect($result)->true();
}
function testItReturnsTrueIfMSSKeyIsValid() {

View File

@ -46,19 +46,26 @@ class BridgeTest extends MailPoetTest {
expect($this->bridge->api instanceof API)->true();
}
function testItChecksMSSKey() {
function testItChecksValidMSSKey() {
$result = $this->bridge->checkMSSKey($this->valid_key);
expect($result)->notEmpty();
expect($result['state'])->equals(Bridge::MAILPOET_KEY_VALID);
expect($this->getMSSKey())->equals($this->valid_key);
}
function testItChecksInvalidMSSKey() {
$result = $this->bridge->checkMSSKey($this->invalid_key);
expect($result)->notEmpty();
expect($result['state'])->equals(Bridge::MAILPOET_KEY_INVALID);
expect($this->getMSSKey())->equals($this->invalid_key);
}
function testItChecksExpiingMSSKey() {
$result = $this->bridge->checkMSSKey($this->expiring_key);
expect($result)->notEmpty();
expect($result['state'])->equals(Bridge::MAILPOET_KEY_EXPIRING);
expect($result['data']['expire_at'])->notEmpty();
expect($this->getMSSKey())->equals($this->expiring_key);
}
function testItReturnsErrorStateOnEmptyAPIResponseCodeDuringMSSCheck() {
@ -67,6 +74,7 @@ class BridgeTest extends MailPoetTest {
$result = $this->bridge->checkMSSKey($this->valid_key);
expect($result)->notEmpty();
expect($result['state'])->equals(Bridge::MAILPOET_KEY_CHECK_ERROR);
expect($this->getMSSKey())->notEquals($this->valid_key);
}
function testItChecksPremiumKey() {
@ -150,6 +158,10 @@ class BridgeTest extends MailPoetTest {
);
}
private function getMSSKey() {
return Setting::getValue(Bridge::API_KEY_SETTING_NAME);
}
private function fillPremiumKey() {
Setting::setValue(
Bridge::PREMIUM_KEY_SETTING_NAME,

View File

@ -34,6 +34,10 @@ class MockAPI {
$this->api_key = $api_key;
}
function getKey() {
return $this->api_key;
}
private function processAPICheckResponse($code) {
switch($code) {
case 200:

View File

@ -15,13 +15,18 @@
>
<!-- tabs -->
<h2 class="nav-tab-wrapper" id="mailpoet_settings_tabs">
<a class="nav-tab" href="#mta"><%= __('Send With...') %></a>
<a class="nav-tab" href="#basics"><%= __('Basics') %></a>
<a class="nav-tab" href="#signup"><%= __('Sign-up Confirmation') %></a>
<a class="nav-tab" href="#mta"><%= __('Send With...') %></a>
<a class="nav-tab" href="#advanced"><%= __('Advanced') %></a>
<# <a class="nav-tab" href="#premium"><%= __('Premium') %></a> #>
<a class="nav-tab" href="#premium"><%= __('Premium') %></a>
</h2>
<!-- sending method -->
<div data-tab="mta" class="mailpoet_panel">
<% include 'settings/mta.html' %>
</div>
<!-- basics -->
<div data-tab="basics" class="mailpoet_panel">
<% include 'settings/basics.html' %>
@ -32,20 +37,15 @@
<% include 'settings/signup.html' %>
</div>
<!-- sending method -->
<div data-tab="mta" class="mailpoet_panel">
<% include 'settings/mta.html' %>
</div>
<!-- advanced -->
<div data-tab="advanced" class="mailpoet_panel">
<% include 'settings/advanced.html' %>
</div>
<# <!-- premium -->
<!-- premium -->
<div data-tab="premium" class="mailpoet_panel">
<% include 'settings/premium.html' %>
</div> #>
</div>
<p class="submit mailpoet_settings_submit" style="display:none;">
<input
@ -68,9 +68,12 @@
if ($('.mailpoet_mta_setup_save').is(':visible')) {
$('.mailpoet_mta_setup_save').trigger('click');
}
<# if ($('#mailpoet_premium_key').val().length > 0) {
var mailpoet_premium_key = $('#mailpoet_premium_key').val();
// sync mss key with premium key
$('#mailpoet_api_key').val(mailpoet_premium_key);
if (mailpoet_premium_key.length > 0) {
$('#mailpoet_premium_key_verify').trigger('click');
} #>
}
saveSettings();
return false;
});

View File

@ -39,6 +39,14 @@
value="<%= settings.mta.frequency.interval %>"
/>
<!-- mta: mailpoet sending service key -->
<input
type="hidden"
id="mailpoet_api_key"
name="mta[mailpoet_api_key]"
value="<%=- settings.mta.mailpoet_api_key -%>"
/>
<!-- smtp: available sending methods -->
<ul class="mailpoet_sending_methods clearfix">
<li
@ -69,14 +77,13 @@
>
<strong><%= __("Solve all of your sending problems!") %></strong>
<br />
<%= __("We offer affordable email packages with speeds up to 50 times faster than the competition.") %>
<%= __("Let MailPoet send your emails and get the Premium features for as little as 10 dollars or euros per month.") %>
<br/>
<br/>
<a
href="https://account.mailpoet.com?s=<%= total_subscribers %>&utm_source=plugin&utm_campaign=purchase&utm_medium=settings"
href="<%= admin_url('admin.php?page=mailpoet-premium') %>"
class="button button-primary"
target="_blank"
><%= __('View Email Plans') %></a>
><%= __('Find out more') %></a>
</p>
<div class="mailpoet_status">
@ -84,9 +91,10 @@
</div>
<div class="mailpoet_actions">
<a
class="button-secondary"
href="#mta/mailpoet"><%= __('Configure') %></a>
<button
class="mailpoet_sending_service_activate button-secondary"
<% if(settings.mta_group == 'mailpoet' or not(mss_key_valid)) %> disabled="disabled"<% endif %>
><%= __('Activate') %></button>
</div>
</li>
<li
@ -135,40 +143,17 @@
</li>
</ul>
<div id="mailpoet_sending_method_setup">
<!-- Sending Method: MailPoet -->
<div
class="mailpoet_sending_method"
data-group="mailpoet"
style="display:none;"
>
<h3><%= __('Already have a key?') %></h3>
<table class="form-table">
<tbody>
<tr>
<th scope="row">
<label for="mailpoet_api_key">
<%= __('Your key') %>
</label>
</th>
<td>
<input
type="text"
class="regular-text"
id="mailpoet_api_key"
name="mta[mailpoet_api_key]"
value="<%=- settings.mta.mailpoet_api_key -%>"
/>
<a
id="mailpoet_api_key_verify"
class="button-secondary"
><%= __('Verify') %></a>
</td>
</tr>
</tbody>
</table>
</div>
<p class="mailpoet_sending_methods_help">
<%= __("Need help to pick? [link]Check out the comparison table of sending methods[/link].")
|replace({
'[link]': '<a target="_blank" href="http://beta.docs.mailpoet.com/article/181-comparison-table-of-sending-methods">',
'[/link]': '</a>'
})
|raw
%>
</p>
<div id="mailpoet_sending_method_setup">
<!-- Sending Method: Website -->
<div
class="mailpoet_sending_method"
@ -737,46 +722,6 @@
});
});
// verifying api key
$('#mailpoet_api_key_verify').on('click', function() {
// get api key
var key = $('#mailpoet_api_key').val();
if(key.length === 0) {
// validation
return MailPoet.Notice.error(
'<%= __('Please specify an API key before validating it.') | escape('js') %>',
{ scroll: true, static: true }
);
}
MailPoet.Modal.loading(true);
MailPoet.Ajax.post({
api_version: window.mailpoet_api_version,
endpoint: 'services',
action: 'checkMSSKey',
data: {
key: key
}
}).always(function() {
MailPoet.Modal.loading(false);
}).done(function(response) {
// Hide server error notices
$('.mailpoet_notice_server').hide();
MailPoet.Notice.success(
response.data.message,
{ scroll: true }
);
}).fail(function(response) {
if (response.errors.length > 0) {
MailPoet.Notice.error(
response.errors.map(function(error) { return error.message; }),
{ scroll: true }
);
}
});
});
// sending frequency update based on selected provider
$('#mailpoet_smtp_provider').on('change keyup', setProviderForm);
$('#mailpoet_web_host').on('change keyup', renderHostSendingFrequency);
@ -797,10 +742,18 @@
});
// save configuration of a sending method
$('.mailpoet_sending_service_activate').on('click', function(e) {
e.preventDefault();
saveSendingMethodConfiguration('mailpoet');
});
$('.mailpoet_mta_setup_save').on('click', function() {
// get selected method
var group = $('.mailpoet_sending_method:visible').data('group'),
emails = $('#'+group+'_frequency_emails').val(),
var group = $('.mailpoet_sending_method:visible').data('group');
saveSendingMethodConfiguration(group);
});
function saveSendingMethodConfiguration(group) {
var emails = $('#'+group+'_frequency_emails').val(),
interval = $('#'+group+'_frequency_interval').val();
// set sending method
@ -809,16 +762,6 @@
"<%= __('You have selected an invalid sending method.') | escape('js') %>"
);
} else {
if(
group === 'mailpoet'
&& $('#mailpoet_api_key').val().trim().length === 0
) {
MailPoet.Notice.error(
"<%= __('You need to specify a MailPoet account key.') | escape('js') %>"
);
return false;
}
// set new sending method active
setSendingMethodGroup(group);
@ -835,7 +778,7 @@
// save settings
$('.mailpoet_settings_submit > input').trigger('click');
}
});
}
function setSignupConfirmation(group) {
if (group === 'mailpoet') {
@ -882,13 +825,10 @@
$('#mailpoet_sending_method_inactive_text')
.toggleClass('mailpoet_hidden', group === 'mailpoet');
if(group === 'mailpoet') {
// Verify key on saving
$('#mailpoet_api_key_verify').trigger('click');
} else {
// Hide server error notices
$('.mailpoet_notice_server').hide();
}
updateMailPoetMethodButton();
}
function getMethodFromGroup(group) {
@ -1078,6 +1018,15 @@
}
});
});
// enable/disable MSS method activate button
function updateMailPoetMethodButton() {
var $ = jQuery;
var group = $('.mailpoet_sending_methods .mailpoet_active').data('group');
var key_invalid = $('.mailpoet_mss_key_valid').hasClass('mailpoet_hidden');
$('.mailpoet_sending_service_activate').prop('disabled', group === 'mailpoet' || key_invalid);
}
</script>
<%= partial(

View File

@ -17,7 +17,7 @@
class="regular-text"
id="mailpoet_premium_key"
name="premium[premium_key]"
value="<%=- settings.premium.premium_key -%>"
value="<%=- settings.premium.premium_key | default(settings.mta.mailpoet_api_key) -%>"
/>
<a
id="mailpoet_premium_key_verify"
@ -25,20 +25,24 @@
><%= __('Verify') %></a>
</div>
<div
class="mailpoet_premium_key_valid mailpoet_success"
<% if not(settings.premium.premium_key) or not(premium_key_valid) %>
style="display: none;"
<% endif %>
class="mailpoet_premium_key_valid mailpoet_key_valid mailpoet_success<% if not(settings.premium.premium_key) or not(premium_key_valid) %> mailpoet_hidden<% endif %>"
>
<%= __('Your license key has been successfully validated.') %>
<%= __('Your Premium key has been successfully validated.') %>
</div>
<div
class="mailpoet_premium_key_invalid mailpoet_error"
<% if not(settings.premium.premium_key) or premium_key_valid %>
style="display: none;"
<% endif %>
class="mailpoet_premium_key_invalid mailpoet_key_invalid mailpoet_error<% if not(settings.premium.premium_key) or premium_key_valid %> mailpoet_hidden<% endif %>"
>
<%= __('Your license key is invalid.') %>
<%= __('Your Premium key is invalid.') %>
</div>
<div
class="mailpoet_mss_key_valid mailpoet_key_valid mailpoet_success<% if not(settings.mta.mailpoet_api_key) or not(mss_key_valid) %> mailpoet_hidden<% endif %>"
>
<%= __('Your MailPoet Sending Service key has been successfully validated.') %>
</div>
<div
class="mailpoet_mss_key_invalid mailpoet_key_invalid mailpoet_error<% if not(settings.mta.mailpoet_api_key) or mss_key_valid %> mailpoet_hidden<% endif %>"
>
<%= __('Your MailPoet Sending Service key is invalid.') %>
</div>
<br/>
<div
@ -66,9 +70,9 @@
<script type="text/javascript">
jQuery(function($) {
$(function() {
// verifying premium key
// verifying license key
$('#mailpoet_premium_key_verify').on('click', function () {
// get premium key
// get license key
var key = $('#mailpoet_premium_key').val();
if(key.length === 0) {
@ -79,10 +83,15 @@
);
}
$('.mailpoet_premium_key_valid, .mailpoet_premium_key_invalid').hide();
verifyMailPoetPremiumKey(key);
verifyMailPoetSendingServiceKey(key);
});
function verifyMailPoetPremiumKey(key) {
$('.mailpoet_premium_key_valid, .mailpoet_premium_key_invalid').addClass('mailpoet_hidden');
$('.mailpoet_premium_download').hide();
MailPoet.Modal.loading(true);
MailPoet.Modal.parallelLoading(true);
MailPoet.Ajax.post({
api_version: window.mailpoet_api_version,
endpoint: 'services',
@ -91,12 +100,12 @@
key: key
}
}).always(function() {
MailPoet.Modal.loading(false);
MailPoet.Modal.parallelLoading(false);
}).done(function(response) {
// Hide server error notices
$('.mailpoet_notice_server').hide();
$('.mailpoet_premium_key_valid').text(response.data.message);
$('.mailpoet_premium_key_valid').show();
$('.mailpoet_premium_key_valid').removeClass('mailpoet_hidden');
if (!response.meta.premium_plugin_active) {
$('.mailpoet_premium_download').show();
}
@ -105,10 +114,41 @@
$('.mailpoet_premium_key_invalid').text(
response.errors.map(function(error) { return error.message; }).join(' ')
);
$('.mailpoet_premium_key_invalid').show();
$('.mailpoet_premium_key_invalid').removeClass('mailpoet_hidden');
}
});
}
function verifyMailPoetSendingServiceKey(key) {
$('.mailpoet_mss_key_valid, .mailpoet_mss_key_invalid').addClass('mailpoet_hidden');
MailPoet.Modal.parallelLoading(true);
MailPoet.Ajax.post({
api_version: window.mailpoet_api_version,
endpoint: 'services',
action: 'checkMSSKey',
data: {
key: key
}
}).always(function() {
MailPoet.Modal.parallelLoading(false);
}).done(function(response) {
// Hide server error notices
$('.mailpoet_notice_server').hide();
$('.mailpoet_mss_key_valid').text(response.data.message);
$('.mailpoet_mss_key_valid').removeClass('mailpoet_hidden');
updateMailPoetMethodButton();
}).fail(function(response) {
if (response.errors.length > 0) {
$('.mailpoet_mss_key_invalid').text(
response.errors.map(function(error) { return error.message; }).join(' ')
);
$('.mailpoet_mss_key_invalid').removeClass('mailpoet_hidden');
updateMailPoetMethodButton();
}
});
}
});
});
</script>