| 1 |
<?php |
| 2 |
/** |
| 3 |
* Add indicator to show that a password reset has been |
| 4 |
* enforced for the user |
| 5 |
* |
| 6 |
* @package Teydea_Studio\Password_Reset |
| 7 |
*/ |
| 8 |
|
| 9 |
namespace Teydea_Studio\Password_Reset\Modules\Reset_Indicator; |
| 10 |
|
| 11 |
use Teydea_Studio\Password_Reset\Dependencies\Utils; |
| 12 |
use Teydea_Studio\Password_Reset\User; |
| 13 |
use WP_User; |
| 14 |
|
| 15 |
if ( ! defined( 'ABSPATH' ) ) { |
| 16 |
exit; // @codeCoverageIgnore |
| 17 |
} |
| 18 |
|
| 19 |
/** |
| 20 |
* The "Module_Reset_Indicator" class |
| 21 |
*/ |
| 22 |
final class Module_Reset_Indicator extends Utils\Module { |
| 23 |
/** |
| 24 |
* Column slug |
| 25 |
* |
| 26 |
* @var string |
| 27 |
*/ |
| 28 |
const COLUMN_SLUG = 'password_reset_enforcement__status'; |
| 29 |
|
| 30 |
/** |
| 31 |
* Register hooks |
| 32 |
* |
| 33 |
* @return void |
| 34 |
*/ |
| 35 |
public function register(): void { |
| 36 |
// Add custom column to the multisite users list table. |
| 37 |
add_filter( 'wpmu_users_columns', [ $this, 'add_custom_column' ] ); |
| 38 |
|
| 39 |
// Add custom column to the single-site users list table. |
| 40 |
add_filter( 'manage_users_columns', [ $this, 'add_custom_column' ] ); |
| 41 |
|
| 42 |
// Render the content of the custom column. |
| 43 |
add_filter( 'manage_users_custom_column', [ $this, 'render_custom_column' ], 10, 3 ); |
| 44 |
|
| 45 |
// Enqueue the column's stylesheet on the users list table. |
| 46 |
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] ); |
| 47 |
|
| 48 |
// Register the admin notice on network admin user profile. |
| 49 |
add_action( 'network_admin_notices', [ $this, 'maybe_register_admin_notice' ] ); |
| 50 |
|
| 51 |
// Register the admin notice on single-site user profile. |
| 52 |
add_action( 'admin_notices', [ $this, 'maybe_register_admin_notice' ] ); |
| 53 |
} |
| 54 |
|
| 55 |
/** |
| 56 |
* Enqueue the column stylesheet on the users list table |
| 57 |
* |
| 58 |
* @return void |
| 59 |
*/ |
| 60 |
public function enqueue_assets(): void { |
| 61 |
$screen = new Utils\Screen( $this->container ); |
| 62 |
|
| 63 |
if ( $screen->is( 'users' ) || $screen->is( 'users-network' ) ) { |
| 64 |
$asset = new Utils\Asset( $this->container, 'reset-indicator' ); |
| 65 |
$asset->enqueue_style(); |
| 66 |
} |
| 67 |
} |
| 68 |
|
| 69 |
/** |
| 70 |
* Add custom column to the users list table |
| 71 |
* |
| 72 |
* @param array<string,string> $columns Existing columns. |
| 73 |
* |
| 74 |
* @return array<string,string> Modified columns. |
| 75 |
*/ |
| 76 |
public function add_custom_column( array $columns ): array { |
| 77 |
// Get the current user. |
| 78 |
$current_user = new User( $this->container ); |
| 79 |
|
| 80 |
// Ensure current user has the managing permissions. |
| 81 |
if ( $current_user->has_managing_permissions() ) { |
| 82 |
$columns[ self::COLUMN_SLUG ] = __( 'Password Reset Enforced?', 'password-reset-enforcement' ); |
| 83 |
} |
| 84 |
|
| 85 |
return $columns; |
| 86 |
} |
| 87 |
|
| 88 |
/** |
| 89 |
* Render the content of the custom column |
| 90 |
* |
| 91 |
* @param string $value The column value. |
| 92 |
* @param string $column_name The column name. |
| 93 |
* @param int $user_id The user ID. |
| 94 |
* |
| 95 |
* @return string The modified column value. |
| 96 |
*/ |
| 97 |
public function render_custom_column( string $value, string $column_name, int $user_id ): string { |
| 98 |
if ( self::COLUMN_SLUG === $column_name ) { |
| 99 |
$wp_user = get_user_by( 'ID', $user_id ); |
| 100 |
|
| 101 |
if ( $wp_user instanceof WP_User ) { |
| 102 |
$user = new User( $this->container, $wp_user ); |
| 103 |
|
| 104 |
if ( false === $user->is_password_reset_required() ) { |
| 105 |
$value = sprintf( '<p class="password-reset-enforcement-reset-indicator__status">%s</p>', esc_html__( 'No', 'password-reset-enforcement' ) ); |
| 106 |
} else { |
| 107 |
$value = sprintf( '<p class="password-reset-enforcement-reset-indicator__status password-reset-enforcement-reset-indicator__status--enforced">%s</p>', esc_html__( 'Yes', 'password-reset-enforcement' ) ); |
| 108 |
$data = $user->get_password_reset_request_data(); |
| 109 |
|
| 110 |
if ( null !== $data ) { |
| 111 |
$items = [ |
| 112 |
__( 'Requested at:', 'password-reset-enforcement' ) => sprintf( |
| 113 |
// Translators: %1$s - timestamp, %2$s - human-readable time difference. |
| 114 |
__( '%1$s (%2$s ago)', 'password-reset-enforcement' ), |
| 115 |
Utils\Date_Time::get_i18n_datetime_string( $data['requested_at'] ), |
| 116 |
human_time_diff( $data['requested_at'] ), |
| 117 |
), |
| 118 |
__( 'Requestor:', 'password-reset-enforcement' ) => $user->get_requestor_display_name( $data['requested_by'] ), |
| 119 |
]; |
| 120 |
|
| 121 |
$value .= '<ul class="password-reset-enforcement-reset-indicator__details">'; |
| 122 |
|
| 123 |
foreach ( $items as $label => $item ) { |
| 124 |
$value .= sprintf( '<li><strong class="password-reset-enforcement-reset-indicator__detail-label">%s</strong> %s</li>', esc_html( $label ), esc_html( $item ) ); |
| 125 |
} |
| 126 |
|
| 127 |
$value .= '</ul>'; |
| 128 |
} |
| 129 |
} |
| 130 |
} |
| 131 |
} |
| 132 |
|
| 133 |
return $value; |
| 134 |
} |
| 135 |
|
| 136 |
/** |
| 137 |
* Maybe register the admin notice |
| 138 |
* |
| 139 |
* @return void |
| 140 |
*/ |
| 141 |
public function maybe_register_admin_notice(): void { |
| 142 |
$screen = new Utils\Screen( $this->container ); |
| 143 |
|
| 144 |
if ( ! $screen->is( 'user-edit' ) ) { |
| 145 |
return; |
| 146 |
} |
| 147 |
|
| 148 |
$user_id = isset( $_GET['user_id'] ) ? Utils\Type::ensure_int( wp_unslash( $_GET['user_id'] ) ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized |
| 149 |
|
| 150 |
if ( 0 === $user_id ) { |
| 151 |
return; |
| 152 |
} |
| 153 |
|
| 154 |
$wp_user = get_user_by( 'ID', $user_id ); |
| 155 |
|
| 156 |
if ( $wp_user instanceof WP_User ) { |
| 157 |
$user = new User( $this->container, $wp_user ); |
| 158 |
|
| 159 |
if ( $user->is_password_reset_required() ) { |
| 160 |
$data = $user->get_password_reset_request_data(); |
| 161 |
|
| 162 |
if ( null === $data ) { |
| 163 |
return; |
| 164 |
} |
| 165 |
|
| 166 |
$requestor = $user->get_requestor_display_name( $data['requested_by'] ); |
| 167 |
|
| 168 |
// Register the admin notice. |
| 169 |
wp_admin_notice( |
| 170 |
wp_kses( |
| 171 |
sprintf( |
| 172 |
// Translators: %1$s - user display name, %2$s - human-readable time difference. |
| 173 |
__( 'A password reset was enforced for this user by <strong>%1$s %2$s ago</strong>, but they have not completed it yet. Please make sure they are informed.', 'password-reset-enforcement' ), |
| 174 |
$requestor, |
| 175 |
human_time_diff( $data['requested_at'] ), |
| 176 |
), |
| 177 |
[ 'strong' => [] ], |
| 178 |
), |
| 179 |
[ 'type' => 'info' ], |
| 180 |
); |
| 181 |
} |
| 182 |
} |
| 183 |
} |
| 184 |
} |
| 185 |
|