Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experimental auditing features #79

Merged
merged 12 commits into from
Apr 10, 2024
14 changes: 14 additions & 0 deletions includes/class-admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -191,4 +191,18 @@ public static function admin_init() {
*/
public static function enqueue_scripts() {
}

/**
* Has experimental auditing features?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: A return tag is missing and would be helpful here and in a handful of other methods in this PR

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 121cc68

*
* @return bool True if experimental auditing features are enabled.
*/
public static function use_experimental_auditing_features() {
$user_slug = defined( 'NEWSPACK_NETWORK_EXPERIMENTAL_AUDITING_USER' ) ? NEWSPACK_NETWORK_EXPERIMENTAL_AUDITING_USER : false;
if ( ! $user_slug ) {
return false;
}
$user = get_user_by( 'login', $user_slug );
return $user && get_current_user_id() === $user->ID;
}
}
2 changes: 2 additions & 0 deletions includes/class-initializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class Initializer {
*/
public static function init() {
Admin::init();
Users::init();

if ( Site_Role::is_hub() ) {
Hub\Admin::init();
Expand All @@ -37,6 +38,7 @@ public static function init() {
if ( Site_Role::is_node() ) {
if ( Node\Settings::get_hub_url() ) {
Node\Webhook::init();
Node\Info_Endpoints::init();
Node\Pulling::init();
Rest_Authenticaton::init_node_filters();
}
Expand Down
109 changes: 109 additions & 0 deletions includes/class-users.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<?php
/**
* Newspack Users Admin page
*
* @package Newspack
*/

namespace Newspack_Network;

/**
* Class to handle the Users admin page
*/
class Users {

/**
* Runs the initialization.
*/
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( 'users_list_table_query_args', [ __CLASS__, 'users_list_table_query_args' ] );
}

/**
* Add a custom column to the Users table
*
* @param array $columns The current columns.
* @return array
*/
public static function manage_users_columns( $columns ) {
if ( Site_Role::is_hub() ) {
$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' );
}
return $columns;
}

/**
* Add content to the custom column
*
* @param string $value The current column value.
* @param string $column_name The current column name.
* @param int $user_id The current user ID.
* @return string
*/
public static function manage_users_custom_column( $value, $column_name, $user_id ) {
if ( 'newspack_network_user' === $column_name ) {
$remote_site = get_user_meta( $user_id, \Newspack_Network\Utils\Users::USER_META_REMOTE_SITE, true );
$remote_id = (int) get_user_meta( $user_id, \Newspack_Network\Utils\Users::USER_META_REMOTE_ID, true );
if ( $remote_site ) {
return sprintf(
'<a href="%swp-admin/user-edit.php?user_id=%d">%s</a>',
trailingslashit( esc_url( $remote_site ) ),
$remote_id,
sprintf( '%s (#%d)', $remote_site, $remote_id )
);
}
}
if ( 'newspack_network_activity' === $column_name && Site_Role::is_hub() ) {
$user = get_user_by( 'id', $user_id );
if ( ! $user ) {
return $value;
}

$last_activity = \Newspack_Network\Hub\Stores\Event_Log::get( [ 'email' => $user->user_email ], 1 );

if ( empty( $last_activity ) ) {
return '-';
}

$last_activity = $last_activity[0];

$summary = $last_activity->get_summary();
$event_log_url = add_query_arg(
[
'page' => \Newspack_Network\Hub\Admin\Event_Log::PAGE_SLUG,
'email' => $user->user_email,
],
admin_url( 'admin.php' )
);
return sprintf(
'%s: <code>%s</code><br><a href="%s">%s</a>',
__( 'Last Activity', 'newspack-network' ),
$summary,
$event_log_url,
__( 'View all', 'newspack-network' )
);

}
return $value;
}

/**
* Handle the filtering of users by multiple roles.
* Unfortunatelly, `get_views` and `get_views_links` are not filterable, so "All" will
* be displayed as the active filter.
*
* @param array $args The current query args.
*/
public static function users_list_table_query_args( $args ) {
if ( isset( $_REQUEST['role__in'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$args['role__in'] = explode( ',', sanitize_text_field( $_REQUEST['role__in'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
unset( $args['role'] );
}
return $args;
}
}
10 changes: 9 additions & 1 deletion includes/cli/backfillers/class-reader-registered.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

namespace Newspack_Network\Backfillers;

use WP_CLI;

/**
* Backfiller class.
*/
Expand All @@ -29,10 +31,14 @@ protected function get_processed_item_output( $event ) {
* @return \Newspack_Network\Incoming_Events\Abstract_Incoming_Event[] $events An array of events.
*/
public function get_events() {
$roles_to_sync = \Newspack_Network\Utils\Users::get_synced_user_roles();
if ( empty( $roles_to_sync ) ) {
WP_CLI::error( 'Incompatible Newspack plugin version or no roles to sync.' );
}
// Get all users registered between this-> and $end.
$users = get_users(
[
'role__in' => \Newspack\Reader_Activation::get_reader_roles(),
'role__in' => $roles_to_sync,
'date_query' => [
'after' => $this->start,
'before' => $this->end,
Expand All @@ -49,6 +55,8 @@ public function get_events() {
]
);

WP_CLI::line( sprintf( 'Found %s user(s) eligible for sync.', count( $users ) ) );

$this->maybe_initialize_progress_bar( 'Processing users', count( $users ) );

$events = [];
Expand Down
3 changes: 0 additions & 3 deletions includes/cli/class-data-backfill.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,6 @@ public static function data_backfill( $args, $assoc_args ) { // phpcs:ignore Gen
}
WP_CLI::line( '' );

if ( ! method_exists( '\Newspack\Reader_Activation', 'get_reader_roles' ) ) {
WP_CLI::error( 'Incompatible Newspack plugin version.' );
}
if ( $live ) {
WP_CLI::line( '⚡️ Heads up! Running live, data will be updated.' );
} else {
Expand Down
67 changes: 36 additions & 31 deletions includes/hub/admin/class-membership-plans-table.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,6 @@
* The Membership_Plans Table
*/
class Membership_Plans_Table extends \WP_List_Table {
/**
* Whether to show local or network plans.
*
* @var bool
*/
private $is_local = false;

/**
* Constructs the controller.
*
* @param bool $is_local Whether to show local or network plans.
*/
public function __construct( $is_local = false ) {
$this->is_local = $is_local;
parent::__construct();
}

/**
* Get the table columns
*
Expand All @@ -42,12 +25,12 @@ public function get_columns() {
'id' => __( 'ID', 'newspack-network' ),
'name' => __( 'Name', 'newspack-network' ),
];
if ( $this->is_local ) {
$columns['node_url'] = __( '-', 'newspack-network' );
} else {
$columns['node_url'] = __( 'Node URL', 'newspack-network' );
}
$columns['site_url'] = __( 'Site URL', 'newspack-network' );
$columns['network_pass_id'] = __( 'Network ID', 'newspack-network' );
if ( \Newspack_Network\Admin::use_experimental_auditing_features() ) {
$columns['active_members_count'] = __( 'Active Members', 'newspack-network' );
$columns['network_pass_discrepancies'] = __( 'Discrepancies', 'newspack-network' );
}
$columns['links'] = __( 'Links', 'newspack-network' );
return $columns;
}
Expand All @@ -57,11 +40,7 @@ public function get_columns() {
*/
public function prepare_items() {
$this->_column_headers = [ $this->get_columns(), [], [], 'id' ];
if ( $this->is_local ) {
$this->items = Membership_Plans::get_local_membership_plans();
} else {
$this->items = Membership_Plans::get_membershp_plans_from_nodes();
}
$this->items = Membership_Plans::get_membershp_plans_from_network();
}

/**
Expand All @@ -72,16 +51,42 @@ public function prepare_items() {
* @return string
*/
public function column_default( $item, $column_name ) {
$memberships_list_url = sprintf( '%s/wp-admin/edit.php?s&post_status=wcm-active&post_type=wc_user_membership&post_parent=%d', $item['site_url'], $item['id'] );

if ( $column_name === 'network_pass_id' && $item[ $column_name ] ) {
return sprintf( '<code>%s</code>', $item[ $column_name ] );
}
if ( $column_name === 'links' ) {
$edit_url = get_edit_post_link( $item['id'] );
if ( isset( $item['node_url'] ) ) {
$edit_url = sprintf( '%s/wp-admin/post.php?post=%d&action=edit', $item['node_url'], $item['id'] );
if ( $column_name === 'network_pass_discrepancies' && isset( $item['network_pass_discrepancies'] ) && $item['network_pass_id'] ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we be checking $item['network_pass_id'] with isset as well? Will this always be present at this stage?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for that, network_pass_id is not optional. network_pass_discrepancies is, because it's only included if the auditing features are enabled.

$discrepancies = $item['network_pass_discrepancies'];
$count = count( $discrepancies );
if ( $count === 0 ) {
return esc_html__( 'None', 'newspack-network' );
}

$memberships_list_url_with_emails_url = add_query_arg(
\Newspack_Network\Woocommerce_Memberships\Admin::MEMBERSHIPS_TABLE_EMAILS_QUERY_PARAM,
implode( ',', $discrepancies ),
$memberships_list_url
);
$message = sprintf(
/* translators: %d is the number of members */
_n(
'%d member doesn\'t match the shared member pool',
'%d members don\'t match the shared member pool',
$count,
'newspack-plugin'
),
$count
);
return sprintf( '<a href="%s">%s</a>', esc_url( $memberships_list_url_with_emails_url ), esc_html( $message ) );
}
if ( $column_name === 'links' ) {
$edit_url = sprintf( '%s/wp-admin/post.php?post=%d&action=edit', $item['site_url'], $item['id'] );
return sprintf( '<a href="%s">%s</a>', esc_url( $edit_url ), esc_html__( 'Edit', 'newspack-network' ) );
}
if ( $column_name === 'active_members_count' && $item[ $column_name ] ) {
return sprintf( '<a href="%s">%s</a>', esc_url( $memberships_list_url ), $item[ $column_name ] );
}
return isset( $item[ $column_name ] ) ? $item[ $column_name ] : '';
}
}
Loading