WP Users list

- migration for filters & segment_filter tables
- models for new tables
- update of Listing JSX to allow for conditional display of item actions
This commit is contained in:
Jonathan Labreuille
2015-11-24 17:12:14 +01:00
parent ebca4257a6
commit 6dd8270bec
11 changed files with 280 additions and 21 deletions

View File

@ -79,6 +79,12 @@ define(
if(custom_actions.length > 0) {
item_actions = custom_actions.map(function(action, index) {
if(action.onFilter !== undefined) {
if(action.onFilter(this.props.item) === false) {
return;
}
}
if(action.refresh) {
return (
<span
@ -121,6 +127,12 @@ define(
);
}
var is_readonly = (
(this.props.onFilterItem !== undefined)
? this.props.onFilterItem(this.props.item)
: false
);
if(this.props.group === 'trash') {
var actions = (
<div>
@ -158,8 +170,9 @@ define(
<div>
<div className="row-actions">
{ item_actions }
{ ' | ' }
{ (is_readonly) ? '' : (
<span className="trash">
{ ' | ' }
<a
href="javascript:;"
onClick={ this.handleTrashItem.bind(
@ -169,6 +182,7 @@ define(
Trash
</a>
</span>
)}
</div>
<button
onClick={ this.handleToggleItem.bind(null, this.props.item.id) }
@ -258,6 +272,7 @@ define(
columns={ this.props.columns }
onSelectItem={ this.props.onSelectItem }
onRenderItem={ this.props.onRenderItem }
onFilterItem={ this.props.onFilterItem }
onDeleteItem={ this.props.onDeleteItem }
onRestoreItem={ this.props.onRestoreItem }
onTrashItem={ this.props.onTrashItem }
@ -721,6 +736,7 @@ define(
<ListingItems
onRenderItem={ this.handleRenderItem }
onFilterItem={ this.props.onFilterItem }
onDeleteItem={ this.handleDeleteItem }
onRestoreItem={ this.handleRestoreItem }
onTrashItem={ this.handleTrashItem }

View File

@ -121,6 +121,20 @@ const item_actions = [
);
refresh();
});
},
onFilter: function(segment) {
return (segment.filters.length === 0);
}
},
{
name: 'sync_segment',
label: 'Update',
className: 'update',
onClick: function(item, refresh) {
console.log(item);
},
onFilter: function(segment) {
return (segment.filters.length > 0);
}
},
{
@ -142,13 +156,15 @@ const bulk_actions = [
];
const SegmentList = React.createClass({
filterItem: function(segment) {
return !!(segment.filters.length > 0);
},
renderItem: function(segment, actions) {
var rowClasses = classNames(
'manage-column',
'column-primary',
'has-row-actions'
);
return (
<div>
<td className={ rowClasses }>
@ -190,6 +206,7 @@ const SegmentList = React.createClass({
limit={ 1000 }
endpoint="segments"
onRenderItem={ this.renderItem }
onFilterItem={ this.filterItem }
columns={ columns }
bulk_actions={ bulk_actions }
item_actions={ item_actions }

42
lib/Config/Hooks.php Normal file
View File

@ -0,0 +1,42 @@
<?php
namespace MailPoet\Config;
class Hooks {
function __construct() {
}
function init() {
// WP Users synchronization
add_action(
'user_register',
'\MailPoet\Filters\WP::synchronizeUser',
1
);
add_action(
'added_existing_user',
'\MailPoet\Filters\WP::synchronizeUser',
1
);
add_action(
'profile_update',
'\MailPoet\Filters\WP::synchronizeUser',
1
);
add_action(
'delete_user',
'\MailPoet\Filters\WP::synchronizeUser',
1
);
// multisite
add_action(
'deleted_user',
'\MailPoet\Filters\WP::synchronizeUser',
1
);
add_action(
'remove_user_from_blog',
'\MailPoet\Filters\WP::synchronizeUser',
1
);
}
}

View File

@ -25,6 +25,8 @@ class Initializer {
$this->setupAnalytics();
$this->setupPermissions();
$this->setupChangelog();
$this->setupHooks();
}
function setupDB() {
@ -41,6 +43,8 @@ class Initializer {
$newsletters = Env::$db_prefix . 'newsletters';
$newsletter_templates = Env::$db_prefix . 'newsletter_templates';
$segments = Env::$db_prefix . 'segments';
$filters = Env::$db_prefix . 'filters';
$segment_filter = Env::$db_prefix . 'segment_filter';
$forms = Env::$db_prefix . 'forms';
$subscriber_segment = Env::$db_prefix . 'subscriber_segment';
$newsletter_segment = Env::$db_prefix . 'newsletter_segment';
@ -53,6 +57,8 @@ class Initializer {
define('MP_SETTINGS_TABLE', $settings);
define('MP_NEWSLETTERS_TABLE', $newsletters);
define('MP_SEGMENTS_TABLE', $segments);
define('MP_FILTERS_TABLE', $filters);
define('MP_SEGMENT_FILTER_TABLE', $segment_filter);
define('MP_FORMS_TABLE', $forms);
define('MP_SUBSCRIBER_SEGMENT_TABLE', $subscriber_segment);
define('MP_NEWSLETTER_TEMPLATES_TABLE', $newsletter_templates);
@ -110,4 +116,10 @@ class Initializer {
$changelog = new Changelog();
$changelog->init();
}
function setupHooks() {
$hooks = new Hooks();
$hooks->init();
}
}

View File

@ -15,6 +15,8 @@ class Migrator {
'newsletters',
'newsletter_templates',
'segments',
'filters',
'segment_filter',
'subscriber_segment',
'newsletter_segment',
'custom_fields',
@ -50,6 +52,7 @@ class Migrator {
function subscribers() {
$attributes = array(
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
'wp_user_id bigint(20) NULL,',
'first_name tinytext NOT NULL,',
'last_name tinytext NOT NULL,',
'email varchar(150) NOT NULL,',
@ -118,6 +121,32 @@ class Migrator {
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() {
$attributes = array(
'id mediumint(9) NOT NULL AUTO_INCREMENT,',

55
lib/Filters/WP.php Normal file
View File

@ -0,0 +1,55 @@
<?php
namespace MailPoet\Filters;
use \MailPoet\Models\Subscriber;
use \MailPoet\Models\Segment;
use \MailPoet\Models\SubscriberSegment;
class WP {
static function synchronizeUser($wp_user_id) {
$wpUser = \get_userdata($wp_user_id);
if($wpUser === false) return;
$subscriber = Subscriber::where('wp_user_id', $wpUser->ID)
->findOne();
switch(current_filter()) {
case 'user_register':
case 'added_existing_user':
case 'profile_update':
// get first name & last name
$first_name = $wpUser->first_name;
$last_name = $wpUser->last_name;
if(empty($wpUser->first_name) && empty($wpUser->last_name)) {
$first_name = $wpUser->display_name;
}
// subscriber data
$data = array(
'wp_user_id'=> $wpUser->ID,
'email' => $wpUser->user_email,
'first_name' => $first_name,
'last_name' => $last_name,
'status' => 'subscribed'
);
if($subscriber !== false) {
$data['id'] = $subscriber->id();
}
$subscriber = Subscriber::createOrUpdate($data);
if($subscriber !== false && $subscriber->id()) {
$segment = Segment::getWPUsers();
$segment->addSubscriber($subscriber->id());
}
break;
case 'delete_user':
case 'deleted_user':
case 'remove_user_from_blog':
if($subscriber !== false && $subscriber->id()) {
$subscriber->delete();
}
break;
}
}
}

50
lib/Models/Filter.php Normal file
View File

@ -0,0 +1,50 @@
<?php
namespace MailPoet\Models;
if(!defined('ABSPATH')) exit;
class Filter extends Model {
static $_table = MP_FILTERS_TABLE;
function __construct() {
parent::__construct();
$this->addValidations('name', array(
'required' => __('You need to specify a name.')
));
}
function delete() {
// delete all relations to subscribers
SegmentFilter::where('filter_id', $this->id)->deleteMany();
return parent::delete();
}
function segments() {
return $this->has_many_through(
__NAMESPACE__.'\Segment',
__NAMESPACE__.'\SegmentFilter',
'filter_id',
'segment_id'
);
}
static function createOrUpdate($data = array()) {
$filter = false;
if(isset($data['id']) && (int)$data['id'] > 0) {
$filter = self::findOne((int)$data['id']);
}
if($filter === false) {
$filter = self::create();
$filter->hydrate($data);
} else {
unset($data['id']);
$filter->set($data);
}
$filter->save();
return $filter;
}
}

View File

@ -38,6 +38,15 @@ class Segment extends Model {
);
}
function segmentFilters() {
return $this->has_many_through(
__NAMESPACE__.'\Filter',
__NAMESPACE__.'\SegmentFilter',
'segment_id',
'filter_id'
);
}
function duplicate($data = array()) {
$duplicate = parent::duplicate($data);
@ -67,6 +76,10 @@ class Segment extends Model {
->delete();
}
static function getWPUsers() {
return self::findOne(1);
}
static function search($orm, $search = '') {
return $orm->whereLike('name', '%'.$search.'%');
}

View File

@ -0,0 +1,12 @@
<?php
namespace MailPoet\Models;
if(!defined('ABSPATH')) exit;
class SegmentFilter extends Model {
public static $_table = MP_SEGMENT_FILTER_TABLE;
function __construct() {
parent::__construct();
}
}

View File

@ -2,6 +2,7 @@
namespace MailPoet\Router;
use \MailPoet\Models\Segment;
use \MailPoet\Models\SubscriberSegment;
use \MailPoet\Models\SegmentFilter;
use \MailPoet\Listing;
if(!defined('ABSPATH')) exit;
@ -31,6 +32,18 @@ class Segments {
// fetch segments relations for each returned 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')
->where(
'relation.segment_id',