WP Users list

- refactored and fixed listing issues (related to Segments)
- removed bulk actions from segments
- added synchronize methods for WP users
- Update action in segments only for WP Users list
- added "type" column to segments (default, wp_users, dynamic...)
- added "status" column to subscriber_segment table (useful soon)
This commit is contained in:
Jonathan Labreuille
2015-11-25 18:31:57 +01:00
parent 6dd8270bec
commit a5d96f1534
11 changed files with 150 additions and 107 deletions

View File

@ -105,6 +105,9 @@ const item_actions = [
refresh(); refresh();
}); });
} }
},
{
name: 'trash'
} }
]; ];

View File

@ -79,33 +79,48 @@ define(
if(custom_actions.length > 0) { if(custom_actions.length > 0) {
item_actions = custom_actions.map(function(action, index) { item_actions = custom_actions.map(function(action, index) {
if(action.onFilter !== undefined) { if(action.display !== undefined) {
if(action.onFilter(this.props.item) === false) { if(action.display(this.props.item) === false) {
return; return;
} }
} }
if(action.refresh) { if(action.name === 'trash') {
return (
<span key={ 'action-'+index } className="trash">
{(index > 0) ? ' | ' : ''}
<a
href="javascript:;"
onClick={ this.handleTrashItem.bind(
null,
this.props.item.id
) }>
Trash
</a>
</span>
);
} else if(action.refresh) {
return ( return (
<span <span
onClick={ this.props.onRefreshItems } onClick={ this.props.onRefreshItems }
key={ 'action-'+index } className={ action.name }> key={ 'action-'+index } className={ action.name }>
{(index > 0) ? ' | ' : ''}
{ action.link(this.props.item) } { action.link(this.props.item) }
{(index < (custom_actions.length - 1)) ? ' | ' : ''}
</span> </span>
); );
} else if(action.link) { } else if(action.link) {
return ( return (
<span <span
key={ 'action-'+index } className={ action.name }> key={ 'action-'+index } className={ action.name }>
{(index > 0) ? ' | ' : ''}
{ action.link(this.props.item) } { action.link(this.props.item) }
{(index < (custom_actions.length - 1)) ? ' | ' : ''}
</span> </span>
); );
} else { } else {
return ( return (
<span <span
key={ 'action-'+index } className={ action.name }> key={ 'action-'+index } className={ action.name }>
{(index > 0) ? ' | ' : ''}
<a href="javascript:;" onClick={ <a href="javascript:;" onClick={
(action.onClick !== undefined) (action.onClick !== undefined)
? action.onClick.bind(null, ? action.onClick.bind(null,
@ -114,7 +129,6 @@ define(
) )
: false : false
}>{ action.label }</a> }>{ action.label }</a>
{(index < (custom_actions.length - 1)) ? ' | ' : ''}
</span> </span>
); );
} }
@ -127,12 +141,6 @@ define(
); );
} }
var is_readonly = (
(this.props.onFilterItem !== undefined)
? this.props.onFilterItem(this.props.item)
: false
);
if(this.props.group === 'trash') { if(this.props.group === 'trash') {
var actions = ( var actions = (
<div> <div>
@ -170,19 +178,6 @@ define(
<div> <div>
<div className="row-actions"> <div className="row-actions">
{ item_actions } { item_actions }
{ (is_readonly) ? '' : (
<span className="trash">
{ ' | ' }
<a
href="javascript:;"
onClick={ this.handleTrashItem.bind(
null,
this.props.item.id
) }>
Trash
</a>
</span>
)}
</div> </div>
<button <button
onClick={ this.handleToggleItem.bind(null, this.props.item.id) } onClick={ this.handleToggleItem.bind(null, this.props.item.id) }
@ -272,7 +267,6 @@ define(
columns={ this.props.columns } columns={ this.props.columns }
onSelectItem={ this.props.onSelectItem } onSelectItem={ this.props.onSelectItem }
onRenderItem={ this.props.onRenderItem } onRenderItem={ this.props.onRenderItem }
onFilterItem={ this.props.onFilterItem }
onDeleteItem={ this.props.onDeleteItem } onDeleteItem={ this.props.onDeleteItem }
onRestoreItem={ this.props.onRestoreItem } onRestoreItem={ this.props.onRestoreItem }
onTrashItem={ this.props.onTrashItem } onTrashItem={ this.props.onTrashItem }
@ -652,7 +646,7 @@ define(
// bulk actions // bulk actions
var bulk_actions = this.props.bulk_actions || []; var bulk_actions = this.props.bulk_actions || [];
if(this.state.group === 'trash') { if(this.state.group === 'trash' && bulk_actions.length > 0) {
bulk_actions = [ bulk_actions = [
{ {
name: 'restore', name: 'restore',
@ -736,7 +730,6 @@ define(
<ListingItems <ListingItems
onRenderItem={ this.handleRenderItem } onRenderItem={ this.handleRenderItem }
onFilterItem={ this.props.onFilterItem }
onDeleteItem={ this.handleDeleteItem } onDeleteItem={ this.handleDeleteItem }
onRestoreItem={ this.handleRestoreItem } onRestoreItem={ this.handleRestoreItem }
onTrashItem={ this.handleTrashItem } onTrashItem={ this.handleTrashItem }

View File

@ -112,6 +112,9 @@ define(
</a> </a>
); );
} }
},
{
name: 'trash'
} }
]; ];

View File

@ -122,19 +122,29 @@ const item_actions = [
refresh(); refresh();
}); });
}, },
onFilter: function(segment) { display: function(segment) {
return (segment.filters.length === 0); return (segment.type !== 'wp_users');
} }
}, },
{ {
name: 'sync_segment', name: 'synchronize_segment',
label: 'Update', label: 'Update',
className: 'update', className: 'update',
onClick: function(item, refresh) { onClick: function(item, refresh) {
console.log(item); return MailPoet.Ajax.post({
endpoint: 'segments',
action: 'synchronize'
}).done(function(response) {
if(response === true) {
MailPoet.Notice.success(
('List "%$1s" has been synchronized.').replace('%$1s', item.name)
);
refresh();
}
});
}, },
onFilter: function(segment) { display: function(segment) {
return (segment.filters.length > 0); return (segment.type === 'wp_users');
} }
}, },
{ {
@ -144,21 +154,19 @@ const item_actions = [
<a href={ item.subscribers_url }>View subscribers</a> <a href={ item.subscribers_url }>View subscribers</a>
); );
} }
},
{
name: 'trash',
display: function(segment) {
return (segment.type !== 'wp_users');
}
} }
]; ];
const bulk_actions = [ const bulk_actions = [
{
name: 'trash',
label: 'Trash',
onSuccess: messages.onTrash
}
]; ];
const SegmentList = React.createClass({ const SegmentList = React.createClass({
filterItem: function(segment) {
return !!(segment.filters.length > 0);
},
renderItem: function(segment, actions) { renderItem: function(segment, actions) {
var rowClasses = classNames( var rowClasses = classNames(
'manage-column', 'manage-column',
@ -206,7 +214,6 @@ const SegmentList = React.createClass({
limit={ 1000 } limit={ 1000 }
endpoint="segments" endpoint="segments"
onRenderItem={ this.renderItem } onRenderItem={ this.renderItem }
onFilterItem={ this.filterItem }
columns={ columns } columns={ columns }
bulk_actions={ bulk_actions } bulk_actions={ bulk_actions }
item_actions={ item_actions } item_actions={ item_actions }

View File

@ -206,6 +206,21 @@ const bulk_actions = [
} }
]; ];
const item_actions = [
{
name: 'edit',
label: 'Edit',
link: function(item) {
return (
<Link to={ `/edit/${item.id}` }>Edit</Link>
);
}
},
{
name: 'trash'
}
];
const SubscriberList = React.createClass({ const SubscriberList = React.createClass({
renderItem: function(subscriber, actions) { renderItem: function(subscriber, actions) {
let row_classes = classNames( let row_classes = classNames(
@ -295,6 +310,7 @@ const SubscriberList = React.createClass({
onRenderItem={ this.renderItem } onRenderItem={ this.renderItem }
columns={ columns } columns={ columns }
bulk_actions={ bulk_actions } bulk_actions={ bulk_actions }
item_actions={ item_actions }
messages={ messages } messages={ messages }
onGetItems={ this.onGetItems } onGetItems={ this.onGetItems }
/> />

View File

@ -9,33 +9,33 @@ class Hooks {
// WP Users synchronization // WP Users synchronization
add_action( add_action(
'user_register', 'user_register',
'\MailPoet\Filters\WP::synchronizeUser', '\MailPoet\Segments\WP::synchronizeUser',
1 1
); );
add_action( add_action(
'added_existing_user', 'added_existing_user',
'\MailPoet\Filters\WP::synchronizeUser', '\MailPoet\Segments\WP::synchronizeUser',
1 1
); );
add_action( add_action(
'profile_update', 'profile_update',
'\MailPoet\Filters\WP::synchronizeUser', '\MailPoet\Segments\WP::synchronizeUser',
1 1
); );
add_action( add_action(
'delete_user', 'delete_user',
'\MailPoet\Filters\WP::synchronizeUser', '\MailPoet\Segments\WP::synchronizeUser',
1 1
); );
// multisite // multisite
add_action( add_action(
'deleted_user', 'deleted_user',
'\MailPoet\Filters\WP::synchronizeUser', '\MailPoet\Segments\WP::synchronizeUser',
1 1
); );
add_action( add_action(
'remove_user_from_blog', 'remove_user_from_blog',
'\MailPoet\Filters\WP::synchronizeUser', '\MailPoet\Segments\WP::synchronizeUser',
1 1
); );
} }

View File

@ -15,8 +15,6 @@ class Migrator {
'newsletters', 'newsletters',
'newsletter_templates', 'newsletter_templates',
'segments', 'segments',
'filters',
'segment_filter',
'subscriber_segment', 'subscriber_segment',
'newsletter_segment', 'newsletter_segment',
'custom_fields', 'custom_fields',
@ -111,6 +109,7 @@ class Migrator {
$attributes = array( $attributes = array(
'id mediumint(9) NOT NULL AUTO_INCREMENT,', 'id mediumint(9) NOT NULL AUTO_INCREMENT,',
'name varchar(90) NOT NULL,', 'name varchar(90) NOT NULL,',
'type varchar(90) NOT NULL DEFAULT "default",',
'description varchar(250) NOT NULL,', 'description varchar(250) NOT NULL,',
'created_at TIMESTAMP NOT NULL DEFAULT 0,', 'created_at TIMESTAMP NOT NULL DEFAULT 0,',
'deleted_at TIMESTAMP NULL DEFAULT NULL,', 'deleted_at TIMESTAMP NULL DEFAULT NULL,',
@ -121,37 +120,12 @@ class Migrator {
return $this->sqlify(__FUNCTION__, $attributes); return $this->sqlify(__FUNCTION__, $attributes);
} }
function filters() {
$attributes = array(
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
'name varchar(90) NOT NULL,',
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
'PRIMARY KEY (id),',
'UNIQUE KEY name (name)'
);
return $this->sqlify(__FUNCTION__, $attributes);
}
function segment_filter() {
$attributes = array(
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
'segment_id mediumint(9) NOT NULL,',
'filter_id mediumint(9) NOT NULL,',
'params longtext NULL,',
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
'PRIMARY KEY (id),',
'UNIQUE KEY segment_filter (segment_id,filter_id)'
);
return $this->sqlify(__FUNCTION__, $attributes);
}
function subscriber_segment() { function subscriber_segment() {
$attributes = array( $attributes = array(
'id mediumint(9) NOT NULL AUTO_INCREMENT,', 'id mediumint(9) NOT NULL AUTO_INCREMENT,',
'subscriber_id mediumint(9) NOT NULL,', 'subscriber_id mediumint(9) NOT NULL,',
'segment_id mediumint(9) NOT NULL,', 'segment_id mediumint(9) NOT NULL,',
'status varchar(12) NOT NULL DEFAULT "subscribed",',
'created_at TIMESTAMP NOT NULL DEFAULT 0,', 'created_at TIMESTAMP NOT NULL DEFAULT 0,',
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', 'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
'PRIMARY KEY (id),', 'PRIMARY KEY (id),',

View File

@ -77,7 +77,20 @@ class Segment extends Model {
} }
static function getWPUsers() { static function getWPUsers() {
return self::findOne(1); $segment = self::where('type', 'wp_users')->findOne();
if($segment === false) {
// create the wp users list
$segment = self::create();
$segment->hydrate(array(
'name' => __('WordPress Users'),
'type' => 'wp_users'
));
$segment->save();
return self::findOne($segment->id());
}
return $segment;
} }
static function search($orm, $search = '') { static function search($orm, $search = '') {

View File

@ -4,6 +4,7 @@ use \MailPoet\Models\Segment;
use \MailPoet\Models\SubscriberSegment; use \MailPoet\Models\SubscriberSegment;
use \MailPoet\Models\SegmentFilter; use \MailPoet\Models\SegmentFilter;
use \MailPoet\Listing; use \MailPoet\Listing;
use \MailPoet\Segments\WP;
if(!defined('ABSPATH')) exit; if(!defined('ABSPATH')) exit;
@ -32,18 +33,6 @@ class Segments {
// fetch segments relations for each returned item // fetch segments relations for each returned item
foreach($listing_data['items'] as &$item) { foreach($listing_data['items'] as &$item) {
$item['filters'] = SegmentFilter::table_alias('relation')
->where(
'relation.segment_id',
$item['id']
)
->join(
MP_FILTERS_TABLE,
'filters.id = relation.filter_id',
'filters'
)
->findArray();
$stats = SubscriberSegment::table_alias('relation') $stats = SubscriberSegment::table_alias('relation')
->where( ->where(
'relation.segment_id', 'relation.segment_id',
@ -55,15 +44,15 @@ class Segments {
'subscribers' 'subscribers'
) )
->select_expr( ->select_expr(
'SUM(CASE status WHEN "subscribed" THEN 1 ELSE 0 END)', 'SUM(CASE subscribers.status WHEN "subscribed" THEN 1 ELSE 0 END)',
'subscribed' 'subscribed'
) )
->select_expr( ->select_expr(
'SUM(CASE status WHEN "unsubscribed" THEN 1 ELSE 0 END)', 'SUM(CASE subscribers.status WHEN "unsubscribed" THEN 1 ELSE 0 END)',
'unsubscribed' 'unsubscribed'
) )
->select_expr( ->select_expr(
'SUM(CASE status WHEN "unconfirmed" THEN 1 ELSE 0 END)', 'SUM(CASE subscribers.status WHEN "unconfirmed" THEN 1 ELSE 0 END)',
'unconfirmed' 'unconfirmed'
) )
->findOne()->asArray(); ->findOne()->asArray();
@ -148,6 +137,12 @@ class Segments {
wp_send_json($result); wp_send_json($result);
} }
function synchronize() {
$result = WP::synchronizeUsers();
wp_send_json($result);
}
function bulkAction($data = array()) { function bulkAction($data = array()) {
$bulk_action = new Listing\BulkAction( $bulk_action = new Listing\BulkAction(
'\MailPoet\Models\Segment', '\MailPoet\Models\Segment',

View File

@ -1,8 +1,7 @@
<?php <?php
namespace MailPoet\Filters; namespace MailPoet\Segments;
use \MailPoet\Models\Subscriber; use \MailPoet\Models\Subscriber;
use \MailPoet\Models\Segment; use \MailPoet\Models\Segment;
use \MailPoet\Models\SubscriberSegment;
class WP { class WP {
static function synchronizeUser($wp_user_id) { static function synchronizeUser($wp_user_id) {
@ -13,9 +12,18 @@ class WP {
->findOne(); ->findOne();
switch(current_filter()) { switch(current_filter()) {
case 'delete_user':
case 'deleted_user':
case 'remove_user_from_blog':
if($subscriber !== false && $subscriber->id()) {
$subscriber->delete();
}
break;
case 'user_register': case 'user_register':
case 'added_existing_user': case 'added_existing_user':
case 'profile_update': case 'profile_update':
default:
// get first name & last name // get first name & last name
$first_name = $wpUser->first_name; $first_name = $wpUser->first_name;
$last_name = $wpUser->last_name; $last_name = $wpUser->last_name;
@ -42,14 +50,39 @@ class WP {
$segment->addSubscriber($subscriber->id()); $segment->addSubscriber($subscriber->id());
} }
break; break;
}
}
case 'delete_user': static function synchronizeUsers() {
case 'deleted_user': // get wordpress users list
case 'remove_user_from_blog': $segment = Segment::getWPUsers();
if($subscriber !== false && $subscriber->id()) {
$subscriber->delete(); // count WP users
$users_count = \count_users()['total_users'];
$linked_subscribers_count = $segment->subscribers()->count();
if($users_count !== $linked_subscribers_count) {
$linked_subscribers = Subscriber::select('wp_user_id')
->whereNotNull('wp_user_id')
->findArray();
$exclude_ids = array();
if(!empty($linked_subscribers)) {
$exclude_ids = array_map(function($subscriber) {
return $subscriber['wp_user_id'];
}, $linked_subscribers);
} }
break;
$users = \get_users(array(
'count_total' => false,
'fields' => 'ID',
'exclude' => $exclude_ids
));
foreach($users as $user) {
static::synchronizeUser($user);
} }
} }
return true;
}
} }

View File

@ -4,9 +4,15 @@
<h2 class="title"> <h2 class="title">
<span id="mailpoet_form_name"><%= form.name %></span> <span id="mailpoet_form_name"><%= form.name %></span>
<input id="mailpoet_form_name_input" type="text" value="" style="display:none;" /> <input id="mailpoet_form_name_input" type="text" value="" style="display:none;" />
<span> <a
<a id="mailpoet_form_edit_name" class="button" href="javascript:;"><%= __('Edit name' ) %></a> id="mailpoet_form_edit_name"
</span> class="add-new-h2"
href="javascript:;"
><%= __('Edit name' ) %></a>
<a
href="<%= admin_url('admin.php?page=mailpoet-forms') | raw %>"
class="add-new-h2"
><%= __('List of forms' ) %></a>
</h2> </h2>
<% endblock %> <% endblock %>