Skip to content

Commit a054967

Browse files
adekbadekdkoo
andauthored
feat: user discrepancies display (#100)
Co-authored-by: Derrick Koo <[email protected]>
1 parent eace5d2 commit a054967

File tree

6 files changed

+185
-46
lines changed

6 files changed

+185
-46
lines changed

includes/class-users.php

Lines changed: 39 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class Users {
1919
*/
2020
public static function init() {
2121
add_filter( 'manage_users_columns', [ __CLASS__, 'manage_users_columns' ] );
22-
add_filter( 'manage_users_custom_column', [ __CLASS__, 'manage_users_custom_column' ], 10, 3 );
22+
add_filter( 'manage_users_custom_column', [ __CLASS__, 'manage_users_custom_column' ], 99, 3 ); // priority must be higher than Jetpack's jetpack_show_connection_status (10).
2323
add_filter( 'users_list_table_query_args', [ __CLASS__, 'users_list_table_query_args' ] );
2424
}
2525

@@ -30,9 +30,7 @@ public static function init() {
3030
* @return array
3131
*/
3232
public static function manage_users_columns( $columns ) {
33-
if ( Site_Role::is_hub() ) {
34-
$columns['newspack_network_activity'] = __( 'Newspack Network Activity', 'newspack-network' );
35-
}
33+
$columns['newspack_network_activity'] = __( 'Newspack Network Activity', 'newspack-network' );
3634
if ( \Newspack_Network\Admin::use_experimental_auditing_features() ) {
3735
$columns['newspack_network_user'] = __( 'Network Original User', 'newspack-network' );
3836
}
@@ -60,36 +58,45 @@ public static function manage_users_custom_column( $value, $column_name, $user_i
6058
);
6159
}
6260
}
63-
if ( 'newspack_network_activity' === $column_name && Site_Role::is_hub() ) {
61+
if ( 'newspack_network_activity' === $column_name ) {
6462
$user = get_user_by( 'id', $user_id );
6563
if ( ! $user ) {
6664
return $value;
6765
}
66+
if ( Site_Role::is_hub() ) {
67+
$last_activity = \Newspack_Network\Hub\Stores\Event_Log::get( [ 'email' => $user->user_email ], 1 );
68+
if ( empty( $last_activity ) ) {
69+
return '-';
70+
}
6871

69-
$last_activity = \Newspack_Network\Hub\Stores\Event_Log::get( [ 'email' => $user->user_email ], 1 );
70-
71-
if ( empty( $last_activity ) ) {
72-
return '-';
72+
$event_log_url = add_query_arg(
73+
[
74+
'page' => EVENT_LOG_PAGE_SLUG,
75+
'email' => urlencode( $user->user_email ),
76+
],
77+
admin_url( 'admin.php' )
78+
);
79+
return sprintf(
80+
'%s: <code>%s</code><br><a href="%s">%s</a>',
81+
__( 'Last Activity', 'newspack-network' ),
82+
$last_activity[0]->get_summary(),
83+
$event_log_url,
84+
__( 'View all', 'newspack-network' )
85+
);
86+
} else {
87+
$event_log_url = add_query_arg(
88+
[
89+
'page' => EVENT_LOG_PAGE_SLUG,
90+
'email' => urlencode( $user->user_email ),
91+
],
92+
untrailingslashit( Node\Settings::get_hub_url() ) . '/wp-admin/admin.php'
93+
);
94+
return sprintf(
95+
'<a href="%s">%s</a>',
96+
$event_log_url,
97+
__( 'View activity', 'newspack-network' )
98+
);
7399
}
74-
75-
$last_activity = $last_activity[0];
76-
77-
$summary = $last_activity->get_summary();
78-
$event_log_url = add_query_arg(
79-
[
80-
'page' => EVENT_LOG_PAGE_SLUG,
81-
'email' => $user->user_email,
82-
],
83-
admin_url( 'admin.php' )
84-
);
85-
return sprintf(
86-
'%s: <code>%s</code><br><a href="%s">%s</a>',
87-
__( 'Last Activity', 'newspack-network' ),
88-
$summary,
89-
$event_log_url,
90-
__( 'View all', 'newspack-network' )
91-
);
92-
93100
}
94101
return $value;
95102
}
@@ -106,6 +113,10 @@ public static function users_list_table_query_args( $args ) {
106113
$args['role__in'] = explode( ',', sanitize_text_field( $_REQUEST['role__in'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
107114
unset( $args['role'] );
108115
}
116+
if ( isset( $_REQUEST['role__not_in'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
117+
$args['role__not_in'] = explode( ',', sanitize_text_field( $_REQUEST['role__not_in'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
118+
unset( $args['role'] );
119+
}
109120
return $args;
110121
}
111122
}

includes/hub/admin/class-nodes-list.php

Lines changed: 76 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,20 @@ public static function init() {
2727
add_action( 'admin_bar_menu', [ __CLASS__, 'admin_bar_menu' ], 100 );
2828
}
2929

30+
/**
31+
* Cache for site info responses.
32+
*
33+
* @var array
34+
*/
35+
private static $node_site_info_cache = [];
36+
37+
/**
38+
* Cache for Hub site info.
39+
*
40+
* @var array
41+
*/
42+
private static $hub_site_info = false;
43+
3044
/**
3145
* Modify columns on post type table
3246
*
@@ -37,16 +51,32 @@ public static function posts_columns( $columns ) {
3751
unset( $columns['date'] );
3852
unset( $columns['stats'] );
3953
if ( \Newspack_Network\Admin::use_experimental_auditing_features() ) {
54+
$sync_users_count = \Newspack_Network\Utils\Users::get_synchronized_users_count();
4055
$sync_users_info = sprintf(
4156
' <span class="dashicons dashicons-info-outline" title="%s"></span>',
4257
sprintf(
4358
/* translators: list of user roles which will entail synchronization */
4459
esc_attr__( 'Users with the following roles: %1$s (%2$d on the Hub)', 'newspack-network' ),
4560
implode( ', ', \Newspack_Network\Utils\Users::get_synced_user_roles() ),
46-
\Newspack_Network\Utils\Users::get_synchronized_users_count()
61+
$sync_users_count
62+
)
63+
);
64+
/* translators: %d is the synchronizable users count. */
65+
$columns['sync_users'] = sprintf( __( 'Synchronizable Users (%d)', 'newspack-network' ), $sync_users_count ) . $sync_users_info;
66+
if ( isset( $_GET['_newspack_user_discrepancies'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
67+
$columns['user_discrepancies'] = __( 'Discrepancies in Sync. Users', 'newspack-network' );
68+
}
69+
70+
$not_sync_users_info = sprintf(
71+
' <span class="dashicons dashicons-info-outline" title="%s"></span>',
72+
sprintf(
73+
/* translators: list of user roles which will entail synchronization */
74+
esc_attr__( 'Users with roles different than the following roles: %1$s (%2$d on the Hub)', 'newspack-network' ),
75+
implode( ', ', \Newspack_Network\Utils\Users::get_synced_user_roles() ),
76+
\Newspack_Network\Utils\Users::get_not_synchronized_users_count()
4777
)
4878
);
49-
$columns['sync_users'] = __( 'Synchronizable Users', 'newspack-network' ) . $sync_users_info;
79+
$columns['not_sync_users'] = __( 'Non-synchronizable Users', 'newspack-network' ) . $not_sync_users_info;
5080
}
5181
$columns['links'] = __( 'Links', 'newspack-network' );
5282
return $columns;
@@ -80,6 +110,38 @@ function ( $bookmark ) {
80110
</p>
81111
<?php
82112
}
113+
if ( ! isset( self::$node_site_info_cache[ $post_id ] ) ) {
114+
self::$node_site_info_cache[ $post_id ] = $node->get_site_info();
115+
}
116+
$node_site_info = self::$node_site_info_cache[ $post_id ];
117+
118+
if ( isset( $_GET['_newspack_user_discrepancies'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
119+
if ( ! self::$hub_site_info ) {
120+
self::$hub_site_info = [
121+
'sync_user_emails' => \Newspack_Network\Utils\Users::get_synchronized_users_emails(),
122+
];
123+
}
124+
125+
// Display user discrepancies.
126+
$node_users_emails = $node_site_info->sync_users_emails ?? [];
127+
// Users who are on the Hub but not on the Node.
128+
$not_on_node = array_diff( self::$hub_site_info['sync_user_emails'], $node_users_emails );
129+
// Users who are not on the Node but are on the Hub.
130+
$not_on_hub = array_diff( $node_users_emails, self::$hub_site_info['sync_user_emails'] );
131+
if ( 'user_discrepancies' === $column ) {
132+
?>
133+
<span>
134+
<?php
135+
echo esc_html(
136+
/* translators: %d - users on the Hub only, %d on the Node only */
137+
sprintf( __( '%1$d on the Hub only, %2$d on the Node only', 'newspack-network' ), count( $not_on_node ), count( $not_on_hub ) )
138+
);
139+
?>
140+
</span>
141+
<?php
142+
}
143+
}
144+
83145
if ( 'sync_users' === $column ) {
84146
$users_link = add_query_arg(
85147
[
@@ -88,7 +150,18 @@ function ( $bookmark ) {
88150
trailingslashit( $node->get_url() ) . 'wp-admin/users.php'
89151
);
90152
?>
91-
<a href="<?php echo esc_url( $users_link ); ?>"><?php echo esc_html( $node->get_sync_users_count() ); ?></a>
153+
<a href="<?php echo esc_url( $users_link ); ?>"><?php echo esc_html( $node_site_info->sync_users_count ?? 0 ); ?></a>
154+
<?php
155+
}
156+
if ( 'not_sync_users' === $column ) {
157+
$users_link = add_query_arg(
158+
[
159+
'role__not_in' => implode( ',', \Newspack_Network\Utils\Users::get_synced_user_roles() ),
160+
],
161+
trailingslashit( $node->get_url() ) . 'wp-admin/users.php'
162+
);
163+
?>
164+
<a href="<?php echo esc_url( $users_link ); ?>"><?php echo esc_html( $node_site_info->not_sync_users_count ?? 0 ); ?></a>
92165
<?php
93166
}
94167
}

includes/hub/class-node.php

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ public function get_bookmarks() {
174174
/**
175175
* Get site info.
176176
*/
177-
private function get_site_info() {
177+
public function get_site_info() {
178178
$response = wp_remote_get( // phpcs:ignore
179179
$this->get_url() . '/wp-json/newspack-network/v1/info',
180180
[
@@ -183,12 +183,4 @@ private function get_site_info() {
183183
);
184184
return json_decode( wp_remote_retrieve_body( $response ) );
185185
}
186-
187-
/**
188-
* Get synchronized users count.
189-
*/
190-
public function get_sync_users_count() {
191-
$site_info = $this->get_site_info();
192-
return $site_info->sync_users_count ?? 0;
193-
}
194186
}

includes/node/class-info-endpoints.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,11 @@ public static function register_routes() {
4646
public static function handle_info_request() {
4747
return rest_ensure_response(
4848
[
49-
'sync_users_count' => \Newspack_Network\Utils\Users::get_synchronized_users_count(),
49+
'sync_users_count' => \Newspack_Network\Utils\Users::get_synchronized_users_count(),
50+
'sync_users_emails' => \Newspack_Network\Utils\Users::get_synchronized_users_emails(),
51+
'not_sync_users_count' => \Newspack_Network\Utils\Users::get_not_synchronized_users_count(),
52+
'not_sync_users_emails' => \Newspack_Network\Utils\Users::get_not_synchronized_users_emails(),
53+
'no_role_users_emails' => \Newspack_Network\Utils\Users::get_no_role_users_emails(),
5054
]
5155
);
5256
}

includes/utils/class-users.php

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,13 +174,68 @@ public static function get_synced_user_roles() {
174174
* Get synchronized users count.
175175
*/
176176
public static function get_synchronized_users_count() {
177-
$users = get_users(
177+
return count( self::get_synchronized_users( [ 'id' ] ) );
178+
}
179+
180+
/**
181+
* Get synchronized users emails.
182+
*/
183+
public static function get_synchronized_users_emails() {
184+
return array_map( 'strtolower', array_column( self::get_synchronized_users( [ 'user_email' ] ), 'user_email' ) );
185+
}
186+
187+
/**
188+
* Get no-role users emails.
189+
*/
190+
public static function get_no_role_users_emails() {
191+
global $wpdb;
192+
$no_role_users_emails = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
193+
"SELECT user_email FROM wp_users WHERE ID IN (SELECT user_id FROM wp_usermeta WHERE meta_key = 'wp_capabilities' AND (meta_value = 'a:0:{}' OR meta_value = '')) OR ID NOT IN (SELECT user_id FROM wp_usermeta WHERE meta_key = 'wp_capabilities')"
194+
);
195+
return array_map( 'strtolower', array_column( $no_role_users_emails, 'user_email' ) );
196+
}
197+
198+
/**
199+
* Get synchronized users.
200+
*
201+
* @param array $fields Fields to return.
202+
*/
203+
public static function get_synchronized_users( $fields = [] ) {
204+
return get_users(
178205
[
179206
'role__in' => self::get_synced_user_roles(),
180-
'fields' => [ 'id' ],
207+
'fields' => $fields,
181208
'number' => -1,
182209
]
183210
);
184-
return count( $users );
211+
}
212+
213+
/**
214+
* Get not synchronized users count.
215+
*
216+
* @param array $fields Fields to return.
217+
*/
218+
public static function get_not_synchronized_users( $fields = [] ) {
219+
return get_users(
220+
[
221+
'role__not_in' => self::get_synced_user_roles(),
222+
'fields' => $fields,
223+
'number' => -1,
224+
]
225+
);
226+
}
227+
228+
/**
229+
* Get synchronized users emails.
230+
*/
231+
public static function get_not_synchronized_users_emails() {
232+
return array_map( 'strtolower', array_column( self::get_not_synchronized_users( [ 'user_email' ] ), 'user_email' ) );
233+
}
234+
235+
/**
236+
* Get not synchronized users count.
237+
*/
238+
public static function get_not_synchronized_users_count() {
239+
return count( self::get_not_synchronized_users( [ 'id' ] ) );
185240
}
186241
}

includes/woocommerce-memberships/class-admin.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,18 @@ public static function init() {
7474
/**
7575
* Get active members' emails.
7676
*
77-
* @param \WC_Memberships_Membership_Plan $plan the membership plan.
77+
* @param \WC_Memberships_Membership_Plan $plan The membership plan.
7878
*/
7979
public static function get_active_members_emails( $plan ) {
8080
$active_memberships = $plan->get_memberships( [ 'post_status' => 'wcm-active' ] );
8181
return array_map(
8282
function ( $membership ) {
8383
$user = get_user_by( 'id', $membership->get_user_id() );
84-
return $user->user_email;
84+
if ( $user ) {
85+
return strtolower( $user->user_email );
86+
} else {
87+
return '';
88+
}
8589
},
8690
$active_memberships
8791
);

0 commit comments

Comments
 (0)