Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions tests/acceptance/PostLock.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { test, expect } from './utils/test-setup';

test.describe( 'Post Lock', () => {
let sharedPostId: string;
let editorId: string;

test.beforeAll( async ( { globalUtils } ) => {
await globalUtils.installWordPress();
sharedPostId = globalUtils.runWPCLICommand( 'post create --post_title="Test Post" --post_status=publish --porcelain' );
editorId = globalUtils.runWPCLICommand( 'user create editor editor@example.com --role=editor --user_pass=password --porcelain' );
} );

test( 'Switch back from a post editing screen releases the post lock', {
annotation: {
type: 'user-story',
description: 'As a user who has switched accounts to edit a post, I want to switch back without seeing the post locked modal, in order to continue editing'
}
}, async ( {
page,
admin,
userSwitching,
globalUtils,
} ) => {
await userSwitching.loginViaPage( 'admin', 'password' );

// Simulate a fresh lock left behind by the editor user.
const setStaleLock = () => {
const now = Math.floor( Date.now() / 1000 );
globalUtils.runWPCLICommand( `post meta update ${sharedPostId} _edit_lock "${now}:${editorId}"` );
};

// Without the after-switch-back query args, the lock modal should be visible.
setStaleLock();
await admin.visitAdminPage( 'post.php', `post=${sharedPostId}&action=edit` );
await expect( page.getByText( 'This post is already being edited' ) ).toBeVisible();

// With the after-switch-back query args, the lock modal should not be visible.
setStaleLock();
await admin.visitAdminPage( 'post.php', `post=${sharedPostId}&action=edit&user_switched=true&switched_back=true` );
await expect( page.getByText( 'This post is already being edited' ) ).not.toBeVisible();
} );
} );
57 changes: 57 additions & 0 deletions tests/integration/PostLockTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php declare(strict_types = 1);

namespace UserSwitching\Tests;

/**
* @covers \user_switching::filter_post_lock_window_after_switch_back
*/
final class PostLockTest extends Test {
public function testPostLockWindowIsZeroedOnPostEditScreenAfterSwitchBack(): void {
$_GET = [
'switched_back' => 'true',
'action' => 'edit',
];

self::assertSame( 0, apply_filters( 'wp_check_post_lock_window', 150 ) );
}

public function testPostLockWindowIsUnchangedWithoutSwitchedBack(): void {
$_GET = [
'action' => 'edit',
];

self::assertSame( 150, apply_filters( 'wp_check_post_lock_window', 150 ) );
}

public function testPostLockWindowIsUnchangedWhenActionIsNotEdit(): void {
$_GET = [
'switched_back' => 'true',
];

self::assertSame( 150, apply_filters( 'wp_check_post_lock_window', 150 ) );
}

public function testStalePostLockIsTreatedAsExpiredAfterSwitchBack(): void {
$current = self::$testers['admin'];
$previous = self::$users['editor'];

$post = self::factory()->post->create_and_get();
self::assertInstanceOf( 'WP_Post', $post );

wp_set_current_user( $current->ID );

// Simulate a fresh lock left behind by the user being switched away from.
update_post_meta( $post->ID, '_edit_lock', time() . ':' . $previous->ID );

// Without the after-switch-back query args, the lock is considered active.
$_GET = [];
self::assertSame( $previous->ID, wp_check_post_lock( $post->ID ) );

// With the after-switch-back query args, the lock is treated as expired.
$_GET = [
'switched_back' => 'true',
'action' => 'edit',
];
self::assertFalse( wp_check_post_lock( $post->ID ) );
}
}
19 changes: 19 additions & 0 deletions user-switching.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ public function init_hooks(): void {
add_filter( 'ms_user_row_actions', [ $this, 'filter_user_row_actions' ], 10, 2 );
add_filter( 'login_message', [ $this, 'filter_login_message' ], 1 );
add_filter( 'removable_query_args', [ $this, 'filter_removable_query_args' ] );
add_filter( 'wp_check_post_lock_window', [ $this, 'filter_post_lock_window_after_switch_back' ] );
add_action( 'wp_meta', [ $this, 'action_wp_meta' ] );
add_filter( 'plugin_row_meta', [ $this, 'filter_plugin_row_meta' ], 10, 2 );
add_action( 'wp_footer', [ $this, 'action_wp_footer' ] );
Expand Down Expand Up @@ -975,6 +976,24 @@ public function filter_removable_query_args( array $args ): array {
] );
}

/**
* Treats the post lock as expired on the post-edit screen immediately after switching back.
*
* @param int $window The post lock window duration in seconds.
* @return int The filtered post lock window duration in seconds.
*/
public function filter_post_lock_window_after_switch_back( int $window ): int {
if ( empty( $_GET['switched_back'] ) ) {
return $window;
}

if ( ( $_GET['action'] ?? '' ) !== 'edit' ) {
return $window;
}

return 0;
}

/**
* Returns the switch to or switch back URL for a given user.
*
Expand Down