Add 'Bounced' status to subscribers [MAILPOET-684]

Fix SubscriberTest->testItHasGroupFilter() making only one of four assertions due to lack of data.
Fix a small issue with Import. Unexpected values for the status field were converted to 'unconfirmed' rather than 'subscribed' due to non-strict comparison in in_array(). This hindered 'bounced' status import as well.
This commit is contained in:
Alexey Stoletniy
2016-11-28 19:37:47 +03:00
parent 299c922cef
commit 36b935b9ae
14 changed files with 89 additions and 8 deletions

View File

@ -28,6 +28,10 @@ var columns = [
name: 'unsubscribed',
label: MailPoet.I18n.t('unsubscribed')
},
{
name: 'bounced',
label: MailPoet.I18n.t('bounced')
},
{
name: 'created_at',
label: MailPoet.I18n.t('createdOn'),
@ -192,6 +196,7 @@ const SegmentList = React.createClass({
const subscribed = ~~(segment.subscribers_count.subscribed || 0);
const unconfirmed = ~~(segment.subscribers_count.unconfirmed || 0);
const unsubscribed = ~~(segment.subscribers_count.unsubscribed || 0);
const bounced = ~~(segment.subscribers_count.bounced || 0);
let segment_name;
@ -229,6 +234,9 @@ const SegmentList = React.createClass({
<td className="column-date" data-colname={ MailPoet.I18n.t('unsubscribed') }>
<abbr>{ unsubscribed.toLocaleString() }</abbr>
</td>
<td className="column-date" data-colname={ MailPoet.I18n.t('bounced') }>
<abbr>{ bounced.toLocaleString() }</abbr>
</td>
<td className="column-date" data-colname={ MailPoet.I18n.t('createdOn') }>
<abbr>{ MailPoet.Date.format(segment.created_at) }</abbr>
</td>

View File

@ -45,7 +45,8 @@ define(
values: {
'subscribed': MailPoet.I18n.t('subscribed'),
'unconfirmed': MailPoet.I18n.t('unconfirmed'),
'unsubscribed': MailPoet.I18n.t('unsubscribed')
'unsubscribed': MailPoet.I18n.t('unsubscribed'),
'bounced': MailPoet.I18n.t('bounced')
},
filter: function(subscriber, value) {
if (~~(subscriber.wp_user_id) > 0 && value === 'unconfirmed') {

View File

@ -255,6 +255,10 @@ const SubscriberList = React.createClass({
case 'unsubscribed':
status = MailPoet.I18n.t('unsubscribed');
break;
case 'bounced':
status = MailPoet.I18n.t('bounced');
break;
}
let segments = false;

View File

@ -87,6 +87,10 @@ class Segment extends Model {
'SUM(CASE subscribers.status WHEN "' . Subscriber::STATUS_UNCONFIRMED . '" THEN 1 ELSE 0 END)',
Subscriber::STATUS_UNCONFIRMED
)
->select_expr(
'SUM(CASE subscribers.status WHEN "' . Subscriber::STATUS_BOUNCED . '" THEN 1 ELSE 0 END)',
Subscriber::STATUS_BOUNCED
)
->findOne()
->asArray();

View File

@ -13,6 +13,7 @@ class Subscriber extends Model {
const STATUS_SUBSCRIBED = 'subscribed';
const STATUS_UNSUBSCRIBED = 'unsubscribed';
const STATUS_UNCONFIRMED = 'unconfirmed';
const STATUS_BOUNCED = 'bounced';
const SUBSCRIPTION_LIMIT_COOLDOWN = 60;
@ -335,6 +336,11 @@ class Subscriber extends Model {
'label' => __('Unsubscribed', 'mailpoet'),
'count' => self::filter(self::STATUS_UNSUBSCRIBED)->count()
),
array(
'name' => self::STATUS_BOUNCED,
'label' => __('Bounced', 'mailpoet'),
'count' => self::filter(self::STATUS_BOUNCED)->count()
),
array(
'name' => 'trash',
'label' => __('Trash', 'mailpoet'),
@ -720,6 +726,12 @@ class Subscriber extends Model {
->where('status', self::STATUS_UNCONFIRMED);
}
static function bounced($orm) {
return $orm
->whereNull('deleted_at')
->where('status', self::STATUS_BOUNCED);
}
static function withoutSegments($orm) {
return $orm->select(MP_SUBSCRIBERS_TABLE.'.*')
->leftOuterJoin(

View File

@ -302,18 +302,24 @@ class Import {
-1,
'-1',
'false'
),
'bounced' => array(
'bounced'
)
);
$subscribers_data['status'] = array_map(function($state) use ($statuses) {
if(in_array(strtolower($state), $statuses['subscribed'])) {
if(in_array(strtolower($state), $statuses['subscribed'], true)) {
return 'subscribed';
}
if(in_array(strtolower($state), $statuses['unsubscribed'])) {
if(in_array(strtolower($state), $statuses['unsubscribed'], true)) {
return 'unsubscribed';
}
if(in_array(strtolower($state), $statuses['unconfirmed'])) {
if(in_array(strtolower($state), $statuses['unconfirmed'], true)) {
return 'unconfirmed';
}
if(in_array(strtolower($state), $statuses['bounced'], true)) {
return 'bounced';
}
return 'subscribed'; // make "subscribed" a default status
}, $subscribers_data['status']);
return array(

View File

@ -290,6 +290,14 @@ class Pages {
'is_checked' => (
$subscriber->status === Subscriber::STATUS_UNSUBSCRIBED
)
),
array(
'value' => array(
Subscriber::STATUS_BOUNCED => __('Bounced', 'mailpoet')
),
'is_checked' => (
$subscriber->status === Subscriber::STATUS_BOUNCED
)
)
)
)

0
testItHasGroupFilter Normal file
View File

View File

@ -5,7 +5,7 @@ use \MailPoet\Models\Subscriber;
class BeaconTest extends MailPoetTest {
function _before() {
// create 3 users (1 confirmed, 1 subscribed, 1 unsubscribed)
// create 4 users (1 confirmed, 1 subscribed, 1 unsubscribed, 1 bounced)
Subscriber::createOrUpdate(array(
'email' => 'user1@mailpoet.com',
'status' => Subscriber::STATUS_SUBSCRIBED
@ -18,6 +18,10 @@ class BeaconTest extends MailPoetTest {
'email' => 'user3@mailpoet.com',
'status' => Subscriber::STATUS_UNSUBSCRIBED
));
Subscriber::createOrUpdate(array(
'email' => 'user4@mailpoet.com',
'status' => Subscriber::STATUS_BOUNCED
));
$this->beacon_data = Beacon::getData();
}

View File

@ -114,16 +114,30 @@ class SubscriberTest extends MailPoetTest {
foreach($subscribers as $subscriber) {
expect($subscriber->status)->equals(Subscriber::STATUS_UNCONFIRMED);
}
$this->subscriber->status = Subscriber::STATUS_SUBSCRIBED;
$this->subscriber->save();
$subscribers = Subscriber::filter('groupBy', Subscriber::STATUS_SUBSCRIBED)
->findMany();
foreach($subscribers as $subscriber) {
expect($subscriber->status)->equals(Subscriber::STATUS_SUBSCRIBED);
}
$this->subscriber->status = Subscriber::STATUS_UNSUBSCRIBED;
$this->subscriber->save();
$subscribers = Subscriber::filter('groupBy', Subscriber::STATUS_UNSUBSCRIBED)
->findMany();
foreach($subscribers as $subscriber) {
expect($subscriber->status)->equals(Subscriber::STATUS_UNSUBSCRIBED);
}
$this->subscriber->status = Subscriber::STATUS_BOUNCED;
$this->subscriber->save();
$subscribers = Subscriber::filter('groupBy', Subscriber::STATUS_BOUNCED)
->findMany();
foreach($subscribers as $subscriber) {
expect($subscriber->status)->equals(Subscriber::STATUS_BOUNCED);
}
}
function testItCanHaveSegment() {
@ -507,6 +521,11 @@ class SubscriberTest extends MailPoetTest {
'deleted_at' => Carbon::now()->toDateTimeString()
));
$subscriber_5 = Subscriber::createOrUpdate(array(
'email' => 'subscriber_5@mailpoet.com',
'status' => Subscriber::STATUS_BOUNCED
));
// counts only subscribed & unconfirmed users
$total = Subscriber::getTotalSubscribers();
expect($total)->equals(2);

View File

@ -153,7 +153,12 @@ class ShortcodesTest extends MailPoetTest {
$this->subscriber->save();
$result =
$shortcodes_object->process(array('[subscriber:count]'));
expect($result[0])->equals(--$subscriber_count);
expect($result[0])->equals($subscriber_count - 1);
$this->subscriber->status = 'bounced';
$this->subscriber->save();
$result =
$shortcodes_object->process(array('[subscriber:count]'));
expect($result[0])->equals($subscriber_count - 1);
}
function testItCanProcessSubscriberCustomFieldShortcodes() {

View File

@ -186,7 +186,12 @@ class ImportTest extends MailPoetTest {
'unsubscribed',
-1,
'-1',
'false'
'false',
#bounced
'bounced',
#unexpected
'qwerty',
null
),
);
list($subscribers_data, $subsciber_fields) =
@ -205,7 +210,10 @@ class ImportTest extends MailPoetTest {
'unsubscribed',
'unsubscribed',
'unsubscribed',
'unsubscribed'
'unsubscribed',
'bounced',
'subscribed',
'subscribed'
)
)
);

View File

@ -30,6 +30,7 @@
'subscribed': __('Subscribed'),
'unconfirmed': __('Unconfirmed'),
'unsubscribed': __('Unsubscribed'),
'bounced': __('Bounced'),
'createdOn': __('Created on'),
'oneSegmentTrashed': __('1 list was moved to the trash'),
'multipleSegmentsTrashed': __('%$1d lists were moved to the trash'),

View File

@ -50,6 +50,7 @@
'unconfirmed': __('Unconfirmed'),
'subscribed': __('Subscribed'),
'unsubscribed': __('Unsubscribed'),
'bounced': __('Bounced'),
'selectList': __('Select a list'),
'unsubscribedOn': __('Unsubscribed on %$1s'),
'subscriberUpdated': __('Subscriber was updated successfully!'),