Add WooCommerce list import scheduling
[MAILPOET-1732]
This commit is contained in:
committed by
M. Shull
parent
02c74db0ed
commit
58d2bbab1a
@ -121,3 +121,20 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: -1000px;
|
top: -1000px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mailpoet_welcome_wizard_centered_column {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mailpoet_wizard_woocommerce_list {
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
label {
|
||||||
|
color: #595c65;
|
||||||
|
display: block;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,13 +1,80 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import MailPoet from 'mailpoet';
|
import MailPoet from 'mailpoet';
|
||||||
|
import ReactHtmlParser from 'react-html-parser';
|
||||||
|
|
||||||
const WizardWooCommerceImportListStep = props => (
|
|
||||||
<div className="mailpoet_welcome_wizard_step_content mailpoet_welcome_wizard_centered_column">
|
|
||||||
<h1>{MailPoet.I18n.t('wooCommerceListImportTitle')}</h1>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
WizardWooCommerceImportListStep.propTypes = {};
|
class WizardWooCommerceImportListStep extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
importType: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.handleOptionChange = this.handleOptionChange.bind(this);
|
||||||
|
this.submit = this.submit.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleOptionChange(event) {
|
||||||
|
this.setState({
|
||||||
|
importType: event.target.value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
submit(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
if (!this.state.importType) return false;
|
||||||
|
this.props.submitForm(this.state.importType);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="mailpoet_welcome_wizard_step_content mailpoet_welcome_wizard_centered_column">
|
||||||
|
<h1>{MailPoet.I18n.t('wooCommerceListImportTitle')}</h1>
|
||||||
|
<p>{MailPoet.I18n.t('wooCommerceListImportInfo1')}</p>
|
||||||
|
<p>{MailPoet.I18n.t('wooCommerceListImportInfo2')}</p>
|
||||||
|
<p><b>{MailPoet.I18n.t('wooCommerceListImportInfo3')}</b></p>
|
||||||
|
<form onSubmit={this.submit} className="mailpoet_wizard_woocommerce_list">
|
||||||
|
<label htmlFor="import_type_subscribed">
|
||||||
|
<input
|
||||||
|
id="import_type_subscribed"
|
||||||
|
type="radio"
|
||||||
|
name="import_type"
|
||||||
|
checked={this.state.importType === 'subscribed'}
|
||||||
|
onChange={this.handleOptionChange}
|
||||||
|
value="subscribed"
|
||||||
|
/>
|
||||||
|
{ReactHtmlParser(MailPoet.I18n.t('wooCommerceListImportCheckboxSubscribed'))}
|
||||||
|
</label>
|
||||||
|
<label htmlFor="import_type_unsubscribed">
|
||||||
|
<input
|
||||||
|
id="import_type_unsubscribed"
|
||||||
|
type="radio"
|
||||||
|
name="import_type"
|
||||||
|
checked={this.state.importType === 'unsubscribed'}
|
||||||
|
onChange={this.handleOptionChange}
|
||||||
|
value="unsubscribed"
|
||||||
|
/>
|
||||||
|
{ReactHtmlParser(MailPoet.I18n.t('wooCommerceListImportCheckboxUnsubscribed'))}
|
||||||
|
</label>
|
||||||
|
<p>{MailPoet.I18n.t('wooCommerceListImportInfo4')}</p>
|
||||||
|
<input
|
||||||
|
className="button button-primary"
|
||||||
|
type="submit"
|
||||||
|
value={MailPoet.I18n.t('wooCommerceListImportSubmit')}
|
||||||
|
disabled={!this.state.importType || this.props.loading}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WizardWooCommerceImportListStep.propTypes = {
|
||||||
|
submitForm: PropTypes.func.isRequired,
|
||||||
|
loading: PropTypes.bool.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
export default WizardWooCommerceImportListStep;
|
export default WizardWooCommerceImportListStep;
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import MailPoet from 'mailpoet';
|
import MailPoet from 'mailpoet';
|
||||||
import WooCommerceImportListStep from './steps/woo_commerce_import_list_step.jsx';
|
import WooCommerceImportListStep from './steps/woo_commerce_import_list_step.jsx';
|
||||||
@ -6,26 +5,42 @@ import WooCommerceImportListStep from './steps/woo_commerce_import_list_step.jsx
|
|||||||
class WooCommerceImportController extends React.Component {
|
class WooCommerceImportController extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
loading: false,
|
loading: false,
|
||||||
};
|
};
|
||||||
this.updateSettings = this.updateSettings.bind(this);
|
this.updateSettings = this.updateSettings.bind(this);
|
||||||
|
this.scheduleImport = this.scheduleImport.bind(this);
|
||||||
|
this.finishWizard = this.finishWizard.bind(this);
|
||||||
|
this.submit = this.submit.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
finishWizard() {
|
finishWizard() {
|
||||||
this.setState({ loading: true });
|
this.setState({ loading: true });
|
||||||
window.location = window.finish_wizard_url;
|
window.location = window.finish_wizard_url;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSettings(data) {
|
updateSettings(data) {
|
||||||
this.setState({ loading: true });
|
|
||||||
return MailPoet.Ajax.post({
|
return MailPoet.Ajax.post({
|
||||||
api_version: window.mailpoet_api_version,
|
api_version: window.mailpoet_api_version,
|
||||||
endpoint: 'settings',
|
endpoint: 'settings',
|
||||||
action: 'set',
|
action: 'set',
|
||||||
data,
|
data,
|
||||||
|
}).fail((response) => {
|
||||||
|
this.setState({ loading: false });
|
||||||
|
if (response.errors.length > 0) {
|
||||||
|
MailPoet.Notice.error(
|
||||||
|
response.errors.map(error => error.message),
|
||||||
|
{ scroll: true }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
scheduleImport() {
|
||||||
|
return MailPoet.Ajax.post({
|
||||||
|
api_version: window.mailpoet_api_version,
|
||||||
|
endpoint: 'importExport',
|
||||||
|
action: 'setupWooCommerceInitialImport',
|
||||||
}).then(() => this.setState({ loading: false })).fail((response) => {
|
}).then(() => this.setState({ loading: false })).fail((response) => {
|
||||||
this.setState({ loading: false });
|
this.setState({ loading: false });
|
||||||
if (response.errors.length > 0) {
|
if (response.errors.length > 0) {
|
||||||
@ -37,13 +52,22 @@ class WooCommerceImportController extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
submit(importType) {
|
||||||
|
this.setState({ loading: true });
|
||||||
|
const settings = {
|
||||||
|
'woocommerce.import_screen_displayed': 1,
|
||||||
|
'mailpoet_subscribe_old_woocommerce_customers.enabled': importType === 'subscribed' ? 1 : 0,
|
||||||
|
};
|
||||||
|
this.updateSettings(settings).then(this.scheduleImport).then(this.finishWizard);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="mailpoet_welcome_wizard_steps mailpoet_welcome_wizard_centered_column">
|
<div className="mailpoet_welcome_wizard_steps mailpoet_welcome_wizard_centered_column">
|
||||||
<div className="mailpoet_welcome_wizard_header">
|
<div className="mailpoet_welcome_wizard_header">
|
||||||
<img src={window.mailpoet_logo_url} width="200" height="87" alt="MailPoet logo" />
|
<img src={window.mailpoet_logo_url} width="200" height="87" alt="MailPoet logo" />
|
||||||
</div>
|
</div>
|
||||||
<WooCommerceImportListStep loading={this.state.loading} />
|
<WooCommerceImportListStep loading={this.state.loading} submitForm={this.submit} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,11 @@
|
|||||||
|
|
||||||
namespace MailPoet\API\JSON\v1;
|
namespace MailPoet\API\JSON\v1;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use MailPoet\API\JSON\Endpoint as APIEndpoint;
|
use MailPoet\API\JSON\Endpoint as APIEndpoint;
|
||||||
use MailPoet\Config\AccessControl;
|
use MailPoet\Config\AccessControl;
|
||||||
|
use MailPoet\Cron\Workers\WooCommerceSync;
|
||||||
|
use MailPoet\Models\ScheduledTask;
|
||||||
use MailPoet\Models\Segment;
|
use MailPoet\Models\Segment;
|
||||||
use MailPoet\Subscribers\ImportExport\Import\MailChimp;
|
use MailPoet\Subscribers\ImportExport\Import\MailChimp;
|
||||||
|
|
||||||
@ -78,4 +81,27 @@ class ImportExport extends APIEndpoint {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setupWooCommerceInitialImport() {
|
||||||
|
try {
|
||||||
|
$task = ScheduledTask::where('type', WooCommerceSync::TASK_TYPE)
|
||||||
|
->whereRaw('status = ? OR status IS NULL', [ScheduledTask::STATUS_SCHEDULED])
|
||||||
|
->findOne();
|
||||||
|
if ($task && $task->status === null) {
|
||||||
|
return $this->successResponse();
|
||||||
|
}
|
||||||
|
if (!$task) {
|
||||||
|
$task = ScheduledTask::create();
|
||||||
|
$task->type = WooCommerceSync::TASK_TYPE;
|
||||||
|
$task->status = ScheduledTask::STATUS_SCHEDULED;
|
||||||
|
}
|
||||||
|
$task->scheduled_at = Carbon::createFromTimestamp(current_time('timestamp'));
|
||||||
|
$task->save();
|
||||||
|
return $this->successResponse();
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return $this->errorResponse([
|
||||||
|
$e->getCode() => $e->getMessage()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -410,7 +410,7 @@ class Menu {
|
|||||||
function wooCommerceListImport() {
|
function wooCommerceListImport() {
|
||||||
if ((bool)(defined('DOING_AJAX') && DOING_AJAX)) return;
|
if ((bool)(defined('DOING_AJAX') && DOING_AJAX)) return;
|
||||||
$data = [
|
$data = [
|
||||||
|
'finish_wizard_url' => $this->wp->adminUrl('admin.php?page=' . self::MAIN_PAGE_SLUG),
|
||||||
];
|
];
|
||||||
$this->displayPage('woocommerce_list_import.html', $data);
|
$this->displayPage('woocommerce_list_import.html', $data);
|
||||||
}
|
}
|
||||||
|
74
tests/integration/API/JSON/v1/ImportExportTest.php
Normal file
74
tests/integration/API/JSON/v1/ImportExportTest.php
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
<?php
|
||||||
|
namespace MailPoet\Test\API\JSON\v1;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use MailPoet\API\JSON\v1\ImportExport;
|
||||||
|
use MailPoet\Cron\Workers\WooCommerceSync;
|
||||||
|
use MailPoet\DI\ContainerWrapper;
|
||||||
|
use MailPoet\Models\ScheduledTask;
|
||||||
|
|
||||||
|
class ImportExportTest extends \MailPoetTest {
|
||||||
|
|
||||||
|
/** @var ImportExport */
|
||||||
|
private $endpoint;
|
||||||
|
|
||||||
|
function _before() {
|
||||||
|
parent::_before();
|
||||||
|
$this->endpoint = ContainerWrapper::getInstance()->get(ImportExport::class);
|
||||||
|
ScheduledTask::where('type', WooCommerceSync::TASK_TYPE)->deleteMany();
|
||||||
|
}
|
||||||
|
|
||||||
|
function testItSchedulesTaskWhenNoneExistss() {
|
||||||
|
$response = $this->endpoint->setupWooCommerceInitialImport();
|
||||||
|
expect($response->status)->equals(200);
|
||||||
|
$task = ScheduledTask::where('type', WooCommerceSync::TASK_TYPE)->findOne();
|
||||||
|
expect($task->status)->equals(ScheduledTask::STATUS_SCHEDULED);
|
||||||
|
$now = time();
|
||||||
|
$scheduled_at = new Carbon($task->scheduled_at);
|
||||||
|
expect($scheduled_at->timestamp)->greaterOrEquals($now - 1);
|
||||||
|
expect($scheduled_at->timestamp)->lessOrEquals($now + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testItReschedulesScheduledTaskToNow() {
|
||||||
|
$original_schedule = Carbon::createFromTimestamp(time() + 3000);
|
||||||
|
$this->createTask(WooCommerceSync::TASK_TYPE, ScheduledTask::STATUS_SCHEDULED, $original_schedule);
|
||||||
|
$this->endpoint->setupWooCommerceInitialImport();
|
||||||
|
$task = ScheduledTask::where('type', WooCommerceSync::TASK_TYPE)->findOne();
|
||||||
|
expect($task->status)->equals(ScheduledTask::STATUS_SCHEDULED);
|
||||||
|
$now = time();
|
||||||
|
$scheduled_at = new Carbon($task->scheduled_at);
|
||||||
|
expect($scheduled_at->timestamp)->greaterOrEquals($now - 1);
|
||||||
|
expect($scheduled_at->timestamp)->lessOrEquals($now + 1);
|
||||||
|
$task_count = ScheduledTask::where('type', WooCommerceSync::TASK_TYPE)->count();
|
||||||
|
expect($task_count)->equals(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testItDoesNothingForRunningTask() {
|
||||||
|
$this->createTask(WooCommerceSync::TASK_TYPE, null);
|
||||||
|
$this->endpoint->setupWooCommerceInitialImport();
|
||||||
|
$task = ScheduledTask::where('type', WooCommerceSync::TASK_TYPE)->findOne();
|
||||||
|
expect($task->status)->equals(null);
|
||||||
|
$task_count = ScheduledTask::where('type', WooCommerceSync::TASK_TYPE)->count();
|
||||||
|
expect($task_count)->equals(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testItIgnoresCompletedAndPausedTasks() {
|
||||||
|
$this->createTask(WooCommerceSync::TASK_TYPE, ScheduledTask::STATUS_PAUSED);
|
||||||
|
$this->createTask(WooCommerceSync::TASK_TYPE, ScheduledTask::STATUS_COMPLETED);
|
||||||
|
$this->endpoint->setupWooCommerceInitialImport();
|
||||||
|
$task_count = ScheduledTask::where('type', WooCommerceSync::TASK_TYPE)->count();
|
||||||
|
expect($task_count)->equals(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createTask($type, $status = null, $scheduled_at = null) {
|
||||||
|
if (!$scheduled_at) {
|
||||||
|
Carbon::createFromTimestamp(current_time('timestamp'));
|
||||||
|
}
|
||||||
|
$task = ScheduledTask::create();
|
||||||
|
$task->type = $type;
|
||||||
|
$task->status = $status;
|
||||||
|
$task->scheduled_at = $scheduled_at;
|
||||||
|
$task->save();
|
||||||
|
return $task;
|
||||||
|
}
|
||||||
|
}
|
@ -12,6 +12,13 @@
|
|||||||
|
|
||||||
<% block translations %>
|
<% block translations %>
|
||||||
<%= localize({
|
<%= localize({
|
||||||
'wooCommerceListImportTitle': __('WooCommerce customers now have their own list'),
|
'wooCommerceListImportTitle': _x('WooCommerce customers now have their own list', 'Title on the customers import page'),
|
||||||
|
'wooCommerceListImportInfo1': __('MailPoet will create a list of your WooCommerce customers, even those who don’t have an account, known as "Guests".'),
|
||||||
|
'wooCommerceListImportInfo2': __('New customers will be able to join this list during checkout. You can manage this new checkout feature in your MailPoet Settings.'),
|
||||||
|
'wooCommerceListImportInfo3': __('To begin, please choose how you want to populate your list:'),
|
||||||
|
'wooCommerceListImportCheckboxSubscribed': __('<b>add and subscribe</b> all my customers to this list because they agreed to receive marketing emails from me'),
|
||||||
|
'wooCommerceListImportCheckboxUnsubscribed': __('<b>add</b> all my customers to the list, but <b>as unsubscribed</b>. They can join this list next time they check out'),
|
||||||
|
'wooCommerceListImportInfo4': __('Their subscription preference on other lists won’t be changed.'),
|
||||||
|
'wooCommerceListImportSubmit': _x('Create my WooCommerce Customers list now!', 'Submit button caption'),
|
||||||
}) %>
|
}) %>
|
||||||
<% endblock %>
|
<% endblock %>
|
||||||
|
Reference in New Issue
Block a user