From 7a169b55ad8ba941ce4bbcc883ad62c2b67e4c12 Mon Sep 17 00:00:00 2001
From: Adam Cassis
Date: Wed, 10 Apr 2024 10:31:55 +0200
Subject: [PATCH 1/3] fix(memberships-data): lowercase returned emails
---
includes/woocommerce-memberships/class-admin.php | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/includes/woocommerce-memberships/class-admin.php b/includes/woocommerce-memberships/class-admin.php
index 38a752e9..29178114 100644
--- a/includes/woocommerce-memberships/class-admin.php
+++ b/includes/woocommerce-memberships/class-admin.php
@@ -74,14 +74,18 @@ public static function init() {
/**
* Get active members' emails.
*
- * @param \WC_Memberships_Membership_Plan $plan the membership plan.
+ * @param \WC_Memberships_Membership_Plan $plan The membership plan.
*/
public static function get_active_members_emails( $plan ) {
$active_memberships = $plan->get_memberships( [ 'post_status' => 'wcm-active' ] );
return array_map(
function ( $membership ) {
$user = get_user_by( 'id', $membership->get_user_id() );
- return $user->user_email;
+ if ( $user ) {
+ return strtolower( $user->user_email );
+ } else {
+ return '';
+ }
},
$active_memberships
);
From 88221c18ef630ca3d89b47300b2af0cf6538e3eb Mon Sep 17 00:00:00 2001
From: Adam Cassis
Date: Thu, 23 May 2024 09:44:44 +0200
Subject: [PATCH 2/3] feat(auditing): user discrepancies
---
includes/class-users.php | 67 ++++++++++++---------
includes/hub/admin/class-nodes-list.php | 79 ++++++++++++++++++++++++-
includes/hub/class-node.php | 10 +---
includes/node/class-info-endpoints.php | 6 +-
includes/utils/class-users.php | 61 ++++++++++++++++++-
5 files changed, 179 insertions(+), 44 deletions(-)
diff --git a/includes/class-users.php b/includes/class-users.php
index 16fa3507..f5391d99 100644
--- a/includes/class-users.php
+++ b/includes/class-users.php
@@ -19,7 +19,7 @@ class Users {
*/
public static function init() {
add_filter( 'manage_users_columns', [ __CLASS__, 'manage_users_columns' ] );
- add_filter( 'manage_users_custom_column', [ __CLASS__, 'manage_users_custom_column' ], 10, 3 );
+ 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).
add_filter( 'users_list_table_query_args', [ __CLASS__, 'users_list_table_query_args' ] );
}
@@ -30,9 +30,7 @@ public static function init() {
* @return array
*/
public static function manage_users_columns( $columns ) {
- if ( Site_Role::is_hub() ) {
- $columns['newspack_network_activity'] = __( 'Newspack Network Activity', 'newspack-network' );
- }
+ $columns['newspack_network_activity'] = __( 'Newspack Network Activity', 'newspack-network' );
if ( \Newspack_Network\Admin::use_experimental_auditing_features() ) {
$columns['newspack_network_user'] = __( 'Network Original User', 'newspack-network' );
}
@@ -60,36 +58,45 @@ public static function manage_users_custom_column( $value, $column_name, $user_i
);
}
}
- if ( 'newspack_network_activity' === $column_name && Site_Role::is_hub() ) {
+ if ( 'newspack_network_activity' === $column_name ) {
$user = get_user_by( 'id', $user_id );
if ( ! $user ) {
return $value;
}
+ if ( Site_Role::is_hub() ) {
+ $last_activity = \Newspack_Network\Hub\Stores\Event_Log::get( [ 'email' => $user->user_email ], 1 );
+ if ( empty( $last_activity ) ) {
+ return '-';
+ }
- $last_activity = \Newspack_Network\Hub\Stores\Event_Log::get( [ 'email' => $user->user_email ], 1 );
-
- if ( empty( $last_activity ) ) {
- return '-';
+ $event_log_url = add_query_arg(
+ [
+ 'page' => EVENT_LOG_PAGE_SLUG,
+ 'email' => urlencode( $user->user_email ),
+ ],
+ admin_url( 'admin.php' )
+ );
+ return sprintf(
+ '%s: %s
%s',
+ __( 'Last Activity', 'newspack-network' ),
+ $last_activity[0]->get_summary(),
+ $event_log_url,
+ __( 'View all', 'newspack-network' )
+ );
+ } else {
+ $event_log_url = add_query_arg(
+ [
+ 'page' => EVENT_LOG_PAGE_SLUG,
+ 'email' => urlencode( $user->user_email ),
+ ],
+ untrailingslashit( Node\Settings::get_hub_url() ) . '/wp-admin/admin.php'
+ );
+ return sprintf(
+ '%s',
+ $event_log_url,
+ __( 'View activity', 'newspack-network' )
+ );
}
-
- $last_activity = $last_activity[0];
-
- $summary = $last_activity->get_summary();
- $event_log_url = add_query_arg(
- [
- 'page' => EVENT_LOG_PAGE_SLUG,
- 'email' => $user->user_email,
- ],
- admin_url( 'admin.php' )
- );
- return sprintf(
- '%s: %s
%s',
- __( 'Last Activity', 'newspack-network' ),
- $summary,
- $event_log_url,
- __( 'View all', 'newspack-network' )
- );
-
}
return $value;
}
@@ -106,6 +113,10 @@ public static function users_list_table_query_args( $args ) {
$args['role__in'] = explode( ',', sanitize_text_field( $_REQUEST['role__in'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
unset( $args['role'] );
}
+ if ( isset( $_REQUEST['role__not_in'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ $args['role__not_in'] = explode( ',', sanitize_text_field( $_REQUEST['role__not_in'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ unset( $args['role'] );
+ }
return $args;
}
}
diff --git a/includes/hub/admin/class-nodes-list.php b/includes/hub/admin/class-nodes-list.php
index d02d43f5..caba2e5e 100644
--- a/includes/hub/admin/class-nodes-list.php
+++ b/includes/hub/admin/class-nodes-list.php
@@ -27,6 +27,20 @@ public static function init() {
add_action( 'admin_bar_menu', [ __CLASS__, 'admin_bar_menu' ], 100 );
}
+ /**
+ * Cache for site info responses.
+ *
+ * @var array
+ */
+ private static $node_site_info_cache = [];
+
+ /**
+ * Cache for Hub site info.
+ *
+ * @var array
+ */
+ private static $hub_site_info = false;
+
/**
* Modify columns on post type table
*
@@ -37,16 +51,32 @@ public static function posts_columns( $columns ) {
unset( $columns['date'] );
unset( $columns['stats'] );
if ( \Newspack_Network\Admin::use_experimental_auditing_features() ) {
+ $sync_users_count = \Newspack_Network\Utils\Users::get_synchronized_users_count();
$sync_users_info = sprintf(
' ',
sprintf(
/* translators: list of user roles which will entail synchronization */
esc_attr__( 'Users with the following roles: %1$s (%2$d on the Hub)', 'newspack-network' ),
implode( ', ', \Newspack_Network\Utils\Users::get_synced_user_roles() ),
- \Newspack_Network\Utils\Users::get_synchronized_users_count()
+ $sync_users_count
+ )
+ );
+ /* translators: %d is the synchronizable users count. */
+ $columns['sync_users'] = sprintf( __( 'Synchronizable Users (%d)', 'newspack-network' ), $sync_users_count ) . $sync_users_info;
+ if ( isset( $_GET['_newspack_user_discrepancies'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ $columns['user_discrepancies'] = __( 'Discrepancies in Sync. Users', 'newspack-network' );
+ }
+
+ $not_sync_users_info = sprintf(
+ ' ',
+ sprintf(
+ /* translators: list of user roles which will entail synchronization */
+ esc_attr__( 'Users with roles different than the following roles: %1$s (%2$d on the Hub)', 'newspack-network' ),
+ implode( ', ', \Newspack_Network\Utils\Users::get_synced_user_roles() ),
+ \Newspack_Network\Utils\Users::get_not_synchronized_users_count()
)
);
- $columns['sync_users'] = __( 'Synchronizable Users', 'newspack-network' ) . $sync_users_info;
+ $columns['not_sync_users'] = __( 'Non-synchronizable Users', 'newspack-network' ) . $not_sync_users_info;
}
$columns['links'] = __( 'Links', 'newspack-network' );
return $columns;
@@ -80,6 +110,38 @@ function ( $bookmark ) {
get_site_info();
+ }
+ $node_site_info = self::$node_site_info_cache[ $post_id ];
+
+ if ( isset( $_GET['_newspack_user_discrepancies'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ if ( ! self::$hub_site_info ) {
+ self::$hub_site_info = [
+ 'sync_user_emails' => \Newspack_Network\Utils\Users::get_synchronized_users_emails(),
+ ];
+ }
+
+ // Display user discrepancies.
+ $node_users_emails = $node_site_info->sync_users_emails ?? [];
+ // Users who are on the Hub but not on the Node.
+ $not_on_node = array_diff( self::$hub_site_info['sync_user_emails'], $node_users_emails );
+ // Users who are not on the Node but are on the Hub.
+ $not_on_hub = array_diff( $node_users_emails, self::$hub_site_info['sync_user_emails'] );
+ if ( 'user_discrepancies' === $column ) {
+ ?>
+
+
+
+ get_url() ) . 'wp-admin/users.php'
);
?>
- get_sync_users_count() ); ?>
+ sync_users_count ?? 0 ); ?>
+ implode( ',', \Newspack_Network\Utils\Users::get_synced_user_roles() ),
+ ],
+ trailingslashit( $node->get_url() ) . 'wp-admin/users.php'
+ );
+ ?>
+ not_sync_users_count ?? 0 ); ?>
get_url() . '/wp-json/newspack-network/v1/info',
[
@@ -183,12 +183,4 @@ private function get_site_info() {
);
return json_decode( wp_remote_retrieve_body( $response ) );
}
-
- /**
- * Get synchronized users count.
- */
- public function get_sync_users_count() {
- $site_info = $this->get_site_info();
- return $site_info->sync_users_count ?? 0;
- }
}
diff --git a/includes/node/class-info-endpoints.php b/includes/node/class-info-endpoints.php
index b5b57238..6761207c 100644
--- a/includes/node/class-info-endpoints.php
+++ b/includes/node/class-info-endpoints.php
@@ -46,7 +46,11 @@ public static function register_routes() {
public static function handle_info_request() {
return rest_ensure_response(
[
- 'sync_users_count' => \Newspack_Network\Utils\Users::get_synchronized_users_count(),
+ 'sync_users_count' => \Newspack_Network\Utils\Users::get_synchronized_users_count(),
+ 'sync_users_emails' => \Newspack_Network\Utils\Users::get_synchronized_users_emails(),
+ 'not_sync_users_count' => \Newspack_Network\Utils\Users::get_not_synchronized_users_count(),
+ 'not_sync_users_emails' => \Newspack_Network\Utils\Users::get_not_synchronized_users_emails(),
+ 'no_role_users_emails' => \Newspack_Network\Utils\Users::get_no_role_users_emails(),
]
);
}
diff --git a/includes/utils/class-users.php b/includes/utils/class-users.php
index 41aa3c58..c4baeaa1 100644
--- a/includes/utils/class-users.php
+++ b/includes/utils/class-users.php
@@ -174,13 +174,68 @@ public static function get_synced_user_roles() {
* Get synchronized users count.
*/
public static function get_synchronized_users_count() {
- $users = get_users(
+ return count( self::get_synchronized_users( [ 'id' ] ) );
+ }
+
+ /**
+ * Get synchronized users emails.
+ */
+ public static function get_synchronized_users_emails() {
+ return array_map( 'strtolower', array_column( self::get_synchronized_users( [ 'user_email' ] ), 'user_email' ) );
+ }
+
+ /**
+ * Get no-role users emails.
+ */
+ public static function get_no_role_users_emails() {
+ global $wpdb;
+ $no_role_users_emails = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
+ "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')"
+ );
+ return array_map( 'strtolower', array_column( $no_role_users_emails, 'user_email' ) );
+ }
+
+ /**
+ * Get synchronized users.
+ *
+ * @param array $fields Fields to return.
+ */
+ public static function get_synchronized_users( $fields = [] ) {
+ return get_users(
[
'role__in' => self::get_synced_user_roles(),
- 'fields' => [ 'id' ],
+ 'fields' => $fields,
'number' => -1,
]
);
- return count( $users );
+ }
+
+ /**
+ * Get not synchronized users count.
+ *
+ * @param array $fields Fields to return.
+ */
+ public static function get_not_synchronized_users( $fields = [] ) {
+ return get_users(
+ [
+ 'role__not_in' => self::get_synced_user_roles(),
+ 'fields' => $fields,
+ 'number' => -1,
+ ]
+ );
+ }
+
+ /**
+ * Get synchronized users emails.
+ */
+ public static function get_not_synchronized_users_emails() {
+ return array_map( 'strtolower', array_column( self::get_not_synchronized_users( [ 'user_email' ] ), 'user_email' ) );
+ }
+
+ /**
+ * Get not synchronized users count.
+ */
+ public static function get_not_synchronized_users_count() {
+ return count( self::get_not_synchronized_users( [ 'id' ] ) );
}
}
From 2b15092383b3ee96202f534f32d78bc5f0205aee Mon Sep 17 00:00:00 2001
From: Adam Cassis
Date: Mon, 1 Jul 2024 09:08:52 +0200
Subject: [PATCH 3/3] Update includes/hub/admin/class-nodes-list.php
Co-authored-by: Derrick Koo
---
includes/hub/admin/class-nodes-list.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/includes/hub/admin/class-nodes-list.php b/includes/hub/admin/class-nodes-list.php
index caba2e5e..1332753b 100644
--- a/includes/hub/admin/class-nodes-list.php
+++ b/includes/hub/admin/class-nodes-list.php
@@ -134,7 +134,7 @@ function ( $bookmark ) {