User Switching - Version 1.6.0

Version Description

Download this release

Release Info

Developer johnbillion
Plugin Icon 128x128 User Switching
Version 1.6.0
Comparing to
See all releases

Code changes from version 1.5.8 to 1.6.0

Files changed (3) hide show
  1. .editorconfig +16 -0
  2. readme.md +122 -107
  3. user-switching.php +206 -96
.editorconfig ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ root = true
2
+
3
+ [*]
4
+ charset = utf-8
5
+ end_of_line = lf
6
+ insert_final_newline = true
7
+ indent_style = tab
8
+ indent_size = 4
9
+ trim_trailing_whitespace = true
10
+
11
+ [*.md]
12
+ trim_trailing_whitespace = false
13
+
14
+ [*.yml]
15
+ indent_style = space
16
+ indent_size = 2
readme.md CHANGED
@@ -1,11 +1,11 @@
1
  # User Switching
2
 
3
- Stable tag: 1.5.8
4
  Requires at least: 3.7
5
  Tested up to: 6.0
6
  Requires PHP: 5.3
7
  License: GPL v2 or later
8
- Tags: users, profiles, user switching, fast user switching, multisite, buddypress, bbpress, become, user management, developer
9
  Contributors: johnbillion
10
  Donate link: https://github.com/sponsors/johnbillion
11
 
@@ -13,21 +13,20 @@ Donate link: https://github.com/sponsors/johnbillion
13
 
14
  Instant switching between user accounts in WordPress.
15
 
16
- [![](https://img.shields.io/badge/ethical-open%20source-4baaaa.svg?style=for-the-badge)](#ethical-open-source)
17
- [![](https://img.shields.io/wordpress/plugin/installs/user-switching?style=for-the-badge)](https://wordpress.org/plugins/user-switching/)
18
- [![](https://img.shields.io/github/workflow/status/johnbillion/user-switching/Test/develop?style=for-the-badge)](https://github.com/johnbillion/user-switching/actions)
19
 
20
  ## Description
21
 
22
- This plugin allows you to quickly swap between user accounts in WordPress at the click of a button. You'll be instantly logged out and logged in as your desired user. This is handy for test environments where you regularly log out and in between different accounts, or for administrators who need to switch between multiple accounts.
23
 
24
  ### Features
25
 
26
  * Switch user: Instantly switch to any user account from the *Users* screen.
27
  * Switch back: Instantly switch back to your originating account.
28
  * Switch off: Log out of your account but retain the ability to instantly switch back in again.
29
- * Switching between users is secure (see the *Security* section below).
30
- * Compatible with WordPress, WordPress Multisite, WooCommerce, BuddyPress, bbPress, and most two-factor authentication plugins.
31
 
32
  ### Security
33
 
@@ -55,7 +54,7 @@ I maintain several other plugins for developers. Check them out:
55
 
56
  ### Privacy Statement
57
 
58
- User Switching makes use of browser cookies in order to allow users to switch to another account. Its cookies operate using the same mechanism as the authentication cookies in WordPress core, therefore their values contain the user's `user_login` field in plain text which should be treated as potentially personally identifiable information. The names of the cookies are:
59
 
60
  * `wordpress_user_sw_{COOKIEHASH}`
61
  * `wordpress_user_sw_secure_{COOKIEHASH}`
@@ -65,29 +64,18 @@ User Switching does not send data to any third party, nor does it include any th
65
 
66
  See also the FAQ for some questions relating to privacy and safety when switching between users.
67
 
68
- ### Ethical Open Source
69
-
70
- User Switching is considered **Ethical Open Source** because it meets all of the criteria of [The Ethical Source Definition (ESD)](https://ethicalsource.dev/definition/):
71
-
72
- 1. It benefits the commons.
73
- 2. It is created in the open.
74
- 3. Its community is welcoming and just.
75
- 4. It puts accessibility first.
76
- 5. It prioritizes user safety.
77
- 6. It protects user privacy.
78
- 7. It encourages fair compensation.
79
-
80
  ## Screenshots
81
 
82
- 1. The *Switch To* link on the Users screen<br>![The Switch To link on the Users screen](.wordpress-org/screenshot-1.png)
83
-
84
- 2. The *Switch To* link on a user's profile<br>![The Switch To link on a user's profile](.wordpress-org/screenshot-2.png)
 
85
 
86
  ## Frequently Asked Questions
87
 
88
  ### Does this plugin work with PHP 8?
89
 
90
- Yes.
91
 
92
  ### What does "Switch off" mean?
93
 
@@ -99,6 +87,10 @@ The *Switch Off* link can be found in your profile menu in the WordPress toolbar
99
 
100
  Yes, and you'll also be able to switch users from the Users screen in Network Admin.
101
 
 
 
 
 
102
  ### Does this plugin work with BuddyPress?
103
 
104
  Yes, and you'll also be able to switch users from member profile screens and the member listing screen.
@@ -107,10 +99,6 @@ Yes, and you'll also be able to switch users from member profile screens and the
107
 
108
  Yes, and you'll also be able to switch users from member profile screens.
109
 
110
- ### Does this plugin work with WooCommerce?
111
-
112
- Yes. For maximum compatibility you should use WooCommerce version 3.6 or later.
113
-
114
  ### Does this plugin work if my site is using a two-factor authentication plugin?
115
 
116
  Yes, mostly.
@@ -125,18 +113,33 @@ A user needs the `edit_users` capability in order to switch user accounts. By de
125
 
126
  Yes. The `switch_users` meta capability can be explicitly granted to a user or a role to allow them to switch users regardless of whether or not they have the `edit_users` capability. For practical purposes, the user or role will also need the `list_users` capability so they can access the Users menu in the WordPress admin area.
127
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  ### Can the ability to switch accounts be denied from users?
129
 
130
  Yes. User capabilities in WordPress can be set to `false` to deny them from a user. Denying the `switch_users` capability prevents the user from switching users, even if they have the `edit_users` capability.
131
 
132
- add_filter( 'user_has_cap', function( $allcaps, $caps, $args, $user ) {
133
- if ( 'switch_to_user' === $args[0] ) {
134
- if ( my_condition() ) {
135
- $allcaps['switch_users'] = false;
136
- }
137
- }
138
- return $allcaps;
139
- }, 9, 4 );
 
 
140
 
141
  Note that this needs to happen before User Switching's own capability filtering, hence the priority of `9`.
142
 
@@ -144,41 +147,47 @@ Note that this needs to happen before User Switching's own capability filtering,
144
 
145
  Yes. Use the `user_switching::maybe_switch_url()` method for this. It takes care of authentication and returns a nonce-protected URL for the current user to switch into the provided user account.
146
 
147
- if ( method_exists( 'user_switching', 'maybe_switch_url' ) ) {
148
- $url = user_switching::maybe_switch_url( $target_user );
149
- if ( $url ) {
150
- printf(
151
- '<a href="%1$s">Switch to %2$s</a>',
152
- esc_url( $url ),
153
- esc_html( $target_user->display_name )
154
- );
155
- }
156
- }
 
 
157
 
158
  This link also works for switching back to the original user, but if you want an explicit link for this you can use the following code:
159
 
160
- if ( method_exists( 'user_switching', 'get_old_user' ) ) {
161
- $old_user = user_switching::get_old_user();
162
- if ( $old_user ) {
163
- printf(
164
- '<a href="%1$s">Switch back to %2$s</a>',
165
- esc_url( user_switching::switch_back_url( $old_user ) ),
166
- esc_html( $old_user->display_name )
167
- );
168
- }
169
- }
 
 
170
 
171
  ### Can I determine whether the current user switched into their account?
172
 
173
  Yes. Use the `current_user_switched()` function for this.
174
 
175
- if ( function_exists( 'current_user_switched' ) ) {
176
- $switched_user = current_user_switched();
177
- if ( $switched_user ) {
178
- // User is logged in and has switched into their account.
179
- // $switched_user is the WP_User object for their originating user.
180
- }
181
- }
 
 
182
 
183
  ### Does this plugin allow a user to frame another user for an action?
184
 
@@ -205,49 +214,55 @@ Yes, there's a third party add-on plugin for this: [Admin Bar User Switching](ht
205
 
206
  Yes. When a user switches to another account, the `switch_to_user` hook is called:
207
 
208
- /**
209
- * Fires when a user switches to another user account.
210
- *
211
- * @since 0.6.0
212
- * @since 1.4.0 The `$new_token` and `$old_token` parameters were added.
213
- *
214
- * @param int $user_id The ID of the user being switched to.
215
- * @param int $old_user_id The ID of the user being switched from.
216
- * @param string $new_token The token of the session of the user being switched to. Can be an empty string
217
- * or a token for a session that may or may not still be valid.
218
- * @param string $old_token The token of the session of the user being switched from.
219
- */
220
- do_action( 'switch_to_user', $user_id, $old_user_id, $new_token, $old_token );
 
 
221
 
222
  When a user switches back to their originating account, the `switch_back_user` hook is called:
223
 
224
- /**
225
- * Fires when a user switches back to their originating account.
226
- *
227
- * @since 0.6.0
228
- * @since 1.4.0 The `$new_token` and `$old_token` parameters were added.
229
- *
230
- * @param int $user_id The ID of the user being switched back to.
231
- * @param int|false $old_user_id The ID of the user being switched from, or false if the user is switching back
232
- * after having been switched off.
233
- * @param string $new_token The token of the session of the user being switched to. Can be an empty string
234
- * or a token for a session that may or may not still be valid.
235
- * @param string $old_token The token of the session of the user being switched from.
236
- */
237
- do_action( 'switch_back_user', $user_id, $old_user_id, $new_token, $old_token );
 
 
238
 
239
  When a user switches off, the `switch_off_user` hook is called:
240
 
241
- /**
242
- * Fires when a user switches off.
243
- *
244
- * @since 0.6.0
245
- * @since 1.4.0 The `$old_token` parameter was added.
246
- *
247
- * @param int $old_user_id The ID of the user switching off.
248
- * @param string $old_token The token of the session of the user switching off.
249
- */
250
- do_action( 'switch_off_user', $old_user_id, $old_token );
 
 
251
 
252
  In addition, User Switching respects the following filters from WordPress core when appropriate:
253
 
@@ -256,10 +271,15 @@ In addition, User Switching respects the following filters from WordPress core w
256
 
257
  ### Do you accept donations?
258
 
259
- [I am accepting sponsorships via the GitHub Sponsors program](https://johnblackbourn.com/donations/) and any support you can give will help me maintain this plugin and keep it free for everyone.
260
 
261
  ## Changelog ##
262
 
 
 
 
 
 
263
  ### 1.5.8 ###
264
 
265
  * Avoid a fatal if the `interim-login` query parameter is present on a page other than wp-login.php.
@@ -437,8 +457,3 @@ In addition, User Switching respects the following filters from WordPress core w
437
  - Spanish Translation by Marcelo Pedra.
438
  - User Switching is now a network-only plugin when used on Multisite.
439
 
440
-
441
- ### 0.8.7 ###
442
-
443
- - Respect the `secure_logged_in_cookie` and `login_redirect` filters.
444
-
1
  # User Switching
2
 
3
+ Stable tag: 1.6.0
4
  Requires at least: 3.7
5
  Tested up to: 6.0
6
  Requires PHP: 5.3
7
  License: GPL v2 or later
8
+ Tags: users, user switching, fast user switching, multisite, woocommerce, buddypress, bbpress
9
  Contributors: johnbillion
10
  Donate link: https://github.com/sponsors/johnbillion
11
 
13
 
14
  Instant switching between user accounts in WordPress.
15
 
16
+ [![](https://img.shields.io/badge/ethical-open%20source-4baaaa.svg?style=flat-square)](#ethical-open-source)
17
+ [![](https://img.shields.io/wordpress/plugin/installs/user-switching?style=flat-square)](https://wordpress.org/plugins/user-switching/)
18
+ [![](https://img.shields.io/github/workflow/status/johnbillion/user-switching/Test/develop?style=flat-square)](https://github.com/johnbillion/user-switching/actions)
19
 
20
  ## Description
21
 
22
+ This plugin allows you to quickly swap between user accounts in WordPress at the click of a button. You'll be instantly logged out and logged in as your desired user. This is handy for testing environments, for helping customers on WooCommerce sites, or for any site where administrators need to switch between multiple accounts.
23
 
24
  ### Features
25
 
26
  * Switch user: Instantly switch to any user account from the *Users* screen.
27
  * Switch back: Instantly switch back to your originating account.
28
  * Switch off: Log out of your account but retain the ability to instantly switch back in again.
29
+ * Compatible with Multisite, WooCommerce, BuddyPress, bbPress, and most two-factor authentication plugins.
 
30
 
31
  ### Security
32
 
54
 
55
  ### Privacy Statement
56
 
57
+ User Switching makes use of browser cookies in order to allow users to switch to another account. Its cookies operate using the same mechanism as the authentication cookies in WordPress core, which means their values contain the user's `user_login` field in plain text which should be treated as potentially personally identifiable information (PII) for privacy and regulatory reasons (GDPR, CCPA, etc). The names of the cookies are:
58
 
59
  * `wordpress_user_sw_{COOKIEHASH}`
60
  * `wordpress_user_sw_secure_{COOKIEHASH}`
64
 
65
  See also the FAQ for some questions relating to privacy and safety when switching between users.
66
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  ## Screenshots
68
 
69
+ 1. The *Switch To* link on the Users screen
70
+ ![The Switch To link on the Users screen](.wordpress-org/screenshot-1.png)
71
+ 2. The *Switch To* link on a user's profile
72
+ ![The Switch To link on a user's profile](.wordpress-org/screenshot-2.png)
73
 
74
  ## Frequently Asked Questions
75
 
76
  ### Does this plugin work with PHP 8?
77
 
78
+ Yes, it's actively tested and working up to PHP 8.1.
79
 
80
  ### What does "Switch off" mean?
81
 
87
 
88
  Yes, and you'll also be able to switch users from the Users screen in Network Admin.
89
 
90
+ ### Does this plugin work with WooCommerce?
91
+
92
+ Yes, and you'll also be able to switch users from various WooCommerce administration screens.
93
+
94
  ### Does this plugin work with BuddyPress?
95
 
96
  Yes, and you'll also be able to switch users from member profile screens and the member listing screen.
99
 
100
  Yes, and you'll also be able to switch users from member profile screens.
101
 
 
 
 
 
102
  ### Does this plugin work if my site is using a two-factor authentication plugin?
103
 
104
  Yes, mostly.
113
 
114
  Yes. The `switch_users` meta capability can be explicitly granted to a user or a role to allow them to switch users regardless of whether or not they have the `edit_users` capability. For practical purposes, the user or role will also need the `list_users` capability so they can access the Users menu in the WordPress admin area.
115
 
116
+ ~~~php
117
+ add_filter( 'user_has_cap', function( $allcaps, $caps, $args, $user ) {
118
+ if ( 'switch_to_user' === $args[0] ) {
119
+ if ( my_condition( $user ) ) {
120
+ $allcaps['switch_users'] = true;
121
+ }
122
+ }
123
+ return $allcaps;
124
+ }, 9, 4 );
125
+ ~~~
126
+
127
+ Note that this needs to happen before User Switching's own capability filtering, hence the priority of `9`.
128
+
129
  ### Can the ability to switch accounts be denied from users?
130
 
131
  Yes. User capabilities in WordPress can be set to `false` to deny them from a user. Denying the `switch_users` capability prevents the user from switching users, even if they have the `edit_users` capability.
132
 
133
+ ~~~php
134
+ add_filter( 'user_has_cap', function( $allcaps, $caps, $args, $user ) {
135
+ if ( 'switch_to_user' === $args[0] ) {
136
+ if ( my_condition( $user ) ) {
137
+ $allcaps['switch_users'] = false;
138
+ }
139
+ }
140
+ return $allcaps;
141
+ }, 9, 4 );
142
+ ~~~
143
 
144
  Note that this needs to happen before User Switching's own capability filtering, hence the priority of `9`.
145
 
147
 
148
  Yes. Use the `user_switching::maybe_switch_url()` method for this. It takes care of authentication and returns a nonce-protected URL for the current user to switch into the provided user account.
149
 
150
+ ~~~php
151
+ if ( method_exists( 'user_switching', 'maybe_switch_url' ) ) {
152
+ $url = user_switching::maybe_switch_url( $target_user );
153
+ if ( $url ) {
154
+ printf(
155
+ '<a href="%1$s">Switch to %2$s</a>',
156
+ esc_url( $url ),
157
+ esc_html( $target_user->display_name )
158
+ );
159
+ }
160
+ }
161
+ ~~~
162
 
163
  This link also works for switching back to the original user, but if you want an explicit link for this you can use the following code:
164
 
165
+ ~~~php
166
+ if ( method_exists( 'user_switching', 'get_old_user' ) ) {
167
+ $old_user = user_switching::get_old_user();
168
+ if ( $old_user ) {
169
+ printf(
170
+ '<a href="%1$s">Switch back to %2$s</a>',
171
+ esc_url( user_switching::switch_back_url( $old_user ) ),
172
+ esc_html( $old_user->display_name )
173
+ );
174
+ }
175
+ }
176
+ ~~~
177
 
178
  ### Can I determine whether the current user switched into their account?
179
 
180
  Yes. Use the `current_user_switched()` function for this.
181
 
182
+ ~~~php
183
+ if ( function_exists( 'current_user_switched' ) ) {
184
+ $switched_user = current_user_switched();
185
+ if ( $switched_user ) {
186
+ // User is logged in and has switched into their account.
187
+ // $switched_user is the WP_User object for their originating user.
188
+ }
189
+ }
190
+ ~~~
191
 
192
  ### Does this plugin allow a user to frame another user for an action?
193
 
214
 
215
  Yes. When a user switches to another account, the `switch_to_user` hook is called:
216
 
217
+ ~~~php
218
+ /**
219
+ * Fires when a user switches to another user account.
220
+ *
221
+ * @since 0.6.0
222
+ * @since 1.4.0 The `$new_token` and `$old_token` parameters were added.
223
+ *
224
+ * @param int $user_id The ID of the user being switched to.
225
+ * @param int $old_user_id The ID of the user being switched from.
226
+ * @param string $new_token The token of the session of the user being switched to. Can be an empty string
227
+ * or a token for a session that may or may not still be valid.
228
+ * @param string $old_token The token of the session of the user being switched from.
229
+ */
230
+ do_action( 'switch_to_user', $user_id, $old_user_id, $new_token, $old_token );
231
+ ~~~
232
 
233
  When a user switches back to their originating account, the `switch_back_user` hook is called:
234
 
235
+ ~~~php
236
+ /**
237
+ * Fires when a user switches back to their originating account.
238
+ *
239
+ * @since 0.6.0
240
+ * @since 1.4.0 The `$new_token` and `$old_token` parameters were added.
241
+ *
242
+ * @param int $user_id The ID of the user being switched back to.
243
+ * @param int|false $old_user_id The ID of the user being switched from, or false if the user is switching back
244
+ * after having been switched off.
245
+ * @param string $new_token The token of the session of the user being switched to. Can be an empty string
246
+ * or a token for a session that may or may not still be valid.
247
+ * @param string $old_token The token of the session of the user being switched from.
248
+ */
249
+ do_action( 'switch_back_user', $user_id, $old_user_id, $new_token, $old_token );
250
+ ~~~
251
 
252
  When a user switches off, the `switch_off_user` hook is called:
253
 
254
+ ~~~php
255
+ /**
256
+ * Fires when a user switches off.
257
+ *
258
+ * @since 0.6.0
259
+ * @since 1.4.0 The `$old_token` parameter was added.
260
+ *
261
+ * @param int $old_user_id The ID of the user switching off.
262
+ * @param string $old_token The token of the session of the user switching off.
263
+ */
264
+ do_action( 'switch_off_user', $old_user_id, $old_token );
265
+ ~~~
266
 
267
  In addition, User Switching respects the following filters from WordPress core when appropriate:
268
 
271
 
272
  ### Do you accept donations?
273
 
274
+ [I am accepting sponsorships via the GitHub Sponsors program](https://github.com/sponsors/johnbillion) and any support you can give will help me maintain this plugin and keep it free for everyone.
275
 
276
  ## Changelog ##
277
 
278
+ ### 1.6.0 ###
279
+
280
+ * Add a 'Switch To' link to the order screen in WooCommerce
281
+ * Add a 'Switch back' link to the My Account screen and the login screen in WooCommerce
282
+
283
  ### 1.5.8 ###
284
 
285
  * Avoid a fatal if the `interim-login` query parameter is present on a page other than wp-login.php.
457
  - Spanish Translation by Marcelo Pedra.
458
  - User Switching is now a network-only plugin when used on Multisite.
459
 
 
 
 
 
 
user-switching.php CHANGED
@@ -5,12 +5,12 @@
5
  * @package user-switching
6
  * @link https://github.com/johnbillion/user-switching
7
  * @author John Blackbourn <john@johnblackbourn.com>
8
- * @copyright 2009-2021 John Blackbourn
9
  * @license GPL v2 or later
10
  *
11
  * Plugin Name: User Switching
12
  * Description: Instant switching between user accounts in WordPress
13
- * Version: 1.5.8
14
  * Plugin URI: https://wordpress.org/plugins/user-switching/
15
  * Author: John Blackbourn & contributors
16
  * Author URI: https://github.com/johnbillion/user-switching/graphs/contributors
@@ -30,6 +30,10 @@
30
  * GNU General Public License for more details.
31
  */
32
 
 
 
 
 
33
  /**
34
  * Main singleton class for the User Switching plugin.
35
  */
@@ -49,28 +53,33 @@ class user_switching {
49
  */
50
  public function init_hooks() {
51
  // Required functionality:
52
- add_filter( 'user_has_cap', array( $this, 'filter_user_has_cap' ), 10, 4 );
53
- add_filter( 'map_meta_cap', array( $this, 'filter_map_meta_cap' ), 10, 4 );
54
- add_filter( 'user_row_actions', array( $this, 'filter_user_row_actions' ), 10, 2 );
55
- add_action( 'plugins_loaded', array( $this, 'action_plugins_loaded' ), 1 );
56
- add_action( 'init', array( $this, 'action_init' ) );
57
- add_action( 'all_admin_notices', array( $this, 'action_admin_notices' ), 1 );
58
- add_action( 'wp_logout', 'user_switching_clear_olduser_cookie' );
59
- add_action( 'wp_login', 'user_switching_clear_olduser_cookie' );
60
 
61
  // Nice-to-haves:
62
- add_filter( 'ms_user_row_actions', array( $this, 'filter_user_row_actions' ), 10, 2 );
63
- add_filter( 'login_message', array( $this, 'filter_login_message' ), 1 );
64
- add_filter( 'removable_query_args', array( $this, 'filter_removable_query_args' ) );
65
- add_action( 'wp_meta', array( $this, 'action_wp_meta' ) );
66
- add_action( 'wp_footer', array( $this, 'action_wp_footer' ) );
67
- add_action( 'personal_options', array( $this, 'action_personal_options' ) );
68
- add_action( 'admin_bar_menu', array( $this, 'action_admin_bar_menu' ), 11 );
69
- add_action( 'bp_member_header_actions', array( $this, 'action_bp_button' ), 11 );
70
- add_action( 'bp_directory_members_actions', array( $this, 'action_bp_button' ), 11 );
 
71
  add_action( 'bbp_template_after_user_details', array( $this, 'action_bbpress_button' ) );
72
- add_action( 'switch_to_user', array( $this, 'forget_woocommerce_session' ) );
73
- add_action( 'switch_back_user', array( $this, 'forget_woocommerce_session' ) );
 
 
 
 
74
  }
75
 
76
  /**
@@ -110,8 +119,14 @@ class user_switching {
110
 
111
  ?>
112
  <tr class="user-switching-wrap">
113
- <th scope="row"><?php echo esc_html_x( 'User Switching', 'User Switching title on user profile screen', 'user-switching' ); ?></th>
114
- <td><a id="user_switching_switcher" href="<?php echo esc_url( $link ); ?>"><?php esc_html_e( 'Switch&nbsp;To', 'user-switching' ); ?></a></td>
 
 
 
 
 
 
115
  </tr>
116
  <?php
117
  }
@@ -126,7 +141,7 @@ class user_switching {
126
  public static function remember() {
127
  /** This filter is documented in wp-includes/pluggable.php */
128
  $cookie_life = apply_filters( 'auth_cookie_expiration', 172800, get_current_user_id(), false );
129
- $current = wp_parse_auth_cookie( '', 'logged_in' );
130
 
131
  if ( ! $current ) {
132
  return false;
@@ -218,7 +233,7 @@ class user_switching {
218
  }
219
 
220
  $redirect_to = self::get_redirect( $old_user, $current_user );
221
- $args = array(
222
  'user_switched' => 'true',
223
  'switched_back' => 'true',
224
  );
@@ -248,7 +263,7 @@ class user_switching {
248
  // Switch off:
249
  if ( switch_off_user() ) {
250
  $redirect_to = self::get_redirect( null, $current_user );
251
- $args = array(
252
  'switched_off' => 'true',
253
  );
254
 
@@ -276,10 +291,10 @@ class user_switching {
276
  */
277
  protected static function get_redirect( WP_User $new_user = null, WP_User $old_user = null ) {
278
  if ( ! empty( $_REQUEST['redirect_to'] ) ) {
279
- $redirect_to = self::remove_query_args( wp_unslash( $_REQUEST['redirect_to'] ) );
280
  $requested_redirect_to = wp_unslash( $_REQUEST['redirect_to'] );
281
  } else {
282
- $redirect_to = '';
283
  $requested_redirect_to = '';
284
  }
285
 
@@ -300,21 +315,21 @@ class user_switching {
300
  * @return void
301
  */
302
  public function action_admin_notices() {
303
- $user = wp_get_current_user();
304
  $old_user = self::get_old_user();
305
 
306
  if ( $old_user ) {
307
  $switched_locale = false;
308
- $lang_attr = '';
309
 
310
  if ( function_exists( 'get_user_locale' ) ) {
311
- $locale = get_user_locale( $old_user );
312
  $switched_locale = switch_to_locale( $locale );
313
- $lang_attr = str_replace( '_', '-', $locale );
314
  }
315
 
316
  ?>
317
- <div id="user_switching" class="updated notice is-dismissible">
318
  <?php
319
  if ( $lang_attr ) {
320
  printf(
@@ -327,7 +342,7 @@ class user_switching {
327
  ?>
328
  <span class="dashicons dashicons-admin-users" style="color:#56c234" aria-hidden="true"></span>
329
  <?php
330
- $message = '';
331
  $just_switched = isset( $_GET['user_switched'] );
332
  if ( $just_switched ) {
333
  $message = esc_html( sprintf(
@@ -379,7 +394,7 @@ class user_switching {
379
  }
380
  } elseif ( isset( $_GET['user_switched'] ) ) {
381
  ?>
382
- <div id="user_switching" class="updated notice is-dismissible">
383
  <p>
384
  <?php
385
  if ( isset( $_GET['switched_back'] ) ) {
@@ -456,16 +471,10 @@ class user_switching {
456
  return;
457
  }
458
 
459
- if ( method_exists( $wp_admin_bar, 'get_node' ) ) {
460
- if ( $wp_admin_bar->get_node( 'user-actions' ) ) {
461
- $parent = 'user-actions';
462
- } else {
463
- return;
464
- }
465
- } elseif ( get_option( 'show_avatars' ) ) {
466
- $parent = 'my-account-with-avatar';
467
  } else {
468
- $parent = 'my-account';
469
  }
470
 
471
  $old_user = self::get_old_user();
@@ -473,14 +482,14 @@ class user_switching {
473
  if ( $old_user ) {
474
  $wp_admin_bar->add_node( array(
475
  'parent' => $parent,
476
- 'id' => 'switch-back',
477
- 'title' => esc_html( sprintf(
478
  /* Translators: 1: user display name; 2: username; */
479
  __( 'Switch back to %1$s (%2$s)', 'user-switching' ),
480
  $old_user->display_name,
481
  $old_user->user_login
482
  ) ),
483
- 'href' => add_query_arg( array(
484
  'redirect_to' => urlencode( self::current_url() ),
485
  ), self::switch_back_url( $old_user ) ),
486
  ) );
@@ -496,10 +505,10 @@ class user_switching {
496
 
497
  $wp_admin_bar->add_node( array(
498
  'parent' => $parent,
499
- 'id' => 'switch-off',
500
  /* Translators: "switch off" means to temporarily log out */
501
- 'title' => esc_html__( 'Switch Off', 'user-switching' ),
502
- 'href' => $url,
503
  ) );
504
  }
505
 
@@ -507,23 +516,23 @@ class user_switching {
507
  if ( $old_user ) {
508
  $wp_admin_bar->add_node( array(
509
  'parent' => 'edit',
510
- 'id' => 'author-switch-back',
511
- 'title' => esc_html( sprintf(
512
  /* Translators: 1: user display name; 2: username; */
513
  __( 'Switch back to %1$s (%2$s)', 'user-switching' ),
514
  $old_user->display_name,
515
  $old_user->user_login
516
  ) ),
517
- 'href' => add_query_arg( array(
518
  'redirect_to' => urlencode( self::current_url() ),
519
  ), self::switch_back_url( $old_user ) ),
520
  ) );
521
  } elseif ( current_user_can( 'switch_to_user', get_queried_object_id() ) ) {
522
  $wp_admin_bar->add_node( array(
523
  'parent' => 'edit',
524
- 'id' => 'author-switch-to',
525
- 'title' => esc_html__( 'Switch&nbsp;To', 'user-switching' ),
526
- 'href' => add_query_arg( array(
527
  'redirect_to' => urlencode( self::current_url() ),
528
  ), self::switch_to_url( get_queried_object() ) ),
529
  ) );
@@ -642,9 +651,9 @@ class user_switching {
642
  /**
643
  * Adds a 'Switch To' link to each list of user actions on the Users screen.
644
  *
645
- * @param string[] $actions Array of actions to display for this user row.
646
- * @param WP_User $user The user object displayed in this row.
647
- * @return string[] Array of actions to display for this user row.
648
  */
649
  public function filter_user_row_actions( array $actions, WP_User $user ) {
650
  $link = self::maybe_switch_url( $user );
@@ -693,10 +702,10 @@ class user_switching {
693
  $components = array_keys( buddypress()->active_components );
694
 
695
  echo bp_get_button( array(
696
- 'id' => 'user_switching',
697
- 'component' => reset( $components ),
698
- 'link_href' => esc_url( $link ),
699
- 'link_text' => esc_html__( 'Switch&nbsp;To', 'user-switching' ),
700
  'wrapper_id' => 'user_switching_switch_to',
701
  ) );
702
  }
@@ -732,13 +741,34 @@ class user_switching {
732
  echo '</ul>';
733
  }
734
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
735
  /**
736
  * Filters the list of query arguments which get removed from admin area URLs in WordPress.
737
  *
738
  * @link https://core.trac.wordpress.org/ticket/23367
739
  *
740
- * @param string[] $args Array of removable query arguments.
741
- * @return string[] Updated array of removable query arguments.
742
  */
743
  public function filter_removable_query_args( array $args ) {
744
  return array_merge( $args, array(
@@ -774,9 +804,9 @@ class user_switching {
774
  */
775
  public static function switch_to_url( WP_User $user ) {
776
  return wp_nonce_url( add_query_arg( array(
777
- 'action' => 'switch_to_user',
778
  'user_id' => $user->ID,
779
- 'nr' => 1,
780
  ), wp_login_url() ), "switch_to_user_{$user->ID}" );
781
  }
782
 
@@ -789,7 +819,7 @@ class user_switching {
789
  public static function switch_back_url( WP_User $user ) {
790
  return wp_nonce_url( add_query_arg( array(
791
  'action' => 'switch_to_olduser',
792
- 'nr' => 1,
793
  ), wp_login_url() ), "switch_to_olduser_{$user->ID}" );
794
  }
795
 
@@ -802,7 +832,7 @@ class user_switching {
802
  public static function switch_off_url( WP_User $user ) {
803
  return wp_nonce_url( add_query_arg( array(
804
  'action' => 'switch_off',
805
- 'nr' => 1,
806
  ), wp_login_url() ), "switch_off_{$user->ID}" );
807
  }
808
 
@@ -853,6 +883,84 @@ class user_switching {
853
  return ( is_ssl() && ( 'https' === parse_url( wp_login_url(), PHP_URL_SCHEME ) ) );
854
  }
855
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
856
  /**
857
  * Instructs WooCommerce to forget the session for the current user, without deleting it.
858
  *
@@ -880,24 +988,25 @@ class user_switching {
880
  * Filters a user's capabilities so they can be altered at runtime.
881
  *
882
  * This is used to:
 
883
  * - Grant the 'switch_to_user' capability to the user if they have the ability to edit the user they're trying to
884
  * switch to (and that user is not themselves).
885
  * - Grant the 'switch_off' capability to the user if they can edit other users.
886
  *
887
  * Important: This does not get called for Super Admins. See filter_map_meta_cap() below.
888
  *
889
- * @param bool[] $user_caps Array of key/value pairs where keys represent a capability name and boolean values
890
- * represent whether the user has that capability.
891
- * @param string[] $required_caps Array of required primitive capabilities for the requested capability.
892
- * @param mixed[] $args {
893
  * Arguments that accompany the requested capability check.
894
  *
895
  * @type string $0 Requested capability.
896
  * @type int $1 Concerned user ID.
897
  * @type mixed ...$2 Optional second and further parameters.
898
  * }
899
- * @param WP_User $user Concerned user object.
900
- * @return bool[] Array of concerned user's capabilities.
901
  */
902
  public function filter_user_has_cap( array $user_caps, array $required_caps, array $args, WP_User $user ) {
903
  if ( 'switch_to_user' === $args[0] ) {
@@ -927,20 +1036,21 @@ class user_switching {
927
  * Filters the required primitive capabilities for the given primitive or meta capability.
928
  *
929
  * This is used to:
 
930
  * - Add the 'do_not_allow' capability to the list of required capabilities when a Super Admin is trying to switch
931
  * to themselves.
932
  *
933
  * It affects nothing else as Super Admins can do everything by default.
934
  *
935
- * @param string[] $required_caps Array of required primitive capabilities for the requested capability.
936
- * @param string $cap Capability or meta capability being checked.
937
- * @param int $user_id Concerned user ID.
938
- * @param mixed[] $args {
939
  * Arguments that accompany the requested capability check.
940
  *
941
  * @type mixed ...$0 Optional second and further parameters.
942
  * }
943
- * @return string[] Array of required capabilities for the requested action.
944
  */
945
  public function filter_map_meta_cap( array $required_caps, $cap, $user_id, array $args ) {
946
  if ( 'switch_to_user' === $cap ) {
@@ -969,7 +1079,7 @@ class user_switching {
969
  /**
970
  * Private class constructor. Use `get_instance()` to get the instance.
971
  */
972
- final private function __construct() {}
973
 
974
  }
975
 
@@ -985,18 +1095,18 @@ if ( ! function_exists( 'user_switching_set_olduser_cookie' ) ) {
985
  * @return void
986
  */
987
  function user_switching_set_olduser_cookie( $old_user_id, $pop = false, $token = '' ) {
988
- $secure_auth_cookie = user_switching::secure_auth_cookie();
989
  $secure_olduser_cookie = user_switching::secure_olduser_cookie();
990
- $expiration = time() + 172800; // 48 hours
991
- $auth_cookie = user_switching_get_auth_cookie();
992
- $olduser_cookie = wp_generate_auth_cookie( $old_user_id, $expiration, 'logged_in', $token );
993
 
994
  if ( $secure_auth_cookie ) {
995
  $auth_cookie_name = USER_SWITCHING_SECURE_COOKIE;
996
- $scheme = 'secure_auth';
997
  } else {
998
  $auth_cookie_name = USER_SWITCHING_COOKIE;
999
- $scheme = 'auth';
1000
  }
1001
 
1002
  if ( $pop ) {
@@ -1124,7 +1234,7 @@ if ( ! function_exists( 'user_switching_get_auth_cookie' ) ) {
1124
  /**
1125
  * Gets the value of the auth cookie containing the list of originating users.
1126
  *
1127
- * @return string[] Array of originating user authentication cookie values. Empty array if there are none.
1128
  */
1129
  function user_switching_get_auth_cookie() {
1130
  if ( user_switching::secure_auth_cookie() ) {
@@ -1159,10 +1269,10 @@ if ( ! function_exists( 'switch_to_user' ) ) {
1159
  return false;
1160
  }
1161
 
1162
- $old_user_id = ( is_user_logged_in() ) ? get_current_user_id() : false;
1163
- $old_token = function_exists( 'wp_get_session_token' ) ? wp_get_session_token() : '';
1164
  $auth_cookies = user_switching_get_auth_cookie();
1165
- $auth_cookie = end( $auth_cookies );
1166
  $cookie_parts = $auth_cookie ? wp_parse_auth_cookie( $auth_cookie ) : false;
1167
 
1168
  if ( $set_old_user && $old_user_id ) {
@@ -1178,12 +1288,12 @@ if ( ! function_exists( 'switch_to_user' ) ) {
1178
  /**
1179
  * Attaches the original user ID and session token to the new session when a user switches to another user.
1180
  *
1181
- * @param array $session Array of extra data.
1182
- * @param int $user_id User ID.
1183
- * @return array Array of extra data.
1184
  */
1185
  $session_filter = function( array $session, $user_id ) use ( $old_user_id, $old_token ) {
1186
- $session['switched_from_id'] = $old_user_id;
1187
  $session['switched_from_session'] = $old_token;
1188
  return $session;
1189
  };
@@ -1196,7 +1306,7 @@ if ( ! function_exists( 'switch_to_user' ) ) {
1196
 
1197
  remove_filter( 'attach_session_information', $session_filter, 99 );
1198
 
1199
- if ( $set_old_user ) {
1200
  /**
1201
  * Fires when a user switches to another user account.
1202
  *
5
  * @package user-switching
6
  * @link https://github.com/johnbillion/user-switching
7
  * @author John Blackbourn <john@johnblackbourn.com>
8
+ * @copyright 2009-2022 John Blackbourn
9
  * @license GPL v2 or later
10
  *
11
  * Plugin Name: User Switching
12
  * Description: Instant switching between user accounts in WordPress
13
+ * Version: 1.6.0
14
  * Plugin URI: https://wordpress.org/plugins/user-switching/
15
  * Author: John Blackbourn & contributors
16
  * Author URI: https://github.com/johnbillion/user-switching/graphs/contributors
30
  * GNU General Public License for more details.
31
  */
32
 
33
+ if ( ! defined( 'ABSPATH' ) ) {
34
+ exit;
35
+ }
36
+
37
  /**
38
  * Main singleton class for the User Switching plugin.
39
  */
53
  */
54
  public function init_hooks() {
55
  // Required functionality:
56
+ add_filter( 'user_has_cap', array( $this, 'filter_user_has_cap' ), 10, 4 );
57
+ add_filter( 'map_meta_cap', array( $this, 'filter_map_meta_cap' ), 10, 4 );
58
+ add_filter( 'user_row_actions', array( $this, 'filter_user_row_actions' ), 10, 2 );
59
+ add_action( 'plugins_loaded', array( $this, 'action_plugins_loaded' ), 1 );
60
+ add_action( 'init', array( $this, 'action_init' ) );
61
+ add_action( 'all_admin_notices', array( $this, 'action_admin_notices' ), 1 );
62
+ add_action( 'wp_logout', 'user_switching_clear_olduser_cookie' );
63
+ add_action( 'wp_login', 'user_switching_clear_olduser_cookie' );
64
 
65
  // Nice-to-haves:
66
+ add_filter( 'ms_user_row_actions', array( $this, 'filter_user_row_actions' ), 10, 2 );
67
+ add_filter( 'login_message', array( $this, 'filter_login_message' ), 1 );
68
+ add_filter( 'removable_query_args', array( $this, 'filter_removable_query_args' ) );
69
+ add_action( 'wp_meta', array( $this, 'action_wp_meta' ) );
70
+ add_filter( 'plugin_row_meta', array( $this, 'filter_plugin_row_meta' ), 10, 4 );
71
+ add_action( 'wp_footer', array( $this, 'action_wp_footer' ) );
72
+ add_action( 'personal_options', array( $this, 'action_personal_options' ) );
73
+ add_action( 'admin_bar_menu', array( $this, 'action_admin_bar_menu' ), 11 );
74
+ add_action( 'bp_member_header_actions', array( $this, 'action_bp_button' ), 11 );
75
+ add_action( 'bp_directory_members_actions', array( $this, 'action_bp_button' ), 11 );
76
  add_action( 'bbp_template_after_user_details', array( $this, 'action_bbpress_button' ) );
77
+ add_action( 'woocommerce_login_form_start', array( $this, 'action_woocommerce_login_form_start' ), 10, 0 );
78
+ add_action( 'woocommerce_admin_order_data_after_order_details', array( $this, 'action_woocommerce_order_details' ), 1 );
79
+ add_filter( 'woocommerce_account_menu_items', array( $this, 'filter_woocommerce_account_menu_items' ), 999 );
80
+ add_filter( 'woocommerce_get_endpoint_url', array( $this, 'filter_woocommerce_get_endpoint_url' ), 10, 2 );
81
+ add_action( 'switch_to_user', array( $this, 'forget_woocommerce_session' ) );
82
+ add_action( 'switch_back_user', array( $this, 'forget_woocommerce_session' ) );
83
  }
84
 
85
  /**
119
 
120
  ?>
121
  <tr class="user-switching-wrap">
122
+ <th scope="row">
123
+ <?php echo esc_html_x( 'User Switching', 'User Switching title on user profile screen', 'user-switching' ); ?>
124
+ </th>
125
+ <td>
126
+ <a id="user_switching_switcher" href="<?php echo esc_url( $link ); ?>">
127
+ <?php esc_html_e( 'Switch&nbsp;To', 'user-switching' ); ?>
128
+ </a>
129
+ </td>
130
  </tr>
131
  <?php
132
  }
141
  public static function remember() {
142
  /** This filter is documented in wp-includes/pluggable.php */
143
  $cookie_life = apply_filters( 'auth_cookie_expiration', 172800, get_current_user_id(), false );
144
+ $current = wp_parse_auth_cookie( '', 'logged_in' );
145
 
146
  if ( ! $current ) {
147
  return false;
233
  }
234
 
235
  $redirect_to = self::get_redirect( $old_user, $current_user );
236
+ $args = array(
237
  'user_switched' => 'true',
238
  'switched_back' => 'true',
239
  );
263
  // Switch off:
264
  if ( switch_off_user() ) {
265
  $redirect_to = self::get_redirect( null, $current_user );
266
+ $args = array(
267
  'switched_off' => 'true',
268
  );
269
 
291
  */
292
  protected static function get_redirect( WP_User $new_user = null, WP_User $old_user = null ) {
293
  if ( ! empty( $_REQUEST['redirect_to'] ) ) {
294
+ $redirect_to = self::remove_query_args( wp_unslash( $_REQUEST['redirect_to'] ) );
295
  $requested_redirect_to = wp_unslash( $_REQUEST['redirect_to'] );
296
  } else {
297
+ $redirect_to = '';
298
  $requested_redirect_to = '';
299
  }
300
 
315
  * @return void
316
  */
317
  public function action_admin_notices() {
318
+ $user = wp_get_current_user();
319
  $old_user = self::get_old_user();
320
 
321
  if ( $old_user ) {
322
  $switched_locale = false;
323
+ $lang_attr = '';
324
 
325
  if ( function_exists( 'get_user_locale' ) ) {
326
+ $locale = get_user_locale( $old_user );
327
  $switched_locale = switch_to_locale( $locale );
328
+ $lang_attr = str_replace( '_', '-', $locale );
329
  }
330
 
331
  ?>
332
+ <div id="user_switching" class="updated notice notice-success is-dismissible">
333
  <?php
334
  if ( $lang_attr ) {
335
  printf(
342
  ?>
343
  <span class="dashicons dashicons-admin-users" style="color:#56c234" aria-hidden="true"></span>
344
  <?php
345
+ $message = '';
346
  $just_switched = isset( $_GET['user_switched'] );
347
  if ( $just_switched ) {
348
  $message = esc_html( sprintf(
394
  }
395
  } elseif ( isset( $_GET['user_switched'] ) ) {
396
  ?>
397
+ <div id="user_switching" class="updated notice notice-success is-dismissible">
398
  <p>
399
  <?php
400
  if ( isset( $_GET['switched_back'] ) ) {
471
  return;
472
  }
473
 
474
+ if ( $wp_admin_bar->get_node( 'user-actions' ) ) {
475
+ $parent = 'user-actions';
 
 
 
 
 
 
476
  } else {
477
+ return;
478
  }
479
 
480
  $old_user = self::get_old_user();
482
  if ( $old_user ) {
483
  $wp_admin_bar->add_node( array(
484
  'parent' => $parent,
485
+ 'id' => 'switch-back',
486
+ 'title' => esc_html( sprintf(
487
  /* Translators: 1: user display name; 2: username; */
488
  __( 'Switch back to %1$s (%2$s)', 'user-switching' ),
489
  $old_user->display_name,
490
  $old_user->user_login
491
  ) ),
492
+ 'href' => add_query_arg( array(
493
  'redirect_to' => urlencode( self::current_url() ),
494
  ), self::switch_back_url( $old_user ) ),
495
  ) );
505
 
506
  $wp_admin_bar->add_node( array(
507
  'parent' => $parent,
508
+ 'id' => 'switch-off',
509
  /* Translators: "switch off" means to temporarily log out */
510
+ 'title' => esc_html__( 'Switch Off', 'user-switching' ),
511
+ 'href' => $url,
512
  ) );
513
  }
514
 
516
  if ( $old_user ) {
517
  $wp_admin_bar->add_node( array(
518
  'parent' => 'edit',
519
+ 'id' => 'author-switch-back',
520
+ 'title' => esc_html( sprintf(
521
  /* Translators: 1: user display name; 2: username; */
522
  __( 'Switch back to %1$s (%2$s)', 'user-switching' ),
523
  $old_user->display_name,
524
  $old_user->user_login
525
  ) ),
526
+ 'href' => add_query_arg( array(
527
  'redirect_to' => urlencode( self::current_url() ),
528
  ), self::switch_back_url( $old_user ) ),
529
  ) );
530
  } elseif ( current_user_can( 'switch_to_user', get_queried_object_id() ) ) {
531
  $wp_admin_bar->add_node( array(
532
  'parent' => 'edit',
533
+ 'id' => 'author-switch-to',
534
+ 'title' => esc_html__( 'Switch&nbsp;To', 'user-switching' ),
535
+ 'href' => add_query_arg( array(
536
  'redirect_to' => urlencode( self::current_url() ),
537
  ), self::switch_to_url( get_queried_object() ) ),
538
  ) );
651
  /**
652
  * Adds a 'Switch To' link to each list of user actions on the Users screen.
653
  *
654
+ * @param array<string,string> $actions Array of actions to display for this user row.
655
+ * @param WP_User $user The user object displayed in this row.
656
+ * @return array<string,string> Array of actions to display for this user row.
657
  */
658
  public function filter_user_row_actions( array $actions, WP_User $user ) {
659
  $link = self::maybe_switch_url( $user );
702
  $components = array_keys( buddypress()->active_components );
703
 
704
  echo bp_get_button( array(
705
+ 'id' => 'user_switching',
706
+ 'component' => reset( $components ),
707
+ 'link_href' => esc_url( $link ),
708
+ 'link_text' => esc_html__( 'Switch&nbsp;To', 'user-switching' ),
709
  'wrapper_id' => 'user_switching_switch_to',
710
  ) );
711
  }
741
  echo '</ul>';
742
  }
743
 
744
+ /**
745
+ * Filters the array of row meta for each plugin in the Plugins list table.
746
+ *
747
+ * @param array<int,string> $plugin_meta An array of the plugin row's meta data.
748
+ * @param string $plugin_file Path to the plugin file relative to the plugins directory.
749
+ * @return array<int,string> An array of the plugin row's meta data.
750
+ */
751
+ public function filter_plugin_row_meta( array $plugin_meta, $plugin_file ) {
752
+ if ( 'user-switching/user-switching.php' !== $plugin_file ) {
753
+ return $plugin_meta;
754
+ }
755
+
756
+ $plugin_meta[] = sprintf(
757
+ '<a href="%1$s"><span class="dashicons dashicons-star-filled" aria-hidden="true" style="font-size:14px;line-height:1.3"></span>%2$s</a>',
758
+ 'https://github.com/sponsors/johnbillion',
759
+ esc_html_x( 'Sponsor', 'verb', 'user-switching' )
760
+ );
761
+
762
+ return $plugin_meta;
763
+ }
764
+
765
  /**
766
  * Filters the list of query arguments which get removed from admin area URLs in WordPress.
767
  *
768
  * @link https://core.trac.wordpress.org/ticket/23367
769
  *
770
+ * @param array<int,string> $args Array of removable query arguments.
771
+ * @return array<int,string> Updated array of removable query arguments.
772
  */
773
  public function filter_removable_query_args( array $args ) {
774
  return array_merge( $args, array(
804
  */
805
  public static function switch_to_url( WP_User $user ) {
806
  return wp_nonce_url( add_query_arg( array(
807
+ 'action' => 'switch_to_user',
808
  'user_id' => $user->ID,
809
+ 'nr' => 1,
810
  ), wp_login_url() ), "switch_to_user_{$user->ID}" );
811
  }
812
 
819
  public static function switch_back_url( WP_User $user ) {
820
  return wp_nonce_url( add_query_arg( array(
821
  'action' => 'switch_to_olduser',
822
+ 'nr' => 1,
823
  ), wp_login_url() ), "switch_to_olduser_{$user->ID}" );
824
  }
825
 
832
  public static function switch_off_url( WP_User $user ) {
833
  return wp_nonce_url( add_query_arg( array(
834
  'action' => 'switch_off',
835
+ 'nr' => 1,
836
  ), wp_login_url() ), "switch_off_{$user->ID}" );
837
  }
838
 
883
  return ( is_ssl() && ( 'https' === parse_url( wp_login_url(), PHP_URL_SCHEME ) ) );
884
  }
885
 
886
+ /**
887
+ * Adds a 'Switch back to {user}' link to the WooCommerce login screen.
888
+ *
889
+ * @return void
890
+ */
891
+ public function action_woocommerce_login_form_start() {
892
+ // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
893
+ echo $this->filter_login_message( '' );
894
+ }
895
+
896
+ /**
897
+ * Adds a 'Switch To' link to the WooCommerce order screen.
898
+ *
899
+ * @param WC_Order $order The WooCommerce order object.
900
+ * @return void
901
+ */
902
+ public function action_woocommerce_order_details( WC_Order $order ) {
903
+ $user = $order->get_user();
904
+
905
+ if ( ! $user || ! current_user_can( 'switch_to_user', $user->ID ) ) {
906
+ return;
907
+ }
908
+
909
+ $url = add_query_arg( array(
910
+ 'redirect_to' => urlencode( $order->get_view_order_url() ),
911
+ ), self::switch_to_url( $user ) );
912
+
913
+ printf(
914
+ '<p class="form-field form-field-wide"><a href="%1$s">%2$s</a></p>',
915
+ esc_url( $url ),
916
+ esc_html__( 'Switch&nbsp;To', 'user-switching' )
917
+ );
918
+ }
919
+
920
+ /**
921
+ * Adds a 'Switch back to {user}' link to the My Account screen in WooCommerce.
922
+ *
923
+ * @param array<string, string> $items Menu items.
924
+ * @return array<string, string> Menu items.
925
+ */
926
+ public function filter_woocommerce_account_menu_items( array $items ) {
927
+ $old_user = self::get_old_user();
928
+
929
+ if ( ! $old_user ) {
930
+ return $items;
931
+ }
932
+
933
+ $items['user-switching-switch-back'] = sprintf(
934
+ /* Translators: 1: user display name; 2: username; */
935
+ __( 'Switch back to %1$s (%2$s)', 'user-switching' ),
936
+ $old_user->display_name,
937
+ $old_user->user_login
938
+ );
939
+
940
+ return $items;
941
+ }
942
+
943
+ /**
944
+ * Sets the URL of the 'Switch back to {user}' link in the My Account screen in WooCommerce.
945
+ *
946
+ * @param string $url The URL for the menu item.
947
+ * @param string $endpoint The endpoint slug for the menu item.
948
+ * @return string The URL for the menu item.
949
+ */
950
+ public function filter_woocommerce_get_endpoint_url( $url, $endpoint ) {
951
+ if ( 'user-switching-switch-back' !== $endpoint ) {
952
+ return $url;
953
+ }
954
+
955
+ $old_user = self::get_old_user();
956
+
957
+ if ( ! $old_user ) {
958
+ return $url;
959
+ }
960
+
961
+ return self::switch_back_url( $old_user );
962
+ }
963
+
964
  /**
965
  * Instructs WooCommerce to forget the session for the current user, without deleting it.
966
  *
988
  * Filters a user's capabilities so they can be altered at runtime.
989
  *
990
  * This is used to:
991
+ *
992
  * - Grant the 'switch_to_user' capability to the user if they have the ability to edit the user they're trying to
993
  * switch to (and that user is not themselves).
994
  * - Grant the 'switch_off' capability to the user if they can edit other users.
995
  *
996
  * Important: This does not get called for Super Admins. See filter_map_meta_cap() below.
997
  *
998
+ * @param array<string,bool> $user_caps Array of key/value pairs where keys represent a capability name and boolean values
999
+ * represent whether the user has that capability.
1000
+ * @param array<int,string> $required_caps Array of required primitive capabilities for the requested capability.
1001
+ * @param array<int,mixed> $args {
1002
  * Arguments that accompany the requested capability check.
1003
  *
1004
  * @type string $0 Requested capability.
1005
  * @type int $1 Concerned user ID.
1006
  * @type mixed ...$2 Optional second and further parameters.
1007
  * }
1008
+ * @param WP_User $user Concerned user object.
1009
+ * @return array<string,bool> Array of concerned user's capabilities.
1010
  */
1011
  public function filter_user_has_cap( array $user_caps, array $required_caps, array $args, WP_User $user ) {
1012
  if ( 'switch_to_user' === $args[0] ) {
1036
  * Filters the required primitive capabilities for the given primitive or meta capability.
1037
  *
1038
  * This is used to:
1039
+ *
1040
  * - Add the 'do_not_allow' capability to the list of required capabilities when a Super Admin is trying to switch
1041
  * to themselves.
1042
  *
1043
  * It affects nothing else as Super Admins can do everything by default.
1044
  *
1045
+ * @param array<int,string> $required_caps Array of required primitive capabilities for the requested capability.
1046
+ * @param string $cap Capability or meta capability being checked.
1047
+ * @param int $user_id Concerned user ID.
1048
+ * @param array<int,mixed> $args {
1049
  * Arguments that accompany the requested capability check.
1050
  *
1051
  * @type mixed ...$0 Optional second and further parameters.
1052
  * }
1053
+ * @return array<int,string> Array of required capabilities for the requested action.
1054
  */
1055
  public function filter_map_meta_cap( array $required_caps, $cap, $user_id, array $args ) {
1056
  if ( 'switch_to_user' === $cap ) {
1079
  /**
1080
  * Private class constructor. Use `get_instance()` to get the instance.
1081
  */
1082
+ private function __construct() {}
1083
 
1084
  }
1085
 
1095
  * @return void
1096
  */
1097
  function user_switching_set_olduser_cookie( $old_user_id, $pop = false, $token = '' ) {
1098
+ $secure_auth_cookie = user_switching::secure_auth_cookie();
1099
  $secure_olduser_cookie = user_switching::secure_olduser_cookie();
1100
+ $expiration = time() + 172800; // 48 hours
1101
+ $auth_cookie = user_switching_get_auth_cookie();
1102
+ $olduser_cookie = wp_generate_auth_cookie( $old_user_id, $expiration, 'logged_in', $token );
1103
 
1104
  if ( $secure_auth_cookie ) {
1105
  $auth_cookie_name = USER_SWITCHING_SECURE_COOKIE;
1106
+ $scheme = 'secure_auth';
1107
  } else {
1108
  $auth_cookie_name = USER_SWITCHING_COOKIE;
1109
+ $scheme = 'auth';
1110
  }
1111
 
1112
  if ( $pop ) {
1234
  /**
1235
  * Gets the value of the auth cookie containing the list of originating users.
1236
  *
1237
+ * @return array<int,string> Array of originating user authentication cookie values. Empty array if there are none.
1238
  */
1239
  function user_switching_get_auth_cookie() {
1240
  if ( user_switching::secure_auth_cookie() ) {
1269
  return false;
1270
  }
1271
 
1272
+ $old_user_id = ( is_user_logged_in() ) ? get_current_user_id() : false;
1273
+ $old_token = function_exists( 'wp_get_session_token' ) ? wp_get_session_token() : '';
1274
  $auth_cookies = user_switching_get_auth_cookie();
1275
+ $auth_cookie = end( $auth_cookies );
1276
  $cookie_parts = $auth_cookie ? wp_parse_auth_cookie( $auth_cookie ) : false;
1277
 
1278
  if ( $set_old_user && $old_user_id ) {
1288
  /**
1289
  * Attaches the original user ID and session token to the new session when a user switches to another user.
1290
  *
1291
+ * @param array<string, mixed> $session Array of extra data.
1292
+ * @param int $user_id User ID.
1293
+ * @return array<string, mixed> Array of extra data.
1294
  */
1295
  $session_filter = function( array $session, $user_id ) use ( $old_user_id, $old_token ) {
1296
+ $session['switched_from_id'] = $old_user_id;
1297
  $session['switched_from_session'] = $old_token;
1298
  return $session;
1299
  };
1306
 
1307
  remove_filter( 'attach_session_information', $session_filter, 99 );
1308
 
1309
+ if ( $set_old_user && $old_user_id ) {
1310
  /**
1311
  * Fires when a user switches to another user account.
1312
  *