User Switching - Version 0.8.5

Version Description

  • Add a 'Switch To' link to bbPress profile screens.

=

Download this release

Release Info

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

Code changes from version 0.8.4 to 0.8.5

Files changed (2) hide show
  1. readme.txt +13 -7
  2. user-switching.php +653 -645
readme.txt CHANGED
@@ -1,10 +1,10 @@
1
  === User Switching ===
2
 
3
  Contributors: johnbillion
4
- Tags: user, users, profiles, switching, wpmu, multisite, buddypress, become, user control, user management, user access, developer
5
  Requires at least: 3.1
6
- Tested up to: 3.7
7
- Stable tag: 0.8.4
8
  License: GPL v2 or later
9
 
10
  Instant switching between user accounts in WordPress.
@@ -19,7 +19,7 @@ This plugin allows you to quickly swap between user accounts in WordPress at the
19
  * Switch back: Instantly switch back to your originating account.
20
  * Switch off: Log out of your account but retain the ability to instantly switch back in again.
21
  * It's completely secure (see the *Security* section below).
22
- * Compatible with WordPress, WordPress Multisite and BuddyPress.
23
 
24
  = Security =
25
 
@@ -80,6 +80,10 @@ Yes, and you'll also be able to switch users from the Users screen in Network Ad
80
 
81
  Yes, and you'll also be able to switch users from member profile screens and the member listing screen.
82
 
 
 
 
 
83
  = Does this work as a mu-plugin? =
84
 
85
  Yes, but you'll need to install `user-switching.php` into the root of your `mu-plugins` directory, not in the `user-switching` subdirectory. This is a restriction of WordPress.
@@ -102,12 +106,14 @@ When a user switches off, the `switch_off_user` hook is called with the old user
102
 
103
  == Upgrade Notice ==
104
 
105
- = 0.8.4 =
106
- * Show admin notices on all possible admin screens.
107
- * Tweak the redirect location for BuddyPress user profiles.
108
 
109
  == Changelog ==
110
 
 
 
 
111
  = 0.8.4 =
112
  * Revert a change in 0.8.3 which switched to using the `login_init` hook. This hook is fired too late.
113
 
1
  === User Switching ===
2
 
3
  Contributors: johnbillion
4
+ Tags: user, users, profiles, switching, wpmu, multisite, buddypress, bbpress, become, user control, user management, user access, developer
5
  Requires at least: 3.1
6
+ Tested up to: 3.8
7
+ Stable tag: 0.8.5
8
  License: GPL v2 or later
9
 
10
  Instant switching between user accounts in WordPress.
19
  * Switch back: Instantly switch back to your originating account.
20
  * Switch off: Log out of your account but retain the ability to instantly switch back in again.
21
  * It's completely secure (see the *Security* section below).
22
+ * Compatible with WordPress, WordPress Multisite, BuddyPress and bbPress.
23
 
24
  = Security =
25
 
80
 
81
  Yes, and you'll also be able to switch users from member profile screens and the member listing screen.
82
 
83
+ = Does this plugin work with bbPress? =
84
+
85
+ Yes, and you'll also be able to switch users from member profile screens.
86
+
87
  = Does this work as a mu-plugin? =
88
 
89
  Yes, but you'll need to install `user-switching.php` into the root of your `mu-plugins` directory, not in the `user-switching` subdirectory. This is a restriction of WordPress.
106
 
107
  == Upgrade Notice ==
108
 
109
+ = 0.8.5 =
110
+ * Add a 'Switch To' link to bbPress profile screens.
 
111
 
112
  == Changelog ==
113
 
114
+ = 0.8.5 =
115
+ * Add a 'Switch To' link to bbPress profile screens.
116
+
117
  = 0.8.4 =
118
  * Revert a change in 0.8.3 which switched to using the `login_init` hook. This hook is fired too late.
119
 
user-switching.php CHANGED
@@ -1,645 +1,653 @@
1
- <?php
2
- /*
3
- Plugin Name: User Switching
4
- Description: Instant switching between user accounts in WordPress
5
- Version: 0.8.4
6
- Plugin URI: https://lud.icro.us/wordpress-plugin-user-switching/
7
- Author: John Blackbourn
8
- Author URI: https://johnblackbourn.com/
9
- Text Domain: user-switching
10
- Domain Path: /languages/
11
- License: GPL v2 or later
12
-
13
- Copyright © 2013 John Blackbourn
14
-
15
- This program is free software; you can redistribute it and/or modify
16
- it under the terms of the GNU General Public License as published by
17
- the Free Software Foundation; either version 2 of the License, or
18
- (at your option) any later version.
19
-
20
- This program is distributed in the hope that it will be useful,
21
- but WITHOUT ANY WARRANTY; without even the implied warranty of
22
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
- GNU General Public License for more details.
24
-
25
- */
26
-
27
- class user_switching {
28
-
29
- /**
30
- * Class constructor. Set up some filters and actions.
31
- */
32
- public function __construct() {
33
-
34
- # Required functionality:
35
- add_filter( 'user_has_cap', array( $this, 'filter_user_has_cap' ), 10, 3 );
36
- add_filter( 'map_meta_cap', array( $this, 'filter_map_meta_cap' ), 10, 4 );
37
- add_filter( 'user_row_actions', array( $this, 'filter_user_row_actions' ), 10, 2 );
38
- add_action( 'plugins_loaded', array( $this, 'action_plugins_loaded' ) );
39
- add_action( 'init', array( $this, 'action_init' ) );
40
- add_action( 'all_admin_notices', array( $this, 'action_admin_notices' ), 1 );
41
- add_action( 'wp_logout', 'wp_clear_olduser_cookie' );
42
- add_action( 'wp_login', 'wp_clear_olduser_cookie' );
43
-
44
- # Nice-to-haves:
45
- add_filter( 'ms_user_row_actions', array( $this, 'filter_user_row_actions' ), 10, 2 );
46
- add_action( 'wp_footer', array( $this, 'action_wp_footer' ) );
47
- add_action( 'personal_options', array( $this, 'action_personal_options' ) );
48
- add_action( 'admin_bar_menu', array( $this, 'action_admin_bar_menu' ), 11 );
49
- add_action( 'bp_adminbar_menus', array( $this, 'action_bp_menus' ), 9 );
50
- add_action( 'bp_member_header_actions', array( $this, 'action_bp_button' ), 11 );
51
- add_filter( 'login_message', array( $this, 'filter_login_message' ), 1 );
52
- add_action( 'bp_directory_members_actions', array( $this, 'action_bp_button' ), 11 );
53
-
54
- }
55
-
56
- /**
57
- * Define the name of the old user cookie. Uses WordPress' cookie hash for increased security.
58
- *
59
- * @return null
60
- */
61
- public function action_plugins_loaded() {
62
- if ( !defined( 'OLDUSER_COOKIE' ) )
63
- define( 'OLDUSER_COOKIE', 'wordpress_olduser_' . COOKIEHASH );
64
- }
65
-
66
- /**
67
- * Output the 'Switch To' link on the user editing screen if we have permission to switch to this user.
68
- *
69
- * @param WP_User $user User object for this screen
70
- * @return null
71
- */
72
- public function action_personal_options( WP_User $user ) {
73
-
74
- if ( ! $link = self::maybe_switch_url( $user->ID ) )
75
- return;
76
-
77
- ?>
78
- <tr>
79
- <th scope="row"><?php _ex( 'User Switching', 'User Switching title on user profile screen', 'user-switching' ); ?></th>
80
- <td><a href="<?php echo $link; ?>"><?php _e( 'Switch&nbsp;To', 'user-switching' ); ?></a></td>
81
- </tr>
82
- <?php
83
- }
84
-
85
- /**
86
- * Return whether or not the current logged in user is being remembered in the form of a persistent browser
87
- * cookie (ie. they checked the 'Remember Me' check box when they logged in). This is used to persist the
88
- * 'remember me' value when the user switches to another user.
89
- *
90
- * @return bool Whether the current user is being 'remembered' or not.
91
- */
92
- public static function remember() {
93
-
94
- $current = wp_parse_auth_cookie( '', 'logged_in' );
95
- $cookie_life = apply_filters( 'auth_cookie_expiration', 172800, get_current_user_id(), false );
96
-
97
- # Here we calculate the expiration length of the current auth cookie and compare it to the default expiration.
98
- # If it's greater than this, then we know the user checked 'Remember Me' when they logged in.
99
- return ( ( $current['expiration'] - time() ) > $cookie_life );
100
-
101
- }
102
-
103
- /**
104
- * Load localisation files and route actions depending on the 'action' query var.
105
- *
106
- * @return null
107
- */
108
- public function action_init() {
109
-
110
- load_plugin_textdomain( 'user-switching', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' );
111
-
112
- if ( !isset( $_REQUEST['action'] ) )
113
- return;
114
-
115
- if ( isset( $_REQUEST['redirect_to'] ) and !empty( $_REQUEST['redirect_to'] ) )
116
- $redirect_to = remove_query_arg( self::remove_query_args(), $_REQUEST['redirect_to'] );
117
- else
118
- $redirect_to = false;
119
-
120
- switch ( $_REQUEST['action'] ) {
121
-
122
- # We're attempting to switch to another user:
123
- case 'switch_to_user':
124
- $user_id = absint( $_REQUEST['user_id'] );
125
-
126
- check_admin_referer( "switch_to_user_{$user_id}" );
127
-
128
- # Switch user:
129
- if ( switch_to_user( $user_id, self::remember() ) ) {
130
-
131
- # Redirect to the dashboard or the home URL depending on capabilities:
132
- if ( $redirect_to )
133
- wp_safe_redirect( add_query_arg( array( 'user_switched' => 'true' ), $redirect_to ) );
134
- else if ( !current_user_can( 'read' ) )
135
- wp_redirect( add_query_arg( array( 'user_switched' => 'true' ), home_url() ) );
136
- else
137
- wp_redirect( add_query_arg( array( 'user_switched' => 'true' ), admin_url() ) );
138
- die();
139
-
140
- } else {
141
- wp_die( __( 'Could not switch users.', 'user-switching' ) );
142
- }
143
- break;
144
-
145
- # We're attempting to switch back to the originating user:
146
- case 'switch_to_olduser':
147
-
148
- check_admin_referer( 'switch_to_olduser' );
149
-
150
- # Fetch the originating user data:
151
- if ( !$old_user = self::get_old_user() )
152
- wp_die( __( 'Could not switch users.', 'user-switching' ) );
153
-
154
- # Switch user:
155
- if ( switch_to_user( $old_user->ID, self::remember(), false ) ) {
156
- if ( $redirect_to )
157
- wp_safe_redirect( add_query_arg( array( 'user_switched' => 'true', 'switched_back' => 'true' ), $redirect_to ) );
158
- else
159
- wp_redirect( add_query_arg( array( 'user_switched' => 'true', 'switched_back' => 'true' ), admin_url( 'users.php' ) ) );
160
- die();
161
- } else {
162
- wp_die( __( 'Could not switch users.', 'user-switching' ) );
163
- }
164
- break;
165
-
166
- # We're attempting to switch off the current user:
167
- case 'switch_off':
168
-
169
- check_admin_referer( 'switch_off' );
170
-
171
- # Switch off:
172
- if ( switch_off_user() ) {
173
- if ( $redirect_to )
174
- wp_safe_redirect( add_query_arg( array( 'switched_off' => 'true' ), $redirect_to ) );
175
- else
176
- wp_redirect( add_query_arg( array( 'switched_off' => 'true' ), home_url() ) );
177
- die();
178
- } else {
179
- wp_die( __( 'Could not switch off.', 'user-switching' ) );
180
- }
181
- break;
182
-
183
- }
184
-
185
- }
186
-
187
- /**
188
- * Display the 'Switched to {user}' and 'Switch back to {user}' messages in the admin area.
189
- *
190
- * @return null
191
- */
192
- public function action_admin_notices() {
193
- $user = wp_get_current_user();
194
-
195
- if ( $old_user = self::get_old_user() ) {
196
-
197
- ?>
198
- <div id="user_switching" class="updated">
199
- <p><?php
200
- if ( isset( $_GET['user_switched'] ) )
201
- printf( __( 'Switched to %1$s (%2$s).', 'user-switching' ), $user->display_name, $user->user_login );
202
- $url = add_query_arg( array(
203
- 'redirect_to' => urlencode( self::current_url() )
204
- ), self::switch_back_url() );
205
- printf( ' <a href="%s">%s</a>.', $url, sprintf( __( 'Switch back to %1$s (%2$s)', 'user-switching' ), $old_user->display_name, $old_user->user_login ) );
206
- ?></p>
207
- </div>
208
- <?php
209
-
210
- } else if ( isset( $_GET['user_switched'] ) ) {
211
-
212
- ?>
213
- <div id="user_switching" class="updated">
214
- <p><?php
215
- if ( isset( $_GET['switched_back'] ) )
216
- printf( __( 'Switched back to %1$s (%2$s).', 'user-switching' ), $user->display_name, $user->user_login );
217
- else
218
- printf( __( 'Switched to %1$s (%2$s).', 'user-switching' ), $user->display_name, $user->user_login );
219
- ?></p>
220
- </div>
221
- <?php
222
-
223
- }
224
- }
225
-
226
- /**
227
- * Validate the latest item in the old_user cookie and return its user data.
228
- *
229
- * @return bool|WP_User False if there's no old user cookie or it's invalid, WP_User object if it's present and valid.
230
- */
231
- public static function get_old_user() {
232
- $cookie = wp_get_olduser_cookie();
233
- if ( !empty( $cookie ) ) {
234
- if ( $old_user_id = wp_validate_auth_cookie( end( $cookie ), 'old_user' ) )
235
- return get_userdata( $old_user_id );
236
- }
237
- return false;
238
- }
239
-
240
- /**
241
- * Adds a 'Switch back to {user}' link to the account menu in WordPress' admin bar.
242
- *
243
- * @param WP_Admin_Bar $wp_admin_bar The admin bar object
244
- * @return null
245
- */
246
- public function action_admin_bar_menu( WP_Admin_Bar $wp_admin_bar ) {
247
-
248
- if ( !function_exists( 'is_admin_bar_showing' ) )
249
- return;
250
- if ( !is_admin_bar_showing() )
251
- return;
252
-
253
- if ( method_exists( $wp_admin_bar, 'get_node' ) and $wp_admin_bar->get_node( 'user-actions' ) )
254
- $parent = 'user-actions';
255
- else if ( get_option( 'show_avatars' ) )
256
- $parent = 'my-account-with-avatar';
257
- else
258
- $parent = 'my-account';
259
-
260
- if ( $old_user = self::get_old_user() ) {
261
-
262
- $wp_admin_bar->add_menu( array(
263
- 'parent' => $parent,
264
- 'id' => 'switch-back',
265
- 'title' => sprintf( __( 'Switch back to %1$s (%2$s)', 'user-switching' ), $old_user->display_name, $old_user->user_login ),
266
- 'href' => add_query_arg( array(
267
- 'redirect_to' => urlencode( self::current_url() )
268
- ), self::switch_back_url() )
269
- ) );
270
-
271
- }
272
-
273
- if ( current_user_can( 'switch_off' ) ) {
274
-
275
- $url = self::switch_off_url();
276
- if ( !is_admin() ) {
277
- $url = add_query_arg( array(
278
- 'redirect_to' => urlencode( self::current_url() )
279
- ), $url );
280
- }
281
-
282
- $wp_admin_bar->add_menu( array(
283
- 'parent' => $parent,
284
- 'id' => 'switch-off',
285
- 'title' => __( 'Switch Off', 'user-switching' ),
286
- 'href' => $url
287
- ) );
288
-
289
- }
290
-
291
- }
292
-
293
- /**
294
- * Adds a 'Switch back to {user}' link to the WordPress footer if the admin toolbar isn't showing.
295
- *
296
- * @return null
297
- */
298
- public function action_wp_footer() {
299
-
300
- if ( !is_admin_bar_showing() and $old_user = self::get_old_user() ) {
301
- $link = sprintf( __( 'Switch back to %1$s (%2$s)', 'user-switching' ), $old_user->display_name, $old_user->user_login );
302
- $url = add_query_arg( array(
303
- 'redirect_to' => urlencode( self::current_url() )
304
- ), self::switch_back_url() );
305
- echo '<p id="user_switching_switch_on"><a href="' . $url . '">' . $link . '</a></p>';
306
- }
307
-
308
- }
309
-
310
- /**
311
- * Adds a 'Switch back to {user}' link to the WordPress login screen.
312
- *
313
- * @param string $message The login screen message
314
- * @return string The login screen message
315
- */
316
- public function filter_login_message( $message ) {
317
-
318
- if ( $old_user = self::get_old_user() ) {
319
- $link = sprintf( __( 'Switch back to %1$s (%2$s)', 'user-switching' ), $old_user->display_name, $old_user->user_login );
320
- $url = self::switch_back_url();
321
- if ( isset( $_REQUEST['redirect_to'] ) and !empty( $_REQUEST['redirect_to'] ) ) {
322
- $url = add_query_arg( array(
323
- 'redirect_to' => $_REQUEST['redirect_to']
324
- ), $url );
325
- }
326
- $message .= '<p class="message"><a href="' . $url . '">' . $link . '</a></p>';
327
- }
328
-
329
- return $message;
330
-
331
- }
332
-
333
- /**
334
- * Adds a 'Switch To' link to each list of user actions on the Users screen.
335
- *
336
- * @param array $actions The actions to display for this user row
337
- * @param WP_User $user The user object displayed in this row
338
- * @return array The actions to display for this user row
339
- */
340
- public function filter_user_row_actions( array $actions, WP_User $user ) {
341
-
342
- if ( ! $link = self::maybe_switch_url( $user->ID ) )
343
- return $actions;
344
-
345
- $actions['switch_to_user'] = '<a href="' . $link . '">' . __( 'Switch&nbsp;To', 'user-switching' ) . '</a>';
346
-
347
- return $actions;
348
- }
349
-
350
- /**
351
- * Adds a 'Switch back to {user}' link to the BuddyPress admin bar.
352
- *
353
- * @return null
354
- */
355
- public function action_bp_menus() {
356
-
357
- if ( !is_admin() and $old_user = self::get_old_user() ) {
358
-
359
- echo '<li id="bp-adminbar-userswitching-menu" style="background-image:none"><a href="' . self::switch_back_url() . '">';
360
- printf( __( 'Switch back to %1$s (%2$s)', 'user-switching' ), $old_user->display_name, $old_user->user_login );
361
- echo '</a></li>';
362
-
363
- }
364
-
365
- }
366
-
367
- /**
368
- * Adds a 'Switch To' link to each member's profile page and profile listings in BuddyPress.
369
- *
370
- * @return null
371
- */
372
- public function action_bp_button() {
373
-
374
- global $bp, $members_template;
375
-
376
- if ( !empty( $members_template ) and empty( $bp->displayed_user->id ) )
377
- $id = absint( $members_template->member->id );
378
- else
379
- $id = absint( $bp->displayed_user->id );
380
-
381
- if ( ! $link = self::maybe_switch_url( $id ) )
382
- return;
383
-
384
- $link = add_query_arg( array(
385
- 'redirect_to' => urlencode( bp_core_get_user_domain( $id ) )
386
- ), $link );
387
-
388
- # Workaround for https://buddypress.trac.wordpress.org/ticket/4212
389
- $components = array_keys( $bp->active_components );
390
- if ( !empty( $components ) )
391
- $component = reset( $components );
392
- else
393
- $component = 'core';
394
-
395
- echo bp_get_button( array(
396
- 'id' => 'user_switching',
397
- 'component' => $component,
398
- 'link_href' => $link,
399
- 'link_text' => __( 'Switch&nbsp;To', 'user-switching' )
400
- ) );
401
-
402
- }
403
-
404
- /**
405
- * Helper function. Returns the switch to or switch back URL for a given user ID.
406
- *
407
- * @param int $user_id The user ID to be switched to.
408
- * @return string|bool The required URL, or false if there's no old user or the user doesn't have the required capability.
409
- */
410
- public static function maybe_switch_url( $user_id ) {
411
-
412
- $old_user = self::get_old_user();
413
-
414
- if ( $old_user and ( $old_user->ID == $user_id ) )
415
- return self::switch_back_url();
416
- else if ( current_user_can( 'switch_to_user', $user_id ) )
417
- return self::switch_to_url( $user_id );
418
- else
419
- return false;
420
-
421
- }
422
-
423
- /**
424
- * Helper function. Returns the nonce-secured URL needed to switch to a given user ID.
425
- *
426
- * @param int $user_id The user ID to be switched to.
427
- * @return string The required URL
428
- */
429
- public static function switch_to_url( $user_id ) {
430
- return wp_nonce_url( add_query_arg( array(
431
- 'action' => 'switch_to_user',
432
- 'user_id' => $user_id
433
- ), wp_login_url() ), "switch_to_user_{$user_id}" );
434
- }
435
-
436
- /**
437
- * Helper function. Returns the nonce-secured URL needed to switch back to the originating user.
438
- *
439
- * @return string The required URL
440
- */
441
- public static function switch_back_url() {
442
- return wp_nonce_url( add_query_arg( array(
443
- 'action' => 'switch_to_olduser'
444
- ), wp_login_url() ), 'switch_to_olduser' );
445
- }
446
-
447
- /**
448
- * Helper function. Returns the nonce-secured URL needed to switch off the current user.
449
- *
450
- * @return string The required URL
451
- */
452
- public static function switch_off_url() {
453
- return wp_nonce_url( add_query_arg( array(
454
- 'action' => 'switch_off'
455
- ), wp_login_url() ), 'switch_off' );
456
- }
457
-
458
- /**
459
- * Helper function. Returns the current URL.
460
- *
461
- * @return string The current URL
462
- */
463
- public static function current_url() {
464
- return ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
465
- }
466
-
467
- /**
468
- * Helper function. Returns an array of query args to remove from the query string when doing a redirect.
469
- *
470
- * @return array Query args to remove from query string.
471
- */
472
- public static function remove_query_args() {
473
- return array(
474
- 'user_switched', 'switched_off', 'switched_back',
475
- 'message', 'updated', 'settings-updated', 'saved',
476
- 'activated', 'activate', 'deactivate',
477
- 'locked', 'skipped', 'deleted', 'trashed', 'untrashed'
478
- );
479
- }
480
-
481
- /**
482
- * Filter the user's capabilities so they can be added/removed on the fly.
483
- *
484
- * This is used to grant the 'switch_to_user' capability to a user if they have the ability to edit the user
485
- * they're trying to switch to (and that user is not themselves), and to grant the 'switch_off' capability to
486
- * a user if they can edit users.
487
- *
488
- * Important: This does not get called for Super Admins. See filter_map_meta_cap() below.
489
- *
490
- * @param array $user_caps User's capabilities
491
- * @param array $required_caps Actual required capabilities for the requested capability
492
- * @param array $args Arguments that accompany the requested capability check:
493
- * [0] => Requested capability from current_user_can()
494
- * [1] => Current user ID
495
- * [2] => Optional second parameter from current_user_can()
496
- * @return array User's capabilities
497
- */
498
- public function filter_user_has_cap( array $user_caps, array $required_caps, array $args ) {
499
- if ( 'switch_to_user' == $args[0] )
500
- $user_caps['switch_to_user'] = ( user_can( $args[1], 'edit_user', $args[2] ) and ( $args[2] != $args[1] ) );
501
- else if ( 'switch_off' == $args[0] )
502
- $user_caps['switch_off'] = user_can( $args[1], 'edit_users' );
503
- return $user_caps;
504
- }
505
-
506
- /**
507
- * Filters the actual required capabilities for a given capability or meta capability.
508
- *
509
- * This is used to add the 'do_not_allow' capability to the list of required capabilities when a super admin
510
- * is trying to switch to themselves. It affects nothing else as super admins can do everything by default.
511
- *
512
- * @param array $required_caps Actual required capabilities for the requested action
513
- * @param string $cap Capability or meta capability being checked
514
- * @param string $user_id Current user ID
515
- * @param array $args Arguments that accompany this capability check
516
- * @return array Required capabilities for the requested action
517
- */
518
- public function filter_map_meta_cap( array $required_caps, $cap, $user_id, array $args ) {
519
- if ( ( 'switch_to_user' == $cap ) and ( $args[0] == $user_id ) )
520
- $required_caps[] = 'do_not_allow';
521
- return $required_caps;
522
- }
523
-
524
- }
525
-
526
- /**
527
- * Sets an authorisation cookie containing the originating user, or appends it if there's more than one.
528
- *
529
- * @param int $old_user_id The ID of the originating user, usually the current logged in user.
530
- * @return null
531
- */
532
- if ( !function_exists( 'wp_set_olduser_cookie' ) ) {
533
- function wp_set_olduser_cookie( $old_user_id ) {
534
- $expiration = time() + 172800; # 48 hours
535
- $cookie = wp_get_olduser_cookie();
536
- $cookie[] = wp_generate_auth_cookie( $old_user_id, $expiration, 'old_user' );
537
- setcookie( OLDUSER_COOKIE, json_encode( $cookie ), $expiration, COOKIEPATH, COOKIE_DOMAIN, false );
538
- }
539
- }
540
-
541
- /**
542
- * Clears the cookie containing the originating user, or pops the latest item off the end if there's more than one.
543
- *
544
- * @param bool $clear_all Whether to clear the cookie or just pop the last user information off the end.
545
- * @return null
546
- */
547
- if ( !function_exists( 'wp_clear_olduser_cookie' ) ) {
548
- function wp_clear_olduser_cookie( $clear_all = true ) {
549
- $cookie = wp_get_olduser_cookie();
550
- if ( $clear_all or empty( $cookie ) ) {
551
- setcookie( OLDUSER_COOKIE, ' ', time() - 31536000, COOKIEPATH, COOKIE_DOMAIN );
552
- } else {
553
- array_pop( $cookie );
554
- $expiration = time() + 172800; # 48 hours
555
- setcookie( OLDUSER_COOKIE, json_encode( $cookie ), $expiration, COOKIEPATH, COOKIE_DOMAIN, false );
556
- }
557
- }
558
- }
559
-
560
- /**
561
- * Gets the value of the cookie containing the list of originating users.
562
- *
563
- * @return array Array of originating user authentication cookies. @see wp_generate_auth_cookie()
564
- */
565
- if ( !function_exists( 'wp_get_olduser_cookie' ) ) {
566
- function wp_get_olduser_cookie() {
567
- if ( isset( $_COOKIE[OLDUSER_COOKIE] ) )
568
- $cookie = json_decode( stripslashes( $_COOKIE[OLDUSER_COOKIE] ) );
569
- if ( !isset( $cookie ) or !is_array( $cookie ) )
570
- $cookie = array();
571
- return $cookie;
572
- }
573
- }
574
-
575
- /**
576
- * Switches the current logged in user to the specified user.
577
- *
578
- * @param int $user_id The ID of the user to switch to.
579
- * @param bool $remember Whether to 'remember' the user in the form of a persistent browser cookie. Optional.
580
- * @param bool $set_old_user Whether to set the old user cookie. Optional.
581
- * @return bool True on success, false on failure.
582
- */
583
- if ( !function_exists( 'switch_to_user' ) ) {
584
- function switch_to_user( $user_id, $remember = false, $set_old_user = true ) {
585
- if ( !$user = get_userdata( $user_id ) )
586
- return false;
587
-
588
- if ( $set_old_user and is_user_logged_in() ) {
589
- $old_user_id = get_current_user_id();
590
- wp_set_olduser_cookie( $old_user_id );
591
- } else {
592
- $old_user_id = false;
593
- wp_clear_olduser_cookie( false );
594
- }
595
-
596
- wp_clear_auth_cookie();
597
- wp_set_auth_cookie( $user_id, $remember );
598
- wp_set_current_user( $user_id );
599
-
600
- if ( $set_old_user )
601
- do_action( 'switch_to_user', $user_id, $old_user_id );
602
- else
603
- do_action( 'switch_back_user', $user_id, $old_user_id );
604
-
605
- return true;
606
- }
607
- }
608
-
609
- /**
610
- * Switches off the current logged in user. This logs the current user out while retaining a cookie allowing them to log straight
611
- * back in using the 'Switch back to {user}' system.
612
- *
613
- * @return bool True on success, false on failure.
614
- */
615
- if ( !function_exists( 'switch_off_user' ) ) {
616
- function switch_off_user() {
617
- if ( !$old_user_id = get_current_user_id() )
618
- return false;
619
-
620
- wp_set_olduser_cookie( $old_user_id );
621
- wp_clear_auth_cookie();
622
-
623
- do_action( 'switch_off_user', $old_user_id );
624
-
625
- return true;
626
- }
627
- }
628
-
629
- /**
630
- * Helper function. Did the current user switch into their account?
631
- *
632
- * @return bool|object False if the user isn't logged in or they didn't switch in; old user object (which evalutes to true) if the user switched into the current user account.
633
- */
634
- if ( !function_exists( 'current_user_switched' ) ) {
635
- function current_user_switched() {
636
- if ( !is_user_logged_in() )
637
- return false;
638
-
639
- return user_switching::get_old_user();
640
- }
641
- }
642
-
643
- global $user_switching;
644
-
645
- $user_switching = new user_switching;
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Plugin Name: User Switching
4
+ Description: Instant switching between user accounts in WordPress
5
+ Version: 0.8.5
6
+ Plugin URI: https://lud.icro.us/wordpress-plugin-user-switching/
7
+ Author: John Blackbourn
8
+ Author URI: https://johnblackbourn.com/
9
+ Text Domain: user-switching
10
+ Domain Path: /languages/
11
+ License: GPL v2 or later
12
+
13
+ Copyright © 2013 John Blackbourn
14
+
15
+ This program is free software; you can redistribute it and/or modify
16
+ it under the terms of the GNU General Public License as published by
17
+ the Free Software Foundation; either version 2 of the License, or
18
+ (at your option) any later version.
19
+
20
+ This program is distributed in the hope that it will be useful,
21
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
22
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
+ GNU General Public License for more details.
24
+
25
+ */
26
+
27
+ class user_switching {
28
+
29
+ /**
30
+ * Class constructor. Set up some filters and actions.
31
+ */
32
+ public function __construct() {
33
+
34
+ # Required functionality:
35
+ add_filter( 'user_has_cap', array( $this, 'filter_user_has_cap' ), 10, 3 );
36
+ add_filter( 'map_meta_cap', array( $this, 'filter_map_meta_cap' ), 10, 4 );
37
+ add_filter( 'user_row_actions', array( $this, 'filter_user_row_actions' ), 10, 2 );
38
+ add_action( 'plugins_loaded', array( $this, 'action_plugins_loaded' ) );
39
+ add_action( 'init', array( $this, 'action_init' ) );
40
+ add_action( 'all_admin_notices', array( $this, 'action_admin_notices' ), 1 );
41
+ add_action( 'wp_logout', 'wp_clear_olduser_cookie' );
42
+ add_action( 'wp_login', 'wp_clear_olduser_cookie' );
43
+
44
+ # Nice-to-haves:
45
+ add_filter( 'ms_user_row_actions', array( $this, 'filter_user_row_actions' ), 10, 2 );
46
+ add_filter( 'login_message', array( $this, 'filter_login_message' ), 1 );
47
+ add_action( 'wp_footer', array( $this, 'action_wp_footer' ) );
48
+ add_action( 'personal_options', array( $this, 'action_personal_options' ) );
49
+ add_action( 'admin_bar_menu', array( $this, 'action_admin_bar_menu' ), 11 );
50
+ add_action( 'bp_member_header_actions', array( $this, 'action_bp_button' ), 11 );
51
+ add_action( 'bp_directory_members_actions', array( $this, 'action_bp_button' ), 11 );
52
+ add_action( 'bbp_template_after_user_details', array( $this, 'action_bbpress_button' ) );
53
+
54
+ }
55
+
56
+ /**
57
+ * Define the name of the old user cookie. Uses WordPress' cookie hash for increased security.
58
+ *
59
+ * @return null
60
+ */
61
+ public function action_plugins_loaded() {
62
+ if ( !defined( 'OLDUSER_COOKIE' ) )
63
+ define( 'OLDUSER_COOKIE', 'wordpress_olduser_' . COOKIEHASH );
64
+ }
65
+
66
+ /**
67
+ * Output the 'Switch To' link on the user editing screen if we have permission to switch to this user.
68
+ *
69
+ * @param WP_User $user User object for this screen
70
+ * @return null
71
+ */
72
+ public function action_personal_options( WP_User $user ) {
73
+
74
+ if ( ! $link = self::maybe_switch_url( $user->ID ) )
75
+ return;
76
+
77
+ ?>
78
+ <tr>
79
+ <th scope="row"><?php _ex( 'User Switching', 'User Switching title on user profile screen', 'user-switching' ); ?></th>
80
+ <td><a href="<?php echo $link; ?>"><?php _e( 'Switch&nbsp;To', 'user-switching' ); ?></a></td>
81
+ </tr>
82
+ <?php
83
+ }
84
+
85
+ /**
86
+ * Return whether or not the current logged in user is being remembered in the form of a persistent browser
87
+ * cookie (ie. they checked the 'Remember Me' check box when they logged in). This is used to persist the
88
+ * 'remember me' value when the user switches to another user.
89
+ *
90
+ * @return bool Whether the current user is being 'remembered' or not.
91
+ */
92
+ public static function remember() {
93
+
94
+ $current = wp_parse_auth_cookie( '', 'logged_in' );
95
+ $cookie_life = apply_filters( 'auth_cookie_expiration', 172800, get_current_user_id(), false );
96
+
97
+ # Here we calculate the expiration length of the current auth cookie and compare it to the default expiration.
98
+ # If it's greater than this, then we know the user checked 'Remember Me' when they logged in.
99
+ return ( ( $current['expiration'] - time() ) > $cookie_life );
100
+
101
+ }
102
+
103
+ /**
104
+ * Load localisation files and route actions depending on the 'action' query var.
105
+ *
106
+ * @return null
107
+ */
108
+ public function action_init() {
109
+
110
+ load_plugin_textdomain( 'user-switching', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' );
111
+
112
+ if ( !isset( $_REQUEST['action'] ) )
113
+ return;
114
+
115
+ if ( isset( $_REQUEST['redirect_to'] ) and !empty( $_REQUEST['redirect_to'] ) )
116
+ $redirect_to = self::remove_query_args( $_REQUEST['redirect_to'] );
117
+ else
118
+ $redirect_to = false;
119
+
120
+ switch ( $_REQUEST['action'] ) {
121
+
122
+ # We're attempting to switch to another user:
123
+ case 'switch_to_user':
124
+ $user_id = absint( $_REQUEST['user_id'] );
125
+
126
+ check_admin_referer( "switch_to_user_{$user_id}" );
127
+
128
+ # Switch user:
129
+ if ( switch_to_user( $user_id, self::remember() ) ) {
130
+
131
+ # Redirect to the dashboard or the home URL depending on capabilities:
132
+ if ( $redirect_to )
133
+ wp_safe_redirect( add_query_arg( array( 'user_switched' => 'true' ), $redirect_to ) );
134
+ else if ( !current_user_can( 'read' ) )
135
+ wp_redirect( add_query_arg( array( 'user_switched' => 'true' ), home_url() ) );
136
+ else
137
+ wp_redirect( add_query_arg( array( 'user_switched' => 'true' ), admin_url() ) );
138
+ die();
139
+
140
+ } else {
141
+ wp_die( __( 'Could not switch users.', 'user-switching' ) );
142
+ }
143
+ break;
144
+
145
+ # We're attempting to switch back to the originating user:
146
+ case 'switch_to_olduser':
147
+
148
+ check_admin_referer( 'switch_to_olduser' );
149
+
150
+ # Fetch the originating user data:
151
+ if ( !$old_user = self::get_old_user() )
152
+ wp_die( __( 'Could not switch users.', 'user-switching' ) );
153
+
154
+ # Switch user:
155
+ if ( switch_to_user( $old_user->ID, self::remember(), false ) ) {
156
+ if ( $redirect_to )
157
+ wp_safe_redirect( add_query_arg( array( 'user_switched' => 'true', 'switched_back' => 'true' ), $redirect_to ) );
158
+ else
159
+ wp_redirect( add_query_arg( array( 'user_switched' => 'true', 'switched_back' => 'true' ), admin_url( 'users.php' ) ) );
160
+ die();
161
+ } else {
162
+ wp_die( __( 'Could not switch users.', 'user-switching' ) );
163
+ }
164
+ break;
165
+
166
+ # We're attempting to switch off the current user:
167
+ case 'switch_off':
168
+
169
+ check_admin_referer( 'switch_off' );
170
+
171
+ # Switch off:
172
+ if ( switch_off_user() ) {
173
+ if ( $redirect_to )
174
+ wp_safe_redirect( add_query_arg( array( 'switched_off' => 'true' ), $redirect_to ) );
175
+ else
176
+ wp_redirect( add_query_arg( array( 'switched_off' => 'true' ), home_url() ) );
177
+ die();
178
+ } else {
179
+ wp_die( __( 'Could not switch off.', 'user-switching' ) );
180
+ }
181
+ break;
182
+
183
+ }
184
+
185
+ }
186
+
187
+ /**
188
+ * Display the 'Switched to {user}' and 'Switch back to {user}' messages in the admin area.
189
+ *
190
+ * @return null
191
+ */
192
+ public function action_admin_notices() {
193
+ $user = wp_get_current_user();
194
+
195
+ if ( $old_user = self::get_old_user() ) {
196
+
197
+ ?>
198
+ <div id="user_switching" class="updated">
199
+ <p><?php
200
+ if ( isset( $_GET['user_switched'] ) )
201
+ printf( __( 'Switched to %1$s (%2$s).', 'user-switching' ), $user->display_name, $user->user_login );
202
+ $url = add_query_arg( array(
203
+ 'redirect_to' => urlencode( self::current_url() )
204
+ ), self::switch_back_url() );
205
+ printf( ' <a href="%s">%s</a>.', $url, sprintf( __( 'Switch back to %1$s (%2$s)', 'user-switching' ), $old_user->display_name, $old_user->user_login ) );
206
+ ?></p>
207
+ </div>
208
+ <?php
209
+
210
+ } else if ( isset( $_GET['user_switched'] ) ) {
211
+
212
+ ?>
213
+ <div id="user_switching" class="updated">
214
+ <p><?php
215
+ if ( isset( $_GET['switched_back'] ) )
216
+ printf( __( 'Switched back to %1$s (%2$s).', 'user-switching' ), $user->display_name, $user->user_login );
217
+ else
218
+ printf( __( 'Switched to %1$s (%2$s).', 'user-switching' ), $user->display_name, $user->user_login );
219
+ ?></p>
220
+ </div>
221
+ <?php
222
+
223
+ }
224
+ }
225
+
226
+ /**
227
+ * Validate the latest item in the old_user cookie and return its user data.
228
+ *
229
+ * @return bool|WP_User False if there's no old user cookie or it's invalid, WP_User object if it's present and valid.
230
+ */
231
+ public static function get_old_user() {
232
+ $cookie = wp_get_olduser_cookie();
233
+ if ( !empty( $cookie ) ) {
234
+ if ( $old_user_id = wp_validate_auth_cookie( end( $cookie ), 'old_user' ) )
235
+ return get_userdata( $old_user_id );
236
+ }
237
+ return false;
238
+ }
239
+
240
+ /**
241
+ * Adds a 'Switch back to {user}' link to the account menu in WordPress' admin bar.
242
+ *
243
+ * @param WP_Admin_Bar $wp_admin_bar The admin bar object
244
+ * @return null
245
+ */
246
+ public function action_admin_bar_menu( WP_Admin_Bar $wp_admin_bar ) {
247
+
248
+ if ( !function_exists( 'is_admin_bar_showing' ) )
249
+ return;
250
+ if ( !is_admin_bar_showing() )
251
+ return;
252
+
253
+ if ( method_exists( $wp_admin_bar, 'get_node' ) and $wp_admin_bar->get_node( 'user-actions' ) )
254
+ $parent = 'user-actions';
255
+ else if ( get_option( 'show_avatars' ) )
256
+ $parent = 'my-account-with-avatar';
257
+ else
258
+ $parent = 'my-account';
259
+
260
+ if ( $old_user = self::get_old_user() ) {
261
+
262
+ $wp_admin_bar->add_menu( array(
263
+ 'parent' => $parent,
264
+ 'id' => 'switch-back',
265
+ 'title' => sprintf( __( 'Switch back to %1$s (%2$s)', 'user-switching' ), $old_user->display_name, $old_user->user_login ),
266
+ 'href' => add_query_arg( array(
267
+ 'redirect_to' => urlencode( self::current_url() )
268
+ ), self::switch_back_url() )
269
+ ) );
270
+
271
+ }
272
+
273
+ if ( current_user_can( 'switch_off' ) ) {
274
+
275
+ $url = self::switch_off_url();
276
+ if ( !is_admin() ) {
277
+ $url = add_query_arg( array(
278
+ 'redirect_to' => urlencode( self::current_url() )
279
+ ), $url );
280
+ }
281
+
282
+ $wp_admin_bar->add_menu( array(
283
+ 'parent' => $parent,
284
+ 'id' => 'switch-off',
285
+ 'title' => __( 'Switch Off', 'user-switching' ),
286
+ 'href' => $url
287
+ ) );
288
+
289
+ }
290
+
291
+ }
292
+
293
+ /**
294
+ * Adds a 'Switch back to {user}' link to the WordPress footer if the admin toolbar isn't showing.
295
+ *
296
+ * @return null
297
+ */
298
+ public function action_wp_footer() {
299
+
300
+ if ( !is_admin_bar_showing() and $old_user = self::get_old_user() ) {
301
+ $link = sprintf( __( 'Switch back to %1$s (%2$s)', 'user-switching' ), $old_user->display_name, $old_user->user_login );
302
+ $url = add_query_arg( array(
303
+ 'redirect_to' => urlencode( self::current_url() )
304
+ ), self::switch_back_url() );
305
+ echo '<p id="user_switching_switch_on"><a href="' . $url . '">' . $link . '</a></p>';
306
+ }
307
+
308
+ }
309
+
310
+ /**
311
+ * Adds a 'Switch back to {user}' link to the WordPress login screen.
312
+ *
313
+ * @param string $message The login screen message
314
+ * @return string The login screen message
315
+ */
316
+ public function filter_login_message( $message ) {
317
+
318
+ if ( $old_user = self::get_old_user() ) {
319
+ $link = sprintf( __( 'Switch back to %1$s (%2$s)', 'user-switching' ), $old_user->display_name, $old_user->user_login );
320
+ $url = self::switch_back_url();
321
+ if ( isset( $_REQUEST['redirect_to'] ) and !empty( $_REQUEST['redirect_to'] ) ) {
322
+ $url = add_query_arg( array(
323
+ 'redirect_to' => $_REQUEST['redirect_to']
324
+ ), $url );
325
+ }
326
+ $message .= '<p class="message"><a href="' . $url . '">' . $link . '</a></p>';
327
+ }
328
+
329
+ return $message;
330
+
331
+ }
332
+
333
+ /**
334
+ * Adds a 'Switch To' link to each list of user actions on the Users screen.
335
+ *
336
+ * @param array $actions The actions to display for this user row
337
+ * @param WP_User $user The user object displayed in this row
338
+ * @return array The actions to display for this user row
339
+ */
340
+ public function filter_user_row_actions( array $actions, WP_User $user ) {
341
+
342
+ if ( ! $link = self::maybe_switch_url( $user->ID ) )
343
+ return $actions;
344
+
345
+ $actions['switch_to_user'] = '<a href="' . $link . '">' . __( 'Switch&nbsp;To', 'user-switching' ) . '</a>';
346
+
347
+ return $actions;
348
+ }
349
+
350
+ /**
351
+ * Adds a 'Switch To' link to each member's profile page and profile listings in BuddyPress.
352
+ *
353
+ * @return null
354
+ */
355
+ public function action_bp_button() {
356
+
357
+ global $bp, $members_template;
358
+
359
+ if ( !empty( $members_template ) and empty( $bp->displayed_user->id ) )
360
+ $id = absint( $members_template->member->id );
361
+ else
362
+ $id = absint( $bp->displayed_user->id );
363
+
364
+ if ( ! $link = self::maybe_switch_url( $id ) )
365
+ return;
366
+
367
+ $link = add_query_arg( array(
368
+ 'redirect_to' => urlencode( bp_core_get_user_domain( $id ) )
369
+ ), $link );
370
+
371
+ # Workaround for https://buddypress.trac.wordpress.org/ticket/4212
372
+ $components = array_keys( $bp->active_components );
373
+ if ( !empty( $components ) )
374
+ $component = reset( $components );
375
+ else
376
+ $component = 'core';
377
+
378
+ echo bp_get_button( array(
379
+ 'id' => 'user_switching',
380
+ 'component' => $component,
381
+ 'link_href' => $link,
382
+ 'link_text' => __( 'Switch&nbsp;To', 'user-switching' )
383
+ ) );
384
+
385
+ }
386
+
387
+ /**
388
+ * Adds a 'Switch To' link to each member's profile page in bbPress.
389
+ *
390
+ * @return null
391
+ */
392
+ public function action_bbpress_button() {
393
+
394
+ $id = bbp_get_user_id();
395
+
396
+ if ( ! $link = self::maybe_switch_url( $id ) )
397
+ return;
398
+
399
+ $link = add_query_arg( array(
400
+ 'redirect_to' => urlencode( bbp_get_user_profile_url( $id ) )
401
+ ), $link );
402
+
403
+ ?>
404
+ <ul>
405
+ <li><a href="<?php echo $link; ?>"><?php _e( 'Switch&nbsp;To', 'user-switching' ); ?></a></li>
406
+ </ul>
407
+ <?php
408
+
409
+ }
410
+
411
+ /**
412
+ * Helper function. Returns the switch to or switch back URL for a given user ID.
413
+ *
414
+ * @param int $user_id The user ID to be switched to.
415
+ * @return string|bool The required URL, or false if there's no old user or the user doesn't have the required capability.
416
+ */
417
+ public static function maybe_switch_url( $user_id ) {
418
+
419
+ $old_user = self::get_old_user();
420
+
421
+ if ( $old_user and ( $old_user->ID == $user_id ) )
422
+ return self::switch_back_url();
423
+ else if ( current_user_can( 'switch_to_user', $user_id ) )
424
+ return self::switch_to_url( $user_id );
425
+ else
426
+ return false;
427
+
428
+ }
429
+
430
+ /**
431
+ * Helper function. Returns the nonce-secured URL needed to switch to a given user ID.
432
+ *
433
+ * @param int $user_id The user ID to be switched to.
434
+ * @return string The required URL
435
+ */
436
+ public static function switch_to_url( $user_id ) {
437
+ return wp_nonce_url( add_query_arg( array(
438
+ 'action' => 'switch_to_user',
439
+ 'user_id' => $user_id
440
+ ), wp_login_url() ), "switch_to_user_{$user_id}" );
441
+ }
442
+
443
+ /**
444
+ * Helper function. Returns the nonce-secured URL needed to switch back to the originating user.
445
+ *
446
+ * @return string The required URL
447
+ */
448
+ public static function switch_back_url() {
449
+ return wp_nonce_url( add_query_arg( array(
450
+ 'action' => 'switch_to_olduser'
451
+ ), wp_login_url() ), 'switch_to_olduser' );
452
+ }
453
+
454
+ /**
455
+ * Helper function. Returns the nonce-secured URL needed to switch off the current user.
456
+ *
457
+ * @return string The required URL
458
+ */
459
+ public static function switch_off_url() {
460
+ return wp_nonce_url( add_query_arg( array(
461
+ 'action' => 'switch_off'
462
+ ), wp_login_url() ), 'switch_off' );
463
+ }
464
+
465
+ /**
466
+ * Helper function. Returns the current URL.
467
+ *
468
+ * @return string The current URL
469
+ */
470
+ public static function current_url() {
471
+ return ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
472
+ }
473
+
474
+ /**
475
+ * Helper function. Removes a list of common confirmation-style query args from a URL.
476
+ *
477
+ * @param string $url A URL
478
+ * @return string The URL with the listed query args removed
479
+ */
480
+ public static function remove_query_args( $url ) {
481
+ return remove_query_arg( array(
482
+ 'user_switched', 'switched_off', 'switched_back',
483
+ 'message', 'updated', 'settings-updated', 'saved',
484
+ 'activated', 'activate', 'deactivate',
485
+ 'locked', 'skipped', 'deleted', 'trashed', 'untrashed'
486
+ ), $url );
487
+ }
488
+
489
+ /**
490
+ * Filter the user's capabilities so they can be added/removed on the fly.
491
+ *
492
+ * This is used to grant the 'switch_to_user' capability to a user if they have the ability to edit the user
493
+ * they're trying to switch to (and that user is not themselves), and to grant the 'switch_off' capability to
494
+ * a user if they can edit users.
495
+ *
496
+ * Important: This does not get called for Super Admins. See filter_map_meta_cap() below.
497
+ *
498
+ * @param array $user_caps User's capabilities
499
+ * @param array $required_caps Actual required capabilities for the requested capability
500
+ * @param array $args Arguments that accompany the requested capability check:
501
+ * [0] => Requested capability from current_user_can()
502
+ * [1] => Current user ID
503
+ * [2] => Optional second parameter from current_user_can()
504
+ * @return array User's capabilities
505
+ */
506
+ public function filter_user_has_cap( array $user_caps, array $required_caps, array $args ) {
507
+ if ( 'switch_to_user' == $args[0] )
508
+ $user_caps['switch_to_user'] = ( user_can( $args[1], 'edit_user', $args[2] ) and ( $args[2] != $args[1] ) );
509
+ else if ( 'switch_off' == $args[0] )
510
+ $user_caps['switch_off'] = user_can( $args[1], 'edit_users' );
511
+ return $user_caps;
512
+ }
513
+
514
+ /**
515
+ * Filters the actual required capabilities for a given capability or meta capability.
516
+ *
517
+ * This is used to add the 'do_not_allow' capability to the list of required capabilities when a super admin
518
+ * is trying to switch to themselves. It affects nothing else as super admins can do everything by default.
519
+ *
520
+ * @param array $required_caps Actual required capabilities for the requested action
521
+ * @param string $cap Capability or meta capability being checked
522
+ * @param string $user_id Current user ID
523
+ * @param array $args Arguments that accompany this capability check
524
+ * @return array Required capabilities for the requested action
525
+ */
526
+ public function filter_map_meta_cap( array $required_caps, $cap, $user_id, array $args ) {
527
+ if ( ( 'switch_to_user' == $cap ) and ( $args[0] == $user_id ) )
528
+ $required_caps[] = 'do_not_allow';
529
+ return $required_caps;
530
+ }
531
+
532
+ }
533
+
534
+ /**
535
+ * Sets an authorisation cookie containing the originating user, or appends it if there's more than one.
536
+ *
537
+ * @param int $old_user_id The ID of the originating user, usually the current logged in user.
538
+ * @return null
539
+ */
540
+ if ( !function_exists( 'wp_set_olduser_cookie' ) ) {
541
+ function wp_set_olduser_cookie( $old_user_id ) {
542
+ $expiration = time() + 172800; # 48 hours
543
+ $cookie = wp_get_olduser_cookie();
544
+ $cookie[] = wp_generate_auth_cookie( $old_user_id, $expiration, 'old_user' );
545
+ setcookie( OLDUSER_COOKIE, json_encode( $cookie ), $expiration, COOKIEPATH, COOKIE_DOMAIN, false );
546
+ }
547
+ }
548
+
549
+ /**
550
+ * Clears the cookie containing the originating user, or pops the latest item off the end if there's more than one.
551
+ *
552
+ * @param bool $clear_all Whether to clear the cookie or just pop the last user information off the end.
553
+ * @return null
554
+ */
555
+ if ( !function_exists( 'wp_clear_olduser_cookie' ) ) {
556
+ function wp_clear_olduser_cookie( $clear_all = true ) {
557
+ $cookie = wp_get_olduser_cookie();
558
+ if ( $clear_all or empty( $cookie ) ) {
559
+ setcookie( OLDUSER_COOKIE, ' ', time() - 31536000, COOKIEPATH, COOKIE_DOMAIN );
560
+ } else {
561
+ array_pop( $cookie );
562
+ $expiration = time() + 172800; # 48 hours
563
+ setcookie( OLDUSER_COOKIE, json_encode( $cookie ), $expiration, COOKIEPATH, COOKIE_DOMAIN, false );
564
+ }
565
+ }
566
+ }
567
+
568
+ /**
569
+ * Gets the value of the cookie containing the list of originating users.
570
+ *
571
+ * @return array Array of originating user authentication cookies. @see wp_generate_auth_cookie()
572
+ */
573
+ if ( !function_exists( 'wp_get_olduser_cookie' ) ) {
574
+ function wp_get_olduser_cookie() {
575
+ if ( isset( $_COOKIE[OLDUSER_COOKIE] ) )
576
+ $cookie = json_decode( stripslashes( $_COOKIE[OLDUSER_COOKIE] ) );
577
+ if ( !isset( $cookie ) or !is_array( $cookie ) )
578
+ $cookie = array();
579
+ return $cookie;
580
+ }
581
+ }
582
+
583
+ /**
584
+ * Switches the current logged in user to the specified user.
585
+ *
586
+ * @param int $user_id The ID of the user to switch to.
587
+ * @param bool $remember Whether to 'remember' the user in the form of a persistent browser cookie. Optional.
588
+ * @param bool $set_old_user Whether to set the old user cookie. Optional.
589
+ * @return bool True on success, false on failure.
590
+ */
591
+ if ( !function_exists( 'switch_to_user' ) ) {
592
+ function switch_to_user( $user_id, $remember = false, $set_old_user = true ) {
593
+ if ( !$user = get_userdata( $user_id ) )
594
+ return false;
595
+
596
+ if ( $set_old_user and is_user_logged_in() ) {
597
+ $old_user_id = get_current_user_id();
598
+ wp_set_olduser_cookie( $old_user_id );
599
+ } else {
600
+ $old_user_id = false;
601
+ wp_clear_olduser_cookie( false );
602
+ }
603
+
604
+ wp_clear_auth_cookie();
605
+ wp_set_auth_cookie( $user_id, $remember );
606
+ wp_set_current_user( $user_id );
607
+
608
+ if ( $set_old_user )
609
+ do_action( 'switch_to_user', $user_id, $old_user_id );
610
+ else
611
+ do_action( 'switch_back_user', $user_id, $old_user_id );
612
+
613
+ return true;
614
+ }
615
+ }
616
+
617
+ /**
618
+ * Switches off the current logged in user. This logs the current user out while retaining a cookie allowing them to log straight
619
+ * back in using the 'Switch back to {user}' system.
620
+ *
621
+ * @return bool True on success, false on failure.
622
+ */
623
+ if ( !function_exists( 'switch_off_user' ) ) {
624
+ function switch_off_user() {
625
+ if ( !$old_user_id = get_current_user_id() )
626
+ return false;
627
+
628
+ wp_set_olduser_cookie( $old_user_id );
629
+ wp_clear_auth_cookie();
630
+
631
+ do_action( 'switch_off_user', $old_user_id );
632
+
633
+ return true;
634
+ }
635
+ }
636
+
637
+ /**
638
+ * Helper function. Did the current user switch into their account?
639
+ *
640
+ * @return bool|object False if the user isn't logged in or they didn't switch in; old user object (which evalutes to true) if the user switched into the current user account.
641
+ */
642
+ if ( !function_exists( 'current_user_switched' ) ) {
643
+ function current_user_switched() {
644
+ if ( !is_user_logged_in() )
645
+ return false;
646
+
647
+ return user_switching::get_old_user();
648
+ }
649
+ }
650
+
651
+ global $user_switching;
652
+
653
+ $user_switching = new user_switching;